webwork/src/main/webwork/util AbstractValueStack.java,NONE,1.1 ValueStack.java,1.31,1.32

[email protected] Sun, 26 May 2002 01:50:29 -0700
Newsgroups gmane.comp.java.webwork.cvs
Message-ID <[email protected]>
Update of /cvsroot/webwork/webwork/src/main/webwork/util
In directory usw-pr-cvs1:/tmp/cvs-serv20922

Modified Files:
	ValueStack.java 
Added Files:
	AbstractValueStack.java 
Log Message:
abstraction of value stack as suggested by Sven
to support Cocoon2

--- NEW FILE: AbstractValueStack.java ---
/*
* WebWork, Web Application Framework
*
* Distributable under Apache license.
* See terms of license at opensource.org
*/
package webwork.util;

import org.apache.log4j.Category;
import webwork.expr.ParseException;
import webwork.expr.Parser;

import java.beans.*;
import java.io.StringReader;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 *	Abstract Value stack. A VS is used by the WebWork system as a way to make
 * values available by using the push and pop methods. They can then
 * be accessed by using the find* methods.
 *
 *	@author Rickard Öberg ([email protected])
 * @author Maurice C. Parker ([email protected])
 *	@version $Revision: 1.1 $
 */
public abstract class AbstractValueStack{
   // Static  -------------------------------------------------------
   public static final String STACK_NAME = "webwork.result";
   static Map classes = new HashMap(); // Method cache
   private static Category log = Category.getInstance(ValueStack.class);

   /**
    * Clear the method cache. This must be called if the
    * application is restarted.
    *
    */
   public static void clearMethods() {
      classes = new HashMap();
   }

   // Attributes ----------------------------------------------------
   private List valueList = Collections.EMPTY_LIST;
   private Parser parser;

   // Constructor ---------------------------------------------------
   public AbstractValueStack() {
   }

   // Public --------------------------------------------------------
   /**
    * Push a value onto the value stack.
    *
    * @param   value  the value
    */
   public void pushValue(Object value) {
      if (valueList == Collections.EMPTY_LIST) {
         valueList = new ArrayList();
      }

      valueList.add(value);

//     log.debug("Push to value stack:");
//     log.debug(toString());
//     new Throwable().printStackTrace();
   }


   /**
    * Pop a value from the value stack.
    *
    * @return  the popped value
    */
   public Object popValue() {
      try {
         return valueList.remove(valueList.size() - 1);
      } catch (IndexOutOfBoundsException e) {
         return null;
      } finally {
//         log.debug("Pop from value stack:");
//         log.debug(toString());
//         new Throwable().printStackTrace();
      }
   }

   /**
    * Returns the size of the value stack.
    *
    * @return size of value stack
    */
   public int size() {
      return valueList != null?valueList.size():0;
   }

   /**
    * Returns TRUE is the value stack is empty.
    *
    * @return true is value stack is empty
    */
   public boolean isEmpty() {
      return size() == 0;
   }

   /**
    * Resolve a WebWork expression statement.
    *
    * @param   expression
    * @return  the boolean result of the expression
    */
   public boolean test(String expression) {
      Parser p = getParser(expression);

      boolean answer = false;

      try {
         answer = p.test();
      } catch (ParseException pe) {
         throw new IllegalArgumentException("\n\nAn error occurred while parsing the expression: \n    \"" +
               expression + "\"\n" +
               pe.getMessage());
      }

      return answer;
   }

    // SK 
    /** Find a value by id. AbstractValueStack expects subclasses to
     *  have some context concept to evaluate @identified expressions.
     */
    abstract protected Object findInContext(String id);

    /** Find a request parameter. AbstractValueStack expects subclasses to
     *  have some request concept to evaluate $parameter expressions
     */
    abstract protected String getRequestParameter(String id);
    // /SK 
    
