Foundations of JSP design patterns: The View Helper pattern

Learn how the View Helper pattern helps you adapt model data to an application's presentation layer

1 2 3 Page 2
Page 2 of 3
 package jspbook.ch08;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.BodyContent;
import java.io.IOException;
import java.util.Locale;
import java.util.Calendar;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.NumberFormat;
import java.text.DecimalFormat;
public class FormatTag extends BodyTagSupport {
   /* Locale object for internationalization of content */
   private Locale locale;
   /* Tag Attributes */
   protected String format;
   /* Static Constants */
   private final static String DATE_LONG = "date";
   private final static String NUMERIC_DECIMAL = "decimal";
   private final static String NUMERIC_ROUNDED = "rounded";
   private final static String NUMERIC_CURRENCY = "currency";
   public FormatTag() {
      locale = Locale.getDefault();
   }
   public void setLocale(Locale locale) {
      this.locale = locale;
   }
   /* Process Tag Body */
   public int doAfterBody() throws JspTagException {
      try {
         BodyContent body = getBodyContent();
         JspWriter out = body.getEnclosingWriter();
         /* Get Input Value */
         String textValue = body.getString().trim();
         /* Output Formatted Value */
         out.println(formatValue(textValue));
      }
      catch (IOException e) {
         throw new JspTagException(e.toString());
      }
      return SKIP_BODY;
   }
   /* Process End Tag */
   public int doEndTag() throws JspTagException {
      return EVAL_PAGE;
   }
   private String formatValue (String _input)
   {
      String formattedValue = "";
      try {
         if(format.equals(DATE_LONG)) {
            Calendar cal = Calendar.getInstance();
            cal.setTime(DateFormat.getDateInstance(
               DateFormat.SHORT).parse(_input));
            SimpleDateFormat df = new SimpleDateFormat("EEE, MMM d, yyyy");
            formattedValue = df.format(cal.getTime());
         } else if(format.equals(NUMERIC_DECIMAL)) {
            DecimalFormat dcf = (DecimalFormat) NumberFormat.getInstance(locale);
            dcf.setMinimumFractionDigits(2);
            dcf.setMaximumFractionDigits(2);
            formattedValue = dcf.format(dcf.parse(_input));
         } else if(format.equals(NUMERIC_ROUNDED)) {
            DecimalFormat dcf = (DecimalFormat) NumberFormat.getInstance(locale);
            dcf.setMinimumFractionDigits(0);
            dcf.setMaximumFractionDigits(0);
            formattedValue = dcf.format(dcf.parse(_input));
         } else if(format.equals(NUMERIC_CURRENCY)) {
            float num = Float.parseFloat(_input);
            DecimalFormat dcf = (DecimalFormat)
            NumberFormat.getCurrencyInstance();
            formattedValue = dcf.format(num);
         }
      }
      catch (Exception e) {
         System.out.println(e.toString());
      }
      return formattedValue;
   }
   /* Attribute Accessor Methods */
   public String getFormat ()
   {
      return this.format;
   }
   public void setFormat (String _format)
   {
      this.format = _format;
   }
}

Finally, you have your JSP page. There's really nothing new here. The page declares a JavaBean to use as the model, sets values in the model, and displays those values in various formats. The formatting takes place through the FormatTag, specified in the helpers.tld file and declared in the JSP page using the taglib directive. Notice how you specify the format in an attribute of the custom tag. The format attribute should be set to a value corresponding to one of the static constants defined in the tag handler. Listing 5 shows the code for the JSP page.

Listing 5. formatHelper.jsp

 <%-- Declare tag that we'll use as our helper --%>
