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