   /**
    * Find a value for a given name.
    *
    * @param   expression
    * @return  the object corresponding to the query
    */
   public Object findValue(String query)
         throws IllegalArgumentException {
      if (query == null || query.equals(""))
         query = ".";

      // The query segments and the current segment
      Query q = Query.getQuery(query);
      QuerySegment[] segments = q.getSegments();
      if (log.isDebugEnabled()) {
         log.debug("findValue() for: " + q);
      }


      QuerySegment segment = segments[0];
      int segmentIdx = 1;

      // The working stack, its pointer, and the current object,
      List workList = null;
      int stackIdx = 0;
      Object value = null;

      /////////////////////////////////////////////////////////////////////////
      // evaluate the first element of the expression to see where to
      // get the requested value.  These should be quick and easy objects
      // to find or create.
      /////////////////////////////////////////////////////////////////////////
      switch (segment.getType()) {

         // get the top value off of the stack
         case QuerySegment.CURRENT:

            if (valueList.size() < 1)
               return null;
            else
               value = valueList.get(valueList.size() - 1);

            if (value instanceof ValueHolder)
               value = ((ValueHolder) value).getValue();

            if (value == null && log.isDebugEnabled())
               log.debug("value for [" + query + "] is null.");

            return value;

            // return the id since it is the actual string
         case QuerySegment.STRING:
            return segment.getId();

            // the integer is the first value and only value in the segment
         case QuerySegment.NUMBER:
            return segment.getValues().get(0);

            // get an attribute
         case QuerySegment.ATTRIBUTE:

            // get the attribute
	     // SK
	     value=findInContext(segment.getId());
	     // /SK

            if (value == null && log.isDebugEnabled()) {
               log.debug("value for [" + query + "] is null.");
               return null;

               //throw new IllegalArgumentException("No such attribute: " + token.image);
            }

            // get the real value
            if (value instanceof ValueHolder)
               value = ((ValueHolder) value).getValue();

            // always have the next segment ready to go
            segment = segments[segmentIdx++];

            // if we don't need to search through this attribute simply return it
            if (segment == null)
               return value;

            // Set the stack index to zero so that we don't try to search the stack
            stackIdx = 0;

            break;

            // return the http request parameter
         case QuerySegment.PARAMETER:
	     // SK
	     return getRequestParameter(segment.getId());
	     // /SK
            // the reserved keywork "true"
         case QuerySegment.TRUE:
            return Boolean.TRUE;

            // the reserved keywork "false"
         case QuerySegment.FALSE:
            return Boolean.FALSE;

            // get the root object off of the bottom of the stack
         case QuerySegment.ROOT:
            if (valueList.size() < 1)
               return null;

            // set up the stack, pointer, and current value
            workList = valueList;
            stackIdx = 0;
            value = workList.get(stackIdx);

            // always have the next segment ready to go
            segment = segments[segmentIdx++];

            // if we don't need to search through the stack then return the value
            // this is very unlikely to happen, but we have account for it anyway
            if (segment == null)
               return value;

            break;

         default:

            if (valueList.size() < 1)
               return null;

            // set up the stack, pointer, and current value
            workList = valueList;
            stackIdx = workList.size() - 1;

            try {
               value = workList.get(stackIdx);
            } catch (IndexOutOfBoundsException e) {
               return null;
               //throw new IllegalArgumentException("Illegal valuestack query:"+query);
            }

            break;
      }

      //log.debug( "first segment id: '" + segment.getId() + "' segment type: '" + segment.getType() + "'" );

      /////////////////////////////////////////////////////////////////////////
      // Now that stack has been set up and the context set (valueIdx) we will
      // begin parsing the rest of the expression and drilling down through
      // the object properties, collection elements, and method calls.
      /////////////////////////////////////////////////////////////////////////
      int saveSegmentIdx = segmentIdx;

      while (true) {
         //log.debug("beginning valuestack search at level: '" + stackIdx);

         int workStackIdx = stackIdx;
         if (value != null) {
            objectWalk: do {
               switch (segment.getType()) {

                  // access a classes property
                  case QuerySegment.PROPERTY:

                     // get the real value
                     if (value instanceof ValueHolder)
                        value = ((ValueHolder) value).getValue();

                     //log.debug( "PROPERTY: attempting to get: " + segment.getId() );
                     try {
                        Method[] methods = getMethod(value.getClass(), segment.getId());
                        if (methods == null) {
                           //log.debug( "PROPERTY: method not found: '" + segment.getId() + "' current value: '" + value + "' (" + value.getClass() + ")");
                           value = null;
                           break objectWalk;
                        } else {
                           value = methods[0].invoke(value, new Object[0]);
                           //log.debug( "PROPERTY: found property value: " + value  + " (" + value.getClass() + ")");
                        }
                     } catch (Exception e) {
                        //log.debug( "PROPERTY: method called failed: " + e.getMessage() );
                        value = null;
                        break objectWalk;
                     }
                     break;

                     // access a class method
                  case QuerySegment.METHOD:

                     // get the real value
                     if (value instanceof ValueHolder)
                        value = ((ValueHolder) value).getValue();

                     //log.debug( "going after method: " + segment.getId() );

                     try {

                        Method[] methods = getMethod(value.getClass(), segment.getId());

                        if (methods == null) {
                           //log.debug( "METHOD: "  + segment.getId() + " was not found." );
                           value = null;
                           break objectWalk;
                        }

                        Iterator it = segment.getValues().iterator();
                        List params = new ArrayList();

                        while (it.hasNext()) {
                           Object param = findValue((String) it.next());
                           params.add(param);
                        }

                        Method target = findMethod(methods, params);

                        // Convert if necessary
                        int paramCount = target.getParameterTypes().length;
                        for (int i = 0; i < paramCount; i++) {
                           Class parameterClass = target.getParameterTypes()[i];

                           //log.debug(paramCount + ": " + parameterClass.getName());

                           if (!parameterClass.equals(String.class) && !parameterClass.equals(params.get(i).getClass())) {
                              // Get property editor
                              PropertyEditor pe;
                              pe = PropertyEditorManager.findEditor(parameterClass);

                              // Convert value
                              if (pe != null) {
                                 pe.setAsText(params.get(i).toString());
                                 Object param = pe.getValue();

                                 //replace with the converted value
                                 params.set(i, param);
                              }
                           }
                        }
                        //log.debug("METHOD: trying call: " + segment.getId() + " param: " + params);

                        value = target.invoke(value, params.toArray());

                     } catch (Exception e) {
                        //log.debug("METHOD: \"" + segment.getId() + "\" exception: \"" + e.getMessage() + "\"");
                        value = null;
                        break objectWalk;

                        //throw new IllegalArgumentException("Invalid single method access. " +
                        //                                   "Error accessing method \"" + token.image +
                        //                                   "\" using parameter: \"" + param + "\"");
                     }
                     break;

                     // access the current value as a collection
                  case QuerySegment.COLLECTION:

                     // get the real value
                     if (value instanceof ValueHolder)
                        value = ((ValueHolder) value).getValue();

                     Object key = findValue(segment.getId());
                     if (key == null) {
                        value = null;
                        break objectWalk;
                     }

                     // Map
                     if (Map.class.isAssignableFrom(value.getClass())) {
                        value = ((Map) value).get(key.toString());
                        break;
                     }
                     // Resource Bundle
                     else if (ResourceBundle.class.isAssignableFrom(value.getClass())) {
                        value = ((ResourceBundle) value).getObject(key.toString());
                        break;
                     }
                     // Array
                     else if (value.getClass().isArray()) {
                        value = ((Object[]) value)[((Integer) key).intValue()];
                        break;
                     }
                     // List
                     else if (List.class.isAssignableFrom(value.getClass())) {
                        value = ((List) value).get(((Integer) key).intValue());
                        break;
                     }
                     // Collection
                     else if (Collection.class.isAssignableFrom(value.getClass())) {
                        // Not very efficient, but at least it works
                        value = ((Collection) value).toArray()[((Integer) key).intValue()];
                        break;
                     }

                     // fail if the user tries to access something other than a Collection
                     value = null;
                     break objectWalk;

                     // access the parent by going up one level on the stack
                  case QuerySegment.PARENT:
                     workStackIdx--;

                     if (workStackIdx < 0) {
                        value = null;
                        break objectWalk;
                        //throw new IllegalArgumentException("Parent object not available.");
                     } else {
                        value = workList.get(workStackIdx);
                     }
                     break;

                     // currently we only allow to expand properties (no parameter methods)
                  case QuerySegment.EXPAND:
                     //log.debug( "EXPAND: going after: " + segment.getId() );
                     try {

                        Object methodName = findValue(segment.getId());
                        if (methodName == null) {
                           value = null;
                           break objectWalk;
                        }

                        value = findValue(methodName.toString());

                     } catch (Exception e) {
                        value = null;
                        break objectWalk;
                     }
                     break;
               }
               //log.debug( "finished case statement" );
               // always have the next segment ready to go
               segment = segments[segmentIdx++];
            } while (segment != null);
         }

         // if we didn't find the value, then move one down the stack and
         // try again.
         if (value == null && stackIdx > 0) {
            stackIdx--;
            value = workList.get(stackIdx);

            // reset the segment index to reset the search
            segmentIdx = saveSegmentIdx;
            segment = segments[segmentIdx - 1];
         } else {
            break;
         }
      }

      // get the real value
      if (value instanceof ValueHolder)
         value = ((ValueHolder) value).getValue();

      if (value == null && log.isDebugEnabled())
         log.debug("value for [" + query + "] is null.");
      return value;
   }

