Core JSTL[c] Mastering the JSP Standard Tag Library

   

4.1 The <c:forEach> Action

You can use the <c:forEach> action in two ways: to iterate over a range of integer values or to iterate over the items contained in a data structure. To accomplish those tasks , the <c:forEach> action offers the following attributes:

  • items : The items that <c:forEach> iterates over; valid data structures for this attribute are listed in Table 4.1 on page 159. This attribute is not specified when you iterate over explicit integer values.

  • var : The name of a scoped variable that references the iteration's current item. If you iterate over explicit integer values, that scoped variable contains the current integer value; if you iterate over a data structure, it contains the current object from that data structure.

  • varStatus : The name of a scoped variable that references an object that has properties corresponding to the status of the iteration. That object's type is LoopTagStatus .

  • begin : If you iterate over explicit integer values, this attribute specifies the starting value. If you iterate over a data structure, this attribute specifies the index of the first item that's accessed in that data structure. If you specify this attribute, its value must be greater than or equal to 0.

  • end : If you iterate over explicit integer values, this attribute specifies the ending value. If you iterate over a data structure, this attribute specifies the index of the last item that's potentially accessed in that data structure. If you specify this attribute, its value must be greater than or equal to the value specified for the begin attribute.

  • step : The amount that the loop index is incremented for every round of an iteration. If you specify this attribute, its value must be greater than or equal to 1.

All of the attributes listed above, with the exception of var and varStatus , can be specified with dynamic values, either runtime expressions for the RT library or EL expressions for the EL library.

The only attributes that you use within the body of <c:forEach> are var and varStatus , which define the names of scoped variables representing the current item in the iteration and a status object, respectively. That status object is discussed in "Iteration Status" on page 171.

Now that we have a basic understanding of the <c:forEach> action, let's take a look at how you use it to iterate over integer values.

Iterating Over Integer Values

If you don't specify the <c:forEach> action's items attribute, you can specify integer values for the begin and end attributes; for example, the following code fragment iterates over the values between 5 and 10 , inclusive:

<c:forEach var='item' begin='5' end='10'> value = <c:out value='${item}'/><br> </c:forEach>

The preceding code fragment produces the following output: 5 6 7 8 9 10 . You can also specify the step attribute, which represents the increment for each round of the iteration; for example:

<c:forEach var='item' begin='5' end='10' step='2'> <c:out value='${item}'/> </c:forEach>

The output of the preceding code fragment is: 5 7 9 .

There are some restrictions on the begin , end , and step attributes:

  • The begin , end , and step attributes must all be integer values.

  • The value of the begin attribute must be greater than or equal to .

  • The value of the end attribute must be greater than the value of the begin attribute. (Backward iteration is not allowed.)

  • The value of the step attribute must be greater than .

Figure 4-2 shows a JSP page that lets you iterate over integer values by specifying the begin , end , and step attributes for the <c:forEach> action.

Figure 4-2. Iterating Over Integer Values with begin , end , and step Attributes

The top-left picture in Figure 4-2 iterates over the numbers 2 through 50 , inclusive, with a step of 3 . The top-right picture shows what happens if you specify the same value for the begin and end attributes ”<c:forEach> will iterate exactly once in that case. The bottom pictures in Figure 4-2 illustrate illegal combinations of values for the begin , end , and step attributes. The bottom-left picture shows a step attribute value of , which is illegal because the iteration would never advance. The bottom-right picture shows a begin value that's greater than the end value, which is illegal because backward iterations are not allowed.

The JSP page shown in Figure 4-2 is listed in Listing 4.2.

Listing 4.2 Iterating Over Explicit Integer Values

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Iterating Over Numeric Values</title> </head> <body> <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %> <%-- Loop over numeric values with looping attributes specified with request parameters --%> <c:catch var='exception'> Iterating over integer values: <c:forEach var='item' begin='${param.begin}' end='${param.end}' step='${param.step}'> <c:out value='${item}'/> </c:forEach> </c:catch> <%-- Because this form does not have an action, this JSP page will be reloaded when the submit button is activated --%> <form> <table> <tr> <td>Begin:</td> <td><input type='text' size='3' value='<c:out value="${param.begin}"/>' name='begin'/> </td> </tr> <tr> <td>End: <td><input type='text' size='3' value='<c:out value="${param.end}"/>' name='end'/> </td> </tr> <tr> <td>Step: <td><input type='text' size='3' value='<c:out value="${param.step}"/>' name='step'/> </td> </tr> </table> <p><input type='submit' value='Iterate Now'/> </form> <%-- Handle exceptions thrown by <c:forEach> --%> <c:if test='${not empty exception}'> <font color='red'> Iteration failed because<i> <c:out value='${exception.message}'/></i> </font> </c:if> </body> </html>

