diff options
2 files changed, 189 insertions, 12 deletions
diff --git a/framework/bundles/org.eclipse.ecf.sharedobject/src/org/eclipse/ecf/core/util/reflection/ClassUtil.java b/framework/bundles/org.eclipse.ecf.sharedobject/src/org/eclipse/ecf/core/util/reflection/ClassUtil.java index faca89130..61f6105ff 100644 --- a/framework/bundles/org.eclipse.ecf.sharedobject/src/org/eclipse/ecf/core/util/reflection/ClassUtil.java +++ b/framework/bundles/org.eclipse.ecf.sharedobject/src/org/eclipse/ecf/core/util/reflection/ClassUtil.java @@ -11,7 +11,7 @@ package org.eclipse.ecf.core.util.reflection; import java.lang.reflect.Method; -import java.util.Arrays; +import java.util.*; /** * @since 2.2 @@ -19,6 +19,19 @@ import java.util.Arrays; */ public class ClassUtil { + private static Map convertor = new HashMap(); + + static { + convertor.put(boolean.class, Boolean.class); + convertor.put(byte.class, Byte.class); + convertor.put(char.class, Character.class); + convertor.put(double.class, Double.class); + convertor.put(float.class, Float.class); + convertor.put(int.class, Integer.class); + convertor.put(long.class, Long.class); + convertor.put(short.class, Short.class); + } + /** * @param aClass The Class providing method under question (Must not be null) * @param aMethodName The method name to search for (Must not be null) @@ -54,25 +67,63 @@ public class ClassUtil { final int parameterCount = someParameterTypes.length; aMethodName = aMethodName.intern(); + final TreeSet matches = new TreeSet(new MethodComparator(someParameterTypes)); OUTER: for (int i = 0; i < candidates.length; i++) { - Method candidate = candidates[i]; - String candidateMethodName = candidate.getName().intern(); - Class[] candidateParameterTypes = candidate.getParameterTypes(); - int candidateParameterCount = candidateParameterTypes.length; + final Method candidate = candidates[i]; + final String candidateMethodName = candidate.getName().intern(); + final Class[] candidateParameterTypes = candidate.getParameterTypes(); + final int candidateParameterCount = candidateParameterTypes.length; if (candidateParameterCount == parameterCount && aMethodName == candidateMethodName) { for (int j = 0; j < candidateParameterCount; j++) { - Class clazzA = candidateParameterTypes[j]; - Class clazzB = someParameterTypes[j]; - // clazzA must be non-null, but clazzB could be null (null given as parameter value) - // so in that case we consider it a match and continue - if (!(clazzB == null || clazzA.isAssignableFrom(clazzB))) { + final Class clazzA = candidateParameterTypes[j]; + final Class clazzB = someParameterTypes[j]; + if (clazzB != null && !isAssignableFrom(clazzA, clazzB)) { continue OUTER; } } - return candidate; + matches.add(candidate); } } + // if no match has been found, fail with NSME - throw new NoSuchMethodException("No such method: " + aMethodName + "(" + Arrays.asList(someParameterTypes) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (matches.size() == 0) { + throw new NoSuchMethodException("No such method: " + aMethodName + "(" + Arrays.asList(someParameterTypes) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + return (Method) matches.first(); + } + + // extends Class.isAssingable(Class) with autoboxing + private static boolean isAssignableFrom(Class clazzA, Class clazzB) { + if (!(clazzA.isPrimitive() ^ clazzB.isPrimitive())) { + return clazzA.isAssignableFrom(clazzB); + } else if (clazzA.isPrimitive()) { + final Class oClazzA = (Class) convertor.get(clazzA); + return oClazzA.isAssignableFrom(clazzB); + } else { + final Class oClazzB = (Class) convertor.get(clazzB); + return clazzA.isAssignableFrom(oClazzB); + } + } + + private static class MethodComparator implements Comparator { + + private final Class[] parameterTypes; + + public MethodComparator(Class[] someParameterTypes) { + parameterTypes = someParameterTypes; + } + + public int compare(Object object1, Object object2) { + final Class[] pt1 = ((Method) object1).getParameterTypes(); + final Class[] pt2 = ((Method) object2).getParameterTypes(); + + if (Arrays.equals(pt1, pt2)) { + return 0; + } else if (Arrays.equals(parameterTypes, pt1)) { + return -1; + } else { + return 1; + } + } } } diff --git a/tests/bundles/org.eclipse.ecf.tests.sharedobject/src/org/eclipse/ecf/tests/sharedobject/util/reflection/ClassUtilTest.java b/tests/bundles/org.eclipse.ecf.tests.sharedobject/src/org/eclipse/ecf/tests/sharedobject/util/reflection/ClassUtilTest.java new file mode 100644 index 000000000..d70a7a55a --- /dev/null +++ b/tests/bundles/org.eclipse.ecf.tests.sharedobject/src/org/eclipse/ecf/tests/sharedobject/util/reflection/ClassUtilTest.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2010 Markus Alexander Kuppe. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Markus Alexander Kuppe (ecf-dev_eclipse.org <at> lemmster <dot> de) - initial API and implementation + ******************************************************************************/ +package org.eclipse.ecf.tests.sharedobject.util.reflection; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + +import junit.framework.TestCase; + +import org.eclipse.ecf.core.util.reflection.ClassUtil; + +public class ClassUtilTest extends TestCase { + + /** + * Test method for {@link org.eclipse.ecf.core.util.reflection.ClassUtil#getMethod(java.lang.Class, java.lang.String, java.lang.Class[])}. + */ + public void testGetPrimitiveMethodWithPrimitive() { + testGetMethod(new Class[] {int.class}, new Class[] {int.class}, new Object[]{new Integer(1)}); + } + + /** + * Test method for {@link org.eclipse.ecf.core.util.reflection.ClassUtil#getMethod(java.lang.Class, java.lang.String, java.lang.Class[])}. + */ + public void testGetPrimitiveMethodWithObject() { + testGetMethod(new Class[] {Integer.class}, new Class[] {int.class}, new Object[]{new Integer(1)}); + } + + /** + * Test method for {@link org.eclipse.ecf.core.util.reflection.ClassUtil#getMethod(java.lang.Class, java.lang.String, java.lang.Class[])}. + */ + public void testGetObjectMethodWithObject() { + testGetMethod(new Class[] {Long.class}, new Class[]{Long.class}, new Object[]{new Long(1L)}); + } + + /** + * Test method for {@link org.eclipse.ecf.core.util.reflection.ClassUtil#getMethod(java.lang.Class, java.lang.String, java.lang.Class[])}. + */ + public void testGetObjectMethodWithPrimitive() { + testGetMethod(new Class[] {long.class}, new Class[]{Long.class}, new Object[]{new Long(1L)}); + } + + /** + * Test method for {@link org.eclipse.ecf.core.util.reflection.ClassUtil#getMethod(java.lang.Class, java.lang.String, java.lang.Class[])}. + */ + public void testGetObjectMethodWhenBoth() { + testGetMethod(new Class[] {Boolean.class}, new Class[]{Boolean.class}, new Object[]{new Boolean(true)}); + } + + /** + * Test method for {@link org.eclipse.ecf.core.util.reflection.ClassUtil#getMethod(java.lang.Class, java.lang.String, java.lang.Class[])}. + */ + public void testGetPrimitiveMethodWhenBoth() { + testGetMethod(new Class[] {boolean.class}, new Class[]{boolean.class}, new Object[]{new Boolean(true)}); + } + + /** + * Test method for {@link org.eclipse.ecf.core.util.reflection.ClassUtil#getMethod(java.lang.Class, java.lang.String, java.lang.Class[])}. + */ + public void testGetMethodWithoutParams() { + testGetMethod(new Class[]{}, new Class[]{}, null); + } + + /** + * Test method for {@link org.eclipse.ecf.core.util.reflection.ClassUtil#getMethod(java.lang.Class, java.lang.String, java.lang.Class[])}. + */ + public void testGetObjectMethodFromSuperclassWithPrimitive() { + testGetMethod(new Class[]{float.class}, new Class[]{Float.class}, new Object[]{new Float(1.0)}); + } + + /** + * Test method for {@link org.eclipse.ecf.core.util.reflection.ClassUtil#getMethod(java.lang.Class, java.lang.String, java.lang.Class[])}. + */ + public void testGetPrimitiveMethodFromSuperclassWithObject() { + testGetMethod(new Class[]{Float.class}, new Class[]{Float.class}, new Object[]{new Float(1.0)}); + } + + // helper + private void testGetMethod(Class[] searchParameterTypes, Class[] expectedParameterTypes, Object[] params) { + Method method = null; + try { + method = ClassUtil.getMethod(TestClass.class, "foo", searchParameterTypes); + } catch (NoSuchMethodException e) { + fail("failed to match expected the method: " + e.getMessage()); + } + + final Class[] someParameterTypes = method.getParameterTypes(); + assertTrue("Parameters don't match", Arrays.equals(expectedParameterTypes, someParameterTypes)); + + try { + assertNotNull("executed method from superclass", method.invoke(new TestClass(), params)); + } catch (IllegalArgumentException e) { + fail(e.getMessage()); + } catch (IllegalAccessException e) { + fail(e.getMessage()); + } catch (InvocationTargetException e) { + fail(e.getMessage()); + } + } + + // helper class + class TestClass extends AbstractTestClass { + public String foo() {return "";} + public String foo(final int i) {return "";} + public String foo(final Long i) {return "";} + public String foo(final boolean b) {return "";} + public String foo(final Boolean b) {return "";} + } + + abstract class AbstractTestClass { + public String foo(final Float f) {return "";} + public String foo() {throw new UnsupportedOperationException();} + public String foo(final int i) {throw new UnsupportedOperationException();} + public String foo(final Long i) {throw new UnsupportedOperationException();} + public String foo(final boolean b) {throw new UnsupportedOperationException();}; + public String foo(final Boolean b) {throw new UnsupportedOperationException();}; + } +} |