   /**
    * Return a string representation of the Stack
    *
    * @return     the stack as a String
    */
   public String toString() {
      String str = "Value stack\n";
      str += "===========\n";
      for (int i = 0; i < valueList.size(); i++) {
         Object val = valueList.get(i);
         str += val == null ? "null\n" : val.toString() + "\n";
      }
      str += "===========\n";
      return str;
   }

   /**
    * Get the parser associated with this ValueStack
    *
    * @return   the Parser for this ValueStack
    */
   private Parser getParser(String expression) {
      if (parser == null) {
         parser = new Parser(new StringReader(expression));
         parser.setValueStack(this);
      } else {
         parser.ReInit(new StringReader(expression));
      }
      return parser;
   }

   /**
    * Get a method with a given name.
    *
    * @param   cl  the class of the method
    * @param   name  the name of the method
    * @return     the wanted method
    * @exception   IntrospectionException
    */

   private Method[] getMethod(Class cl, String name)
         throws IntrospectionException {
      Map methods = (Map) classes.get(cl);
      if (methods == null) {
         // Get methods that can be invoked for this class
         methods = new HashMap();

         // Find get methods for properties
         BeanInfo bi = Introspector.getBeanInfo(cl);
         PropertyDescriptor[] pd = bi.getPropertyDescriptors();
         for (int i = 0; i < pd.length; ++i) {
            Method method = pd[i].getReadMethod();

            // Check if readable property
            if (method != null) {
               if (!Modifier.isPublic(cl.getModifiers())) {
                  // Find a method in an interface that *is* public
                  Class[] interfaces = bi.getBeanDescriptor().getBeanClass().getInterfaces();
                  for (int j = 0; j < interfaces.length; j++) {
                     try {
                        //log.debug("Try "+interfaces[j]);
                        method = interfaces[j].getMethod(method.getName(), new Class[0]);
                        break;
                     } catch (Exception e) {
                        // Ignore
                     }
                  }

                  // We're in trouble! Try to sneak through security
                  if (method.equals(pd[i].getReadMethod())) {
                     AccessibleObject.setAccessible(new AccessibleObject[]{method}, true);
                  }
               }

               // Save method in map
               //log.debug( "GET_METHOD: class: " + cl + " method: " + method.getName());
               methods.put(pd[i].getName(), new Method[]{method});
            }
         }

         // Find param methods
         Method[] getters = cl.getMethods();
         for (int i = 0; i < getters.length; i++) {
            Method method = getters[i];
            if (!method.getName().startsWith("set") && !method.getReturnType().equals(Void.TYPE)) {
               // Valid method
               // Check if public
               if (!Modifier.isPublic(cl.getModifiers())) {
                  // Find a method in an interface that *is* public
                  Class[] interfaces = cl.getInterfaces();
                  for (int j = 0; j < interfaces.length; j++) {
                     try {
                        method = interfaces[j].getMethod(method.getName(), method.getParameterTypes());
                        break;
                     } catch (Exception e) {
                        // Ignore
                     }
                  }

                  // We're in trouble! Try to sneak through security
                  if (method.equals(getters[i])) {
                     AccessibleObject.setAccessible(new AccessibleObject[]{method}, true);
                  }
               }

               // Get name
               String methodName = method.getName();
               if (methodName.startsWith("get"))
                  methodName = Introspector.decapitalize(methodName.substring(3));
               else if (methodName.startsWith("is"))
                  methodName = Introspector.decapitalize(methodName.substring(2));

               // Save method in map
               Method[] current = (Method[]) methods.get(methodName);
               Method[] newlist = null;
               if (current == null) {
                  newlist = new Method[]{method};
               } else {
                  newlist = new Method[current.length + 1];
                  System.arraycopy(current, 0, newlist, 0, current.length);
                  newlist[current.length] = method;
               }
               methods.put(methodName, newlist);
               //log.debug( "GET_METHOD: class: " + cl + " method: " + method.getName());
            }
         }

         // Add map to class map
         // RO: Use clone to avoid synchronization
         Map newClassMap = (Map) ((HashMap) classes).clone();
         newClassMap.put(cl, methods);
         classes = newClassMap;
         //log.debug("Added "+cl+" to class map:"+methods);
      }

      // Get named method/property getter
      Method[] nameMethods = (Method[]) methods.get(name);

      //log.debug("Got "+cl+" "+name+":"+m);
      return nameMethods;
   }

