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;
}