DynamicAttributes in Simple Tag

We wrote a Simple Tag that generates an HTML select-option structure that works fine but it is very limited: we can't use attributes other than the ones specified in its declaration.

It would be a code nightmare to implement a full fledged solution for all the attributes that could be used in a HTML select, fortunately there is another chance: using dynamic attributes.

We modify the tag class that now will implement the DynamicAttributes interface, that defines a method, setDynamicAttribute(), that let us manage any other attribute passed to the tag. We store them in a private map, and use when we create the HTML select tag.

Here is the code for the modified class:
package ch10;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class SelectTagEx extends SimpleTagSupport
implements DynamicAttributes {
    private List<String> options;
    private String name;

    /** stores the dynamic attributes */
    private Map<String,Object> tagAttrs = new HashMap<String,Object>();

    public void setOptions(List<String> options) {
        this.options = options;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void setDynamicAttribute(String uri, String name, Object value) // 1
    throws JspException {
        tagAttrs.put(name, value);
    }

    @Override
    public void doTag() throws JspException, IOException {
        JspWriter out = getJspContext().getOut();

        // generate the HTML select open tag
        // 'name' is a mandatory attribute
        out.print("<select name=" + this.name + ' ');
        for(String attrName : tagAttrs.keySet()) { // 2
            String attrDefinition =
                String.format("%s='%s' ", attrName, tagAttrs.get(attrName));
            out.print(attrDefinition);
        }
        out.print('>');

        // generate the required HTML option tags
        for(String option : options) {
            out.print("<option value='" + option + "'>" + option + "</option>");
        }

        // generate the HTML select close tag
        out.print(" </select>");
    }
}
1. We use the setDynamicAttribute() method for storing the attribute name and value in the member map - there is no use here for the first (quite obscure) parameter.
2. We loop on all the elements in the member map, and put them as attributes in HTML select tag.

We have to change accordingly the tag declaration in the TLD file, specifying that it accepts dynamic attributes:
<tag>
    <name>select</name>
    <tag-class>ch10.SelectTagEx</tag-class>
    <body-content>empty</body-content>
    <description>Select-option tag</description>
    <attribute>
        <name>options</name>
        <type>java.util.List</type>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
    <attribute>
        <name>name</name>
        <required>true</required>
    </attribute>
    <dynamic-attributes>true</dynamic-attributes>
</tag>
Now we can put in our tag even attributes that are not explicitely declared in our Simple tag. The nuisance here is that there is no check on the attribute names, so we could enter in the list whatever we like, and our tag is going to accept it without complaining:
<st:select name='color' blabber='blah' options='${applicationScope.colors}' />
Simple tag handlers are discussed in chapter ten of Head First Servlet and JSP.

No comments:

Post a Comment