   public Method findMethod(Method[] m, List params) {
      if (m.length == 1)
         return m[0];

      List match = new ArrayList();
      int noOfArgument = params.size();
      for (int i = 0; i < m.length; i++) {
         if (m[i].getParameterTypes().length == noOfArgument)
            match.add(m[i]);
      }

      //log.debug("No of match for: " + m[0].getName() + " , " + noOfArgument + ", " + match.size());

      if (match.size() == 1)
         return (Method) match.get(0);

      Method exact = null;
      List close = new ArrayList();
      List convert = new ArrayList();

      for (Iterator i = match.iterator(); i.hasNext();) {
         Method current = (Method) i.next();
         Class[] paramClass = current.getParameterTypes();
         boolean exactMatch = true;
         boolean closeMatch = true;
         boolean convertable = true;

         for (int j = 0; j < paramClass.length; j++) {
            Class p = params.get(j).getClass();
            //log.debug("Argument: " + j + " , parameterClass: " + paramClass[j].getName()
            //+ " , argumentClass: " + p.getName());
            if (paramClass[j].getName().equals(p.getName())) {
               continue;
            } else if (paramClass[j].isAssignableFrom(p)) {
               exactMatch = false;
               continue;
            } else {
               exactMatch = false;
               closeMatch = false;
               try {
                  // Get property editor
                  PropertyEditor pe;
                  pe = PropertyEditorManager.findEditor(paramClass[j]);

                  // Convert value
                  pe.setAsText(params.get(j).toString());
                  pe.getValue();
               } catch (Exception e) {
                  convertable = false;
               }

               if (!convertable)
                  break;
            }
         }

         if (exactMatch) {
            exact = current;
            break;
         } else if (closeMatch) {
            close.add(current);
         } else if (convertable) {
            convert.add(current);
         }

      }

      if (exact != null)
         return exact;
      else if (close.size() > 0)
         return (Method) close.get(0);
      else if (convert.size() > 0)
         return (Method) convert.get(0);
      else
         return null;

   }

   // Inner classes -------------------------------------------------
   // Value providers that want to use lazy evaluation should use this
   // interface
   public interface ValueHolder {
      // Public -----------------------------------------------------
      public Object getValue();
   }

}

Index: ValueStack.java
===================================================================
RCS file: /cvsroot/webwork/webwork/src/main/webwork/util/ValueStack.java,v
retrieving revision 1.31
retrieving revision 1.32
diff -u -d -r1.31 -r1.32
--- ValueStack.java	2 May 2002 18:55:50 -0000	1.31
+++ ValueStack.java	26 May 2002 08:50:27 -0000	1.32
@@ -6,33 +6,24 @@
 */
 package webwork.util;
 
-import org.apache.log4j.Category;
-import webwork.expr.ParseException;
-import webwork.expr.Parser;
-
 import javax.servlet.ServletRequest;
 import javax.servlet.jsp.PageContext;
-import java.beans.*;
-import java.io.StringReader;
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.*;
 
 /**
- *	Value stack. A VS is used by the WebWork system as a way to make
- * values available by using the push and pop methods. They can then
- * be accessed by using the find* methods.
+ *	Value stack implementation for a servlet based scenario.
  *
  *	@author Rickard Öberg ([email protected])
- *  @author Maurice C. Parker ([email protected])
+ * @author Maurice C. Parker ([email protected])
  *	@version $Revision$
  */
