4.2 The <c:forTokens> ActionYou can use <c:forEach> to iterate over a comma-separated string, like this: <c:forEach items='ONE, TWO, THREE, FOUR' var='item'> <c:out value='${item}'/> </c:forEach> The output of the preceding code fragment is ONE TWO THREE FOUR . But the <c:forEach> action can only iterate over strings delimited with commas. If you want to iterate over a string with delimiters other than commas, you can use the <c:forTokens> action, like this: <c:forTokens items='ONE TWO THREE FOUR' delims='' var='item'> <c:out value='${item}'/> </c:forTokens> The preceding code fragment, which produces the same output as the previous code fragment, uses <c:forTokens> with a character as a delimiter. There is no default delimiter for <c:forTokens>, so the delims attribute is mandatory. You can also specify more than one delimiter for <c:forTokens>, like this: <c:forTokens items='(A B C)-(D E F)' delims='()' var='item'> <c:out value='${item}'/><br> </c:forTokens> How many times does the <c:forTokens> action in the preceding code fragment iterate? The answer is 3 because there are three tokens between the specified delimiters. [4] The output of the preceding code fragment is:
A B C - D E F The first token consists of the characters A B C , which reside between the first pair of ( and ) delimiters. The second token is the - character, which resides between the ) and ( delimiters, and the third token is the string D E F , which resides between the second pair of ( and ) delimiters. Like <c:forEach>, <c:forTokens> has var , varStatus , begin , end , and step attributes. Unlike <c:forEach> whose begin and end attributes have different meanings depending on whether <c:forEach> iterates over integer values or a data structure, <c:forTokens> has begin and end attributes that always represent indexes into the tokens that it iterates over. For example, the following code fragment uses the <c:forTokens> begin , end , and step attributes: <c:forTokens items='(A B C)-(D E F)-(G H I)-(J K L)-(M N O)-(P Q R)' delims='()' var='item' begin='2' end='8' step='2'> <c:out value='${item}'/><br> </c:forTokens> The string that <c:forTokens> iterates over in the preceding code fragment contains 11 tokens, but that action only executes four rounds of iterations which begin with the third token and end with the ninth token because the begin and end attributes are specified as 2 and 8 , respectively. The output of the code fragment is: D E F G H I J K L M N O Using <c:forTokens> with multiple delimiters is most useful when you have nested data. For example, consider the JSP page shown in Figure 4-6, which creates an HTML table from a single string in which table headings and table data are delimited with brackets and data for each column is delimited with commas. Figure 4-6. Using <c:forTokens> to Create an HTML Table from a Single String
The JSP page shown in Figure 4-6 is listed in Listing 4.5. Listing 4.5 Iterating Over Strings with <c:forEach> and <c:forTokens><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Iterating Over Strings</title> </head> <body> <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %> Build a Table from a Single String! <% // This string, which could ultimately come from a data // source such as a flat file, could be stored in // request scope (or any other scope) by a business // component. The page author just needs to know the // name of the corresponding scoped variable and // the delimiters. // The first token delimited with the characters [] // represents table column names. The rest of the tokens // represent table data. String s = "[Name, Age, Sex, Street Address]" + "[Phyllis Keeney, 62, Female,123 Cherry Lane]" + "[George Wilson, 24, Male, 16 Rue Florence]" + "[Lynn Seckinger, 36, Female, 2096 Oak St]" + "[Roy Martin, 44, Male, 29419 112th Ave]" + "[William Bates, 96, Male, 800 Birch St]" + "[Kathy Giuseppi, 13, Female, 1245 Genesee St]"; // Store the string in request scope pageContext.setAttribute("dataForSimpleTable", s, PageContext.REQUEST_SCOPE); %> <%-- Create the HTML table with <c:forTokens> and <c:forEach> --%> <p><table border='1'> <c:forTokens var='tableData' varStatus='status' items='${dataForSimpleTable}' delims='[]'> <c:choose> <%-- If it's the first token, create the table headers --%> <c:when test='${status.first}'> <tr> <%-- Instead of <c:forEach> we could've used <c:forTokens> with commas for delimiters, as we do below --%> <c:forEach var='tableHeader' items='${tableData}'> <th><c:out value='${tableHeader}'/></th> </c:forEach> </tr> </c:when> <%-- If it's not the first token, create the table rows --%> <c:otherwise> <tr> <%-- Instead of <c:forTokens> with commas for delimiters, we could've used <c:forEach>, as we did above --%> <c:forTokens var='rowData' items='${tableData}' delims=','> <td><c:out value='${rowData}'/> </c:forTokens> </tr> </c:otherwise> </c:choose> </c:forTokens> </table> </body> </html> The preceding JSP page creates one long string in a scriptlet and stores that string in request scope. Subsequently, the JSP page iterates over that string with three iterations, like this: <p><table border='1'> < c:forTokens var='tableData' varStatus='status' items='${dataForSimpleTable}' delims='[]' > <c:choose> <%-- If it's the first token, create the table headers --%> <c:when test='${status.first}'> <tr> <%-- Instead of <c:forEach> we could've used <c:forTokens> with commas for delimiters, as we do below --%> < c:forEach var='tableHeader' items='${tableData}'> <th><c:out value='${tableHeader}'/></th> </c:forEach> </tr> </c:when> <%-- If it's not the first token, create the table rows --%> <c:otherwise> <tr> <%-- Instead of <c:forTokens> with commas for delimiters, we could've used <c:forEach>, as we did above --%> < c:forTokens var='rowData' items='${tableData}' delims=',' > <td><c:out value='${rowData}'/> </c:forTokens> </tr> </c:otherwise> </c:choose> </c:forTokens> </table> </body> </html> In the preceding JSP page, the outermost <c:forTokens> action iterates over tokens between brackets; since there are seven sets of brackets, that action will iterate seven times. Also, notice that the outermost <c:forTokens> action specifies the string status for the varStatus attribute, so a scoped variable named status ”accessible in the body of the <c:forTokens> action ”provides access to the status of the iteration. If it's the first iteration, a nested <c:forEach> action iterates over the comma-separated string that represents table headers. If it's not the first iteration, a nested <c:forTokens> action iterates over the comma-separated strings that represent the table's data. The JSP page uses both a <c:forEach> action and a <c:forTokens> action to reiterate that both of those actions can iterate over comma-separated strings. The preceding JSP page specifies the varStatus attribute of the <c:forTokens> action and uses the resulting scoped variable to detect the first iteration. That scoped variable is an object whose type is LoopTagStatus , which is discussed in the next section. |