001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.servlet.tags.form;
018
019import javax.servlet.jsp.JspException;
020
021import org.springframework.lang.Nullable;
022import org.springframework.util.Assert;
023import org.springframework.util.StringUtils;
024
025/**
026 * The {@code <label>} tag renders a form field label in an HTML 'label' tag.
027 *
028 * <p>See the "formTags" showcase application that ships with the
029 * full Spring distribution for an example of this class in action.
030 *
031 * <p>
032 * <table>
033 * <caption>Attribute Summary</caption>
034 * <thead>
035 * <tr>
036 * <th class="colFirst">Attribute</th>
037 * <th class="colOne">Required?</th>
038 * <th class="colOne">Runtime Expression?</th>
039 * <th class="colLast">Description</th>
040 * </tr>
041 * </thead>
042 * <tbody>
043 * <tr class="altColor">
044 * <td><p>cssClass</p></td>
045 * <td><p>false</p></td>
046 * <td><p>true</p></td>
047 * <td><p>HTML Optional Attribute.</p></td>
048 * </tr>
049 * <tr class="rowColor">
050 * <td><p>cssErrorClass</p></td>
051 * <td><p>false</p></td>
052 * <td><p>true</p></td>
053 * <td><p>HTML Optional Attribute. Used only when errors are present.</p></td>
054 * </tr>
055 * <tr class="altColor">
056 * <td><p>cssStyle</p></td>
057 * <td><p>false</p></td>
058 * <td><p>true</p></td>
059 * <td><p>HTML Optional Attribute</p></td>
060 * </tr>
061 * <tr class="rowColor">
062 * <td><p>dir</p></td>
063 * <td><p>false</p></td>
064 * <td><p>true</p></td>
065 * <td><p>HTML Standard Attribute</p></td>
066 * </tr>
067 * <tr class="altColor">
068 * <td><p>for</p></td>
069 * <td><p>false</p></td>
070 * <td><p>true</p></td>
071 * <td><p>HTML Standard Attribute</p></td>
072 * </tr>
073 * <tr class="rowColor">
074 * <td><p>htmlEscape</p></td>
075 * <td><p>false</p></td>
076 * <td><p>true</p></td>
077 * <td><p>Enable/disable HTML escaping of rendered values.</p></td>
078 * </tr>
079 * <tr class="altColor">
080 * <td><p>id</p></td>
081 * <td><p>false</p></td>
082 * <td><p>true</p></td>
083 * <td><p>HTML Standard Attribute</p></td>
084 * </tr>
085 * <tr class="rowColor">
086 * <td><p>lang</p></td>
087 * <td><p>false</p></td>
088 * <td><p>true</p></td>
089 * <td><p>HTML Standard Attribute</p></td>
090 * </tr>
091 * <tr class="altColor">
092 * <td><p>onclick</p></td>
093 * <td><p>false</p></td>
094 * <td><p>true</p></td>
095 * <td><p>HTML Event Attribute</p></td>
096 * </tr>
097 * <tr class="rowColor">
098 * <td><p>ondblclick</p></td>
099 * <td><p>false</p></td>
100 * <td><p>true</p></td>
101 * <td><p>HTML Event Attribute</p></td>
102 * </tr>
103 * <tr class="altColor">
104 * <td><p>onkeydown</p></td>
105 * <td><p>false</p></td>
106 * <td><p>true</p></td>
107 * <td><p>HTML Event Attribute</p></td>
108 * </tr>
109 * <tr class="rowColor">
110 * <td><p>onkeypress</p></td>
111 * <td><p>false</p></td>
112 * <td><p>true</p></td>
113 * <td><p>HTML Event Attribute</p></td>
114 * </tr>
115 * <tr class="altColor">
116 * <td><p>onkeyup</p></td>
117 * <td><p>false</p></td>
118 * <td><p>true</p></td>
119 * <td><p>HTML Event Attribute</p></td>
120 * </tr>
121 * <tr class="rowColor">
122 * <td><p>onmousedown</p></td>
123 * <td><p>false</p></td>
124 * <td><p>true</p></td>
125 * <td><p>HTML Event Attribute</p></td>
126 * </tr>
127 * <tr class="altColor">
128 * <td><p>onmousemove</p></td>
129 * <td><p>false</p></td>
130 * <td><p>true</p></td>
131 * <td><p>HTML Event Attribute</p></td>
132 * </tr>
133 * <tr class="rowColor">
134 * <td><p>onmouseout</p></td>
135 * <td><p>false</p></td>
136 * <td><p>true</p></td>
137 * <td><p>HTML Event Attribute</p></td>
138 * </tr>
139 * <tr class="altColor">
140 * <td><p>onmouseover</p></td>
141 * <td><p>false</p></td>
142 * <td><p>true</p></td>
143 * <td><p>HTML Event Attribute</p></td>
144 * </tr>
145 * <tr class="rowColor">
146 * <td><p>onmouseup</p></td>
147 * <td><p>false</p></td>
148 * <td><p>true</p></td>
149 * <td><p>HTML Event Attribute</p></td>
150 * </tr>
151 * <tr class="altColor">
152 * <td><p>path</p></td>
153 * <td><p>true</p></td>
154 * <td><p>true</p></td>
155 * <td><p>Path to errors object for data binding</p></td>
156 * </tr>
157 * <tr class="rowColor">
158 * <td><p>tabindex</p></td>
159 * <td><p>false</p></td>
160 * <td><p>true</p></td>
161 * <td><p>HTML Standard Attribute</p></td>
162 * </tr>
163 * <tr class="altColor">
164 * <td><p>title</p></td>
165 * <td><p>false</p></td>
166 * <td><p>true</p></td>
167 * <td><p>HTML Standard Attribute</p></td>
168 * </tr>
169 * </tbody>
170 * </table>
171 *
172 * @author Rob Harrop
173 * @author Juergen Hoeller
174 * @since 2.0
175 */
176@SuppressWarnings("serial")
177public class LabelTag extends AbstractHtmlElementTag {
178
179        /**
180         * The HTML '{@code label}' tag.
181         */
182        private static final String LABEL_TAG = "label";
183
184        /**
185         * The name of the '{@code for}' attribute.
186         */
187        private static final String FOR_ATTRIBUTE = "for";
188
189
190        /**
191         * The {@link TagWriter} instance being used.
192         * <p>Stored so we can close the tag on {@link #doEndTag()}.
193         */
194        @Nullable
195        private TagWriter tagWriter;
196
197        /**
198         * The value of the '{@code for}' attribute.
199         */
200        @Nullable
201        private String forId;
202
203
204        /**
205         * Set the value of the '{@code for}' attribute.
206         * <p>Defaults to the value of {@link #getPath}; may be a runtime expression.
207         */
208        public void setFor(String forId) {
209                this.forId = forId;
210        }
211
212        /**
213         * Get the value of the '{@code id}' attribute.
214         * <p>May be a runtime expression.
215         */
216        @Nullable
217        protected String getFor() {
218                return this.forId;
219        }
220
221
222        /**
223         * Writes the opening '{@code label}' tag and forces a block tag so
224         * that body content is written correctly.
225         * @return {@link javax.servlet.jsp.tagext.Tag#EVAL_BODY_INCLUDE}
226         */
227        @Override
228        protected int writeTagContent(TagWriter tagWriter) throws JspException {
229                tagWriter.startTag(LABEL_TAG);
230                tagWriter.writeAttribute(FOR_ATTRIBUTE, resolveFor());
231                writeDefaultAttributes(tagWriter);
232                tagWriter.forceBlock();
233                this.tagWriter = tagWriter;
234                return EVAL_BODY_INCLUDE;
235        }
236
237        /**
238         * Overrides {@code #getName()} to always return {@code null},
239         * because the '{@code name}' attribute is not supported by the
240         * '{@code label}' tag.
241         * @return the value for the HTML '{@code name}' attribute
242         */
243        @Override
244        @Nullable
245        protected String getName() throws JspException {
246                // This also suppresses the 'id' attribute (which is okay for a <label/>)
247                return null;
248        }
249
250        /**
251         * Determine the '{@code for}' attribute value for this tag,
252         * autogenerating one if none specified.
253         * @see #getFor()
254         * @see #autogenerateFor()
255         */
256        protected String resolveFor() throws JspException {
257                if (StringUtils.hasText(this.forId)) {
258                        return getDisplayString(evaluate(FOR_ATTRIBUTE, this.forId));
259                }
260                else {
261                        return autogenerateFor();
262                }
263        }
264
265        /**
266         * Autogenerate the '{@code for}' attribute value for this tag.
267         * <p>The default implementation delegates to {@link #getPropertyPath()},
268         * deleting invalid characters (such as "[" or "]").
269         */
270        protected String autogenerateFor() throws JspException {
271                return StringUtils.deleteAny(getPropertyPath(), "[]");
272        }
273
274        /**
275         * Close the '{@code label}' tag.
276         */
277        @Override
278        public int doEndTag() throws JspException {
279                Assert.state(this.tagWriter != null, "No TagWriter set");
280                this.tagWriter.endTag();
281                return EVAL_PAGE;
282        }
283
284        /**
285         * Disposes of the {@link TagWriter} instance.
286         */
287        @Override
288        public void doFinally() {
289                super.doFinally();
290                this.tagWriter = null;
291        }
292
293}