<%@ taglib uri="/helpers" prefix="helpers" %>
<html>
   <head>
      <title>Text Formatting Example</title>
   </head>
   <body>
      <font face="Arial"/>
      <%-- Declare bean that will act as our model --%>
      <jsp:useBean id="myBean" class="jspbook.ch08.beans.FormattingModel"/>
      <jsp:setProperty name="myBean" property="dateValue" value="12/01/01"/>
      <jsp:setProperty name="myBean" property="currencyValue" value="23500.253"/>
      <%-- Display Formatted Values (using helper) --%>
      <center>
         <h1>Various Date and Currency Formats</h1>
         <br/><br/>
         <table cellpadding="5">
            <tr>
               <th>Format</th>
               <th>Original Value</th>
               <th>Formatted Value</th>
            </tr>
            <tr>
               <td>Long Date</td>
               <td>
                  <jsp:getProperty name="myBean" property="dateValue"/>
               </td>
               <td>
                  <helpers:FormatTag format="date">
                     <jsp:getProperty name="myBean" property="dateValue"/>
                  </helpers:FormatTag>
                </td>
            </tr>
            <tr>
               <td>Decimal (NN.NN)</td>
               <td>${myBean.currencyValue}</td>
               <td>
                  <helpers:FormatTag format="decimal">
                     ${myBean.currencyValue}
                  </helpers:FormatTag>
               </td>
            </tr>
            <tr>
               <td>Integer (N,NNN)</td>
               <td>${myBean.currencyValue}</td>
               <td>
                  <helpers:FormatTag format="rounded">
                     ${myBean.currencyValue}
                  </helpers:FormatTag>
               </td>
            </tr>
            <tr>
               <td>Currency (N,NNN.NN)</td>
               <td>${myBean.currencyValue}</td>
               <td>
                  <helpers:FormatTag format="currency">
                     ${myBean.currencyValue}
                  </helpers:FormatTag>
               </td>
            </tr>
         </table>
      </center>
   </body>
</html>

Figure 4 shows the results of formatting a date value and a numeric value using each of the different formats provided by the tag handler.

Figure 4. Format text helper example. Click on thumbnail to view full-sized image.

Creating menus

It can be an advantage in some cases to dynamically generate menus or collections of hyperlinks. The controller would execute the appropriate action, which in turn would generate a list of link items. The view, using a helper, would generate a formatted menu containing the links stored in the model.

As an example, the view will be a JSP page that will display a menu screen listing accessory items for a specific product. This is a good example of why you may need to dynamically generate menu screens. Users presumably click a specific product in the company catalog and expect to see a list of accessories from which they can choose. This application pulls the necessary product information from the database and produces a set of product accessories to be displayed as hyperlinks within the view.

For this example, you'll skip the link-generation part and simply provide a static model with which to work. The model will be a JavaBean containing a hashtable of link items. The hashtable will be keyed by the text to display, storing the links as values. You'll retrieve the links as a comma-delimited list and insert it inside a custom tag. Listing 6 shows what the static model looks like.

Listing 6. MenuModel.java

 package jspbook.ch08.beans;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Enumeration;
public class MenuModel implements Serializable {
   Hashtable links = new Hashtable();
   String list = "";
   public MenuModel()
   {
      /* Initialize model with sample values */
      links.put("Fold-away Keyboard", "/Controller?action=display&item=101");
      links.put("Standard Leather Case", "/Controller?action=display&item=102");
      links.put("Deluxe 3-Pocket Case", "/Controller?action=display&item=103");
      links.put("Travel Cable", "/Controller?action=display&item=104");
      links.put("Stylus Pack", "/Controller?action=display&item=105");
      links.put("8MB Backup Module", "/Controller?action=display&item=106");
   }
   /* Accessor Methods */
   public void setList (String _list)
   {
      this.list = _list;
   }
   public String getList ()
   {
      StringBuffer csvList = new StringBuffer();
      /* Transform hashtable into comma-separated list */
      Enumeration enum = links.keys();
      while (enum.hasMoreElements()) {
         String linkName = (String) enum.nextElement();
         String linkURL = (String) links.get(linkName);
         csvList.append(linkName).append(",").append(linkURL).append("\n");
      }
      return csvList.toString();
   }
}

The helper you'll use is a custom tag that extends the BodyTagSupport class so that it can process the body content stored within its start and end tags. This custom tag needs to read in the list of link items and output a list of hyperlinks. It does this in the doAfterBody() method, looping through each line of the body content and parsing out the link name and the link URL. Note that if there's white space after the body content that's returned from the model, as there may well be if the page is formatted, then an extra line of spaces will be read in. You need to check for this. Listing 7 shows what the code looks like for your helper class.

