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 java.util.HashMap;
020import java.util.Map;
021import javax.servlet.jsp.JspException;
022import javax.servlet.jsp.tagext.DynamicAttributes;
023
024import org.springframework.util.CollectionUtils;
025import org.springframework.util.ObjectUtils;
026import org.springframework.util.StringUtils;
027
028/**
029 * Base class for databinding-aware JSP tags that render HTML element. Provides
030 * a set of properties corresponding to the set of HTML attributes that are common
031 * across elements.
032 *
033 * <p>Additionally, this base class allows for rendering non-standard attributes
034 * as part of the tag's output.  These attributes are accessible to subclasses if
035 * needed via the {@link AbstractHtmlElementTag#getDynamicAttributes() dynamicAttributes}
036 * map.
037 *
038 * @author Rob Harrop
039 * @author Jeremy Grelle
040 * @author Rossen Stoyanchev
041 * @since 2.0
042 */
043@SuppressWarnings("serial")
044public abstract class AbstractHtmlElementTag extends AbstractDataBoundFormElementTag implements DynamicAttributes {
045
046        public static final String CLASS_ATTRIBUTE = "class";
047
048        public static final String STYLE_ATTRIBUTE = "style";
049
050        public static final String LANG_ATTRIBUTE = "lang";
051
052        public static final String TITLE_ATTRIBUTE = "title";
053
054        public static final String DIR_ATTRIBUTE = "dir";
055
056        public static final String TABINDEX_ATTRIBUTE = "tabindex";
057
058        public static final String ONCLICK_ATTRIBUTE = "onclick";
059
060        public static final String ONDBLCLICK_ATTRIBUTE = "ondblclick";
061
062        public static final String ONMOUSEDOWN_ATTRIBUTE = "onmousedown";
063
064        public static final String ONMOUSEUP_ATTRIBUTE = "onmouseup";
065
066        public static final String ONMOUSEOVER_ATTRIBUTE = "onmouseover";
067
068        public static final String ONMOUSEMOVE_ATTRIBUTE = "onmousemove";
069
070        public static final String ONMOUSEOUT_ATTRIBUTE = "onmouseout";
071
072        public static final String ONKEYPRESS_ATTRIBUTE = "onkeypress";
073
074        public static final String ONKEYUP_ATTRIBUTE = "onkeyup";
075
076        public static final String ONKEYDOWN_ATTRIBUTE = "onkeydown";
077
078
079        private String cssClass;
080
081        private String cssErrorClass;
082
083        private String cssStyle;
084
085        private String lang;
086
087        private String title;
088
089        private String dir;
090
091        private String tabindex;
092
093        private String onclick;
094
095        private String ondblclick;
096
097        private String onmousedown;
098
099        private String onmouseup;
100
101        private String onmouseover;
102
103        private String onmousemove;
104
105        private String onmouseout;
106
107        private String onkeypress;
108
109        private String onkeyup;
110
111        private String onkeydown;
112
113        private Map<String, Object> dynamicAttributes;
114
115
116        /**
117         * Set the value of the '{@code class}' attribute.
118         * May be a runtime expression.
119         */
120        public void setCssClass(String cssClass) {
121                this.cssClass = cssClass;
122        }
123
124        /**
125         * Get the value of the '{@code class}' attribute.
126         * May be a runtime expression.
127         */
128        protected String getCssClass() {
129                return this.cssClass;
130        }
131
132        /**
133         * The CSS class to use when the field bound to a particular tag has errors.
134         * May be a runtime expression.
135         */
136        public void setCssErrorClass(String cssErrorClass) {
137                this.cssErrorClass = cssErrorClass;
138        }
139
140        /**
141         * The CSS class to use when the field bound to a particular tag has errors.
142         * May be a runtime expression.
143         */
144        protected String getCssErrorClass() {
145                return this.cssErrorClass;
146        }
147
148        /**
149         * Set the value of the '{@code style}' attribute.
150         * May be a runtime expression.
151         */
152        public void setCssStyle(String cssStyle) {
153                this.cssStyle = cssStyle;
154        }
155
156        /**
157         * Get the value of the '{@code style}' attribute.
158         * May be a runtime expression.
159         */
160        protected String getCssStyle() {
161                return this.cssStyle;
162        }
163
164        /**
165         * Set the value of the '{@code lang}' attribute.
166         * May be a runtime expression.
167         */
168        public void setLang(String lang) {
169                this.lang = lang;
170        }
171
172        /**
173         * Get the value of the '{@code lang}' attribute.
174         * May be a runtime expression.
175         */
176        protected String getLang() {
177                return this.lang;
178        }
179
180        /**
181         * Set the value of the '{@code title}' attribute.
182         * May be a runtime expression.
183         */
184        public void setTitle(String title) {
185                this.title = title;
186        }
187
188        /**
189         * Get the value of the '{@code title}' attribute.
190         * May be a runtime expression.
191         */
192        protected String getTitle() {
193                return this.title;
194        }
195
196        /**
197         * Set the value of the '{@code dir}' attribute.
198         * May be a runtime expression.
199         */
200        public void setDir(String dir) {
201                this.dir = dir;
202        }
203
204        /**
205         * Get the value of the '{@code dir}' attribute.
206         * May be a runtime expression.
207         */
208        protected String getDir() {
209                return this.dir;
210        }
211
212        /**
213         * Set the value of the '{@code tabindex}' attribute.
214         * May be a runtime expression.
215         */
216        public void setTabindex(String tabindex) {
217                this.tabindex = tabindex;
218        }
219
220        /**
221         * Get the value of the '{@code tabindex}' attribute.
222         * May be a runtime expression.
223         */
224        protected String getTabindex() {
225                return this.tabindex;
226        }
227
228        /**
229         * Set the value of the '{@code onclick}' attribute.
230         * May be a runtime expression.
231         */
232        public void setOnclick(String onclick) {
233                this.onclick = onclick;
234        }
235
236        /**
237         * Get the value of the '{@code onclick}' attribute.
238         * May be a runtime expression.
239         */
240        protected String getOnclick() {
241                return this.onclick;
242        }
243
244        /**
245         * Set the value of the '{@code ondblclick}' attribute.
246         * May be a runtime expression.
247         */
248        public void setOndblclick(String ondblclick) {
249                this.ondblclick = ondblclick;
250        }
251
252        /**
253         * Get the value of the '{@code ondblclick}' attribute.
254         * May be a runtime expression.
255         */
256        protected String getOndblclick() {
257                return this.ondblclick;
258        }
259
260        /**
261         * Set the value of the '{@code onmousedown}' attribute.
262         * May be a runtime expression.
263         */
264        public void setOnmousedown(String onmousedown) {
265                this.onmousedown = onmousedown;
266        }
267
268        /**
269         * Get the value of the '{@code onmousedown}' attribute.
270         * May be a runtime expression.
271         */
272        protected String getOnmousedown() {
273                return this.onmousedown;
274        }
275
276        /**
277         * Set the value of the '{@code onmouseup}' attribute.
278         * May be a runtime expression.
279         */
280        public void setOnmouseup(String onmouseup) {
281                this.onmouseup = onmouseup;
282        }
283
284        /**
285         * Get the value of the '{@code onmouseup}' attribute.
286         * May be a runtime expression.
287         */
288        protected String getOnmouseup() {
289                return this.onmouseup;
290        }
291
292        /**
293         * Set the value of the '{@code onmouseover}' attribute.
294         * May be a runtime expression.
295         */
296        public void setOnmouseover(String onmouseover) {
297                this.onmouseover = onmouseover;
298        }
299
300        /**
301         * Get the value of the '{@code onmouseover}' attribute.
302         * May be a runtime expression.
303         */
304        protected String getOnmouseover() {
305                return this.onmouseover;
306        }
307
308        /**
309         * Set the value of the '{@code onmousemove}' attribute.
310         * May be a runtime expression.
311         */
312        public void setOnmousemove(String onmousemove) {
313                this.onmousemove = onmousemove;
314        }
315
316        /**
317         * Get the value of the '{@code onmousemove}' attribute.
318         * May be a runtime expression.
319         */
320        protected String getOnmousemove() {
321                return this.onmousemove;
322        }
323
324        /**
325         * Set the value of the '{@code onmouseout}' attribute.
326         * May be a runtime expression.
327         */
328        public void setOnmouseout(String onmouseout) {
329                this.onmouseout = onmouseout;
330        }
331        /**
332         * Get the value of the '{@code onmouseout}' attribute.
333         * May be a runtime expression.
334         */
335        protected String getOnmouseout() {
336                return this.onmouseout;
337        }
338
339        /**
340         * Set the value of the '{@code onkeypress}' attribute.
341         * May be a runtime expression.
342         */
343        public void setOnkeypress(String onkeypress) {
344                this.onkeypress = onkeypress;
345        }
346
347        /**
348         * Get the value of the '{@code onkeypress}' attribute.
349         * May be a runtime expression.
350         */
351        protected String getOnkeypress() {
352                return this.onkeypress;
353        }
354
355        /**
356         * Set the value of the '{@code onkeyup}' attribute.
357         * May be a runtime expression.
358         */
359        public void setOnkeyup(String onkeyup) {
360                this.onkeyup = onkeyup;
361        }
362
363        /**
364         * Get the value of the '{@code onkeyup}' attribute.
365         * May be a runtime expression.
366         */
367        protected String getOnkeyup() {
368                return this.onkeyup;
369        }
370
371        /**
372         * Set the value of the '{@code onkeydown}' attribute.
373         * May be a runtime expression.
374         */
375        public void setOnkeydown(String onkeydown) {
376                this.onkeydown = onkeydown;
377        }
378
379        /**
380         * Get the value of the '{@code onkeydown}' attribute.
381         * May be a runtime expression.
382         */
383        protected String getOnkeydown() {
384                return this.onkeydown;
385        }
386
387        /**
388         * Get the map of dynamic attributes.
389         */
390        protected Map<String, Object> getDynamicAttributes() {
391                return this.dynamicAttributes;
392        }
393
394        /**
395         * {@inheritDoc}
396         */
397        @Override
398        public void setDynamicAttribute(String uri, String localName, Object value ) throws JspException {
399                if (this.dynamicAttributes == null) {
400                        this.dynamicAttributes = new HashMap<String, Object>();
401                }
402                if (!isValidDynamicAttribute(localName, value)) {
403                        throw new IllegalArgumentException(
404                                        "Attribute " + localName + "=\"" + value + "\" is not allowed");
405                }
406                this.dynamicAttributes.put(localName, value);
407        }
408
409        /**
410         * Whether the given name-value pair is a valid dynamic attribute.
411         */
412        protected boolean isValidDynamicAttribute(String localName, Object value) {
413                return true;
414        }
415
416        /**
417         * Writes the default attributes configured via this base class to the supplied {@link TagWriter}.
418         * Subclasses should call this when they want the base attribute set to be written to the output.
419         */
420        @Override
421        protected void writeDefaultAttributes(TagWriter tagWriter) throws JspException {
422                super.writeDefaultAttributes(tagWriter);
423                writeOptionalAttributes(tagWriter);
424        }
425
426        /**
427         * Writes the optional attributes configured via this base class to the supplied {@link TagWriter}.
428         * The set of optional attributes that will be rendered includes any non-standard dynamic attributes.
429         * Called by {@link #writeDefaultAttributes(TagWriter)}.
430         */
431        protected void writeOptionalAttributes(TagWriter tagWriter) throws JspException {
432                tagWriter.writeOptionalAttributeValue(CLASS_ATTRIBUTE, resolveCssClass());
433                tagWriter.writeOptionalAttributeValue(STYLE_ATTRIBUTE,
434                                ObjectUtils.getDisplayString(evaluate("cssStyle", getCssStyle())));
435                writeOptionalAttribute(tagWriter, LANG_ATTRIBUTE, getLang());
436                writeOptionalAttribute(tagWriter, TITLE_ATTRIBUTE, getTitle());
437                writeOptionalAttribute(tagWriter, DIR_ATTRIBUTE, getDir());
438                writeOptionalAttribute(tagWriter, TABINDEX_ATTRIBUTE, getTabindex());
439                writeOptionalAttribute(tagWriter, ONCLICK_ATTRIBUTE, getOnclick());
440                writeOptionalAttribute(tagWriter, ONDBLCLICK_ATTRIBUTE, getOndblclick());
441                writeOptionalAttribute(tagWriter, ONMOUSEDOWN_ATTRIBUTE, getOnmousedown());
442                writeOptionalAttribute(tagWriter, ONMOUSEUP_ATTRIBUTE, getOnmouseup());
443                writeOptionalAttribute(tagWriter, ONMOUSEOVER_ATTRIBUTE, getOnmouseover());
444                writeOptionalAttribute(tagWriter, ONMOUSEMOVE_ATTRIBUTE, getOnmousemove());
445                writeOptionalAttribute(tagWriter, ONMOUSEOUT_ATTRIBUTE, getOnmouseout());
446                writeOptionalAttribute(tagWriter, ONKEYPRESS_ATTRIBUTE, getOnkeypress());
447                writeOptionalAttribute(tagWriter, ONKEYUP_ATTRIBUTE, getOnkeyup());
448                writeOptionalAttribute(tagWriter, ONKEYDOWN_ATTRIBUTE, getOnkeydown());
449
450                if (!CollectionUtils.isEmpty(this.dynamicAttributes)) {
451                        for (String attr : this.dynamicAttributes.keySet()) {
452                                tagWriter.writeOptionalAttributeValue(attr, getDisplayString(this.dynamicAttributes.get(attr)));
453                        }
454                }
455        }
456
457        /**
458         * Gets the appropriate CSS class to use based on the state of the current
459         * {@link org.springframework.web.servlet.support.BindStatus} object.
460         */
461        protected String resolveCssClass() throws JspException {
462                if (getBindStatus().isError() && StringUtils.hasText(getCssErrorClass())) {
463                        return ObjectUtils.getDisplayString(evaluate("cssErrorClass", getCssErrorClass()));
464                }
465                else {
466                        return ObjectUtils.getDisplayString(evaluate("cssClass", getCssClass()));
467                }
468        }
469
470}