You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by pr...@apache.org on 2007/10/12 07:14:39 UTC

svn commit: r584046 [1/2] - in /commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util: ./ introspection/

Author: proyal
Date: Thu Oct 11 22:14:37 2007
New Revision: 584046

URL: http://svn.apache.org/viewvc?rev=584046&view=rev
Log:
Refresh the introspection code with the latest from Velocity (mostly) to gain varargs support (for JEXL-25)

Added:
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/ArrayListWrapper.java   (with props)
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/MapGetExecutor.java   (with props)
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectionUtils.java   (with props)
Modified:
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/AbstractExecutor.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/BooleanPropertyExecutor.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/GetExecutor.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/PropertyExecutor.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Info.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Introspector.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/VelMethod.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/VelPropertyGet.java
    commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/VelPropertySet.java

Modified: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/AbstractExecutor.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/AbstractExecutor.java?rev=584046&r1=584045&r2=584046&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/AbstractExecutor.java (original)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/AbstractExecutor.java Thu Oct 11 22:14:37 2007
@@ -36,12 +36,12 @@
 public abstract class AbstractExecutor {
     /** The executor instance log. */
     protected Log rlog = null;
-    
+
     /**
      * Method to be executed.
      */
     protected Method method = null;
-    
+
     /**
      * Execute method against context.
      *

Added: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/ArrayListWrapper.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/ArrayListWrapper.java?rev=584046&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/ArrayListWrapper.java (added)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/ArrayListWrapper.java Thu Oct 11 22:14:37 2007
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.commons.jexl.util;
+
+import java.lang.reflect.Array;
+import java.util.AbstractList;
+
+/**
+ * A class that wraps an array with a List interface.
+ *
+ * @author Chris Schultz <chris@christopherschultz.net$gt;
+ * @version $Revision$ $Date: 2006-04-14 19:40:41 $
+ */
+public class ArrayListWrapper extends AbstractList {
+    private Object array;
+
+    public ArrayListWrapper( Object array ) {
+        this.array = array;
+    }
+
+    public Object get( int index ) {
+        return Array.get( array, index );
+    }
+
+    public Object set( int index, Object element ) {
+        Object old = get( index );
+        Array.set( array, index, element );
+        return old;
+    }
+
+    public int size() {
+        return Array.getLength( array );
+    }
+}

Propchange: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/ArrayListWrapper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/BooleanPropertyExecutor.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/BooleanPropertyExecutor.java?rev=584046&r1=584045&r2=584046&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/BooleanPropertyExecutor.java (original)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/BooleanPropertyExecutor.java Thu Oct 11 22:14:37 2007
@@ -68,15 +68,23 @@
             sb = new StringBuffer("is");
             sb.append(property);
 
-            c = sb.charAt(2);
-
-            if (Character.isLowerCase(c)) {
-                sb.setCharAt(2, Character.toUpperCase(c));
-            }
-
             methodUsed = sb.toString();
             method = introspector.getMethod(clazz, methodUsed, params);
 
+            if (null == method)
+            {
+                c = sb.charAt(2);
+
+                if (Character.isLowerCase(c)) {
+                    sb.setCharAt(2, Character.toUpperCase(c));
+                } else {
+                    sb.setCharAt(2, Character.toLowerCase(c));
+                }
+
+                methodUsed = sb.toString();
+                method = introspector.getMethod(clazz, methodUsed, params);
+            }
+
             if (method != null) {
                 /*
                  *  now, this has to return a boolean
@@ -88,8 +96,13 @@
 
                 method = null;
             }
+            /**
+             * pass through application level runtime exceptions
+             */
+        } catch( RuntimeException e ) {
+            throw e;
         } catch (Exception e) {
-            rlog.error("PROGRAMMER ERROR : BooleanPropertyExector() : " + e);
+            rlog.error("PROGRAMMER ERROR : BooleanPropertyExector() : " + e, e);
         }
     }
 }

Modified: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/GetExecutor.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/GetExecutor.java?rev=584046&r1=584045&r2=584046&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/GetExecutor.java (original)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/GetExecutor.java Thu Oct 11 22:14:37 2007
@@ -35,11 +35,11 @@
  */
 public class GetExecutor extends AbstractExecutor {
     /**
-     * Container to hold the 'key' part of 
+     * Container to hold the 'key' part of
      * get(key).
      */
-    private final Object[] args = new Object[1];
-    
+    private final Object[] args;
+
     /**
      * Default constructor.
      *
@@ -53,7 +53,19 @@
             org.apache.commons.jexl.util.introspection.Introspector ispect,
             Class c, String key) throws Exception {
         rlog = r;
-        args[0] = key;
+        // If you passed in null as property, we don't use the value
+        // for parameter lookup. Instead we just look for get() without
+        // any parameters.
+        //
+        // In any other case, the following condition will set up an array
+        // for looking up get(String) on the class.
+
+        if (key != null)
+        {
+            args = new Object[] { key };
+        } else {
+            args = null;
+        }
         method = ispect.getMethod(c, "get", args);
     }
 

Added: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/MapGetExecutor.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/MapGetExecutor.java?rev=584046&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/MapGetExecutor.java (added)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/MapGetExecutor.java Thu Oct 11 22:14:37 2007
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl.util;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+
+
+/**
+ * GetExecutor that is smart about Maps. If it detects one, it does not
+ * use Reflection but a cast to access the getter.
+ *
+ * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
+ * @version $Id$
+ */
+public class MapGetExecutor extends AbstractExecutor {
+    private final String property;
+
+    public MapGetExecutor(final Log rlog, final Class clazz, final String property) {
+        this.rlog = rlog;
+        this.property = property;
+        discover(clazz);
+    }
+
+    protected void discover(final Class clazz) {
+        Class[] interfaces = clazz.getInterfaces();
+        for (int i = 0; i < interfaces.length; i++) {
+            if (interfaces[i].equals(Map.class)) {
+                try {
+                    if (property != null) {
+                        method = Map.class.getMethod("get", new Class[]{Object.class});
+                    }
+                    /**
+                     * pass through application level runtime exceptions
+                     */
+                } catch (RuntimeException e) {
+                    throw e;
+                } catch (Exception e) {
+                    rlog.error("While looking for get('" + property + "') method:", e);
+                }
+                break;
+            }
+        }
+    }
+
+    public Object execute(final Object o) {
+        return ((Map) o).get(property);
+    }
+}

Propchange: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/MapGetExecutor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/PropertyExecutor.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/PropertyExecutor.java?rev=584046&r1=584045&r2=584046&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/PropertyExecutor.java (original)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/PropertyExecutor.java Thu Oct 11 22:14:37 2007
@@ -70,7 +70,7 @@
 
             /*
              *  start with get<property>
-             *  this leaves the property name 
+             *  this leaves the property name
              *  as is...
              */
             sb = new StringBuffer("get");
@@ -79,17 +79,14 @@
             methodUsed = sb.toString();
 
             method = introspector.getMethod(clazz, methodUsed, params);
-             
+
             if (method != null) {
                 return;
             }
-        
+
             /*
              *  now the convenience, flip the 1st character
              */
-         
-            sb = new StringBuffer("get");
-            sb.append(property);
 
             c = sb.charAt(PROPERTY_START_INDEX);
 
@@ -105,8 +102,12 @@
             if (method != null) {
                 return;
             }
-            
-        } catch (Exception e) {
+        /**
+         * pass through application level runtime exceptions
+         */
+        } catch( RuntimeException e ) {
+            throw e;
+        } catch(Exception e) {
             rlog.error("PROGRAMMER ERROR : PropertyExector() : " + e);
         }
     }

