The problem
I'm trying to create a custom tag handler that its purpose is to loop over the given list and concatenate the items with the given delimiter. The tag's signature is: <custom:joinList list="${product.vendors}" delimiter=", " var="vendor">
. A couple of notes. The list
attribute should be a Collection.class
object. The delimiter
is always a String
and the var
is the variable that the body could have access within each loop. So the tag should always have a body to print each item and then the tag handler would append the delimiter
at the end.
For example that's that's how to call the tag from a JSP:
<custom:joinList list="${product.vendors}" delimiter=", " var="vendor">
${vendor.id} // Vendor obviously has a getId() method
</custom:joinList>
What I've tried
Firstly I created a class extending the javax.servlet.jsp.tagext.SimpleTagSupport
and in the doTag()
method I was passing the next item in the list as an attribute in the pageContext
.
Secondly I tried to extend javax.servlet.jsp.tagext.TagSupport
but then I can't figure out how to write into the out
writer after each body execution.
Code examples
The TLD that defines the tag:
<tag>
<description>Joins a Collection with the given delimiter param</description>
<name>joinList</name>
<tag-class>com.myproject.tags.JoinListTag</tag-class>
<body-content>tagdependent</body-content>
<attribute>
<description>The collection to be printed</description>
<name>list</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<description>The delimiter that is going to be used</description>
<name>delimiter</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<description>The item that will return on each loop to get a handle on each iteration</description>
<name>var</name>
<required>true</required>
</attribute>
</tag>
And here is the custom tag handler, which I guess is pretty straight forward.
public class JoinListTag extends SimpleTagSupport {
private Iterator iterator;
private String delimiter;
private String var;
public void setList(Collection list) {
if (list.size() > 0) {
this.iterator = list.iterator();
}
}
public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
}
public void setVar(String var) {
this.var = var;
}
@Override
public void doTag() throws JspException, IOException {
if (iterator == null) {
return;
}
while (iterator.hasNext()) {
getJspContext().setAttribute(var, iterator.next()); // define the variable to the body
getJspBody().invoke(null); // invoke the body
if (iterator.hasNext()) {
getJspContext().getOut().print(delimiter); // apply the delimiter
}
}
}
}
From the above I was expecting to print 1, 2, 3
if the product.vendors
List was populated like that, but instead I get ${vendor.id}, ${vendor.id}, ${vendor.id}
So eventually it was a single word change.
In my TLD I define the body-content
of the tag as tagdependent
. I had to change that to scriptless
. Apparently going through the docs once again won't hurt anybody...