You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by di...@apache.org on 2007/10/28 09:18:57 UTC
svn commit: r589298 -
/commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java
Author: dion
Date: Sun Oct 28 01:18:56 2007
New Revision: 589298
URL: http://svn.apache.org/viewvc?rev=589298&view=rev
Log:
JEXL-25 bring across code from Peters update
Modified:
commons/proper/jexl/branches/2.0/src/java/org/apache/commons/jexl/util/introspection/MethodMap.java
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=589298&r1=589297&r2=589298&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 Sun Oct 28 01:18:56 2007
@@ -26,31 +26,39 @@
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();
+ // CSOFF: VisibilityModifier
+ Map methodByNameMap = new Hashtable();
+ // CSON: VisibilityModifier
/**
* 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,9 +76,9 @@
/**
* Return a list of methods with the same name.
- *
- * @param key The method name.
- * @return List list of methods
+ *
+ * @param key the name.
+ * @return List list of methods.
*/
public List get(String key) {
return (List) methodByNameMap.get(key);
@@ -78,28 +86,35 @@
/**
* <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
- */
- public Method find(String methodName, Object[] args) throws AmbiguousException {
+ * @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
+ */
+ // CSOFF: RedundantThrows
+ public Method find(String methodName, Object[] args)
+ throws AmbiguousException {
List methodList = get(methodName);
if (methodList == null) {
@@ -113,32 +128,40 @@
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);
- }
+ } // CSON: RedundantThrows
+
/**
- * simple distinguishable exception, used when we run across ambiguous
- * overloading.
+ * Simple distinguishable exception, used when
+ * we run across ambiguous overloading. Caught
+ * by the introspector.
*/
- public static class AmbiguousException extends Exception {
- /** serialization version id jdk13 generated. */
- static final long serialVersionUID = 8758118091728717367L;
+ public static class AmbiguousException extends RuntimeException {
+ /**
+ * Version Id for serializable.
+ */
+ private static final long serialVersionUID = -2314636505414551663L;
}
+
+ // CSOFF: RedundantThrows
/**
- * 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
+ * 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 methods, Class[] classes) throws AmbiguousException {
+ private static Method getMostSpecific(List methods, Class[] classes)
+ throws AmbiguousException {
LinkedList applicables = getApplicables(methods, classes);
if (applicables.isEmpty()) {
@@ -157,14 +180,17 @@
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();
+ // CSOFF: MissingSwitchDefault
switch (moreSpecific(appArgs, max.getParameterTypes())) {
case MORE_SPECIFIC:
/*
@@ -181,10 +207,11 @@
* won't add it into the set of maximally specific
* methods
*/
+
lessSpecific = true;
break;
}
- }
+ } // CSON: MissingSwitchDefault
if (!lessSpecific) {
maximals.addLast(app);
@@ -197,32 +224,47 @@
}
return (Method) 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.
+ * @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 +279,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 +288,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 +310,90 @@
}
/**
- * 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])) {
+ 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;
}
- }
-
- 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)) {
- 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;
+ // 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.
- */
+ * @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);
+ }
- 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;
+ /**
+ * @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);
}
}