FileDocCategorySizeDatePackage
LoopTag.javaAPI DocExample7858Thu Jun 28 16:14:16 BST 2001com.ora.jsp.tags.generic

LoopTag.java

package com.ora.jsp.tags.generic;

import java.beans.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

/**
 * This class is a custom action for looping through the elements
 * of a multi-valued bean or bean property. The bean or bean property
 * must be an array, a Vector, a Dictionary or an Enumeration. The 
 * action body is evaluated once for each element.
 *
 * @author Hans Bergsten, Gefion software <hans@gefionsoftware.com>
 * @version 1.0
 */
public class LoopTag extends BodyTagSupport {
    // Property variables
    private String name;
    private String property;
    private String loopId;
    private String className;
    
    // Enumeration used for iteration
    private Enumeration enum;

    /**
     * Sets the name attribute, i.e. the name of the variable holding
     * a reference to the bean with the multi-value property to loop
     * through.
     *
     * @param name the bean variable name
     */
    public void setName(String name) {
        this.name = name;
    }
    
    /**
     * Sets the property attribute, i.e. the name of the multi-value 
     * property to loop through.
     *
     * @param property the property name
     */
    public void setProperty(String property) {
        this.property = property;
    }

    /**
     * Sets the loopId attribute, i.e. the name of the variable to
     * hold the element reference in the body.
     *
     * @param property the property name
     */
    public void setLoopId(String loopId) {
        this.loopId = loopId;
    }

    /**
     * Sets the class attribute, i.e. the name of the class for the 
     * multi-value property elements.
     *
     * @param class the element class name
     */
    public void setClassName(String className) {
        this.className = className;
    }
    
    /**
     * Creates an Enumeration of all loop values, either using the object
     * specified by the name attribute directly, or using the specified
     * property. The object or the property must be one of Enumeration,
     * Vector, Dictionary or Object[] (no primitive type arrays), or a
     * subclass/subinterface of one of them.
     *
     * Makes the first element available to the body in a variable
     * with the name specified by the loopId attribute.
     */
    public int doStartTag() throws JspException {
        Object obj = pageContext.findAttribute(name);
        if (obj == null) {
            throw new JspException("Variable " + name + " not found");
        }
        
        Object mvObj = obj;
        try {
            // Get the multi-value object using the specified property getter 
            // method, if any
            if (property != null) {
                mvObj = getProperty(obj, property);
            }

            enum = getEnumeration(mvObj);
        }
        catch (JspException e) {
            throw new JspException("Error getting loop data from " + name + ": " +
                e.getMessage());
        }
        
        // Set the first loop value, if any
        if (enum != null && enum.hasMoreElements()) {
            Object currValue = enum.nextElement();
            pageContext.setAttribute(loopId, currValue);
        	return EVAL_BODY_TAG;
        }
        else {
        	return SKIP_BODY;
        }
    }

    /**
     * Makes the next element available to the body in a variable
     * with the name specified by the loopId attribute, or returns
     * SKIP_BODY if all elements have been processed.
     */
    public int doAfterBody() throws JspException {
        if (enum.hasMoreElements()) {
            Object currValue = enum.nextElement();
            pageContext.setAttribute(loopId, currValue);
        	return EVAL_BODY_TAG;
        }
        else {
        	return SKIP_BODY;
        }
    }
    
    /**
     * Writes the accumulated body contents to the JspWriter.
     */
    public int doEndTag() throws JspException {
        // Test if bodyContent is set, since it will be null if the
        // body was never evaluated (doStartTag returned SKIP_BODY)
        if (getBodyContent() != null) {
            try {
                getPreviousOut().print(getBodyContent().getString());
            }
            catch (IOException e) {}
        }
        return EVAL_PAGE;
    }
    
    /**
     * Releases all instance variables.
     */
    public void release() {
        name = null;
        property = null;
        loopId = null;
        className = null;
        enum = null;
        super.release();
    }

    /**
     * Returns an Object representing an Enumeration, a Vector, 
     * a Dictionary or an array of objects (no primitive types),
     * using the specified property getter method on the specified
     * object.
     *
     * @param obj the Object with the multi-value property
     * @param property the property name
     * @return the multi-value Object
     */
    private Object getProperty(Object obj, String property) 
        throws JspException {
        Object mvObj = null;
        
        Method method = null;
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
            for (int i = 0; pds != null && i < pds.length; i++) {
                if (pds[i].getName().equals(property)) {
                    method = pds[i].getReadMethod();
                    break;
                }
            }
        }
        catch (IntrospectionException e) {
            throw new JspException("Error analyzing the bean class: " + 
                e.getMessage());
        }

        if (method == null) {
            throw new JspException("Property " + property + " not found");
        }
        
        // Make sure the property is not of a primitive type
        if (method.getReturnType().isPrimitive()) {
            throw new JspException("Invalid property data type");
        }

        // Invoke the method to get the multi-value Object
        Object[] args = {};
        try {
            mvObj = method.invoke(obj, args);
        }
        catch (Exception e) {
            throw new JspException("Failed to get property " + property + ": " + 
                e.getMessage());
        }
        return mvObj;
    }
    
    /**
     * Returns an Enumeration of the values in the specified multi-value
     * object, which can be an Enumeration, Vector, Dictionary or array
     * of Objects (not primitive types).
     *
     * @param obj the multi-value Object
     * @return an Enumeration
     * @exception JspException if invalid type
     */
    private Enumeration getEnumeration(Object obj) throws JspException {
        if (obj == null) {
            return null;
        }
        
        Enumeration enum = null;
        if (Enumeration.class.isAssignableFrom(obj.getClass())) {
            enum = (Enumeration) obj;
        }
        else if (Vector.class.isAssignableFrom(obj.getClass())) {
            enum = ((Vector) obj).elements();
        }
        else if (Dictionary.class.isAssignableFrom(obj.getClass())) {
            enum = ((Dictionary) obj).elements();
        }
        else if (obj.getClass().isArray()) {
            Object[] oa = (Object[]) obj;
            Vector v = new Vector(oa.length);
            for (int i = 0; i < oa.length; i++) {
                v.addElement(oa[i]);
            }
            enum = v.elements();
        }
        else {
            throw new JspException("Invalid data type");
        }
        return enum;
    }
}