2.8 Method Invocation
One of the most hotly debated topics within the JSTL expert group was whether the expression language should let you invoke arbitrary methods.
The major point of contention was whether that ability fit the philosophy of the expression language and whether it would encourage Java code in JSP pages. As you may have discerned so far and as you will learn more about as you explore JSTL actions throughout the rest of this book, the expression language and JSTL actions are implemented so that developers don't need to be concerned with types; for example, you iterate over a list, array, or comma-separated string in exactly the same fashion, without regard to their types, with the <c:forEach> action and EL expressions. If you could also invoke arbitrary methods on objects, that capability could compromise that intent and would open the door to another kind of expression language that contains EL expressions and Java statements.
The final decision for JSTL 1.0 was to disallow direct method invocation in the expression language.16 You can only indirectly invoke a strict subset of methods for certain kinds of objects by specifying JavaBeans property names or array, list, or map indexes; see "A Closer Look at the [] Operator" on page 56 for more information.
Although that decision was probably for the best, you can still run into the need for method invocation pretty quickly; for example, consider the JSP page shown in Figure 211, which accesses the first item in a list.
Figure 211 Accessing the First Item in a List
The JSP page shown in Figure 211 is listed in Listing 2.24.
Listing 2.24 Accessing the First Item in a List
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Invoking Methods</title> </head> <body> <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %> <%@ page import='java.util.LinkedList' %> <% LinkedList list = new LinkedList(); list.add("item one"); list.add("item two"); list.add("item three"); list.add("item four"); list.add("item five"); pageContext.setAttribute("list", list); %> The list starts with <b><c:out value='${list[0]}'/></b> </body> </html>
The preceding JSP page is simple: In a scriptlet, it creates a linked list and stores that list in page scope under the name list. Subsequently, the expression ${list[0]} is used to access the first item in the list, and the output is item one.
So far, so good. But what if you want to access the last item in the list? To do that, you need to know how many items are in the list so that you can specify the proper position in the list. If you look at the Java documentation for the LinkedList class, you'll see that it has a size method that returns the number of items in the list. You might try to access the last item in the list like this:
<%-- Beware! this code will throw an exception --%> The list starts with <b><c:out value='${list[0]}'/></b> and ends with <b><c:out value='${list[list.size-1]}'/></b>
As you might guess, the preceding code fragment will throw an exception like the one shown in Figure 212.
Figure 212 Trying to Access the Last Item in a List
The problem is that we are trying to invoke the list's size method (which is a valid LinkedList method), but it's not a JavaBeans-compliant getter method, so the expression list.size-1 cannot be evaluated.
There are two ways to address this dilemma. First, you can use the RT Core library, like this:
<c_rt:out value='<%= list[list.size()-1] %>'/>
Second, if you want to avoid Java code in your JSP pages, you can implement a simple wrapper class that contains a list and provides access to the list's size property with a JavaBeans-compliant getter method. That bean is listed in Listing 2.25.
Listing 2.25 WEB-INF/classes/beans/ListWrapper.java
package beans; import java.util.List; public class ListWrapper { private List list; // JavaBean accessors for first name public ListWrapper(List list) { this.list = list; } public List getList() { return list; } public int getSize() { return list.size(); } }
The preceding wrapper class has two JavaBeans properties: list and size; the former provides access to the list, and the latter provides access to the list's size. Listing 2.26 lists a JSP page that uses one of those wrappers.
Listing 2.26 Using a Wrapper to Access an Object's Properties
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Invoking Methods</title> </head> <body> <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %> <%@ page import='java.util.LinkedList' %> <%@ page import='beans.ListWrapper' %> <% LinkedList list = new LinkedList(); list.add("item one"); list.add("item two"); list.add("item three"); list.add("item four"); list.add("item five"); ListWrapper listWrapper = new ListWrapper(list); pageContext.setAttribute("listWrapper", listWrapper); %> The first item is <b><c:out value='${listWrapper.list[0]}'/></b> and the last item is <b> <c:out value='${listWrapper.list[listWrapper.size-1]}'/> </b> <p> Here are all the items in the list: <p> <ul> <c:forEach var='item' items='${listWrapper.list}'> <li><c:out value='${item}'/></li> </c:forEach> </ul> </body> </html>
Like the JSP page listed in Listing 2.24 on page 87, the preceding JSP page creates a list and populates it. But this time, the list is stored in a wrapper and the wrapper is stored in page scope. The JSP page accesses the list with the expression listWrapper.list and accesses the list's size with the expression listWrapper.size.
The JSP page listed in Listing 2.26 is shown in Figure 213.
Figure 213 Using a JavaBeans Wrapper to Access a List's Size
The JSP page shown in Figure 213 and listed in Listing 2.26 displays the first and last items in the list and iterates over all of the items in the list. See "Iteration Actions" on page 150 for more information about iterating over collections.