SPR-8694 HTML5 updates to the "type" attribute of the Spring Form tags.

Since dynamic attributes were allowed in Spring 3, it raised the 
possibility to specify a type attribute on a number of the form tags.
Where it makes sense (see below) that attribute is now rejected
and reversely where it makes sense it is accepted.

InputTag allows types other than "text" but rejects type="radio" or 
type="checkbox" since there is a good reason for those to be used 
only in conjunction with the appropriate form library tags.

Other HTML input tags such as PasswordTag, HiddenInputTag, 
Checkbox(es)Tag and RadioBox(es)Tag check the dynamic attributes 
and reject them if they contain a type attribute since.
This commit is contained in:
Rossen Stoyanchev
2011-11-02 21:38:50 +00:00
parent e8dd35ce5e
commit c290a4e68a
14 changed files with 193 additions and 9 deletions

View File

@@ -25,6 +25,7 @@ import javax.servlet.jsp.JspException;
*
* @author Thomas Risberg
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 2.5
*/
public abstract class AbstractCheckedElementTag extends AbstractHtmlInputElementTag {
@@ -88,6 +89,14 @@ public abstract class AbstractCheckedElementTag extends AbstractHtmlInputElement
@Override
protected abstract int writeTagContent(TagWriter tagWriter) throws JspException;
/**
* Flags "type" as an illegal dynamic attribute.
*/
@Override
protected boolean isValidDynamicAttribute(String localName, Object value) {
return !"type".equals(localName);
}
/**
* Return the type of the HTML input element to generate:
* "checkbox" or "radio".

View File

@@ -21,6 +21,7 @@ import java.util.Map;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.DynamicAttributes;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -37,6 +38,7 @@ import org.springframework.util.StringUtils;
*
* @author Rob Harrop
* @author Jeremy Grelle
* @author Rossen Stoyanchev
* @since 2.0
*/
public abstract class AbstractHtmlElementTag extends AbstractDataBoundFormElementTag implements DynamicAttributes {
@@ -396,8 +398,19 @@ public abstract class AbstractHtmlElementTag extends AbstractDataBoundFormElemen
if (this.dynamicAttributes == null) {
this.dynamicAttributes = new HashMap<String, Object>();
}
if (!isValidDynamicAttribute(localName, value)) {
throw new IllegalArgumentException(
"Attribute " + localName + "=\"" + value + "\" is not allowed");
}
dynamicAttributes.put(localName, value);
}
/**
* Whether the given name-value pair is a valid dynamic attribute.
*/
protected boolean isValidDynamicAttribute(String localName, Object value) {
return true;
}
/**
* Writes the default attributes configured via this base class to the supplied {@link TagWriter}.

View File

@@ -29,10 +29,20 @@ import javax.servlet.jsp.JspException;
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 2.0
*/
@SuppressWarnings("serial")
public class HiddenInputTag extends AbstractHtmlElementTag {
/**
* Flags "type" as an illegal dynamic attribute.
*/
@Override
protected boolean isValidDynamicAttribute(String localName, Object value) {
return !"type".equals(localName);
}
/**
* Writes the HTML '<code>input</code>' tag to the supplied {@link TagWriter} including the
* databound value.

View File

@@ -16,20 +16,18 @@
package org.springframework.web.servlet.tags.form;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import org.springframework.web.servlet.support.RequestDataValueProcessor;
/**
* Data-binding-aware JSP tag for rendering an HTML '<code>input</code>'
* element with a '<code>type</code>' of '<code>text</code>'.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 2.0
*/
@SuppressWarnings("serial")
public class InputTag extends AbstractHtmlInputElementTag {
public static final String SIZE_ATTRIBUTE = "size";
@@ -142,7 +140,9 @@ public class InputTag extends AbstractHtmlInputElementTag {
tagWriter.startTag("input");
writeDefaultAttributes(tagWriter);
tagWriter.writeAttribute("type", getType());
if (!hasDynamicTypeAttribute()) {
tagWriter.writeAttribute("type", getType());
}
writeValue(tagWriter);
// custom optional attributes
@@ -156,6 +156,10 @@ public class InputTag extends AbstractHtmlInputElementTag {
return SKIP_BODY;
}
private boolean hasDynamicTypeAttribute() {
return getDynamicAttributes() != null && getDynamicAttributes().containsKey("type");
}
/**
* Writes the '<code>value</code>' attribute to the supplied {@link TagWriter}.
* Subclasses may choose to override this implementation to control exactly
@@ -163,9 +167,24 @@ public class InputTag extends AbstractHtmlInputElementTag {
*/
protected void writeValue(TagWriter tagWriter) throws JspException {
String value = getDisplayString(getBoundValue(), getPropertyEditor());
tagWriter.writeAttribute("value", processFieldValue(getName(), value, getType()));
String type = hasDynamicTypeAttribute() ? (String) getDynamicAttributes().get("type") : getType();
tagWriter.writeAttribute("value", processFieldValue(getName(), value, type));
}
/**
* Flags {@code type="checkbox"} and {@code type="radio"} as illegal
* dynamic attributes.
*/
@Override
protected boolean isValidDynamicAttribute(String localName, Object value) {
if ("type".equals(localName)) {
if ("checkbox".equals(value) || "radio".equals(value)) {
return false;
}
}
return true;
}
/**
* Get the value of the '<code>type</code>' attribute. Subclasses
* can override this to change the type of '<code>input</code>' element

View File

@@ -24,6 +24,7 @@ import javax.servlet.jsp.JspException;
*
* @author Rob Harrop
* @author Rick Evans
* @author Rossen Stoyanchev
* @since 2.0
*/
public class PasswordInputTag extends InputTag {
@@ -47,6 +48,14 @@ public class PasswordInputTag extends InputTag {
this.showPassword = showPassword;
}
/**
* Flags "type" as an illegal dynamic attribute.
*/
@Override
protected boolean isValidDynamicAttribute(String localName, Object value) {
return !"type".equals(localName);
}
/**
* Return '<code>password</code>' causing the rendered HTML '<code>input</code>'
* element to have a '<code>type</code>' of '<code>password</code>'.