Modified: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java?rev=584046&r1=584045&r2=584046&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java (original)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/ClassMap.java Thu Oct 11 22:14:37 2007
@@ -19,56 +19,51 @@
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.util.Hashtable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.logging.Log;
+
+
 /**
  * Taken from the Velocity tree so we can be self-sufficient
- * 
+ *
  * A cache of introspection information for a specific class instance. Keys
- * {@link java.lang.Method} objects by a concatenation of the method name and
+ * {@link Method} objects by a concatenation of the method name and
  * the names of classes that make up the parameters.
- * 
- * @since 1.0
+ *
  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
  * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
  * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
  * @version $Id$
+ * @since 1.0
  */
 public class ClassMap {
-    /** represents a miss on the cached data. */
-    private static final class CacheMiss {
-    }
-
-    /** constant for a miss on the cached data. */
-    private static final CacheMiss CACHE_MISS = new CacheMiss();
-
-    /** represents null or missing arguments. */
-    private static final Object OBJECT = new Object();
-
     /**
      * Class passed into the constructor used to as the basis for the Method
      * map.
      */
 
-    private Class clazz;
+    private final Class clazz;
+    private final Log rlog;
 
-    /**
-     * Cache of Methods, or CACHE_MISS, keyed by method name and actual
-     * arguments used to find it.
-     */
-    private final Map methodCache = new Hashtable();
-
-    /** map from method name and args to a {@link Method}. */
-    private final MethodMap methodMap = new MethodMap();
+    private final MethodCache methodCache;
 
     /**
      * Standard constructor.
-     * @param aClass the class to deconstruct. 
+     *
+     * @param aClass the class to deconstruct.
      */
-    public ClassMap(Class aClass) {
+    public ClassMap(Class aClass, Log rlog) {
         clazz = aClass;
+        this.rlog = rlog;
+        methodCache = new MethodCache();
+
         populateMethodCache();
     }
 
@@ -80,390 +75,232 @@
     }
 
     /**
-     * Find a Method using the methodKey provided.
-     * 
-     * Look in the methodMap for an entry. If found, it'll either be a
-     * CACHE_MISS, in which case we simply give up, or it'll be a Method, in
-     * which case, we return it.
-     * 
-     * If nothing is found, then we must actually go and introspect the method
-     * from the MethodMap.
-     * 
-     * @param name method name
-     * @param params method parameters
-     * @return CACHE_MISS or a {@link Method}
-     * @throws MethodMap.AmbiguousException if the method and parameters are ambiguous.
-     */
-    public Method findMethod(String name, Object[] params) throws MethodMap.AmbiguousException {
-        String methodKey = makeMethodKey(name, params);
-        Object cacheEntry = methodCache.get(methodKey);
-
-        if (cacheEntry == CACHE_MISS) {
-            return null;
-        }
-
-        if (cacheEntry == null) {
-            try {
-                cacheEntry = methodMap.find(name, params);
-            } catch (MethodMap.AmbiguousException ae) {
-                /*
-                 * that's a miss :)
-                 */
-
-                methodCache.put(methodKey, CACHE_MISS);
-
-                throw ae;
-            }
-
-            if (cacheEntry == null) {
-                methodCache.put(methodKey, CACHE_MISS);
-            } else {
-                methodCache.put(methodKey, cacheEntry);
-            }
-        }
-
-        // Yes, this might just be null.
-
-        return (Method) cacheEntry;
+     * Find a Method using the method name and parameter objects.
+     *
+     * @param name   The method name to look up.
+     * @param params An array of parameters for the method.
+     * @return A Method object representing the method to invoke or null.
+     * @throws MethodMap.AmbiguousException When more than one method is a match for the parameters.
+     */
+    public Method findMethod(final String name, final Object[] params)
+            throws MethodMap.AmbiguousException {
+        return methodCache.get(name, params);
     }
 
     /**
-     * Populate the Map of direct hits. These are taken from all the public
-     * methods that our class provides.
+     * Populate the Map of direct hits. These
+     * are taken from all the public methods
+     * that our class, its parents and their implemented interfaces provide.
      */
     private void populateMethodCache() {
-
-        /*
-         * get all publicly accessible methods
-         */
-
-        Method[] methods = getAccessibleMethods(clazz);
-
-        /*
-         * map and cache them
-         */
-
-        for (int i = 0; i < methods.length; i++) {
-            Method method = methods[i];
-
-            /*
-             * now get the 'public method', the method declared by a public
-             * interface or class. (because the actual implementing class may be
-             * a facade...
-             */
-
-            Method publicMethod = getPublicMethod(method);
-
-            /*
-             * it is entirely possible that there is no public method for the
-             * methods of this class (i.e. in the facade, a method that isn't on
-             * any of the interfaces or superclass in which case, ignore it.
-             * Otherwise, map and cache
-             */
-
-            if (publicMethod != null) {
-                methodMap.add(publicMethod);
-                methodCache.put(makeMethodKey(publicMethod), publicMethod);
+        //
+        // Build a list of all elements in the class hierarchy. This one is bottom-first (i.e. we start
+        // with the actual declaring class and its interfaces and then move up (superclass etc.) until we
+        // hit java.lang.Object. That is important because it will give us the methods of the declaring class
+        // which might in turn be abstract further up the tree.
+        //
+        // We also ignore all SecurityExceptions that might happen due to SecurityManager restrictions (prominently
+        // hit with Tomcat 5.5).
+        //
+        // We can also omit all that complicated getPublic, getAccessible and upcast logic that the class map had up
+        // until Velocity 1.4. As we always reflect all elements of the tree (that's what we have a cache for), we will
+        // hit the public elements sooner or later because we reflect all the public elements anyway.
+        //
+        List classesToReflect = new ArrayList();
+
+        // Ah, the miracles of Java for(;;) ...
+        for (Class classToReflect = getCachedClass(); classToReflect != null; classToReflect = classToReflect.getSuperclass())
+        {
+            if (Modifier.isPublic(classToReflect.getModifiers())) {
+                classesToReflect.add(classToReflect);
+            }
+            Class[] interfaces = classToReflect.getInterfaces();
+            for (int i = 0; i < interfaces.length; i++) {
+                if (Modifier.isPublic(interfaces[i].getModifiers())) {
+                    classesToReflect.add(interfaces[i]);
+                }
             }
         }
-    }
 
-    /**
-     * Make a methodKey for the given method using the concatenation of the name
-     * and the types of the method parameters.
-     */
-    private String makeMethodKey(Method method) {
-        Class[] parameterTypes = method.getParameterTypes();
+        for (Iterator it = classesToReflect.iterator(); it.hasNext();) {
+            Class classToReflect = (Class) it.next();
 
-        StringBuffer methodKey = new StringBuffer(method.getName());
+            try {
+                Method[] methods = classToReflect.getMethods();
 
-        for (int j = 0; j < parameterTypes.length; j++) {
-            /*
-             * If the argument type is primitive then we want to convert our
-             * primitive type signature to the corresponding Object type so
-             * introspection for methods with primitive types will work
-             * correctly.
-             */
-            if (parameterTypes[j].isPrimitive()) {
-                if (parameterTypes[j].equals(Boolean.TYPE))
-                    methodKey.append("java.lang.Boolean");
-                else if (parameterTypes[j].equals(Byte.TYPE))
-                    methodKey.append("java.lang.Byte");
-                else if (parameterTypes[j].equals(Character.TYPE))
-                    methodKey.append("java.lang.Character");
-                else if (parameterTypes[j].equals(Double.TYPE))
-                    methodKey.append("java.lang.Double");
-                else if (parameterTypes[j].equals(Float.TYPE))
-                    methodKey.append("java.lang.Float");
-                else if (parameterTypes[j].equals(Integer.TYPE))
-                    methodKey.append("java.lang.Integer");
-                else if (parameterTypes[j].equals(Long.TYPE))
-                    methodKey.append("java.lang.Long");
-                else if (parameterTypes[j].equals(Short.TYPE))
-                    methodKey.append("java.lang.Short");
-            } else {
-                methodKey.append(parameterTypes[j].getName());
+                for (int i = 0; i < methods.length; i++) {
+                    // Strictly spoken that check shouldn't be necessary
+                    // because getMethods only returns public methods.
+                    int modifiers = methods[i].getModifiers();
+                    if (Modifier.isPublic(modifiers)) //  && !)
+                    {
+                        // Some of the interfaces contain abstract methods. That is fine, because the actual object must
+                        // implement them anyway (else it wouldn't be implementing the interface). If we find an abstract
+                        // method in a non-interface, we skip it, because we do want to make sure that no abstract methods end up in
+                        // the cache.
+                        if (classToReflect.isInterface() || !Modifier.isAbstract(modifiers)) {
+                            methodCache.put(methods[i]);
+                        }
+                    }
+                }
             }
-        }
-
-        return methodKey.toString();
-    }
-
-    private static String makeMethodKey(String method, Object[] params) {
-        StringBuffer methodKey = new StringBuffer().append(method);
-
-        for (int j = 0; j < params.length; j++) {
-            Object arg = params[j];
-
-            if (arg == null) {
-                arg = OBJECT;
+            catch (SecurityException se) // Everybody feels better with...
+            {
+                if (rlog.isDebugEnabled()) {
+                    rlog.debug("While accessing methods of " + classToReflect + ": ", se);
+                }
             }
-
-            methodKey.append(arg.getClass().getName());
         }
-
-        return methodKey.toString();
     }
 
     /**
-     * Retrieves public methods for a class. In case the class is not public,
-     * retrieves methods with same signature as its public methods from public
-     * superclasses and interfaces (if they exist). Basically upcasts every
-     * method to the nearest acccessible method.
-     */
-    private static Method[] getAccessibleMethods(Class clazz) {
-        Method[] methods = clazz.getMethods();
-
-        /*
-         * Short circuit for the (hopefully) majority of cases where the clazz
-         * is public
-         */
+     * This is the cache to store and look up the method information.
+     *
+     * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
+     * @version $Id$
+     */
+    private static final class MethodCache {
+        private static final class CacheMiss {
+        }
+
+        private static final CacheMiss CACHE_MISS = new CacheMiss();
+
+        private static final Object OBJECT = new Object();
+
+        private static final Map convertPrimitives = new HashMap();
+
+        static {
+            convertPrimitives.put(Boolean.TYPE, Boolean.class.getName());
+            convertPrimitives.put(Byte.TYPE, Byte.class.getName());
+            convertPrimitives.put(Character.TYPE, Character.class.getName());
+            convertPrimitives.put(Double.TYPE, Double.class.getName());
+            convertPrimitives.put(Float.TYPE, Float.class.getName());
+            convertPrimitives.put(Integer.TYPE, Integer.class.getName());
+            convertPrimitives.put(Long.TYPE, Long.class.getName());
+            convertPrimitives.put(Short.TYPE, Short.class.getName());
+        }
+
+        /**
+         * Cache of Methods, or CACHE_MISS, keyed by method
+         * name and actual arguments used to find it.
+         */
+        private final Map cache = new HashMap();
+
+        /**
+         * Map of methods that are searchable according to method parameters to find a match
+         */
+        private final MethodMap methodMap = new MethodMap();
+
+        /**
+         * Find a Method using the method name and parameter objects.
+         *
+         * Look in the methodMap for an entry.  If found,
+         * it'll either be a CACHE_MISS, in which case we
+         * simply give up, or it'll be a Method, in which
+         * case, we return it.
+         *
+         * If nothing is found, then we must actually go
+         * and introspect the method from the MethodMap.
+         *
+         * @param name   The method name to look up.
+         * @param params An array of parameters for the method.
+         * @return A Method object representing the method to invoke or null.
+         * @throws MethodMap.AmbiguousException When more than one method is a match for the parameters.
+         */
+        public synchronized Method get(final String name, final Object[] params)
+                throws MethodMap.AmbiguousException {
+            String methodKey = makeMethodKey(name, params);
 
-        if (Modifier.isPublic(clazz.getModifiers())) {
-            return methods;
-        }
+            Object cacheEntry = cache.get(methodKey);
 
-        /*
-         * No luck - the class is not public, so we're going the longer way.
-         */
-
-        MethodInfo[] methodInfos = new MethodInfo[methods.length];
-
-        for (int i = methods.length; i-- > 0;) {
-            methodInfos[i] = new MethodInfo(methods[i]);
-        }
-
-        int upcastCount = getAccessibleMethods(clazz, methodInfos, 0);
-
-        /*
-         * Reallocate array in case some method had no accessible counterpart.
-         */
-
-        if (upcastCount < methods.length) {
-            methods = new Method[upcastCount];
-        }
-
-        int j = 0;
-        for (int i = 0; i < methodInfos.length; ++i) {
-            MethodInfo methodInfo = methodInfos[i];
-            if (methodInfo.upcast) {
-                methods[j++] = methodInfo.method;
+            // We looked this up before and failed.
+            if (cacheEntry == CACHE_MISS) {
+                return null;
             }
-        }
-        return methods;
-    }
-
-    /**
-     * Recursively finds a match for each method, starting with the class, and
-     * then searching the superclass and interfaces.
-     * 
-     * @param clazz Class to check
-     * @param methodInfos array of methods we are searching to match
-     * @param upcastCount current number of methods we have matched
-     * @return count of matched methods
-     */
-    private static int getAccessibleMethods(Class clazz, MethodInfo[] methodInfos, int upcastCount) {
-        int l = methodInfos.length;
-
-        /*
-         * if this class is public, then check each of the currently
-         * 'non-upcasted' methods to see if we have a match
-         */
 
-        if (Modifier.isPublic(clazz.getModifiers())) {
-            for (int i = 0; i < l && upcastCount < l; ++i) {
+            if (cacheEntry == null) {
                 try {
-                    MethodInfo methodInfo = methodInfos[i];
-
-                    if (!methodInfo.upcast) {
-                        methodInfo.tryUpcasting(clazz);
-                        upcastCount++;
-                    }
-                } catch (NoSuchMethodException e) {
+                    // That one is expensive...
+                    cacheEntry = methodMap.find(name, params);
+                }
+                catch (MethodMap.AmbiguousException ae) {
                     /*
-                     * Intentionally ignored - it means it wasn't found in the
-                     * current class
+                     *  that's a miss :-)
                      */
+                    cache.put(methodKey, CACHE_MISS);
+                    throw ae;
                 }
-            }
 
-            /*
-             * Short circuit if all methods were upcast
-             */
-
-            if (upcastCount == l) {
-                return upcastCount;
+                cache.put(methodKey,
+                        (cacheEntry != null) ? cacheEntry : CACHE_MISS);
             }
-        }
-
-        /*
-         * Examine superclass
-         */
 
-        Class superclazz = clazz.getSuperclass();
+            // Yes, this might just be null.
 
-        if (superclazz != null) {
-            upcastCount = getAccessibleMethods(superclazz, methodInfos, upcastCount);
-
-            /*
-             * Short circuit if all methods were upcast
-             */
-
-            if (upcastCount == l) {
-                return upcastCount;
-            }
+            return (Method) cacheEntry;
         }
 
-        /*
-         * Examine interfaces. Note we do it even if superclazz == null. This is
-         * redundant as currently java.lang.Object does not implement any
-         * interfaces, however nothing guarantees it will not in future.
-         */
+        public synchronized void put(Method method) {
+            String methodKey = makeMethodKey(method);
 
-        Class[] interfaces = clazz.getInterfaces();
-
-        for (int i = interfaces.length; i-- > 0;) {
-            upcastCount = getAccessibleMethods(interfaces[i], methodInfos, upcastCount);
-
-            /*
-             * Short circuit if all methods were upcast
-             */
-
-            if (upcastCount == l) {
-                return upcastCount;
+            // We don't overwrite methods. Especially not if we fill the
+            // cache from defined class towards java.lang.Object because
+            // abstract methods in superclasses would else overwrite concrete
+            // classes further down the hierarchy.
+            if (cache.get(methodKey) == null) {
+                cache.put(methodKey, method);
+                methodMap.add(method);
             }
         }
 
-        return upcastCount;
-    }
-
-    /**
-     * For a given method, retrieves its publicly accessible counterpart. This
-     * method will look for a method with same name and signature declared in a
-     * public superclass or implemented interface of this method's declaring
-     * class. This counterpart method is publicly callable.
-     * 
-     * @param method a method whose publicly callable counterpart is requested.
-     * @return the publicly callable counterpart method. Note that if the
-     *         parameter method is itself declared by a public class, this
-     *         method is an identity function.
-     */
-    public static Method getPublicMethod(Method method) {
-        Class clazz = method.getDeclaringClass();
-
-        /*
-         * Short circuit for (hopefully the majority of) cases where the
-         * declaring class is public.
+        /**
+         * Make a methodKey for the given method using
+         * the concatenation of the name and the
+         * types of the method parameters.
+         *
+         * @param method to be stored as key
+         * @return key for ClassMap
          */
+        private String makeMethodKey(final Method method) {
+            Class[] parameterTypes = method.getParameterTypes();
 
-        if ((clazz.getModifiers() & Modifier.PUBLIC) != 0) {
-            return method;
-        }
-
-        return getPublicMethod(clazz, method.getName(), method.getParameterTypes());
-    }
-
-    /**
-     * Looks up the method with specified name and signature in the first public
-     * superclass or implemented interface of the class.
-     * 
-     * @param class the class whose method is sought
-     * @param name the name of the method
-     * @param paramTypes the classes of method parameters
-     */
-    private static Method getPublicMethod(Class clazz, String name, Class[] paramTypes) {
-        /*
-         * if this class is public, then try to get it
-         */
+            StringBuffer methodKey = new StringBuffer(method.getName());
 
-        if ((clazz.getModifiers() & Modifier.PUBLIC) != 0) {
-            try {
-                return clazz.getMethod(name, paramTypes);
-            } catch (NoSuchMethodException e) {
+            for (int j = 0; j < parameterTypes.length; j++) {
                 /*
-                 * If the class does not have the method, then neither its
-                 * superclass nor any of its interfaces has it so quickly return
-                 * null.
+                 * If the argument type is primitive then we want
+                 * to convert our primitive type signature to the
+                 * corresponding Object type so introspection for
+                 * methods with primitive types will work correctly.
+                 *
+                 * The lookup map (convertPrimitives) contains all eight
+                 * primitives (boolean, byte, char, double, float, int, long, short)
+                 * known to Java. So it should never return null for the key passed in.
                  */
-                return null;
+                if (parameterTypes[j].isPrimitive()) {
+                    methodKey.append((String) convertPrimitives.get(parameterTypes[j]));
+                } else {
+                    methodKey.append(parameterTypes[j].getName());
+                }
             }
-        }
-
-        /*
-         * try the superclass
-         */
-
-        Class superclazz = clazz.getSuperclass();
 
-        if (superclazz != null) {
-            Method superclazzMethod = getPublicMethod(superclazz, name, paramTypes);
-
-            if (superclazzMethod != null) {
-                return superclazzMethod;
-            }
+            return methodKey.toString();
         }
 
-        /*
-         * and interfaces
-         */
+        private String makeMethodKey(String method, Object[] params) {
+            StringBuffer methodKey = new StringBuffer().append(method);
 
-        Class[] interfaces = clazz.getInterfaces();
+            for (int j = 0; j < params.length; j++) {
+                Object arg = params[j];
 
-        for (int i = 0; i < interfaces.length; ++i) {
-            Method interfaceMethod = getPublicMethod(interfaces[i], name, paramTypes);
+                if (arg == null) {
+                    arg = OBJECT;
+                }
 
-            if (interfaceMethod != null) {
-                return interfaceMethod;
+                methodKey.append(arg.getClass().getName());
             }
-        }
-
-        return null;
-    }
-
-    /**
-     * Used for the iterative discovery process for public methods.
-     */
-    private static final class MethodInfo {
-        Method method;
-
-        String name;
-
-        Class[] parameterTypes;
-
-        boolean upcast;
-
-        MethodInfo(Method method) {
-            this.method = null;
-            name = method.getName();
-            parameterTypes = method.getParameterTypes();
-            upcast = false;
-        }
 
-        void tryUpcasting(Class clazz) throws NoSuchMethodException {
-            method = clazz.getMethod(name, parameterTypes);
-            name = null;
-            parameterTypes = null;
-            upcast = true;
+            return methodKey.toString();
         }
     }
 }

Modified: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Info.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Info.java?rev=584046&r1=584045&r2=584046&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Info.java (original)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Info.java Thu Oct 11 22:14:37 2007
@@ -1,71 +1,89 @@
+package org.apache.commons.jexl.util.introspection;
+
 /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
  */
-package org.apache.commons.jexl.util.introspection;
 
 /**
- * Little class to carry in info such as template name, line and column for
- * information error reporting from the uberspector implementations
- * 
- * Taken from velocity for self-sufficiency.
- * 
- * @since 1.0
+ *  Little class to carry in info such as template name, line and column
+ *  for information error reporting from the uberspector implementations
+ *
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  * @version $Id$
  */
-public class Info {
-    /** line number. */
+public class Info
+{
     private int line;
-    /** column number. */
     private int column;
-    /** name. */
     private String templateName;
-    /** 
-     * Create info.
-     * @param tn template name
-     * @param l line number
-     * @param c column
-     */
-    public Info(String tn, int l, int c) {
-        templateName = tn;
-        line = l;
-        column = c;
+
+    /**
+     * @param source Usually a template name.
+     * @param line The line number from <code>source</code>.
+     * @param column The column number from <code>source</code>.
+     */
+    public Info(String source, int line, int column)
+    {
+        this.templateName = source;
+        this.line = line;
+        this.column = column;
     }
 
     /**
-     * Gets the template name.
-     * @return template name
+     * Force callers to set the location information.
      */
-    public String getTemplateName() {
+    private Info()
+    {
+    }
+
+    /**
+     * @return The template name.
+     */
+    public String getTemplateName()
+    {
         return templateName;
     }
 
     /**
-     * Gets the line number.
-     * @return line number.
+     * @return The line number.
      */
-    public int getLine() {
+    public int getLine()
+    {
         return line;
     }
 
     /**
-     * Gets the column number.
-     * @return the column.
+     * @return The column number.
      */
-    public int getColumn() {
+    public int getColumn()
+    {
         return column;
+    }
+
+    /**
+     * Formats a textual representation of this object as <code>SOURCE
+     * [line X, column Y]</code>.
+     *
+     * @return String representing this object.
+     */
+    public String toString()
+    {
+        return getTemplateName() + " [line " + getLine() + ", column " +
+            getColumn() + ']';
     }
 }

Added: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectionUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectionUtils.java?rev=584046&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectionUtils.java (added)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectionUtils.java Thu Oct 11 22:14:37 2007
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl.util.introspection;
+
+/**
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
+ * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @author Nathan Bubna
+ * @version $Id: IntrospectionUtils.java 476785 2006-11-19 10:06:21Z henning $
+ */
+public class IntrospectionUtils {
+
+    /**
+     * Determines whether a type represented by a class object is
+     * convertible to another type represented by a class object using a
+     * method invocation conversion, treating object types of primitive
+     * types as if they were primitive types (that is, a Boolean actual
+     * parameter type matches boolean primitive formal type). This behavior
+     * is because this method is used to determine applicable methods for
+     * an actual parameter list, and primitive types are represented by
+     * their object duals in reflective method calls.
+     *
+     * @param formal         the formal parameter type to which the actual
+     *                       parameter type should be convertible
+     * @param actual         the actual parameter type.
+     * @param possibleVarArg whether or not we're dealing with the last parameter
+     *                       in the method declaration
+     * @return true if either formal type is assignable from actual type,
+     *         or formal is a primitive type and actual is its corresponding object
+     *         type or an object type of a primitive type that can be converted to
+     *         the formal type.
+     */
+    public static boolean isMethodInvocationConvertible(Class formal,
+                                                        Class actual,
+                                                        boolean possibleVarArg) {
+        /* if it's a null, it means the arg was null */
+        if (actual == null && !formal.isPrimitive()) {
+            return true;
+        }
+
+        /* Check for identity or widening reference conversion */
+        if (actual != null && formal.isAssignableFrom(actual)) {
+            return true;
+        }
+
+        /* Check for boxing with widening primitive conversion. Note that
+         * actual parameters are never primitives. */
+        if (formal.isPrimitive()) {
+            if (formal == Boolean.TYPE && actual == Boolean.class)
+                return true;
+            if (formal == Character.TYPE && actual == Character.class)
+                return true;
+            if (formal == Byte.TYPE && actual == Byte.class)
+                return true;
+            if (formal == Short.TYPE &&
+                    (actual == Short.class || actual == Byte.class))
+                return true;
+            if (formal == Integer.TYPE &&
+                    (actual == Integer.class || actual == Short.class ||
+                            actual == Byte.class))
+                return true;
+            if (formal == Long.TYPE &&
+                    (actual == Long.class || actual == Integer.class ||
+                            actual == Short.class || actual == Byte.class))
+                return true;
+            if (formal == Float.TYPE &&
+                    (actual == Float.class || actual == Long.class ||
+                            actual == Integer.class || actual == Short.class ||
+                            actual == Byte.class))
+                return true;
+            if (formal == Double.TYPE &&
+                    (actual == Double.class || actual == Float.class ||
+                            actual == Long.class || actual == Integer.class ||
+                            actual == Short.class || actual == Byte.class))
+                return true;
+        }
+
+        /* Check for vararg conversion. */
+        if (possibleVarArg && formal.isArray()) {
+            if (actual.isArray()) {
+                actual = actual.getComponentType();
+            }
+            return isMethodInvocationConvertible(formal.getComponentType(),
+                    actual, false);
+        }
+        return false;
+    }
+
+    /**
+     * Determines whether a type represented by a class object is
+     * convertible to another type represented by a class object using a
+     * method invocation conversion, without matching object and primitive
+     * types. This method is used to determine the more specific type when
+     * comparing signatures of methods.
+     *
+     * @param formal         the formal parameter type to which the actual
+     *                       parameter type should be convertible
+     * @param actual         the actual parameter type.
+     * @param possibleVarArg whether or not we're dealing with the last parameter
+     *                       in the method declaration
+     * @return true if either formal type is assignable from actual type,
+     *         or formal and actual are both primitive types and actual can be
+     *         subject to widening conversion to formal.
+     */
+    public static boolean isStrictMethodInvocationConvertible(Class formal,
+                                                              Class actual,
+                                                              boolean possibleVarArg) {
+        /* we shouldn't get a null into, but if so */
+        if (actual == null && !formal.isPrimitive()) {
+            return true;
+        }
+
+        /* Check for identity or widening reference conversion */
+        if (formal.isAssignableFrom(actual)) {
+            return true;
+        }
+
+        /* Check for widening primitive conversion. */
+        if (formal.isPrimitive()) {
+            if (formal == Short.TYPE && (actual == Byte.TYPE))
+                return true;
+            if (formal == Integer.TYPE &&
+                    (actual == Short.TYPE || actual == Byte.TYPE))
+                return true;
+            if (formal == Long.TYPE &&
+                    (actual == Integer.TYPE || actual == Short.TYPE ||
+                            actual == Byte.TYPE))
+                return true;
+            if (formal == Float.TYPE &&
+                    (actual == Long.TYPE || actual == Integer.TYPE ||
+                            actual == Short.TYPE || actual == Byte.TYPE))
+                return true;
+            if (formal == Double.TYPE &&
+                    (actual == Float.TYPE || actual == Long.TYPE ||
+                            actual == Integer.TYPE || actual == Short.TYPE ||
+                            actual == Byte.TYPE))
+                return true;
+        }
+
+        /* Check for vararg conversion. */
+        if (possibleVarArg && formal.isArray()) {
+            if (actual.isArray()) {
+                actual = actual.getComponentType();
+            }
+            return isStrictMethodInvocationConvertible(formal.getComponentType(),
+                    actual, false);
+        }
+        return false;
+    }
+}

Propchange: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectionUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Introspector.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Introspector.java?rev=584046&r1=584045&r2=584046&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Introspector.java (original)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Introspector.java Thu Oct 11 22:14:37 2007
@@ -70,6 +70,7 @@
      *  @param logger a {@link Log}.
      */
     public Introspector(Log logger) {
+        super(logger);
         this.rlog = logger;
     }
 
@@ -83,9 +84,9 @@
      *               the parameters
      *
      * @return The desired Method object.
-     * @throws Exception if the superclass does.
+     * @throws IllegalArgumentException When the parameters passed in can not be used for introspection.
      */
-    public Method getMethod(Class c, String name, Object[] params) throws Exception {
+    public Method getMethod(Class c, String name, Object[] params) throws IllegalArgumentException {
         /*
          *  just delegate to the base class
          */

Modified: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java?rev=584046&r1=584045&r2=584046&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java (original)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java Thu Oct 11 22:14:37 2007
@@ -17,71 +17,83 @@
 
 package org.apache.commons.jexl.util.introspection;
 
-import java.util.Map;
-import java.util.Set;
+import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
-import java.lang.reflect.Method;
+import org.apache.commons.logging.Log;
 
 /**
  * This basic function of this class is to return a Method object for a
  * particular class given the name of a method and the parameters to the method
  * in the form of an Object[]
- * 
+ * <p/>
  * The first time the Introspector sees a class it creates a class method map
  * for the class in question. Basically the class method map is a Hastable where
  * Method objects are keyed by a concatenation of the method name and the names
  * of classes that make up the parameters.
- * 
+ *
  * For example, a method with the following signature:
- * 
+ *
  * public void method(String a, StringBuffer b)
- * 
+ *
  * would be mapped by the key:
- * 
+ *
  * "method" + "java.lang.String" + "java.lang.StringBuffer"
- * 
+ *
  * This mapping is performed for all the methods in a class and stored for
- * 
- * @since 1.0
+ *
  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
  * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
  * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
  * @author <a href="mailto:paulo.gaspar@krankikom.de">Paulo Gaspar</a>
+ * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
  * @version $Id$
+ * @since 1.0
  */
 public class IntrospectorBase {
+    private final Log rlog;
+
     /**
      * Holds the method maps for the classes we know about, keyed by Class
      * object.
      */
-    protected Map classMethodMaps = new HashMap();
+    protected final Map classMethodMaps = new HashMap();
 
     /**
      * Holds the qualified class names for the classes we hold in the
      * classMethodMaps hash.
      */
-    protected Set cachedClassNames = new HashSet();
+    private Set cachedClassNames = new HashSet();
+
+    public IntrospectorBase(Log rlog) {
+        this.rlog = rlog;
+    }
 
     /**
      * Gets the method defined by <code>name</code> and <code>params</code>
      * for the Class <code>c</code>.
-     * 
-     * @param c Class in which the method search is taking place
-     * @param name Name of the method being searched for
+     *
+     * @param c      Class in which the method search is taking place
+     * @param name   Name of the method being searched for
      * @param params An array of Objects (not Classes) that describe the the
-     *            parameters
-     * 
+     *               parameters
      * @return The desired Method object.
-     * @throws Exception on any logical error.
+     * @throws IllegalArgumentException     When the parameters passed in can not be used for introspection.
+     * @throws MethodMap.AmbiguousException When the method map contains more than one match for the requested signature.
      */
-    public Method getMethod(Class c, String name, Object[] params) throws Exception {
+    public Method getMethod(Class c, String name, Object[] params) throws IllegalArgumentException, MethodMap.AmbiguousException {
         if (c == null) {
-            throw new Exception("Introspector.getMethod(): Class method key was null: " + name);
+            throw new IllegalArgumentException("Introspector.getMethod(): Class method key was null: " + name);
+        }
+
+        if (params == null) {
+            throw new IllegalArgumentException("params object is null!");
         }
 
-        ClassMap classMap = null;
+        ClassMap classMap;
 
         synchronized (classMethodMaps) {
             classMap = (ClassMap) classMethodMaps.get(c);
@@ -116,7 +128,7 @@
      * @return a {@link ClassMap}
      */
     protected ClassMap createClassMap(Class c) {
-        ClassMap classMap = new ClassMap(c);
+        ClassMap classMap = new ClassMap(c,rlog);
         classMethodMaps.put(c, classMap);
         cachedClassNames.add(c.getName());
 

Modified: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java?rev=584046&r1=584045&r2=584046&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java (original)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java Thu Oct 11 22:14:37 2007
@@ -26,31 +26,37 @@
 import java.util.Map;
 
 /**
- * 
  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
  * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
  * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
- * @since 1.0
  * @version $Id$
+ * @since 1.0
  */
 public class MethodMap {
-    /** whether a method is more specific than a previously compared one. */
+    /**
+     * whether a method is more specific than a previously compared one.
+     */
     private static final int MORE_SPECIFIC = 0;
-    /** whether a method is less specific than a previously compared one. */
+    /**
+     * whether a method is less specific than a previously compared one.
+     */
     private static final int LESS_SPECIFIC = 1;
-    /** A method doesn't match a previously compared one. */
+    /**
+     * A method doesn't match a previously compared one.
+     */
     private static final int INCOMPARABLE = 2;
 
     /**
      * Keep track of all methods with the same name.
      */
-    protected Map methodByNameMap = new Hashtable();
+    Map methodByNameMap = new Hashtable();
 
     /**
      * Add a method to a list of methods by name. For a particular class we are
      * keeping track of all the methods with the same name.
+     *
      * @param method the method.
      */
     public void add(Method method) {
@@ -68,8 +74,8 @@
 
     /**
      * Return a list of methods with the same name.
-     * 
-     * @param key The method name.
+     *
+     * @param key
      * @return List list of methods
      */
     public List get(String key) {
@@ -78,28 +84,34 @@
 
     /**
      * <p>
-     * Find a method. Attempts to find the most specific applicable method using
-     * the algorithm described in the JLS section 15.12.2 (with the exception
-     * that it can't distinguish a primitive type argument from an object type
-     * argument, since in reflection primitive type arguments are represented by
-     * their object counterparts, so for an argument of type (say)
-     * java.lang.Integer, it will not be able to decide between a method that
-     * takes int and a method that takes java.lang.Integer as a parameter.
+     * Find a method.  Attempts to find the
+     * most specific applicable method using the
+     * algorithm described in the JLS section
+     * 15.12.2 (with the exception that it can't
+     * distinguish a primitive type argument from
+     * an object type argument, since in reflection
+     * primitive type arguments are represented by
+     * their object counterparts, so for an argument of
+     * type (say) java.lang.Integer, it will not be able
+     * to decide between a method that takes int and a
+     * method that takes java.lang.Integer as a parameter.
      * </p>
-     * 
+     *
      * <p>
-     * This turns out to be a relatively rare case where this is needed -
-     * however, functionality like this is needed.
+     * This turns out to be a relatively rare case
+     * where this is needed - however, functionality
+     * like this is needed.
      * </p>
-     * 
+     *
      * @param methodName name of method
-     * @param args the actual arguments with which the method is called
-     * @return the most specific applicable method, or null if no method is
-     *         applicable.
-     * @throws AmbiguousException if there is more than one maximally specific
-     *             applicable method
+     * @param args       the actual arguments with which the method is called
+     * @return the most specific applicable method, or null if no
+     *         method is applicable.
+     * @throws AmbiguousException if there is more than one maximally
+     *                            specific applicable method
      */
-    public Method find(String methodName, Object[] args) throws AmbiguousException {
+    public Method find(String methodName, Object[] args)
+            throws AmbiguousException {
         List methodList = get(methodName);
 
         if (methodList == null) {
@@ -113,32 +125,31 @@
             Object arg = args[i];
 
             /*
-             * if we are careful down below, a null argument goes in there so we
-             * can know that the null was passed to the method
+             * if we are careful down below, a null argument goes in there
+             * so we can know that the null was passed to the method
              */
-            classes[i] = arg == null ? null : arg.getClass();
+            classes[i] =
+                    arg == null ? null : arg.getClass();
         }
 
         return getMostSpecific(methodList, classes);
     }
 
     /**
-     * simple distinguishable exception, used when we run across ambiguous
-     * overloading.
-     */
-    public static class AmbiguousException extends Exception {
-        /** serialization version id jdk13 generated. */
-        static final long serialVersionUID = 8758118091728717367L;
+     * Simple distinguishable exception, used when
+     * we run across ambiguous overloading.  Caught
+     * by the introspector.
+     */
+    public static class AmbiguousException extends RuntimeException {
+        /**
+         * Version Id for serializable
+         */
+        private static final long serialVersionUID = -2314636505414551663L;
     }
 
-    /**
-     * Gets the most specific method from a list.
-     * @param methods list of {@link Method methods}
-     * @param classes argument types
-     * @return the most specific method, or null
-     * @throws AmbiguousException if there is more than one specific method
-     */
-    private static Method getMostSpecific(List methods, Class[] classes) throws AmbiguousException {
+
+    private static Method getMostSpecific(List methods, Class[] classes)
+            throws AmbiguousException {
         LinkedList applicables = getApplicables(methods, classes);
 
         if (applicables.isEmpty()) {
@@ -157,32 +168,38 @@
 
         LinkedList maximals = new LinkedList();
 
-        for (Iterator applicable = applicables.iterator(); applicable.hasNext();) {
+        for (Iterator applicable = applicables.iterator();
+             applicable.hasNext();) {
             Method app = (Method) applicable.next();
             Class[] appArgs = app.getParameterTypes();
             boolean lessSpecific = false;
 
-            for (Iterator maximal = maximals.iterator(); !lessSpecific && maximal.hasNext();) {
+            for (Iterator maximal = maximals.iterator();
+                 !lessSpecific && maximal.hasNext();) {
                 Method max = (Method) maximal.next();
 
                 switch (moreSpecific(appArgs, max.getParameterTypes())) {
-                    case MORE_SPECIFIC:
+                    case MORE_SPECIFIC: {
                         /*
                          * This method is more specific than the previously
                          * known maximally specific, so remove the old maximum.
                          */
+
                         maximal.remove();
                         break;
+                    }
 
-                    case LESS_SPECIFIC:
+                    case LESS_SPECIFIC: {
                         /*
                          * This method is less specific than some of the
                          * currently known maximally specific methods, so we
                          * won't add it into the set of maximally specific
                          * methods
                          */
+
                         lessSpecific = true;
                         break;
+                    }
                 }
             }
 
@@ -202,27 +219,43 @@
     /**
      * Determines which method signature (represented by a class array) is more
      * specific. This defines a partial ordering on the method signatures.
-     * 
+     *
      * @param c1 first signature to compare
      * @param c2 second signature to compare
-     * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if c1
-     *         is less specific than c2, INCOMPARABLE if they are incomparable.
+     * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
+     *         c1 is less specific than c2, INCOMPARABLE if they are incomparable.
      */
     private static int moreSpecific(Class[] c1, Class[] c2) {
         boolean c1MoreSpecific = false;
         boolean c2MoreSpecific = false;
 
+        // compare lengths to handle comparisons where the size of the arrays
+        // doesn't match, but the methods are both applicable due to the fact
+        // that one is a varargs method
+        if (c1.length > c2.length) {
+            return MORE_SPECIFIC;
+        }
+        if (c2.length > c1.length) {
+            return LESS_SPECIFIC;
+        }
+
+        // ok, move on and compare those of equal lengths
         for (int i = 0; i < c1.length; ++i) {
             if (c1[i] != c2[i]) {
-                c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible(c2[i], c1[i]);
-                c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible(c1[i], c2[i]);
+                boolean last = (i == c1.length - 1);
+                c1MoreSpecific =
+                        c1MoreSpecific ||
+                                isStrictConvertible(c2[i], c1[i], last);
+                c2MoreSpecific =
+                        c2MoreSpecific ||
+                                isStrictConvertible(c1[i], c2[i], last);
             }
         }
 
         if (c1MoreSpecific) {
             if (c2MoreSpecific) {
                 /*
-                 * Incomparable due to cross-assignable arguments (i.e.
+                 *  Incomparable due to cross-assignable arguments (i.e.
                  * foo(String, Object) vs. foo(Object, String))
                  */
 
@@ -237,8 +270,8 @@
         }
 
         /*
-         * Incomparable due to non-related arguments (i.e. foo(Runnable) vs.
-         * foo(Serializable))
+         * Incomparable due to non-related arguments (i.e.
+         * foo(Runnable) vs. foo(Serializable))
          */
 
         return INCOMPARABLE;
@@ -246,11 +279,11 @@
 
     /**
      * Returns all methods that are applicable to actual argument types.
-     * 
+     *
      * @param methods list of all candidate methods
      * @param classes the actual types of the arguments
-     * @return a list that contains only applicable methods (number of formal
-     *         and actual arguments matches, and argument types are assignable
+     * @return a list that contains only applicable methods (number of
+     *         formal and actual arguments matches, and argument types are assignable
      *         to formal types through a method invocation conversion).
      */
     private static LinkedList getApplicables(List methods, Class[] classes) {
@@ -268,155 +301,76 @@
     }
 
     /**
-     * Returns true if the supplied method is applicable to actual argument
-     * types.
-     * @param method the method to check
-     * @param classes possible argument types
-     * @return true if the arguments are applicable to the method.
+     * Returns true if the supplied method is applicable to actual
+     * argument types.
+     *
+     * @param method  method that will be called
+     * @param classes arguments to method
+     * @return true if method is applicable to arguments
      */
     private static boolean isApplicable(Method method, Class[] classes) {
         Class[] methodArgs = method.getParameterTypes();
 
-        if (methodArgs.length != classes.length) {
-            return false;
-        }
-
-        for (int i = 0; i < classes.length; ++i) {
-            if (!isMethodInvocationConvertible(methodArgs[i], classes[i])) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Determines whether a type represented by a class object is convertible to
-     * another type represented by a class object using a method invocation
-     * conversion, treating object types of primitive types as if they were
-     * primitive types (that is, a Boolean actual parameter type matches boolean
-     * primitive formal type). This behavior is because this method is used to
-     * determine applicable methods for an actual parameter list, and primitive
-     * types are represented by their object duals in reflective method calls.
-     * 
-     * @param formal the formal parameter type to which the actual parameter
-     *            type should be convertible
-     * @param actual the actual parameter type.
-     * @return true if either formal type is assignable from actual type, or
-     *         formal is a primitive type and actual is its corresponding object
-     *         type or an object type of a primitive type that can be converted
-     *         to the formal type.
-     */
-    private static boolean isMethodInvocationConvertible(Class formal, Class actual) {
-        /*
-         * if it's a null, it means the arg was null
-         */
-        if (actual == null && !formal.isPrimitive()) {
-            return true;
-        }
-
-        /*
-         * Check for identity or widening reference conversion
-         */
-
-        if (actual != null && formal.isAssignableFrom(actual)) {
-            return true;
-        }
-
-        /*
-         * Check for boxing with widening primitive conversion. Note that actual
-         * parameters are never primitives.
-         */
-
-        if (formal.isPrimitive()) {
-            if (formal == Boolean.TYPE && actual == Boolean.class) {
-                return true;
-            }
-            if (formal == Character.TYPE && actual == Character.class) {
-                return true;
-            }
-            if (formal == Byte.TYPE && actual == Byte.class) {
-                return true;
-            }
-            if (formal == Short.TYPE && (actual == Short.class || actual == Byte.class)) {
+        if (methodArgs.length > classes.length) {
+            // if there's just one more methodArg than class arg
+            // and the last methodArg is an array, then treat it as a vararg
+            if (methodArgs.length == classes.length + 1 &&
+                    methodArgs[methodArgs.length - 1].isArray()) {
                 return true;
+            } else {
+                return false;
             }
-            if (formal == Integer.TYPE && (actual == Integer.class || actual == Short.class || actual == Byte.class)) {
-                return true;
+        } else if (methodArgs.length == classes.length) {
+            // this will properly match when the last methodArg
+            // is an array/varargs and the last class is the type of array
+            // (e.g. String when the method is expecting String...)
+            for (int i = 0; i < classes.length; ++i) {
+                if (!isConvertible(methodArgs[i], classes[i], false)) {
+                    // if we're on the last arg and the method expects an array
+                    if (i == classes.length - 1 && methodArgs[i].isArray()) {
+                        // check to see if the last arg is convertible
+                        // to the array's component type
+                        return isConvertible(methodArgs[i], classes[i], true);
+                    }
+                    return false;
+                }
             }
-            if (formal == Long.TYPE
-                && (actual == Long.class || actual == Integer.class || actual == Short.class || actual == Byte.class)) {
-                return true;
+        } else if (methodArgs.length > 0) // more arguments given than the method accepts; check for varargs
+        {
+            // check that the last methodArg is an array
+            Class lastarg = methodArgs[methodArgs.length - 1];
+            if (!lastarg.isArray()) {
+                return false;
             }
-            if (formal == Float.TYPE
-                && (actual == Float.class || actual == Long.class || actual == Integer.class 
-                    || actual == Short.class || actual == Byte.class)) {
-                return true;
+
+            // check that they all match up to the last method arg
+            for (int i = 0; i < methodArgs.length - 1; ++i) {
+                if (!isConvertible(methodArgs[i], classes[i], false)) {
+                    return false;
+                }
             }
-            if (formal == Double.TYPE
-                && (actual == Double.class || actual == Float.class || actual == Long.class || actual == Integer.class
-                    || actual == Short.class || actual == Byte.class)) {
-                return true;
+
+            // check that all remaining arguments are convertible to the vararg type
+            Class vararg = lastarg.getComponentType();
+            for (int i = methodArgs.length - 1; i < classes.length; ++i) {
+                if (!isConvertible(vararg, classes[i], false)) {
+                    return false;
+                }
             }
         }
 
-        return false;
+        return true;
     }
 
-    /**
-     * Determines whether a type represented by a class object is convertible to
-     * another type represented by a class object using a method invocation
-     * conversion, without matching object and primitive types. This method is
-     * used to determine the more specific type when comparing signatures of
-     * methods.
-     * 
-     * @param formal the formal parameter type to which the actual parameter
-     *            type should be convertible
-     * @param actual the actual parameter type.
-     * @return true if either formal type is assignable from actual type, or
-     *         formal and actual are both primitive types and actual can be
-     *         subject to widening conversion to formal.
-     */
-    private static boolean isStrictMethodInvocationConvertible(Class formal, Class actual) {
-        /*
-         * we shouldn't get a null into, but if so
-         */
-        if (actual == null && !formal.isPrimitive()) {
-            return true;
-        }
-
-        /*
-         * Check for identity or widening reference conversion
-         */
-
-        if (formal.isAssignableFrom(actual)) {
-            return true;
-        }
-
-        /*
-         * Check for widening primitive conversion.
-         */
+    private static boolean isConvertible(Class formal, Class actual,
+                                         boolean possibleVarArg) {
+        return IntrospectionUtils.
+                isMethodInvocationConvertible(formal, actual, possibleVarArg);
+    }
 
-        if (formal.isPrimitive()) {
-            if (formal == Short.TYPE && (actual == Byte.TYPE)) {
-                return true;
-            }
-            if (formal == Integer.TYPE && (actual == Short.TYPE || actual == Byte.TYPE)) {
-                return true;
-            }
-            if (formal == Long.TYPE && (actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE)) {
-                return true;
-            }
-            if (formal == Float.TYPE
-                && (actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE)) {
-                return true;
-            }
-            if (formal == Double.TYPE
-                && (actual == Float.TYPE || actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE 
-                    || actual == Byte.TYPE)) {
-                return true;
-            }
-        }
-        return false;
+    private static boolean isStrictConvertible(Class formal, Class actual,
+                                               boolean possibleVarArg) {
+        return IntrospectionUtils.
+                isStrictMethodInvocationConvertible(formal, actual, possibleVarArg);
     }
 }

Modified: commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java?rev=584046&r1=584045&r2=584046&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java (original)
+++ commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java Thu Oct 11 22:14:37 2007
@@ -1,79 +1,79 @@
+package org.apache.commons.jexl.util.introspection;
+
 /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
  */
 
-package org.apache.commons.jexl.util.introspection;
-
 import java.util.Iterator;
 
 /**
  * 'Federated' introspection/reflection interface to allow the introspection
- * behavior in Velocity to be customized.
- * 
- * @since 1.0
+ *  behavior in Velocity to be customized.
+ *
  * @author <a href="mailto:geirm@apache.org">Geir Magusson Jr.</a>
  * @version $Id$
  */
-public interface Uberspect {
+public interface Uberspect
+{
     /**
-     * Initializer - will be called before use.
-     * @throws Exception on any error.
+     *  Initializer - will be called before use
+     * @throws Exception
      */
-    void init() throws Exception;
+    public void init() throws Exception;
 
     /**
-     * To support iteratives - #foreach().
-     * @param info template info.
-     * @param obj to get the iterator for.
-     * @throws Exception on any error.
-     * @return an iterator over obj.
+     *  To support iteratives - #foreach()
+     * @param obj
+     * @param info
+     * @return An Iterator.
+     * @throws Exception
      */
-    Iterator getIterator(Object obj, Info info) throws Exception;
+    public Iterator getIterator(Object obj, Info info) throws Exception;
 
     /**
-     * Returns a general method, corresponding to $foo.bar( $woogie ).
-     * @param obj the object
-     * @param method the method name
-     * @param args method arguments
-     * @param info template info
-     * @throws Exception on any error.
-     * @return a {@link VelMethod}.
+     *  Returns a general method, corresponding to $foo.bar( $woogie )
+     * @param obj
+     * @param method
+     * @param args
+     * @param info
+     * @return A Velocity Method.
+     * @throws Exception
      */
-    VelMethod getMethod(Object obj, String method, Object[] args, Info info) throws Exception;
+    public VelMethod getMethod(Object obj, String method, Object[] args, Info info) throws Exception;
 
     /**
-     * Property getter - returns VelPropertyGet appropos for #set($foo =
-     * $bar.woogie).
-     * @param obj the object to get the property from.
-     * @param identifier property name
-     * @param info template info
-     * @throws Exception on any error.
-     * @return a {@link VelPropertyGet}.
+     * Property getter - returns VelPropertyGet appropos for #set($foo = $bar.woogie)
+     * @param obj
+     * @param identifier
+     * @param info
+     * @return A Velocity Getter.
+     * @throws Exception
      */
-    VelPropertyGet getPropertyGet(Object obj, String identifier, Info info) throws Exception;
+    public VelPropertyGet getPropertyGet(Object obj, String identifier, Info info) throws Exception;
 
     /**
-     * Property setter - returns VelPropertySet appropos for #set($foo.bar =
-     * "geir").
-     * @param obj the object to get the property from.
-     * @param identifier property name
-     * @param arg value to set.
-     * @param info template info
-     * @throws Exception on any error.
-     * @return a {@link VelPropertySet}.
+     * Property setter - returns VelPropertySet appropos for #set($foo.bar = "geir")
+     * @param obj
+     * @param identifier
+     * @param arg
+     * @param info
+     * @return A Velocity Setter.
+     * @throws Exception
      */
-    VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info info) throws Exception;
+    public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info info) throws Exception;
 }