If you specify an illegal combination of begin , end , and step attributes, the <c:forEach> action will throw an exception. In the preceding JSP page, those exceptions are caught by the <c:catch> action, which stores the exception in a scoped variable. At the end of the JSP page, the code handles those exceptions by displaying an error message.

One more thing: you can only specify integer values for the begin , end , and step attributes. If you specify a floating-point value for any of those attributes, the <c:forEach> action will throw an exception, as illustrated in Figure 4-3.

Figure 4-3. The <c:forEach> Action Iterates Over Integer Values

Iterating over integer values is the easiest way to use the <c:forEach> action. You can also iterate over data structures, which is not much more difficult, but requires you to know which data structures <c:forEach> will accept. Iterating over data structures with <c:forEach> is the topic of the next section.

Iterating Over Data Structures

The <c:forEach> action can iterate over a wide range of data structures, which are listed in Table 4.1.

As you can see from Table 4.1, <c:forEach> can iterate over collections, maps, comma-separated strings, and arrays of primitive types. All of those data structures are specified with the items attribute.

Table 4.1. Data Structures That <c:forEach> Can Iterate Over

Data Type

Description

java.util.Collection

Any type of collection, including List , LinkedList , Vector , ArrayList , Stack , and Set . The current item of the iteration is the object stored in the collection; that item can be any Java type.

java.util.Map

Any type of map, including Hashtable , HashMap , TreeMap , and WeakHashMap . The current item of the iteration is an instance of Map.Entry , which has key and value properties.

java.lang.Object[]

An array of Java objects. The current item of the iteration is an object in the array; that item can be any Java type.

java.lang.String

A comma-separated string; (i.e., "one,two,three") . You can iterate over strings that have delimiters other than commas with the <c:forTokens> action, which is discussed in "The <c:forTokens> Action" on page 166.

Arrays of primitive types

Arrays that contain primitive types: boolean , byte , char , short , int , long , float , and double . The current item in the iteration is a Java object that wraps the primitive type; for example, for an array of int s, the current item in the iteration will be an instance of java.lang.Integer .

java.util.Iterator, java.util.Enumeration

Any iterator or enumeration. The current item of the iteration can be any Java object. Warning: iterators and enumerations cannot be reset, so you can only iterate over them once.

You can also specify an iterator or an enumeration with the items attribute, and <c:forEach> will use that iterator or enumeration to iterate over the underlying collection. If you use an iterator or an enumeration, you should be aware that they can only be used once because iterators and enumerations are not resettable.

The beauty of the <c:forEach> action is that it doesn't matter what kind of data structure it iterates over ”you always use <c:forEach> in the same manner. For example, consider the following code fragment, which iterates over an array of primitive types ( int s) and an array of Java objects:

Iterating Over Arrays of Primitive Types: <% int[] primitiveValues = {1,2,3,4,5,6,7,8,9,10}; pageContext.setAttribute("primitiveValues", primitiveValues); %> <c:forEach items='${primitiveValues}' var='item'> <c:out value='${item}'/> </c:forEach> Iterating Over Arrays of Objects: <% Object[] objectValues = { new Integer(1), new Integer(2), new Integer(3), new Integer(4), new Integer(5), new Integer(6), new Integer(7), new Integer(8), new Integer(9), new Integer(10) }; pageContext.setAttribute("objectValues", objectValues , PageContext.REQUEST_SCOPE ); %> <c:forEach items='${objectValues}' var='item'> <c:out value='${item}'/> </c:forEach>

In the preceding code fragment, notice that it doesn't matter whether you iterate over primitive types or Java objects; in both cases, you just specify the items and var attributes and you access the current item in the iteration in the same manner. For both uses of <c:forEach> in the code fragment, the output will be 1 2 3 4 5 6 7 8 9 10 . Also, notice that the array of int s is stored in page scope, but the array of Java objects is stored in request scope; that fact, however, is of no consequence to <c:forEach> because <c:forEach> only needs to know the name of the scoped variable and not its scope.

There is one exception to the uniform manner in which you access items in data structures with <c:forEach> ”maps are handled a little differently, for example:

Iterating Over A Map: <% java.util.Hashtable h = new java.util.Hashtable(); h.put("key1", "value1"); h.put("key2", "value2"); h.put("key3", "value3"); h.put("key4", "value4"); pageContext.setAttribute("map", h); %> <c:forEach items='${map}' var='mapItem'> <%-- Remember that items in a Hashtable don't necessarily come out in the same order they went in. --%> (<c:out value='${ mapItem.key }'/> <c:out value='${ mapItem.value }'/>) </c:forEach>

Maps get their name by virtue of the fact that they store keys that are mapped to values. One example of a map is a hashtable, like the one in the preceding code fragment. When you use <c:forEach> to iterate over a map, the current value of the iteration is an instance of Map.Entry , which has key and value properties. In the preceding code fragment those properties are used to access the keys and values stored in the hashtable. The output of the code fragment is this: Iterating Over A Map: (key4 value4) (key3 value3) (key2 value2) (key1 value1) .