-public final class ValueStack {
-   // Static  -------------------------------------------------------
-   public static final String STACK_NAME = "webwork.result";
-   static Map classes = new HashMap(); // Method cache
-   private static Category log = Category.getInstance(ValueStack.class);
+public class ValueStack extends AbstractValueStack
+{
+
+   // Constructor ---------------------------------------------------
+   public ValueStack()
+   {
+      super();
+   }
 
    /**
     * Get the value stack for a given request. If there is no
@@ -41,7 +32,8 @@
     * @param   request  the request for which a stack shall be returned
     * @return     the value stack
     */
-   public static ValueStack getStack(ServletRequest request) {
+   public static ValueStack getStack(ServletRequest request)
+   {
       ValueStack stack = (ValueStack) request.getAttribute(STACK_NAME);
       if (stack == null) {
          stack = new ValueStack();
@@ -58,7 +50,8 @@
     * @param   context  the page context to associate with the stack
     * @return     the value stack
     */
-   public static ValueStack getStack(PageContext context) {
+   public static ValueStack getStack(PageContext context)
+   {
       ServletRequest request = context.getRequest();
       ValueStack stack = (ValueStack) request.getAttribute(STACK_NAME);
       if (stack == null) {
@@ -70,471 +63,8 @@
       return stack;
    }
 
-   /**
-    * Clear the method cache. This must be called if the
-    * application is restarted.
-    *
-    */
-   public static void clearMethods() {
-      classes = new HashMap();
-   }
-
-   // Attributes ----------------------------------------------------
-   private List valueList = Collections.EMPTY_LIST;
    private ServletRequest request;
    private PageContext context;
-   private Parser parser;
-
-   // Constructor ---------------------------------------------------
-   public ValueStack() {
-   }
-
-   // Public --------------------------------------------------------
-   /**
-    * Push a value onto the value stack.
-    *
-    * @param   value  the value
-    */
-   public void pushValue(Object value) {
-      if (valueList == Collections.EMPTY_LIST) {
-         valueList = new ArrayList();
-      }
-
-      valueList.add(value);
-
-//     log.debug("Push to value stack:");
-//     log.debug(toString());
-//     new Throwable().printStackTrace();
-   }
-
-
-   /**
-    * Pop a value from the value stack.
-    *
-    * @return  the popped value
-    */
-   public Object popValue() {
-      try {
-         return valueList.remove(valueList.size() - 1);
-      } catch (IndexOutOfBoundsException e) {
-         return null;
-      } finally {
-//         log.debug("Pop from value stack:");
-//         log.debug(toString());
-//         new Throwable().printStackTrace();
-      }
-   }
-
-   /**
-    * Returns the size of the value stack.
-    *
-    * @return size of value stack
-    */
-   public int size() {
-      return valueList != null?valueList.size():0;
-   }
-
-   /**
-    * Returns TRUE is the value stack is empty.
-    *
-    * @return true is value stack is empty
-    */
-   public boolean isEmpty() {
-      return size() == 0;
-   }
-
-   /**
-    * Resolve a WebWork expression statement.
-    *
-    * @param   expression
-    * @return  the boolean result of the expression
-    */
-   public boolean test(String expression) {
-      Parser p = getParser(expression);
-
-      boolean answer = false;
-
-      try {
-         answer = p.test();
-      } catch (ParseException pe) {
-         throw new IllegalArgumentException("\n\nAn error occurred while parsing the expression: \n    \"" +
-               expression + "\"\n" +
-               pe.getMessage());
-      }
-
-      return answer;
-   }
-
-   /**
-    * Find a value for a given name.
-    *
-    * @param   expression
-    * @return  the object corresponding to the query
-    */
-   public Object findValue(String query)
-         throws IllegalArgumentException {
-      if (query == null || query.equals(""))
-         query = ".";
-
-      // The query segments and the current segment
-      Query q = Query.getQuery(query);
-      QuerySegment[] segments = q.getSegments();
-      if (log.isDebugEnabled()) {
-         log.debug("findValue() for: " + q);
-      }
-
-
-      QuerySegment segment = segments[0];
-      int segmentIdx = 1;
-
-      // The working stack, its pointer, and the current object,
-      List workList = null;
-      int stackIdx = 0;
-      Object value = null;
-
-      /////////////////////////////////////////////////////////////////////////
-      // evaluate the first element of the expression to see where to
-      // get the requested value.  These should be quick and easy objects
-      // to find or create.
-      /////////////////////////////////////////////////////////////////////////
-      switch (segment.getType()) {
-
-         // get the top value off of the stack
-         case QuerySegment.CURRENT:
-
-            if (valueList.size() < 1)
-               return null;
-            else
-               value = valueList.get(valueList.size() - 1);
-
-            if (value instanceof ValueHolder)
-               value = ((ValueHolder) value).getValue();
-
-            if (value == null && log.isDebugEnabled())
-               log.debug("value for [" + query + "] is null.");
-
-            return value;
-
-            // return the id since it is the actual string
-         case QuerySegment.STRING:
-            return segment.getId();
-
-            // the integer is the first value and only value in the segment
-         case QuerySegment.NUMBER:
-            return segment.getValues().get(0);
-
-            // get an attribute
-         case QuerySegment.ATTRIBUTE:
-
-            // get the attribute
-            if (context != null) {
-               value = context.findAttribute(segment.getId()); // Used in a JSP environment
-            } else {
-               value = request.getAttribute(segment.getId()); // Rarely, if ever, used. Ignore
-            }
-
-            if (value == null && log.isDebugEnabled()) {
-               log.debug("value for [" + query + "] is null.");
-               return null;
-
-               //throw new IllegalArgumentException("No such attribute: " + token.image);
-            }
-
-            // get the real value
-            if (value instanceof ValueHolder)
-               value = ((ValueHolder) value).getValue();
-
-            // always have the next segment ready to go
-            segment = segments[segmentIdx++];
-
-            // if we don't need to search through this attribute simply return it
-            if (segment == null)
-               return value;
-
-            // Set the stack index to zero so that we don't try to search the stack
-            stackIdx = 0;
-
-            break;
-
-            // return the http request parameter
-         case QuerySegment.PARAMETER:
-            return request.getParameter(segment.getId());
-
-            // the reserved keywork "true"
-         case QuerySegment.TRUE:
-            return Boolean.TRUE;
-
-            // the reserved keywork "false"
-         case QuerySegment.FALSE:
-            return Boolean.FALSE;
-
-            // get the root object off of the bottom of the stack
-         case QuerySegment.ROOT:
-            if (valueList.size() < 1)
-               return null;
-
-            // set up the stack, pointer, and current value
-            workList = valueList;
-            stackIdx = 0;
-            value = workList.get(stackIdx);
-
-            // always have the next segment ready to go
-            segment = segments[segmentIdx++];
-
-            // if we don't need to search through the stack then return the value
-            // this is very unlikely to happen, but we have account for it anyway
-            if (segment == null)
-               return value;
-
-            break;
-
-         default:
-
-            if (valueList.size() < 1)
-               return null;
-
-            // set up the stack, pointer, and current value
-            workList = valueList;
-            stackIdx = workList.size() - 1;
-
-            try {
-               value = workList.get(stackIdx);
-            } catch (IndexOutOfBoundsException e) {
-               return null;
-               //throw new IllegalArgumentException("Illegal valuestack query:"+query);
-            }
-
-            break;
-      }
-
-      //log.debug( "first segment id: '" + segment.getId() + "' segment type: '" + segment.getType() + "'" );
-
-      /////////////////////////////////////////////////////////////////////////
-      // Now that stack has been set up and the context set (valueIdx) we will
-      // begin parsing the rest of the expression and drilling down through
-      // the object properties, collection elements, and method calls.
-      /////////////////////////////////////////////////////////////////////////
-      int saveSegmentIdx = segmentIdx;
-
-      while (true) {
-         //log.debug("beginning valuestack search at level: '" + stackIdx);
-
-         int workStackIdx = stackIdx;
-         if (value != null) {
-            objectWalk: do {
-               switch (segment.getType()) {
-
-                  // access a classes property
-                  case QuerySegment.PROPERTY:
-
-                     // get the real value
-                     if (value instanceof ValueHolder)
-                        value = ((ValueHolder) value).getValue();
-
-                     //log.debug( "PROPERTY: attempting to get: " + segment.getId() );
-                     try {
-                        Method[] methods = getMethod(value.getClass(), segment.getId());
-                        if (methods == null) {
-                           //log.debug( "PROPERTY: method not found: '" + segment.getId() + "' current value: '" + value + "' (" + value.getClass() + ")");
-                           value = null;
-                           break objectWalk;
-                        } else {
-                           value = methods[0].invoke(value, new Object[0]);
-                           //log.debug( "PROPERTY: found property value: " + value  + " (" + value.getClass() + ")");
-                        }
-                     } catch (Exception e) {
-                        //log.debug( "PROPERTY: method called failed: " + e.getMessage() );
-                        value = null;
-                        break objectWalk;
-                     }
-                     break;
-
-                     // access a class method
-                  case QuerySegment.METHOD:
-
-                     // get the real value
-                     if (value instanceof ValueHolder)
-                        value = ((ValueHolder) value).getValue();
-
-                     //log.debug( "going after method: " + segment.getId() );
-
-                     try {
-
-                        Method[] methods = getMethod(value.getClass(), segment.getId());
-
-                        if (methods == null) {
-                           //log.debug( "METHOD: "  + segment.getId() + " was not found." );
-                           value = null;
-                           break objectWalk;
-                        }
-
-                        Iterator it = segment.getValues().iterator();
-                        List params = new ArrayList();
-
-                        while (it.hasNext()) {
-                           Object param = findValue((String) it.next());
-                           params.add(param);
-                        }
-
-                        Method target = findMethod(methods, params);
-
-                        // Convert if necessary
-                        int paramCount = target.getParameterTypes().length;
-                        for (int i = 0; i < paramCount; i++) {
-                           Class parameterClass = target.getParameterTypes()[i];
-
-                           //log.debug(paramCount + ": " + parameterClass.getName());
-
-                           if (!parameterClass.equals(String.class) && !parameterClass.equals(params.get(i).getClass())) {
-                              // Get property editor
-                              PropertyEditor pe;
-                              pe = PropertyEditorManager.findEditor(parameterClass);
-
-                              // Convert value
-                              if (pe != null) {
-                                 pe.setAsText(params.get(i).toString());
-                                 Object param = pe.getValue();
-
-                                 //replace with the converted value
-                                 params.set(i, param);
-                              }
-                           }
-                        }
-                        //log.debug("METHOD: trying call: " + segment.getId() + " param: " + params);
-
-                        value = target.invoke(value, params.toArray());
-
-                     } catch (Exception e) {
-                        //log.debug("METHOD: \"" + segment.getId() + "\" exception: \"" + e.getMessage() + "\"");
-                        value = null;
-                        break objectWalk;
-
-                        //throw new IllegalArgumentException("Invalid single method access. " +
-                        //                                   "Error accessing method \"" + token.image +
-                        //                                   "\" using parameter: \"" + param + "\"");
-                     }
-                     break;
-
-                     // access the current value as a collection
-                  case QuerySegment.COLLECTION:
-
-                     // get the real value
-                     if (value instanceof ValueHolder)
-                        value = ((ValueHolder) value).getValue();
-
-                     Object key = findValue(segment.getId());
-                     if (key == null) {
-                        value = null;
-                        break objectWalk;
-                     }
-
-                     // Map
-                     if (Map.class.isAssignableFrom(value.getClass())) {
-                        value = ((Map) value).get(key.toString());
-                        break;
-                     }
-                     // Resource Bundle
-                     else if (ResourceBundle.class.isAssignableFrom(value.getClass())) {
-                        value = ((ResourceBundle) value).getObject(key.toString());
-                        break;
-                     }
-                     // Array
-                     else if (value.getClass().isArray()) {
-                        value = ((Object[]) value)[((Integer) key).intValue()];
-                        break;
-                     }
-                     // List
-                     else if (List.class.isAssignableFrom(value.getClass())) {
-                        value = ((List) value).get(((Integer) key).intValue());
-                        break;
-                     }
-                     // Collection
-                     else if (Collection.class.isAssignableFrom(value.getClass())) {
-                        // Not very efficient, but at least it works
-                        value = ((Collection) value).toArray()[((Integer) key).intValue()];
-                        break;
-                     }
-
-                     // fail if the user tries to access something other than a Collection
-                     value = null;
-                     break objectWalk;
-
-                     // access the parent by going up one level on the stack
-                  case QuerySegment.PARENT:
-                     workStackIdx--;
-
-                     if (workStackIdx < 0) {
-                        value = null;
-                        break objectWalk;
-                        //throw new IllegalArgumentException("Parent object not available.");
-                     } else {
-                        value = workList.get(workStackIdx);
-                     }
-                     break;
-
-                     // currently we only allow to expand properties (no parameter methods)
-                  case QuerySegment.EXPAND:
-                     //log.debug( "EXPAND: going after: " + segment.getId() );
-                     try {
-
-                        Object methodName = findValue(segment.getId());
-                        if (methodName == null) {
-                           value = null;
-                           break objectWalk;
-                        }
-
-                        value = findValue(methodName.toString());
-
-                     } catch (Exception e) {
-                        value = null;
-                        break objectWalk;
-                     }
-                     break;
-               }
-               //log.debug( "finished case statement" );
-               // always have the next segment ready to go
-               segment = segments[segmentIdx++];
-            } while (segment != null);
-         }
-
-         // if we didn't find the value, then move one down the stack and
-         // try again.
-         if (value == null && stackIdx > 0) {
-            stackIdx--;
-            value = workList.get(stackIdx);
-
-            // reset the segment index to reset the search
-            segmentIdx = saveSegmentIdx;
-            segment = segments[segmentIdx - 1];
-         } else {
-            break;
-         }
-      }
-
-      // get the real value
-      if (value instanceof ValueHolder)
-         value = ((ValueHolder) value).getValue();
-
-      if (value == null && log.isDebugEnabled())
-         log.debug("value for [" + query + "] is null.");
-      return value;
-   }
-
-   /**
-    * Return a string representation of the Stack
-    *
-    * @return     the stack as a String
-    */
-   public String toString() {
-      String str = "Value stack\n";
-      str += "===========\n";
-      for (int i = 0; i < valueList.size(); i++) {
-         Object val = valueList.get(i);
-         str += val == null ? "null\n" : val.toString() + "\n";
-      }
-      str += "===========\n";
-      return str;
-   }
 
    // Private -------------------------------------------------------
    /**
@@ -544,7 +74,8 @@
     *
     * @param   request
     */
-   private void setRequest(ServletRequest request) {
+   private void setRequest(ServletRequest request)
+   {
       this.request = request;
    }
 
@@ -554,219 +85,24 @@
     *
     * @param   request
     */
-   private void setContext(PageContext context) {
+   private void setContext(PageContext context)
+   {
       this.context = context;
    }
 
-   /**
-    * Get the parser associated with this ValueStack
-    *
-    * @return   the Parser for this ValueStack
-    */
-   private Parser getParser(String expression) {
-      if (parser == null) {
-         parser = new Parser(new StringReader(expression));
-         parser.setValueStack(this);
+   protected Object findInContext(String id)
+   {
+      if (context != null) {
+         return context.findAttribute(id); // Used in a JSP environment
       } else {
-         parser.ReInit(new StringReader(expression));
+         return request.getAttribute(id); // Rarely, if ever, used. Ignore
       }
-      return parser;
    }
 
-   /**
-    * Get a method with a given name.
-    *
-    * @param   cl  the class of the method
-    * @param   name  the name of the method
-    * @return     the wanted method
-    * @exception   IntrospectionException
-    */
-
-   private Method[] getMethod(Class cl, String name)
-         throws IntrospectionException {
-      Map methods = (Map) classes.get(cl);
-      if (methods == null) {
-         // Get methods that can be invoked for this class
-         methods = new HashMap();
-
-         // Find get methods for properties
-         BeanInfo bi = Introspector.getBeanInfo(cl);
-         PropertyDescriptor[] pd = bi.getPropertyDescriptors();
-         for (int i = 0; i < pd.length; ++i) {
-            Method method = pd[i].getReadMethod();
-
-            // Check if readable property
-            if (method != null) {
-               if (!Modifier.isPublic(cl.getModifiers())) {
-                  // Find a method in an interface that *is* public
-                  Class[] interfaces = bi.getBeanDescriptor().getBeanClass().getInterfaces();
-                  for (int j = 0; j < interfaces.length; j++) {
-                     try {
-                        //log.debug("Try "+interfaces[j]);
-                        method = interfaces[j].getMethod(method.getName(), new Class[0]);
-                        break;
-                     } catch (Exception e) {
-                        // Ignore
-                     }
-                  }
-
-                  // We're in trouble! Try to sneak through security
-                  if (method.equals(pd[i].getReadMethod())) {
-                     AccessibleObject.setAccessible(new AccessibleObject[]{method}, true);
-                  }
-               }
-
-               // Save method in map
-               //log.debug( "GET_METHOD: class: " + cl + " method: " + method.getName());
-               methods.put(pd[i].getName(), new Method[]{method});
-            }
-         }
-
-         // Find param methods
-         Method[] getters = cl.getMethods();
-         for (int i = 0; i < getters.length; i++) {
-            Method method = getters[i];
-            if (!method.getName().startsWith("set") && !method.getReturnType().equals(Void.TYPE)) {
-               // Valid method
-               // Check if public
-               if (!Modifier.isPublic(cl.getModifiers())) {
-                  // Find a method in an interface that *is* public
-                  Class[] interfaces = cl.getInterfaces();
-                  for (int j = 0; j < interfaces.length; j++) {
-                     try {
-                        method = interfaces[j].getMethod(method.getName(), method.getParameterTypes());
-                        break;
-                     } catch (Exception e) {
-                        // Ignore
-                     }
-                  }
-
-                  // We're in trouble! Try to sneak through security
-                  if (method.equals(getters[i])) {
-                     AccessibleObject.setAccessible(new AccessibleObject[]{method}, true);
-                  }
-               }
-
-               // Get name
-               String methodName = method.getName();
-               if (methodName.startsWith("get"))
-                  methodName = Introspector.decapitalize(methodName.substring(3));
-               else if (methodName.startsWith("is"))
-                  methodName = Introspector.decapitalize(methodName.substring(2));
-
-               // Save method in map
-               Method[] current = (Method[]) methods.get(methodName);
-               Method[] newlist = null;
-               if (current == null) {
-                  newlist = new Method[]{method};
-               } else {
-                  newlist = new Method[current.length + 1];
-                  System.arraycopy(current, 0, newlist, 0, current.length);
-                  newlist[current.length] = method;
-               }
-               methods.put(methodName, newlist);
-               //log.debug( "GET_METHOD: class: " + cl + " method: " + method.getName());
-            }
-         }
-
-         // Add map to class map
-         // RO: Use clone to avoid synchronization
-         Map newClassMap = (Map) ((HashMap) classes).clone();
-         newClassMap.put(cl, methods);
-         classes = newClassMap;
-         //log.debug("Added "+cl+" to class map:"+methods);
-      }
-
-      // Get named method/property getter
-      Method[] nameMethods = (Method[]) methods.get(name);
-
-      //log.debug("Got "+cl+" "+name+":"+m);
-      return nameMethods;
+   protected String getRequestParameter(String id)
+   {
+      return request.getParameter(id);
    }
 
-   public Method findMethod(Method[] m, List params) {
-      if (m.length == 1)
-         return m[0];
 
-      List match = new ArrayList();
-      int noOfArgument = params.size();
-      for (int i = 0; i < m.length; i++) {
-         if (m[i].getParameterTypes().length == noOfArgument)
-            match.add(m[i]);
-      }
-
-      //log.debug("No of match for: " + m[0].getName() + " , " + noOfArgument + ", " + match.size());
-
-      if (match.size() == 1)
-         return (Method) match.get(0);
-
-      Method exact = null;
-      List close = new ArrayList();
-      List convert = new ArrayList();
-
-      for (Iterator i = match.iterator(); i.hasNext();) {
-         Method current = (Method) i.next();
-         Class[] paramClass = current.getParameterTypes();
-         boolean exactMatch = true;
-         boolean closeMatch = true;
-         boolean convertable = true;
-
-         for (int j = 0; j < paramClass.length; j++) {
-            Class p = params.get(j).getClass();
-            //log.debug("Argument: " + j + " , parameterClass: " + paramClass[j].getName()
-            //+ " , argumentClass: " + p.getName());
-            if (paramClass[j].getName().equals(p.getName())) {
-               continue;
-            } else if (paramClass[j].isAssignableFrom(p)) {
-               exactMatch = false;
-               continue;
-            } else {
-               exactMatch = false;
-               closeMatch = false;
-               try {
-                  // Get property editor
-                  PropertyEditor pe;
-                  pe = PropertyEditorManager.findEditor(paramClass[j]);
-
-                  // Convert value
-                  pe.setAsText(params.get(j).toString());
-                  pe.getValue();
-               } catch (Exception e) {
-                  convertable = false;
-               }
-
-               if (!convertable)
-                  break;
-            }
-         }
-
-         if (exactMatch) {
-            exact = current;
-            break;
-         } else if (closeMatch) {
-            close.add(current);
-         } else if (convertable) {
-            convert.add(current);
-         }
-
-      }
-
-      if (exact != null)
-         return exact;
-      else if (close.size() > 0)
-         return (Method) close.get(0);
-      else if (convert.size() > 0)
-         return (Method) convert.get(0);
-      else
-         return null;
-
-   }
-
-   // Inner classes -------------------------------------------------
-   // Value providers that want to use lazy evaluation should use this
-   // interface
-   public interface ValueHolder {
-      // Public -----------------------------------------------------
-      public Object getValue();
-   }
 }



_______________________________________________________________

Don't miss the 2002 Sprint PCS Application Developer's Conference
August 25-28 in Las Vegas -- http://devcon.sprintpcs.com/adp/index.cfm