Introduction
Let's think about a list that contains a class object with properties int A and int B; my goal is to print the result of A + B of each object in the list. How can I achieve this without using any scriptlet or adding a property that would be used only for this scope?
The simple solution come with creating a custom tag library.
Background
Following the idea of the IValueConverter interface in WPF, I have replicated the concept using java standard tag libraries.
Using the code
My purpose is to create a custom tag that receives:
- a converter object , which manipulates the data and print the output
- the input parameter to pass to the converter
- the variable var [optional] which contains the result value
The code in the page will be something like this
<c:forEach items="${requestScope.list}" var="item">
<fstarred:converter converter="${requestScope.sumconverter}" input="${item}" />
</c:forEach>
First off, create a file called fstarred-custom-tag.tld inside the WEB-INF/tld folder.
<taglib>
<jsp-version>1.2</jsp-version>
<tlib-version>1.0</tlib-version>
<display-name>Display functions</display-name>
<description>JSTL Fabrizio Starred Custom library</description>
<short-name>starredcustom</short-name>
<uri></uri>
<tag>
<name>convert</name>
<tag-class>it.eng.inps.psr.struts.utility.TagConverter</tag-class>
<attribute>
<name>converter</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>input</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
Inside the jsp-config node of the web.xml file we declare our tld file
<taglib>
<taglib-uri>/tags/fstarred-custom</taglib-uri>
<taglib-location>/WEB-INF/tld/fstarred-custom.tld</taglib-location>
</taglib>
Now it's time to create the base object: this is the converter interface. Each specific converter object will implement this object.
public interface IConverter {
public Object convert(Object o);
}
the TagConverter extends SimpleTagSupport, and contains both the converter implementation and the object parameter; the method doTag will process the passed parameter object and will put the result in the var variable.
If var is not set, then will directly print on the page.
public class TagConverter extends SimpleTagSupport {
private IConverter converter;
private Object input;
private String var;
public void setVar(String var) {
this.var = var;
}
public void setInput(Object input) {
this.input = input;
}
public void setConverter(IConverter converter) {
this.converter = converter;
}
@Override
public void doTag() throws JspException, IOException {
Object output = converter.convert(input);
PageContext pageContext = (PageContext) getJspContext();
JspWriter out = pageContext.getOut();
if (var != null && var.length() > 0)
{
pageContext.setAttribute(var, output);
}
else
{
try
{
out.write(String.valueOf(output));
} catch (Exception e) {
e.printStackTrace();
}
}
}
...
At this point, we have created all the required stuff to create our custom tag.
The final job is to create our Converter implementation, which implements IConverter interface with the convert(Object o) method; here we put the logic code that will return the result passed to var or printed onto page.
public class SumConverter implements IConverter {
public Object convert(Object o) {
MyClass object = (MyClass)o;
return o.A + o.B;
}
}
The SumConverter object is stored into the request inside the method called from the Servlet
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
[...]
SumConverter converter = new SumConverter();
request.setAttribute("sumconverter", converter);
[...]
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher("/my_page.jsp");
dispatcher.forward(request, response);
}
Finally, we put the code in the jsp:
<%@ taglib uri="/tags/fstarred-custom" prefix="starredcustom" %>
[...].
<!-- this code cycles the list and print the result of A+B properties for each item -->
<c:forEach items="${requestScope.list}" var="item">
<starredcustom:converter converter="${requestScope.sumconverter}" input="${item}" var="addresult" />
<c:out value="${addresult}"></c:out>
</c:forEach>
Points of Interest
The strong point of this solution is the possiblity to manipulate the object without creating any properties served only for the view's scope. Moreover, this code requires only JSTL and it's not tied with any framework.