The JSP page shown in Figure 4-4 uses <c:forEach> to access request headers and their values.

Figure 4-4. Iterating Over Request Headers: A Map That Contains Arrays

The JSP page shown in Figure 4-4 is listed in Listing 4.3.

Listing 4.3 Iterating Over Data Structures

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Iterating Over Data Structures</title> </head> <body> <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %> Looping Over Request Headers:<p> <%-- Loop over the JSTL headerValues implicit object, which is a map --%> < c:forEach items=' ${headerValues} ' var='hv'> <ul> <%-- Display the key of the current item; that item is a Map.Entry --%> <li><b><c:out value=' ${hv.key} '/></b><br> <ul> <%-- The value of the current item, which is accessed with the value method from Map.Entry, is an array of strings representing request header values, so we iterate over that array of strings --%> < c:forEach items=' ${hv.value} ' var='value'> <li><c:out value='${value}'/> </ c:forEach > </ul> </ul> </ c:forEach > </body> </html>

The preceding JSP page uses the <c:forEach> action twice, with one action nested inside the other. The outer <c:forEach> action iterates over the JSTL headerValues implicit object, which is a map of request headers. Each item in that map is an instance of Map.Entry , which is the case for all maps. The key property of that entry references the request header name, and the value property references an array of strings representing the request header's values. The inner <c:forEach> action iterates over that array of strings.

Using the begin , end , and step Attributes for Data Structures

If you specify the items attribute for the <c:forEach> action ”meaning you are iterating over a data structure ”the begin and end attributes have different meanings than when you iterate over integer values. When you iterate over a data structure, the begin attribute specifies the starting index in the data structure and the end attribute specifies the ending index. The step attribute specifies the amount that the index is incremented for each round of the iteration.

The JSP page shown in Figure 4-5 iterates over a vector of strings and lets you specify the begin , end , and step attributes for the iteration. The strings in the vector represent numbers from 1 to 10 .

Figure 4-5. Iterating Over a Vector with begin , end , and step Attributes

The top picture in Figure 4-5 shows the JSP page when it iterates over all of the strings contained in the vector. The begin attribute is set to , which means that the first item in the iteration will be the first item in the data structure; in this case, that's the first string ” "ONE" ”in the vector. The end attribute is set to 1000 , which is well beyond the last index in the vector. If you specify an end attribute greater than the last index in a data structure, <c:forEach> will stop iterating after the last item in that data structure.

The bottom picture in Figure 4-5 shows the JSP page when it iterates over a subset of the strings contained in the vector; the begin attribute is set to 1 and the end attribute is set to 8 . Data structure indexes are zero-based , so in this case, the begin attribute specifies the second string in the vector, and the end attribute specifies the second-last string.

The JSP page shown in Figure 4-5 is listed in Listing 4.4.

Listing 4.4 Iterating Over a Vector with begin , end , and step Attributes

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Iterating Over A Vector</title> </head> <body> <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %> <%@ page import='java.util.Vector' %> <% // Create a vector of strings Vector v= new Vector(); v.add("ONE"); v.add("TWO"); v.add("THREE"); v.add("FOUR"); v.add("FIVE"); v.add("SIX"); v.add("SEVEN"); v.add("EIGHT"); v.add("NINE"); v.add("TEN"); // Store the vector in page scope pageContext.setAttribute("vector", v); %> <%-- Loop over a vector with looping attributes specified with request parameters --%> <c:catch var='exception'> Iterating over a vector: <c:forEach items='${vector}' var='item' begin='${param.begin}' end='${param.end}' step='${param.step}'> <c:out value='${item}'/> </c:forEach> </c:catch> <%-- Because this form does not specify an action, this JSP page will be reloaded when the submit button is activated --%> <form> <table> <tr> <td>Begin:</td> <td><input type='text' size='3' value='<c:out value="${param.begin}"/>' name='begin'/> </td> <td>End:</td> <td><input type='text' size='3' value='<c:out value="${param.end}"/>' name='end'/> </td> <td>Step:</td> <td><input type='text' size='3' value='<c:out value="${param.step}"/>' name='step'/> </td> </tr> </table> <p><input type='submit' value='Iterate Now'/> </form> <%-- Handle exceptions thrown by <c:forEach> --%> <c:if test='${not empty exception}'> <font color='red'> Iteration failed because<i> <c:out value='${exception.message}'/></i> </font> </c:if> </body> </html>

Like the JSP page listed in Figure 4-2, the preceding JSP page contains a form that lets you specify the begin , end , and step attributes. Also, the preceding JSP page uses <c:forEach> to iterate over a vector of strings stored in page scope with the begin , end , and step attributes specified in the form.

   

Категории