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}