[ Team LiB ] Previous Section Next Section

22.4 Dynamic Attribute Values and Types

Throughout this book, you've seen how action element attributes can be given dynamic values, evaluated at runtime. A dynamic attribute value can be assigned by an EL expression, a Java expression (as shown in Chapter 16), or by a <jsp:attribute> element.

Not all attributes accept dynamic values, though. To tell the container that a custom action attribute accepts a dynamic value, or a request-time attribute value as it's also called, you have to declare this fact in the TLD:

<tag>
  <name>geekContestEntry</name>
  <tag-class>com.xmp.GeekContextEntry</tag-class>
  <description>
    Saves the submitted data in the Geek Contest database.
  </description>
  
  <attribute>
    <name>yearsSinceLastVacation</name>
    <rtexprvalue>true</rtexprvalue>
  </attribute>
  <attribute>
    <name>hoursWithoutSleep</name>
    <rtexprvalue>true</rtexprvalue>
  </attribute>
  <attribute>
    <name>employersInAMonth</name>
    <rtexprvalue>true</rtexprvalue>
  </attribute>
</tag>

An <rtexprexprvalue> element with the value true enables this feature. You can then assign dynamic values to the attributes in a page like this:

<xmp:geekContestEntry
  yearsSinceLastVacation="${param.noVacation}"
  hoursWithoutSleep='<%= request.getParameter("noSleep") %>'>
  <jsp:attribute name="employersInAMonth">
     <xmp:getAvgEmployers id="${param.geekId}" />
  </jsp:attribute>
</xmp:geekContestEntry>

An EL expression assigns the value of the noVacation request parameter to the first attribute, a Java expression assigns the noSleep parameter value to the second attribute, and a <jsp:attribute> element assigns the value produced by another custom action to the third attribute.

In the tag handler, each action attribute is implemented as a property setter method that takes one argument (the value), in accordance with the JavaBeans conventions. As with regular beans, the property type for a tag handler attribute can be of any Java type, for instance an int:

public setYearsSinceLastVacation(int value) {
    yearsSinceLastVacation = value;
}

When a Java expression is used to set the value of the attribute, the return type of the expression must match the Java type declared for the property; otherwise an exception is thrown. For an EL expression, the value is converted to the attribute's type according to the rules described in Table 22-1.

Table 22-1. EL type conversion rules

Attribute Java type

Value conversion rule

String

null: to empty string ("")

All other types: to the corresponding String value

char or Character

null or empty string: to 0

String: to the first character

Numeric types: to the Short value of the number

Primitive number or Number

null: 0

Character or char: to the value represented by the character code

String: parse as an Integer or Floating point literal

Numeric types: to the requested precision

boolean or Boolean

null: to false

String: to true if the value is "true," ignoring case, otherwise false

Other type

null: keep as null

String: use the PropertyEditor for the requested type, if any, otherwise null if the string is empty

Other: type cast, if possible

When the value is set by a <jsp:attribute> element, the body is evaluated and the result is converted to a String. This value is then converted to the attribute's type according to the rules described next.

Even an attribute that accepts a dynamic value must sometimes be set to a fixed value. Let's look at an example. For an attribute of type int, you can assign a fixed value with an EL expression like this:

<xmp:geekContestEntry
  yearsSinceLastVacation="${5}"
  ...
/>

It would be much nicer, however, if the value could be entered as a regular text value and still be converted to the correct type:

<xmp:geekContestEntry
  yearsSinceLastVacation="5"
  ...
/>

That's exactly what the container does, with some help from the tag handler developer for types other than the most basic ones. This lets a page author set an action attribute declared to accept a request-time attribute value to either a static text value or a dynamic value. The next two sections describe this mechanism in detail.

22.4.1 Conversions Performed by the Container

The container automatically takes care of the conversion from text values to the most commonly used Java types. A JSP 2.0-compliant container supports the type conversions shown in Table 22-2.

Table 22-2. Conversion of text value to property type

Property type

Conversion method

boolean or Boolean

Boolean.valueOf(String), false for an empty string

byte or Byte

Byte.valueOf(String), 0 for an empty string

char or Character

String.charAt(0), 0 for an empty string

double or Double

Double.valueOf(String), 0 for an empty string

int or Integer

Integer.valueOf(String), 0 for an empty string

float or Float

Float.valueOf(String), 0 for an empty string

long or Long

Long.valueOf(String), 0 for an empty string

short or Short

Short.valueOf(String), 0 for an empty string

Object

new String(String)

These rules apply to attributes for standard actions and custom actions alike.

22.4.2 Using a PropertyEditor for Conversion

If the standard conversion rules are not enough for your needs, you can use a bean PropertyEditor to convert a literal string value to any Java data type you like. If an action attribute value is specified as a literal string for an attribute of a type other than String, the container looks for a property editor that can convert the string to the attribute's data type. The property editor is also used for EL expressions that evaluate to a String, and for <jsp:attribute> element values.

Say you have an attribute of type java.util.Date. To let the page author specify it as a text value, you need a PropertyEditor that converts a String to a Date. Here's how it's done.

First you implement the PropertyEditor:

package com.foo;
  
import java.beans.*;
import java.text.*;
import java.util.*;
public class MyDatePE extends PropertyEditorSupport 
    implements PropertyEditor {
  
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    private Date value;
  
    public Object getValue(  ) {
        return value;
    }
  
    public void setAsText(String text) 
        throws IllegalArgumentException {
  
        try {
            value = sdf.parse(text);
        }
        catch (ParseException e) {
            throw new IllegalArgumentException(e.getMessage(  ));
        }
    }
}

The container calls the setAsText( ) method with the attribute's String value. This method creates a Date object from the string and saves it in the instance variable named value. The container then calls the getValue( ) method—which returns the new Date object—and uses the value to set the action's attribute value.

Simple enough, but you must also tell the container to use your PropertyEditor for this action. You can do so by creating a BeanInfo class for the action's tag handler:

package com.foo;
  
import java.beans.*;
import java.util.*;
public class MyTagBeanInfo extends SimpleBeanInfo {
  
    public PropertyDescriptor[] getPropertyDescriptors(  ) {
        PropertyDescriptor[] pds = new PropertyDescriptor[4];
        try {
            pds[0] = new PropertyDescriptor("anInt", MyTag.class,
                null, "setAnInt");
            pds[1] = new PropertyDescriptor("aString", MyTag.class,
                null, "setAString");
            pds[2] = new PropertyDescriptor("firstDate", MyTag.class,
                null, "setFirstDate");
            pds[3] = new PropertyDescriptor("secondDate", MyTag.class,
                null, "setSecondDate");
        }
        catch (Exception e) {}
  
        pds[2].setPropertyEditorClass(MyDatePE.class);
        pds[3].setPropertyEditorClass(MyDatePE.class);
        return pds;
    }
}

This BeanInfo class is for a tag handler with four attributes, named anInt, aString, firstDate, and secondDate. The getPropertyDescriptors( ) method first creates an array with one PropertyDescriptor for each attribute and then sets the property editors for the two Date attributes to the PropertyEditor class described earlier.

A BeanInfo class is automatically bound to its bean class (in this case, the tag handler class is considered to be a bean) through a class-naming convention: the name of the BeanInfo class for a bean simply has the same name as the bean class plus "BeanInfo." So in this example, MyTagBeanInfo is the BeanInfo class for the MyTag class. The MyTag class is a regular tag handler class. You don't need to do anything special in the tag-handler class itself to use a PropertyEditor to convert string values to other types.

    [ Team LiB ] Previous Section Next Section