Listing 7. MenuTag.java

 package jspbook.ch08;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.BodyContent;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.StringTokenizer;
public class MenuTag extends BodyTagSupport {
   /* Tag Attributes */
   protected String links;
   /* Process Tag Body */
   public int doAfterBody() throws JspTagException {
      try {
         BodyContent body = getBodyContent();
         JspWriter out = body.getEnclosingWriter();
         /* Parse records and output as list of hyperlinks */
         BufferedReader contentReader = new BufferedReader(body.getReader());
         String record = "";
         while ((record = contentReader.readLine()) != null) {
            StringTokenizer st = new StringTokenizer(record, ",");
            while (st.hasMoreTokens()) {
               String linkName = st.nextToken();
               if(!linkName.trim().equals("")){
                  String linkURL = st.nextToken();
                  out.println("<a href='" + linkURL + "'>");
                  out.println(linkName + "</a>");
                  out.println("<br/><br/>");
               }
            }
         }
      }
      catch (IOException e) {
         throw new JspTagException(e.toString());
      }
      return SKIP_BODY;
   }
   /* Process End Tag */
   public int doEndTag() throws JspTagException {
      return EVAL_PAGE;
   }
   /* Attribute Accessor Methods */
   public String getLinks ()
   {
      return this.links;
   }
   public void setLinks (String _links)
   {
      this.links = _links;
   }
}

Using this tag in a JSP page is fairly simple, as intended. At the point you'd like to display the list of hyperlinks, you insert a custom tag to do the job for you. Inside the tag's body, you place a small snippet of EL code to retrieve the comma-separated list of link items. Because you're executing code inside the tag, you must declare the tag in your tag library descriptor file with the <bodycontent> tag containing the value jsp. Here's the full tag descriptor from the helpers.tld file and the code for the JSP page:

 <tag>
   <name>MenuTag</name>
   <tag-class>jspbook.ch08.MenuTag</tag-class>
   <body-content>jsp</body-content>
</tag>

Listing 8 shows the menuHelper.jsp page. The menu items are retrieved inside the MenuTag using the getList() method of the MenuModel JavaBean. The tag handler parses these items and outputs them as a list of hyperlinks (see Figure 5 for the results).

Listing 8. menuHelper.jsp

 <%-- Declare tag that we'll use as our helper --%>
<%@ taglib uri="/helpers" prefix="helpers" %>
<html>
   <head>
      <title>Product Accessories</title>
   </head>
   <body>
      <font face="Arial"/>
      <%-- Declare bean that will act as our model --%>
      <jsp:useBean id="myBean" class="jspbook.ch08.beans.MenuModel"/>
      <%-- Display Product Accessory Links (using helper) --%>
      <center>
         <b>Product Accessories for: Deluxe PDA</b>
         <br/><br/>
         <helpers:MenuTag>
            ${myBean.list}
         </helpers:MenuTag>
      </center>
   </body>
</html>
Figure 5. Menu helper example. Click on thumbnail to view full-sized image.

Creating custom list formats

A common element of most Web applications is a grouping of several related items displayed as a bulleted list. The standard HTML list element, using the <ul> tags, displays list items using a standard bullet. Using an appropriate helper to display the list enables you to customize the way the list is displayed. In this example, you'll build a helper that adapts a list of items to a formatted list with a selection of three different list styles. Here's the descriptor for the tag:

 <tag>
   <name>ListTag</name>
   <tag-class>jspbook.ch08.ListTag</tag-class>
   <body-content>jsp</body-content>
   <attribute>
      <name>format</name>
      <required>yes</required>
      <rtexprvalue>true</rtexprvalue>
   </attribute>
</tag>

The code for the tag defines the format attribute as in the FormatTag and processes its body content in much the same way as the MenuTag does. It's important to point out that a list can be formatted in many ways. In this example, you're simply double-spacing the list items and providing a choice of bullets. Listing 9 shows the code for the tag.

Listing 9. ListTag.java

1 2 3 Page 2
Page 2 of 3