You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ra...@apache.org on 2009/08/01 02:36:52 UTC

svn commit: r799779 [3/3] - in /commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl: ./ context/ parser/ scripting/ util/ util/introspection/

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Info.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Info.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Info.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Info.java Sat Aug  1 00:36:51 2009
@@ -17,22 +17,22 @@
 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
+ * Little class to carry in info such as a file or template name, line and column for
+ * information error reporting from the uberspector implementations.
  * 
- * Taken from velocity for self-sufficiency.
+ * Originally taken from velocity for self-sufficiency.
  * 
  * @since 1.0
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  * @version $Id$
  */
-public class Info {
+public class Info implements DebugInfo {
     /** line number. */
-    private int line;
+    private final int line;
     /** column number. */
-    private int column;
+    private final int column;
     /** name. */
-    private String templateName;
+    private final String name;
     /** 
      * Create info.
      * @param tn template name
@@ -40,17 +40,49 @@
      * @param c column
      */
     public Info(String tn, int l, int c) {
-        templateName = tn;
+        name = tn;
         line = l;
         column = c;
     }
 
     /**
+     * Formats this info in the form 'name&#064;line:column'.
+     * @return the formatted info
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(name != null? name : "");
+        if (line > 0) {
+            sb.append("@");
+            sb.append(line);
+            if (column > 0) {
+                sb.append(":");
+                sb.append(column);
+            }
+        }
+        return sb.toString();
+    }
+    
+    /** {@inheritDoc} */
+    public String debugString() {
+        return toString();
+    }
+
+    /**
+     * Gets the file/script/url name.
+     * @return template name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
      * Gets the template name.
      * @return template name
      */
+    @Deprecated
     public String getTemplateName() {
-        return templateName;
+        return name;
     }
 
     /**

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Introspector.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Introspector.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Introspector.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Introspector.java Sat Aug  1 00:36:51 2009
@@ -14,36 +14,39 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.commons.jexl.util.introspection;
 
 import java.lang.reflect.Method;
-
+import java.lang.reflect.Constructor;
 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 Hashtable where Method objects are keyed by a
+ * is a map where Method objects are keyed by a
  * concatenation of the method name and the names of
  * classes that make up the parameters.
- *
+ * </p>
+ * <p>
  * For example, a method with the following signature:
- *
+ * <code>
  * public void method(String a, StringBuffer b)
- *
+ * </code>
  * would be mapped by the key:
- *
- * "method" + "java.lang.String" + "java.lang.StringBuffer"
- *
+ * <code>
+ * { "method", {"java.lang.String", "java.lang.StringBuffer" } }
+ * </code>
+ * </p>
+ * <p>
  * This mapping is performed for all the methods in a class
- * and stored for
+ * and stored for the lifetime of the instance. It is thus advised when possible
+ * to share one instance of this class accross Uberspect instances.
+ * </p>
  * @since 1.0
  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
  * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
@@ -51,60 +54,68 @@
  * @author <a href="mailto:paulo.gaspar@krankikom.de">Paulo Gaspar</a>
  * @version $Id$
  */
-public class Introspector extends IntrospectorBase {
+public final class Introspector extends IntrospectorBase {
     /**
      *  define a public string so that it can be looked for
      *  if interested.
      */
-
     public static final String CACHEDUMP_MSG =
         "Introspector : detected classloader change. Dumping cache.";
 
     /**
-     *  our engine runtime services.
-     */
-    private final Log rlog;
-
-    /**
-     *  Recieves our RuntimeServices object.
+     *  Creates a new instance.
      *  @param logger a {@link Log}.
      */
     public Introspector(Log logger) {
         super(logger);
-        this.rlog = logger;
     }
 
     /**
-     * Gets the method defined by <code>name</code> and
-     * <code>params</code> for the Class <code>c</code>.
+     * Gets the method defined by <code>key</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 params An array of Objects (not Classes) that describe the
-     *               the parameters
+     * @param key MethodKey of the method being searched for
      *
      * @return The desired Method object.
      * @throws IllegalArgumentException When the parameters passed in can not be used for introspection.
      * CSOFF: RedundantThrows
      */
     @Override
-    public Method getMethod(Class<?> c, String name, Object[] params) throws IllegalArgumentException {
+    public Method getMethod(Class<?> c, MethodKey key) throws IllegalArgumentException {
         //  just delegate to the base class
         try {
-            return super.getMethod(c, name, params);
-        } catch (MethodMap.AmbiguousException ae) {
+            return super.getMethod(c, key);
+        } catch (MethodKey.AmbiguousException ae) {
             // whoops.  Ambiguous.  Make a nice log message and return null...
-            StringBuilder msg = new StringBuilder("Introspection Error : Ambiguous method invocation ");
-            msg.append(name).append("( ");
-            for (int i = 0; i < params.length; i++) {
-                if (i > 0) {
-                    msg.append(", ");
-                }
-                msg.append(null == params[i] ? "null" : params[i].getClass().getName());
+            if (rlog != null) {
+                rlog.error("ambiguous method invocation: "
+                           + c.getName() + "."
+                           + key.debugString());
             }
-            msg.append(") for class ").append(c.getName());
+        }
+        return null;
+    }
+
+    // CSON: RedundantThrows
+    /**
+     * Gets the method defined by <code>key</code> for the Class <code>c</code>.
+     *
+     * @param key MethodKey of the method being searched for
+     *
+     * @return The desired Method object.
+     * @throws IllegalArgumentException When the parameters passed in can not be used for introspection.
+     * CSOFF: RedundantThrows
+     */
+    @Override
+    public Constructor<?> getConstructor(MethodKey key) throws IllegalArgumentException {
+        //  just delegate to the base class
+        try {
+            return super.getConstructor(key);
+        } catch (MethodKey.AmbiguousException ae) {
+            // whoops.  Ambiguous.  Make a nice log message and return null...
             if (rlog != null) {
-                rlog.error(msg.toString());
+                rlog.error("ambiguous constructor invocation: new "
+                           + key.debugString());
             }
         }
         return null;
@@ -119,4 +130,4 @@
         super.clearCache();
         rlog.info(CACHEDUMP_MSG);
     }
-}
\ No newline at end of file
+}

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java Sat Aug  1 00:36:51 2009
@@ -18,9 +18,15 @@
 package org.apache.commons.jexl.util.introspection;
 
 import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.ref.SoftReference;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.HashMap;
+import java.util.WeakHashMap;
 import java.util.Set;
+import java.util.List;
+import java.util.LinkedList;
 
 import org.apache.commons.logging.Log;
 
@@ -54,20 +60,26 @@
  */
 public class IntrospectorBase {
     /** the logger. */
-    private final Log rlog;
+    protected final Log rlog;
 
     /**
-     * Holds the method maps for the classes we know about, keyed by Class
-     * Made WeakHashMap so we wont prevent a class from being GCed.
-     * object.
+     * Holds the method maps for the classes we know about, keyed by Class.
+     * Implemented as WeakHashMap so we wont prevent an unused class from being GCed.
      */
-    protected final Map<Class<?>, ClassMap> classMethodMaps = new java.util.WeakHashMap<Class<?>, ClassMap>();
-
+    private final Map<Class<?>, ClassMap> classMethodMaps = new WeakHashMap<Class<?>, ClassMap>();
     /**
      * Holds the qualified class names for the classes we hold in the
      * classMethodMaps hash.
      */
     private Set<String> cachedClassNames = new HashSet<String>();
+    /**
+     * Holds the map of classes ctors we know about as well as unknown ones.
+     */
+    private final Map<MethodKey, Constructor<?>> constructorsMap = new HashMap<MethodKey, Constructor<?>>();
+    /**
+     * Holds the set of classes we have introspected.
+     */
+    private Map<String, SoftReference<Class<?>>> constructibleClasses = new HashMap<String, SoftReference<Class<?>>>();
 
     /**
      * Create the introspector.
@@ -78,33 +90,21 @@
     }
 
     /**
-     * Gets the method defined by <code>name</code> and <code>params</code>
-     * for the Class <code>c</code>.
+     * Gets the method defined by the <code>MethodKey</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 params An array of Objects (not Classes) that describe the the
-     *               parameters
+     * @param c     Class in which the method search is taking place
+     * @param key   Key of the method being searched for
      * @return The desired Method object.
      * @throws IllegalArgumentException     When the parameters passed in can not be used for introspection.
-     * @throws MethodMap.AmbiguousException When the method map contains more than 
+     * @throws MethodKey.AmbiguousException When the method map contains more than
      *  one match for the requested signature.
      *  CSOFF: RedundantThrows
      */
-    public Method getMethod(Class<?> c, String name, Object[] params)
-    throws IllegalArgumentException, MethodMap.AmbiguousException {
-        if (c == null) {
-            throw new IllegalArgumentException("Introspector.getMethod(): Class method key was null: " + name);
-        }
-
-        if (params == null) {
-            throw new IllegalArgumentException("params object is null!");
-        }
-
+    public Method getMethod(Class<?> c, MethodKey key) {
         ClassMap classMap = getMap(c);
+        return classMap.findMethod(key);
 
-        return classMap.findMethod(name, params);
-    } // CSON: RedundantThrows
+    }  // CSON: RedundantThrows
 
     /**
      * Gets the accessible methods names known for a given class.
@@ -121,6 +121,80 @@
     }
 
     /**
+     * A Constructor get cache-miss.
+     */
+    private static class CacheMiss {
+        /** The constructor used as cache-miss. */
+        public CacheMiss() {}
+    }
+    /** The cache-miss marker for the constructors map. */
+    private static final Constructor<?> CTOR_MISS = CacheMiss.class.getConstructors()[0];
+    
+    /**
+     * Gets the constructor defined by the <code>MethodKey</code>.
+     *
+     * @param key   Key of the constructor being searched for
+     * @return The desired Constructor object.
+     * @throws IllegalArgumentException     When the parameters passed in can not be used for introspection.
+     * @throws MethodKey.AmbiguousException When the method map contains more than
+     *  one match for the requested signature.
+     *  CSOFF: RedundantThrows
+     */
+    public Constructor<?> getConstructor(final MethodKey key) {
+        Constructor<?> ctor = null;
+        synchronized(constructorsMap) {
+            ctor = constructorsMap.get(key);
+            // that's a clear miss
+            if (CTOR_MISS.equals(ctor)) {
+                return null;
+            }
+            // let's introspect...
+            if (ctor == null) {
+                final String cname = key.getMethod();
+                // do we know about this class?
+                Class<?> clazz = null;
+                SoftReference<Class<?>> sc = constructibleClasses.get(cname);
+                if (sc != null) {
+                    clazz = sc.get();
+                    if (clazz == null) {
+                        // we knew about this class & it's gone, clear cache
+                        constructorsMap.clear();
+                        constructibleClasses = new HashMap<String, SoftReference<Class<?>>>();
+                    }
+                }
+                try {
+                    // do find the most specific ctor
+                    if (clazz == null) {
+                        clazz = Class.forName(cname);
+                        // add it to list of know loaded classes
+                        constructibleClasses.put(cname, new SoftReference<Class<?>>(clazz));
+                    }
+                    List<Constructor<?>> l = new LinkedList<Constructor<?>>();
+                    for(Constructor<?> ictor : clazz.getConstructors()) {
+                        l.add(ictor);
+                    }
+                    // try to find one
+                    ctor = MethodKey.CONSTRUCTORS.getMostSpecific(l, key.getParameters());
+                    if (ctor != null) {
+                        constructorsMap.put(key, ctor);
+                    } else {
+                        constructorsMap.put(key, CTOR_MISS);
+                    }
+                } catch(ClassNotFoundException xnotfound) {
+                    if (rlog.isDebugEnabled()) {
+                        rlog.debug("could not load class " + cname, xnotfound);
+                    }
+                    ctor = null;
+                } catch(MethodKey.AmbiguousException xambiguous) {
+                    rlog.warn("ambiguous ctor detected for " + cname, xambiguous);
+                    ctor = null;
+                }
+            }
+        }
+        return ctor;
+    }
+
+    /**
      * Gets the ClassMap for a given class.
      * @param c the class
      * @return the class map
@@ -156,7 +230,7 @@
      * @param c class.
      * @return a {@link ClassMap}
      */
-    protected ClassMap createClassMap(Class<?> c) {
+    private ClassMap createClassMap(Class<?> c) {
         ClassMap classMap = new ClassMap(c,rlog);
         classMethodMaps.put(c, classMap);
         cachedClassNames.add(c.getName());
@@ -168,15 +242,10 @@
      * Clears the classmap and classname caches.
      */
     protected void clearCache() {
-        /*
-         * since we are synchronizing on this object, we have to clear it rather
-         * than just dump it.
-         */
+        // since we are synchronizing on this object, we have to clear it rather
+        // than just dump it.
         classMethodMaps.clear();
-
-        /*
-         * for speed, we can just make a new one and let the old one be GC'd
-         */
+        // for speed, we can just make a new one and let the old one be GC'd
         cachedClassNames = new HashSet<String>();
     }
 }
\ No newline at end of file

Added: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java?rev=799779&view=auto
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java (added)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java Sat Aug  1 00:36:51 2009
@@ -0,0 +1,625 @@
+/*
+ * 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;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * A method key usable by the introspector cache.
+ * <p>
+ * This stores a method (or class) name and parameters.
+ * </p>
+ * <p>
+ * This replaces the original key scheme which used to build the key
+ * by concatenating the method name and parameters class names as one string
+ * with the exception that primitive types were converted to their object class equivalents.
+ * </p>
+ * <p>
+ * The key is still based on the same information, it is just wrapped in an object instead.
+ * Primitive type classes are converted to they object equivalent to make a key;
+ * int foo(int) and int foo(Integer) do generate the same key.
+ * </p>
+ * A key can be constructed either from arguments (array of objects) or from parameters
+ * (array of class).
+ * Roughly 3x faster than string key to access the map & uses less memory.
+ * 
+ * For the parameters methods:
+ * @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
+ */
+public final class MethodKey {
+    /** The hash code. */
+    private final int hashCode;
+    /** The method name. */
+    private final String method;
+    /** The parameters. */
+    private final Class<?>[] params;
+    /** A marker for empty parameter list. */
+    private static final Class<?>[] NOARGS = new Class<?>[0];
+    /** The hash code constants. */
+    private static final int HASH = 37;
+
+    /**
+     * Creates a key from a method.
+     * @param aMethod the method to generate the key from.
+     */
+    MethodKey(Method aMethod) {
+        this(aMethod.getName(), aMethod.getParameterTypes());
+    }
+
+    /**
+     * Creates a key from a method name and a set of arguments.
+     * @param aMethod the method to generate the key from
+     * @param args the intended method arguments
+     */
+    public MethodKey(String aMethod, Object[] args) {
+        super();
+        // !! keep this in sync with the other ctor (hash code) !!
+        this.method = aMethod;
+        int hash = this.method.hashCode();
+        final int size;
+        // CSOFF: InnerAssignment
+        if (args != null && (size = args.length) > 0) {
+            this.params = new Class<?>[size];
+            for (int p = 0; p < size; ++p) {
+                Object arg = args[p];
+                // null arguments use void as Void.class as marker
+                Class<?> parm = arg == null ? Void.class : arg.getClass();
+                hash = (HASH * hash) + parm.hashCode();
+                this.params[p] = parm;
+            }
+        } else {
+            this.params = NOARGS;
+        }
+        this.hashCode = hash;
+    }
+
+    /**
+     * Creates a key from a method name and a set of parameters.
+     * @param aMethod the method to generate the key from
+     * @param args the intended method parameters
+     */
+    MethodKey(String aMethod, Class<?>[] args) {
+        super();
+        // !! keep this in sync with the other ctor (hash code) !!
+        this.method = aMethod.intern();
+        int hash = this.method.hashCode();
+        final int size;
+        // CSOFF: InnerAssignment
+        if (args != null && (size = args.length) > 0) {
+            this.params = new Class<?>[size];
+            for (int p = 0; p < size; ++p) {
+                Class<?> parm = ClassMap.MethodCache.primitiveClass(args[p]);
+                hash = (HASH * hash) + parm.hashCode();
+                this.params[p] = parm;
+            }
+        } else {
+            this.params = NOARGS;
+        }
+        this.hashCode = hash;
+    }
+
+    /**
+     * Gets this key's method name.
+     * @return the method name
+     */
+    String getMethod() {
+        return method;
+    }
+
+    /**
+     * Gets this key's method parameter classes.
+     * @return the parameters
+     */
+    Class<?>[] getParameters() {
+        return params;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return hashCode;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object arg) {
+        if (arg instanceof MethodKey) {
+            MethodKey key = (MethodKey) arg;
+            return method.equals(key.method) && Arrays.equals(params, key.params);
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder(method);
+        for (Class<?> c : params) {
+            builder.append(c == Void.class ? "null" : c.getName());
+        }
+        return builder.toString();
+    }
+
+    /**
+     * Outputs a human readable debug representation of this key.
+     * @return method(p0, p1, ...)
+     */
+    public String debugString() {
+        StringBuilder builder = new StringBuilder(method);
+        builder.append('(');
+        for (int i = 0; i < params.length; i++) {
+            if (i > 0) {
+                builder.append(", ");
+            }
+            builder.append(Void.class == params[i] ? "null" : params[i].getName());
+        }
+        builder.append(')');
+        return builder.toString();
+    }
+
+    /**
+     * whether a method/ctor is more specific than a previously compared one.
+     */
+    private static final int MORE_SPECIFIC = 0;
+    /**
+     * whether a method/ctor is less specific than a previously compared one.
+     */
+    private static final int LESS_SPECIFIC = 1;
+    /**
+     * A method/ctor doesn't match a previously compared one.
+     */
+    private static final int INCOMPARABLE = 2;
+
+    /**
+     * 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 = -2314636505414551664L;
+    }
+
+    /**
+     * Utility for parameters matching.
+     * @param <T> method or contructor
+     */
+     public abstract static class Parameters<T> {
+        /**
+         * Extract the parameter types from its applicable argument.
+         * @param app a method or constructor
+         * @return the parameters
+         */
+        protected abstract Class<?>[] getParameterTypes(T app);
+
+        // CSOFF: RedundantThrows
+        /**
+         * Gets the most specific method that is applicable to actual argument types.
+         * @param methods a list of methods.
+         * @param classes list of argument types.
+         * @return the most specific method.
+         * @throws MethodKey.AmbiguousException if there is more than one.
+         */
+        protected T getMostSpecific(List<T> methods, Class<?>[] classes) {
+            LinkedList<T> applicables = getApplicables(methods, classes);
+
+            if (applicables.isEmpty()) {
+                return null;
+            }
+
+            if (applicables.size() == 1) {
+                return applicables.getFirst();
+            }
+
+            /*
+             * This list will contain the maximally specific methods. Hopefully at
+             * the end of the below loop, the list will contain exactly one method,
+             * (the most specific method) otherwise we have ambiguity.
+             */
+
+            LinkedList<T> maximals = new LinkedList<T>();
+
+            for (Iterator<T> applicable = applicables.iterator();
+                    applicable.hasNext();) {
+                T app = applicable.next();
+                Class<?>[] appArgs = getParameterTypes(app);
+
+                boolean lessSpecific = false;
+
+                for (Iterator<T> maximal = maximals.iterator();
+                        !lessSpecific && maximal.hasNext();) {
+                    T max = maximal.next();
+
+                    // CSOFF: MissingSwitchDefault
+                    switch (moreSpecific(appArgs, getParameterTypes(max))) {
+                        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:
+                            /*
+                             * 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;
+                    }
+                } // CSON: MissingSwitchDefault
+
+                if (!lessSpecific) {
+                    maximals.addLast(app);
+                }
+            }
+
+            if (maximals.size() > 1) {
+                // We have more than one maximally specific method
+                throw new AmbiguousException();
+            }
+
+            return maximals.getFirst();
+        } // CSON: RedundantThrows
+
+        /**
+         * 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.
+         */
+        private 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]) {
+                    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.
+                     * foo(String, Object) vs. foo(Object, String))
+                     */
+
+                    return INCOMPARABLE;
+                }
+
+                return MORE_SPECIFIC;
+            }
+
+            if (c2MoreSpecific) {
+                return LESS_SPECIFIC;
+            }
+
+            /*
+             * Incomparable due to non-related arguments (i.e.
+             * foo(Runnable) vs. foo(Serializable))
+             */
+
+            return INCOMPARABLE;
+        }
+
+        /**
+         * 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
+         *         to formal types through a method invocation conversion).
+         */
+        protected LinkedList<T> getApplicables(List<T> methods, Class<?>[] classes) {
+            LinkedList<T> list = new LinkedList<T>();
+
+            for (Iterator<T> imethod = methods.iterator(); imethod.hasNext();) {
+                T method = imethod.next();
+                if (isApplicable(method, classes)) {
+                    list.add(method);
+                }
+
+            }
+            return list;
+        }
+
+        /**
+         * 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 boolean isApplicable(T method, Class<?>[] classes) {
+            Class<?>[] methodArgs = getParameterTypes(method);
+
+            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
+                return methodArgs.length == classes.length + 1 && methodArgs[methodArgs.length - 1].isArray();
+            } 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;
+                    }
+                }
+            } 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;
+                }
+
+                // 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;
+                    }
+                }
+
+                // 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 true;
+        }
+
+        /**
+         * @see isInvocationConvertible(Class, Class, boolean)
+         * @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 see isMethodInvocationConvertible.
+         */
+        private boolean isConvertible(Class<?> formal, Class<?> actual,
+                boolean possibleVarArg) {
+            // if we see Void.class as the class of an argument most likely
+            // obtained through a MethodKey, we consider it
+            // as a wildcard; non primitives are thus convertible.
+            if (actual.equals(Void.class) && !formal.isPrimitive()) {
+                return true;
+            }
+            return isInvocationConvertible(formal, actual, possibleVarArg);
+        }
+
+        /**
+         * @see isStrictInvocationConvertible(Class, Class, boolean)
+         * @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 see isStrictMethodInvocationConvertible.
+         */
+        private boolean isStrictConvertible(Class<?> formal, Class<?> actual,
+                boolean possibleVarArg) {
+            return isStrictInvocationConvertible(formal, actual, possibleVarArg);
+        }
+
+    }
+    /**
+     * 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 isInvocationConvertible(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;
+        }
+
+        // CSOFF: NeedBraces
+        /* 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;
+        }
+        // CSON: NeedBraces
+
+        /* Check for vararg conversion. */
+        if (possibleVarArg && formal.isArray()) {
+            if (actual.isArray()) {
+                actual = actual.getComponentType();
+            }
+            return isInvocationConvertible(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 isStrictInvocationConvertible(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;
+        }
+
+        // CSOFF: NeedBraces
+        /* 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;
+        }
+        // CSON: NeedBraces
+
+        /* Check for vararg conversion. */
+        if (possibleVarArg && formal.isArray()) {
+            if (actual.isArray()) {
+                actual = actual.getComponentType();
+            }
+            return isStrictInvocationConvertible(formal.getComponentType(),
+                    actual, false);
+        }
+        return false;
+    }
+
+    /**
+     * The parameter matching service for methods.
+     */
+    public static final Parameters<Method> METHODS = new Parameters<Method>() {
+        protected Class<?>[] getParameterTypes(Method app) {
+            return app.getParameterTypes();
+        }
+    };
+
+
+    /**
+     * The parameter matching service for constructors.
+     */
+    public static final Parameters<Constructor<?>> CONSTRUCTORS = new Parameters<Constructor<?>>() {
+        protected Class<?>[] getParameterTypes(Constructor<?> app) {
+            return app.getParameterTypes();
+        }
+    };
+}

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodKey.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java Sat Aug  1 00:36:51 2009
@@ -20,8 +20,6 @@
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -34,20 +32,7 @@
  * @version $Id$
  * @since 1.0
  */
-public class MethodMap {
-    /**
-     * 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.
-     */
-    private static final int LESS_SPECIFIC = 1;
-    /**
-     * A method doesn't match a previously compared one.
-     */
-    private static final int INCOMPARABLE = 2;
-
+final class MethodMap {
     /**
      * Keep track of all methods with the same name.
      */
@@ -118,26 +103,26 @@
      * @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
+     * @throws MethodKey.AmbiguousException if there is more than one maximally
      *                            specific applicable method
      */
     // CSOFF: RedundantThrows
-    public Method find(String methodName, Object[] args) throws AmbiguousException {
-        return find(new ClassMap.MethodKey(methodName, args));
+    public Method find(String methodName, Object[] args) throws MethodKey.AmbiguousException {
+        return find(new MethodKey(methodName, args));
     }
 
     /**
      * Finds a method by key.
      * @param methodKey the key
      * @return the method
-     * @throws AmbiguousException if find is ambiguous
+     * @throws MethodKey.AmbiguousException if find is ambiguous
      */
-    Method find(ClassMap.MethodKey methodKey) throws AmbiguousException {
+    Method find(MethodKey methodKey) throws MethodKey.AmbiguousException {
         List<Method> methodList = get(methodKey.getMethod());
         if (methodList == null) {
             return null;
         }
-        return getMostSpecific(methodList, methodKey.getParameters());
+        return MethodKey.METHODS.getMostSpecific(methodList, methodKey.getParameters());
     } // CSON: RedundantThrows
 
 
@@ -154,247 +139,4 @@
     }
 
 
-    // CSOFF: RedundantThrows
-    /**
-     * Gets the most specific method that is applicable to actual argument types.
-     * @param methods a list of methods.
-     * @param classes list of argument types.
-     * @return the most specific method.
-     * @throws AmbiguousException if there is more than one.
-     */
-    private static Method getMostSpecific(List<Method> methods, Class<?>[] classes)
-            throws AmbiguousException {
-        LinkedList<Method> applicables = getApplicables(methods, classes);
-
-        if (applicables.isEmpty()) {
-            return null;
-        }
-
-        if (applicables.size() == 1) {
-            return applicables.getFirst();
-        }
-
-        /*
-         * This list will contain the maximally specific methods. Hopefully at
-         * the end of the below loop, the list will contain exactly one method,
-         * (the most specific method) otherwise we have ambiguity.
-         */
-
-        LinkedList<Method> maximals = new LinkedList<Method>();
-
-        for (Iterator<Method> applicable = applicables.iterator();
-             applicable.hasNext();) {
-            Method app = applicable.next();
-            Class<?>[] appArgs = app.getParameterTypes();
-            boolean lessSpecific = false;
-
-            for (Iterator<Method> maximal = maximals.iterator();
-                 !lessSpecific && maximal.hasNext();) {
-                Method max = maximal.next();
-
-                // CSOFF: MissingSwitchDefault
-                switch (moreSpecific(appArgs, max.getParameterTypes())) {
-                    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:
-                        /*
-                         * 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;
-                }
-            } // CSON: MissingSwitchDefault
-
-            if (!lessSpecific) {
-                maximals.addLast(app);
-            }
-        }
-
-        if (maximals.size() > 1) {
-            // We have more than one maximally specific method
-            throw new AmbiguousException();
-        }
-
-        return maximals.getFirst();
-    } // CSON: RedundantThrows
-
-
-    /**
-     * 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.
-     */
-    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]) {
-                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.
-                 * foo(String, Object) vs. foo(Object, String))
-                 */
-
-                return INCOMPARABLE;
-            }
-
-            return MORE_SPECIFIC;
-        }
-
-        if (c2MoreSpecific) {
-            return LESS_SPECIFIC;
-        }
-
-        /*
-         * Incomparable due to non-related arguments (i.e.
-         * foo(Runnable) vs. foo(Serializable))
-         */
-
-        return INCOMPARABLE;
-    }
-
-    /**
-     * 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
-     *         to formal types through a method invocation conversion).
-     */
-    private static LinkedList<Method> getApplicables(List<Method> methods, Class<?>[] classes) {
-        LinkedList<Method> list = new LinkedList<Method>();
-
-        for (Iterator<Method> imethod = methods.iterator(); imethod.hasNext();) {
-            Method method = imethod.next();
-            if (isApplicable(method, classes)) {
-                list.add(method);
-            }
-
-        }
-        return list;
-    }
-
-    /**
-     * 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) {
-            // if there's just one more methodArg than class arg
-            // and the last methodArg is an array, then treat it as a vararg
-            return methodArgs.length == classes.length + 1
-                && methodArgs[methodArgs.length - 1].isArray();
-        } 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;
-                }
-            }
-        } 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;
-            }
-
-            // 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;
-                }
-            }
-
-            // 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 true;
-    }
-
-    /**
-     * @see IntrospectionUtils#isMethodInvocationConvertible(Class, Class, boolean)
-     * @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 see isMethodInvocationConvertible.
-     */
-    private static boolean isConvertible(Class<?> formal, Class<?> actual,
-                                         boolean possibleVarArg) {
-        return IntrospectionUtils.
-                isMethodInvocationConvertible(formal, actual, possibleVarArg);
-    }
-
-    /**
-     * @see IntrospectionUtils#isStrictMethodInvocationConvertible(Class, Class, boolean)
-     * @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 see isStrictMethodInvocationConvertible.
-     */
-    private static boolean isStrictConvertible(Class<?> formal, Class<?> actual,
-                                               boolean possibleVarArg) {
-        return IntrospectionUtils.
-                isStrictMethodInvocationConvertible(formal, actual, possibleVarArg);
-    }
 }
\ No newline at end of file

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/Uberspect.java Sat Aug  1 00:36:51 2009
@@ -18,6 +18,7 @@
 package org.apache.commons.jexl.util.introspection;
 
 import java.util.Iterator;
+import java.lang.reflect.Constructor;
 
 /**
  * 'Federated' introspection/reflection interface to allow the introspection
@@ -28,11 +29,6 @@
  * @version $Id$
  */
 public interface Uberspect {
-    /**
-     * Initializer - will be called before use.
-     */
-    void init();
-
     /** Gets underlying introspector.
      * @return the introspector
      */
@@ -44,9 +40,17 @@
      * @param obj to get the iterator for.
      * @return an iterator over obj.
      */
-    Iterator<?> getIterator(Object obj, Info info);
+    Iterator<?> getIterator(Object obj, DebugInfo info);
 
     /**
+     * Returns a class constructor.
+     * @param ctorHandle a class or class name
+     * @param args constructor arguments
+     * @param info template info
+     * @return a {@link Constructor}.
+     */
+    Constructor<?> getConstructor(Object ctorHandle, Object[] args, DebugInfo info);
+    /**
      * Returns a general method, corresponding to $foo.bar( $woogie ).
      * @param obj the object
      * @param method the method name
@@ -54,7 +58,7 @@
      * @param info template info
      * @return a {@link VelMethod}.
      */
-    VelMethod getMethod(Object obj, String method, Object[] args, Info info);
+    VelMethod getMethod(Object obj, String method, Object[] args, DebugInfo info);
 
     /**
      * Property getter - returns VelPropertyGet appropos for #set($foo =
@@ -64,7 +68,7 @@
      * @param info template info
      * @return a {@link VelPropertyGet}.
      */
-    VelPropertyGet getPropertyGet(Object obj, String identifier, Info info);
+    VelPropertyGet getPropertyGet(Object obj, Object identifier, DebugInfo info);
 
     /**
      * Property setter - returns VelPropertySet appropos for #set($foo.bar =
@@ -75,5 +79,5 @@
      * @param info template info
      * @return a {@link VelPropertySet}.
      */
-    VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info info);
+    VelPropertySet getPropertySet(Object obj, Object identifier, Object arg, DebugInfo info);
 }
\ No newline at end of file

Modified: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java?rev=799779&r1=799778&r2=799779&view=diff
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java (original)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java Sat Aug  1 00:36:51 2009
@@ -17,77 +17,55 @@
 
 package org.apache.commons.jexl.util.introspection;
 
-import java.lang.reflect.Array;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
 import java.util.Collection;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.Map;
 
-import org.apache.commons.jexl.util.AbstractExecutor;
 import org.apache.commons.jexl.util.ArrayIterator;
-import org.apache.commons.jexl.util.ArrayListWrapper;
-import org.apache.commons.jexl.util.BooleanPropertyExecutor;
 import org.apache.commons.jexl.util.EnumerationIterator;
-import org.apache.commons.jexl.util.GetExecutor;
-import org.apache.commons.jexl.util.PropertyExecutor;
-import org.apache.commons.jexl.util.MapGetExecutor;
 import org.apache.commons.logging.Log;
 
 /**
  * Implementation of Uberspect to provide the default introspective
- * functionality of Velocity.
+ * functionality of JEXL.
+ * <p>This is the class to derive to customize introspection.</p>
  *
  * @since 1.0
  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
  * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
  * @version $Id$
  */
-public class UberspectImpl implements Uberspect, UberspectLoggable {
-    /**
-     * Index of the first character of the property.
-     */
-    private static final int PROPERTY_START_INDEX = 3;
-    /**
-     * Static signature for method(object,object).
-     */
-    static final Class<?>[] OBJECT_OBJECT = { Object.class, Object.class };
-    /**
-     * Our runtime logger.
-     */
-    private Log rlog;
-    /**
-     * The default Velocity introspector.
-     */
-    private Introspector introspector;
-    
+public class UberspectImpl extends org.apache.commons.jexl.util.Introspector implements Uberspect {
     /** {@inheritDoc} */
     public Introspector getIntrospector() {
         return introspector;
     }
+
     /**
-     * init - does nothing - we need to have setRuntimeLogger called before
-     * getting our introspector, as the default vel introspector depends upon
-     * it.
+     * Creates a new UberspectImpl.
+     * @param runtimeLogger the logger used for all logging needs
      */
-    public void init() {
+    public UberspectImpl(Log runtimeLogger) {
+        this(runtimeLogger, new Introspector(runtimeLogger));
     }
 
     /**
-     * Sets the runtime logger - this must be called before anything else
-     * besides init() as to get the logger. Makes the pull model appealing...
-     * @param runtimeLogger service to use for logging.
+     * Creates a new UberspectImpl.
+     * @param runtimeLogger the logger used for all logging needs
+     * @param intro the introspector to use
      */
-    public void setRuntimeLogger(Log runtimeLogger) {
-        rlog = runtimeLogger;
-        introspector = new Introspector(rlog);
+    public UberspectImpl(Log runtimeLogger, Introspector intro) {
+        super(runtimeLogger, intro);
     }
 
     /**
      * {@inheritDoc}
      */
-    public Iterator<?> getIterator(Object obj, Info i) {
+     public Iterator<?> getIterator(Object obj, DebugInfo i) {
         if (obj.getClass().isArray()) {
             return new ArrayIterator(obj);
         } else if (obj instanceof Collection<?>) {
@@ -95,18 +73,15 @@
         } else if (obj instanceof Map<?,?>) {
             return ((Map<?,?>) obj).values().iterator();
         } else if (obj instanceof Iterator<?>) {
-                rlog.warn("Warning! The iterative "
-                    + " is an Iterator in the #foreach() loop at [" + i.getLine() + ","
-                    + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable,"
-                    + " if used in more than once, this may lead to" + " unexpected results.");
+            rlog.warn(i.debugString()
+                    + "The iterative is not resetable; if used more than once, "
+                    + "this may lead to unexpected results.");
 
             return ((Iterator<?>) obj);
         } else if (obj instanceof Enumeration<?>) {
-                rlog.warn("Warning! The iterative "
-                    + " is an Enumeration in the #foreach() loop at [" + i.getLine() + ","
-                    + i.getColumn() + "]" + " in template " + i.getTemplateName() + ". Because it's not resetable,"
-                    + " if used in more than once, this may lead to" + " unexpected results.");
-
+            rlog.warn(i.debugString()
+                    + "The iterative is not resetable; if used more than once, "
+                    + "this may lead to unexpected results.");
             return new EnumerationIterator<Object>((Enumeration<Object>) obj);
         } else {
             // look for an iterator() method to support the JDK5 Iterable
@@ -119,12 +94,12 @@
                 if (Iterator.class.isAssignableFrom(returns)) {
                     return (Iterator<?>) iter.invoke(obj, (Object[])null);
                 } else {
-                    rlog.error("iterator() method of reference in #foreach loop at "
-                            + i + " does not return a true Iterator.");
+                    rlog.error(i.debugString()
+                            + "iterator() method does not return a true Iterator.");
                 }
-            // CSOFF: EmptyBlock    
+            // CSOFF: EmptyBlock
             } catch (NoSuchMethodException nsme) {
-                // eat this one, but let all other exceptions thru 
+                // eat this one, but let all other exceptions thru
             } catch (IllegalArgumentException e) { // CSON: EmptyBlock
                 throw new RuntimeException(e);
             } catch (IllegalAccessException e) {
@@ -135,8 +110,8 @@
         }
 
         /*  we have no clue what this is  */
-            rlog.warn("Could not determine type of iterator in " + "#foreach loop " + " at [" + i.getLine() + ","
-                + i.getColumn() + "]" + " in template " + i.getTemplateName());
+        rlog.warn(i.toString()
+                + "Could not determine type of iterator");
 
         return null;
     }
@@ -144,382 +119,28 @@
     /**
      * {@inheritDoc}
      */
-    public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i) {
-        if (obj == null) {
-            return null;
-        }
-
-        Method m = introspector.getMethod(obj.getClass(), methodName, args);
-        if (m != null) {
-            return new VelMethodImpl(m);
-        } else if (obj.getClass().isArray()) {
-            // check for support via our array->list wrapper
-            m = introspector.getMethod(ArrayListWrapper.class, methodName, args);
-            if (m != null) {
-                // and create a method that knows to wrap the value
-                // before invoking the method
-                return new VelMethodImpl(m, true);
-            }
-        } else if (obj instanceof Class<?>) {
-            m = introspector.getMethod((Class<?>) obj, methodName, args);
-        }
-
-        return (m == null) ? null : new VelMethodImpl(m);
-    }
+   public Constructor<?> getConstructor(Object ctorHandle, Object[] args, DebugInfo i) {
+        return getConstructor(ctorHandle, args);
+   }
 
     /**
      * {@inheritDoc}
      */
-    public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) {
-        AbstractExecutor executor;
-
-        Class<?> claz = obj.getClass();
-
-        /*
-         * first try for a getFoo() type of property (also getfoo() )
-         */
-
-        executor = new PropertyExecutor(rlog, introspector, claz, identifier);
-
-        /*
-         * Let's see if we are a map...
-         */
-        if (!executor.isAlive()) {
-            executor = new MapGetExecutor(rlog, claz, identifier);
-        }
-
-        /*
-         *  look for boolean isFoo()
-         */
-        if (!executor.isAlive()) {
-            executor = new BooleanPropertyExecutor(rlog, introspector, claz, identifier);
-        }
-
-        /*
-         * if that didn't work, look for get("foo")
-         */
-
-        if (!executor.isAlive()) {
-            executor = new GetExecutor(rlog, introspector, claz, identifier);
-        }
-
-        return new VelGetterImpl(executor);
+    public VelMethod getMethod(Object obj, String methodName, Object[] args, DebugInfo i) {
+        return getMethodExecutor(obj, methodName, args);
     }
 
     /**
      * {@inheritDoc}
      */
-    public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) {
-        Class<?> claz = obj.getClass();
-
-        VelMethod vm = null;
-        try {
-            /*
-             * first, we introspect for the set<identifier> setter method
-             */
-
-            Object[] params = {arg};
-
-            try {
-                vm = getMethod(obj, "set" + identifier, params, i);
-
-                if (vm == null) {
-                    throw new NoSuchMethodException();
-                }
-            } catch (NoSuchMethodException nsme2) {
-                StringBuilder sb = new StringBuilder("set");
-                sb.append(identifier);
-
-                if (Character.isLowerCase(sb.charAt(PROPERTY_START_INDEX))) {
-                    sb.setCharAt(PROPERTY_START_INDEX, Character.toUpperCase(sb.charAt(PROPERTY_START_INDEX)));
-                } else {
-                    sb.setCharAt(PROPERTY_START_INDEX, Character.toLowerCase(sb.charAt(PROPERTY_START_INDEX)));
-                }
-
-                vm = getMethod(obj, sb.toString(), params, i);
-
-                if (vm == null) {
-                    throw new NoSuchMethodException();
-                }
-            }
-        } catch (NoSuchMethodException nsme) {
-            /*
-             * right now, we only support the Map interface
-             */
-
-            if (Map.class.isAssignableFrom(claz)) {
-                vm = getMethod(obj, "put", OBJECT_OBJECT, i);
-                if (vm != null) {
-                    return new VelSetterImpl(vm, identifier);
-                }
-            }
-        }
-
-        return (vm == null) ? null : new VelSetterImpl(vm);
-    }
-
-    /**
-     * An implementation of {@link VelMethod}.
-     * CSOFF: VisibilityModifier
-     */
-    public static class VelMethodImpl implements VelMethod {
-        /** the method to run. */
-        final Method method;
-        /** whether the method is a vararg method. */
-        Boolean isVarArg;
-        /** whether the method is to wrap an array so that get(index),
-         * set(index) etc can be called on it.
-         */
-        final boolean wrapArray;
-
-        /**
-         * Create a new instance.
-         *
-         * @param m the method.
-         */
-        public VelMethodImpl(Method m) {
-            this(m, false);
-        }
-
-        // CSOFF: HiddenField
-        /**
-         * Create a new instance.
-         * @param method the method.
-         * @param wrapArray whether this wraps an array.
-         */
-        public VelMethodImpl(Method method, boolean wrapArray) {
-            this.method = method;
-            this.wrapArray = wrapArray;
-            // CSON: HiddenField
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public Object invoke(Object o, Object[] params) throws Exception {
-            if (isVarArg()) {
-                Class<?>[] formal = method.getParameterTypes();
-                int index = formal.length - 1;
-                Class<?> type = formal[index].getComponentType();
-                if (params.length >= index) {
-                    params = handleVarArg(type, index, params);
-                }
-            } else if (wrapArray) {
-                o = new ArrayListWrapper(o);
-            }
-
-            try {
-                return method.invoke(o, params);
-            } catch (InvocationTargetException e) {
-                final Throwable t = e.getTargetException();
-
-                if (t instanceof Exception) {
-                    throw (Exception) t;
-                } else if (t instanceof Error) {
-                    throw (Error) t;
-                } else {
-                    throw e;
-                }
-            }
-        }
-
-        /**
-         * @return true if this method can accept a variable number of arguments
-         */
-        public boolean isVarArg() {
-            if (isVarArg == null) {
-                Class<?>[] formal = method.getParameterTypes();
-                if (formal == null || formal.length == 0) {
-                    this.isVarArg = Boolean.FALSE;
-                } else {
-                    Class<?> last = formal[formal.length - 1];
-                    // if the last arg is an array, then
-                    // we consider this a varargs method
-                    this.isVarArg = Boolean.valueOf(last.isArray());
-                }
-            }
-            return isVarArg.booleanValue();
-        }
-
-        /**
-         * @param type   The vararg class type (aka component type
-         *               of the expected array arg)
-         * @param index  The index of the vararg in the method declaration
-         *               (This will always be one less than the number of
-         *               expected arguments.)
-         * @param actual The actual parameters being passed to this method
-         * @return The actual parameters adjusted for the varargs in order
-         * to fit the method declaration.
-         */
-        private Object[] handleVarArg(Class<?> type, int index, Object[] actual) {
-            // if no values are being passed into the vararg
-            if (actual.length == index) {
-                // create an empty array of the expected type
-                actual = new Object[]{Array.newInstance(type, 0)};
-            } else if (actual.length == index + 1) {
-                // if one value is being passed into the vararg
-                // make sure the last arg is an array of the expected type
-                if (IntrospectionUtils.isMethodInvocationConvertible(type,
-                        actual[index].getClass(),
-                        false)) {
-                    // create a 1-length array to hold and replace the last param
-                    Object lastActual = Array.newInstance(type, 1);
-                    Array.set(lastActual, 0, actual[index]);
-                    actual[index] = lastActual;
-                }
-            } else if (actual.length > index + 1) {
-                // if multiple values are being passed into the vararg
-                // put the last and extra actual in an array of the expected type
-                int size = actual.length - index;
-                Object lastActual = Array.newInstance(type, size);
-                for (int i = 0; i < size; i++) {
-                    Array.set(lastActual, i, actual[index + i]);
-                }
-
-                // put all into a new actual array of the appropriate size
-                Object[] newActual = new Object[index + 1];
-                for (int i = 0; i < index; i++) {
-                    newActual[i] = actual[i];
-                }
-                newActual[index] = lastActual;
-
-                // replace the old actual array
-                actual = newActual;
-            }
-            return actual;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public boolean isCacheable() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public String getMethodName() {
-            return method.getName();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public Class<?> getReturnType() {
-            return method.getReturnType();
-        }
-    } // CSON: VisibilityModifier
-
-
-    /**
-     * Concrete instance of VelPropertyGet.
-     */
-    public static class VelGetterImpl implements VelPropertyGet {
-        /**
-         * executor for performing the get.
-         */
-        protected final AbstractExecutor ae;
-
-        /**
-         * Create the getter using an {@link AbstractExecutor} to
-         * do the work.
-         *
-         * @param exec the executor.
-         */
-        public VelGetterImpl(AbstractExecutor exec) {
-            ae = exec;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public Object invoke(Object o) throws Exception {
-            return ae.execute(o);
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public boolean isCacheable() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public boolean isAlive() {
-            return ae.isAlive();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public String getMethodName() {
-            return ae.getMethod().getName();
-        }
+    public VelPropertyGet getPropertyGet(Object obj, Object identifier, DebugInfo i) {
+        return getGetExecutor(obj, identifier);
     }
 
     /**
-     * Concrete instance of VelPropertySet.
+     * {@inheritDoc}
      */
-    public static class VelSetterImpl implements VelPropertySet {
-        /**
-         * the method to call.
-         */
-        protected VelMethod vm = null;
-        /**
-         * the key for indexed and other properties.
-         */
-        protected String putKey = null;
-
-        /**
-         * Create an instance.
-         *
-         * @param velmethod the method to call on set.
-         */
-        public VelSetterImpl(VelMethod velmethod) {
-            this.vm = velmethod;
-        }
-
-        /**
-         * Create an instance.
-         *
-         * @param velmethod the method to call on set.
-         * @param key       the index or other value passed to a
-         *                  setProperty(xxx, value) method.
-         */
-        public VelSetterImpl(VelMethod velmethod, String key) {
-            this.vm = velmethod;
-            putKey = key;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public Object invoke(Object o, Object value) throws Exception {
-            if (putKey == null) {
-                Object[] a0 = { value };
-                return vm.invoke(o, a0);
-            } else {
-                Object[] a1 = { putKey, value };
-                return vm.invoke(o, a1);
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public boolean isCacheable() {
-            return true;
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public String getMethodName() {
-            return vm.getMethodName();
-        }
-
+    public VelPropertySet getPropertySet(final Object obj, final Object identifier, Object arg, DebugInfo i) {
+        return getSetExecutor(obj, identifier, arg);
     }
 }
\ No newline at end of file

Added: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html?rev=799779&view=auto
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html (added)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html Sat Aug  1 00:36:51 2009
@@ -0,0 +1,39 @@
+<html>
+ <head>
+  <title>Package Documentation for org.apache.commons.jexl.introspection Package</title>
+ </head>
+ <body bgcolor="white">
+  Provides low &amp; high-level introspective services.
+  <br><br>
+  <p>
+   <ul>
+    <li><a href="#low">Low level</a></li>
+    <li><a href="#high">High level</a></li>
+   </ul>
+  </p>
+  <h2><a name="low">Low level</a></h2>
+  <p>
+  The IntrospectorBase, Introspector, ClassMap, IntrospectionUtils form the
+  base of the introspection service. They allow to describe classes and their
+  methods, keeping them in a cache (@see Introspector) to speed up property
+  getters/setters and method discovery used during expression evaluation.
+  </p>
+  <p>
+  The cache materialized in Introspector creates one entry per class containing a map of all
+  accessible public methods keyed by name and signature.
+  </p>
+
+  <h2><a name="high">High level</a></h2>
+  
+  <p>
+  The Uberspect, VelMethod, VelPropertyGet and VelPropertySet interfaces
+  form the exposed face of introspective services.
+  </p>
+  <p>
+  The Uberspectimpl is the concrete class implementing the Uberspect interface.
+  Deriving from this class is the preferred way of augmenting Jexl introspective
+  capabilities when special needs to be fullfilled or when default behaviors
+  need to be modified.
+  </p>
+</body>
+</html>

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/package.html
------------------------------------------------------------------------------
    svn:mime-type = text/html

Added: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html
URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html?rev=799779&view=auto
==============================================================================
--- commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html (added)
+++ commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html Sat Aug  1 00:36:51 2009
@@ -0,0 +1,17 @@
+<html>
+ <head>
+  <title>Package Documentation for org.apache.commons.jexl Package</title>
+ </head>
+ <body bgcolor="white">
+  Provides utilities for introspection services.
+  <br><br>
+  <p>
+  This set of classes implement the various forms of setters and getters
+  used by Jexl. These are specialized forms for 'pure' properties, discovering
+  methods of the {s,g}etProperty form, for Maps, Lists and Duck-typing -
+  attempting to discover a 'get' or 'put' method, making an object walk and
+  quack.
+  </p>
+  
+</body>
+</html>

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/package.html
------------------------------------------------------------------------------
    svn:mime-type = text/html