Index: main/java/java/beans/Statement.java =================================================================== --- main/java/java/beans/Statement.java (revision 422708) +++ main/java/java/beans/Statement.java (working copy) @@ -25,6 +25,10 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Vector; import java.security.AccessController; import java.security.PrivilegedAction; @@ -264,22 +268,22 @@ } /** - * Method lookup is performed initially in the current class - * then in superclass, then in super class of super class and so on. + * Searches for best matching method for given name and argument types. */ private static Method findMethod(Class targetClass, String methodName, Object[] arguments, boolean methodIsStatic) throws NoSuchMethodException { + Class[] argClasses = getClasses(arguments); - - Method result = null; - Method[] methods = targetClass.getDeclaredMethods(); - + Method[] methods = targetClass.getMethods(); + Vector foundMethods = new Vector(); + Method[] foundMethodsArr; + for (int i = 0; i < methods.length; ++i) { Method method = methods[i]; int mods = method.getModifiers(); - if (method.getName().equals(methodName) && Modifier.isPublic(mods) + if (method.getName().equals(methodName) && (methodIsStatic ? Modifier.isStatic(mods) : true)) { Class[] parameterTypes = method.getParameterTypes(); @@ -306,27 +310,22 @@ } if (found) { - result = method; - break; + foundMethods.addElement(method); } } } } - if (result == null) { - // let's look for this method in the super class - Class parent = targetClass.getSuperclass(); - - if (parent != null) { - result = findMethod(parent, methodName, arguments, - methodIsStatic); - } else { - throw new NoSuchMethodException("No method with name " - + methodName + " is found"); - } + if (foundMethods.size() == 0) { + throw new NoSuchMethodException("No method with name " + + methodName + " is found"); } + + foundMethodsArr = foundMethods.toArray(new Method[foundMethods.size()]); + Arrays.sort(foundMethodsArr, + new MethodComparator(methodName, argClasses)); - return result; + return foundMethodsArr[0]; } private static boolean isPrimitiveWrapper(Class wrapper, Class base) { @@ -339,13 +338,35 @@ || (base == float.class) && (wrapper == Float.class) || (base == double.class) && (wrapper == Double.class); } + + private static Class getPrimitiveWrapper(Class base) { + Class res = null; + + if (base == boolean.class) { + res = Boolean.class; + } else if (base == byte.class) { + res = Byte.class; + } else if (base == char.class) { + res = Character.class; + } else if (base == short.class) { + res = Short.class; + } else if (base == int.class) { + res = Integer.class; + } else if (base == long.class) { + res = Long.class; + } else if (base == float.class) { + res = Float.class; + } else if (base == double.class) { + res = Double.class; + } + return res; + } static String convertClassName(Class type) { Class componentType = type.getComponentType(); Class resultType = (componentType == null) ? type : componentType; String result = resultType.getName(); int k = result.lastIndexOf('.'); - ; if (k != -1 && k < result.length()) { result = result.substring(k + 1); @@ -393,4 +414,127 @@ return false; } } + + /** + * Comparator to determine which of two methods is "closer" to the reference + * method. + */ + static class MethodComparator implements Comparator { + + static int INFINITY = Integer.MAX_VALUE; + + private String referenceMethodName; + private Class[] referenceMethodArgumentTypes; + private HashMap cache; + + public MethodComparator(String refMethodName, Class[] refArgumentTypes) + { + this.referenceMethodName = refMethodName; + this.referenceMethodArgumentTypes = refArgumentTypes; + cache = new HashMap(); + } + + public int compare(Method m1, Method m2) { + Integer norm1 = cache.get(m1); + Integer norm2 = cache.get(m2); + + if (norm1 == null) { + norm1 = getNorm(m1); + cache.put(m1, norm1); + } + if (norm2 == null) { + norm2 = getNorm(m2); + cache.put(m2, norm2); + } + return (norm1 - norm2); + } + + /** + * Returns the norm for given method. The norm is the "distance" from + * the reference method to the given method. + * @param m the method to calculate the norm for + * @return norm of given method + */ + private int getNorm(Method m) { + String methodName = m.getName(); + Class[] argumentTypes = m.getParameterTypes(); + int totalNorm = 0; + + if (!referenceMethodName.equals(methodName) || + referenceMethodArgumentTypes.length != argumentTypes.length) { + return INFINITY; + } + + for (int i = 0; i < referenceMethodArgumentTypes.length; i++) { + + if (referenceMethodArgumentTypes[i] == null) { + if (argumentTypes[i].isPrimitive()) { + return INFINITY; + } else { + // doesn't affect the norm calculation if null + continue; + } + } + + if (referenceMethodArgumentTypes[i].isPrimitive()) + { + referenceMethodArgumentTypes[i] = + getPrimitiveWrapper(referenceMethodArgumentTypes[i]); + } + + if (argumentTypes[i].isPrimitive()) { + argumentTypes[i] = getPrimitiveWrapper(argumentTypes[i]); + } + + totalNorm += getDistance(referenceMethodArgumentTypes[i], + argumentTypes[i]); + } + + return totalNorm; + } + + /** + * Returns a "hierarchy distance" between two classes. + * @param clz1 + * @param clz2 should be superclass or superinterface of clz1 + * @return hierarchy distance from clz1 to clz2, Integer.MAX_VALUE if + * clz2 is not assignable from clz1. + */ + private static int getDistance(Class clz1, Class clz2) { + Class superClz; + int superDist = INFINITY; + + if (!clz2.isAssignableFrom(clz1)) { + return INFINITY; + } + if (clz1.getName().equals(clz2.getName())) { + return 0; + } + + superClz = clz1.getSuperclass(); + if (superClz != null) { + superDist = getDistance(superClz, clz2); + } + if (clz2.isInterface()) { + Class[] interfaces = clz1.getInterfaces(); + int bestDist = INFINITY; + + for (int i = 0; i < interfaces.length; i++) { + int curDist = getDistance(interfaces[i], clz2); + + if (curDist < bestDist) { + bestDist = curDist; + } + } + + if (superDist < bestDist) { + bestDist = superDist; + } + return (bestDist != INFINITY ? bestDist + 1 : INFINITY); + } else { + return (superDist != INFINITY ? superDist + 1 : INFINITY); + } + } + } + } Index: test/java/org/apache/harmony/beans/tests/java/beans/StatementTest.java =================================================================== --- test/java/org/apache/harmony/beans/tests/java/beans/StatementTest.java (revision 422708) +++ test/java/org/apache/harmony/beans/tests/java/beans/StatementTest.java (working copy) @@ -241,7 +241,6 @@ assertSame(oa, t.getArguments()); assertSame(arg1, t.getArguments()[0]); assertSame(arg2, t.getArguments()[1]); - System.out.println(t.toString()); assertEquals("Object.method(Object, \"string\");", t.toString()); } @@ -449,8 +448,11 @@ */ public void testExecute_OverloadedMethods() throws Exception { MockObject mo = new MockObject(false); - Object[] arguments = new Object[] { new Object() }; - Statement t = new Statement(mo, "method", arguments); + Object[] arguments; + Statement t; + + arguments = new Object[] { new Object() }; + t = new Statement(mo, "method", arguments); t.execute(); MockObject.assertCalled("method2", arguments); @@ -458,6 +460,11 @@ t = new Statement(mo, "method", arguments); t.execute(); MockObject.assertCalled("method3", arguments); + + arguments = new Object[] { new Integer(117) }; + t = new Statement(mo, "method", arguments); + t.execute(); + MockObject.assertCalled("method1-3", arguments); } /* @@ -864,9 +871,15 @@ receivedArguments.add(o); } + public void method(Number n) { + reset(); + calledMethod = "method1-2"; + receivedArguments.add(n); + } + public void method(Integer o) { reset(); - calledMethod = "method1-2"; + calledMethod = "method1-3"; receivedArguments.add(o); } @@ -881,7 +894,7 @@ calledMethod = "method3"; receivedArguments.add(o); } - + public void method(Object o, Object o2) { reset(); calledMethod = "method4";