/* * 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.lang3.reflect; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.mutable.Mutable; import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.ClassUtils.Interfaces; import org.apache.commons.lang3.reflect.testbed.GenericConsumer; import org.apache.commons.lang3.reflect.testbed.GenericParent; import org.apache.commons.lang3.reflect.testbed.StringParameterizedChild; import org.junit.Before; import org.junit.Test; /** * Unit tests MethodUtils * @version $Id$ */ public class MethodUtilsTest { private static interface PrivateInterface {} static class TestBeanWithInterfaces implements PrivateInterface { public String foo() { return "foo()"; } } public static class TestBean { public static String bar() { return "bar()"; } public static String bar(final int i) { return "bar(int)"; } public static String bar(final Integer i) { return "bar(Integer)"; } public static String bar(final double d) { return "bar(double)"; } public static String bar(final String s) { return "bar(String)"; } public static String bar(final Object o) { return "bar(Object)"; } public static void oneParameterStatic(final String s) { // empty } @SuppressWarnings("unused") private void privateStuff() { } public String foo() { return "foo()"; } public String foo(final int i) { return "foo(int)"; } public String foo(final Integer i) { return "foo(Integer)"; } public String foo(final double d) { return "foo(double)"; } public String foo(final String s) { return "foo(String)"; } public String foo(final Object o) { return "foo(Object)"; } public void oneParameter(final String s) { // empty } } private static class TestMutable implements Mutable<Object> { @Override public Object getValue() { return null; } @Override public void setValue(final Object value) { } } private TestBean testBean; private final Map<Class<?>, Class<?>[]> classCache = new HashMap<Class<?>, Class<?>[]>(); @Before public void setUp() throws Exception { testBean = new TestBean(); classCache.clear(); } @Test public void testConstructor() throws Exception { assertNotNull(MethodUtils.class.newInstance()); } @Test public void testInvokeMethod() throws Exception { assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY)); assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo", (Object[]) null)); assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo", (Object[]) null, (Class<?>[]) null)); assertEquals("foo(String)", MethodUtils.invokeMethod(testBean, "foo", "")); assertEquals("foo(Object)", MethodUtils.invokeMethod(testBean, "foo", new Object())); assertEquals("foo(Object)", MethodUtils.invokeMethod(testBean, "foo", Boolean.TRUE)); assertEquals("foo(Integer)", MethodUtils.invokeMethod(testBean, "foo", NumberUtils.INTEGER_ONE)); assertEquals("foo(int)", MethodUtils.invokeMethod(testBean, "foo", NumberUtils.BYTE_ONE)); assertEquals("foo(double)", MethodUtils.invokeMethod(testBean, "foo", NumberUtils.LONG_ONE)); assertEquals("foo(double)", MethodUtils.invokeMethod(testBean, "foo", NumberUtils.DOUBLE_ONE)); } @Test public void testInvokeExactMethod() throws Exception { assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY)); assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo", (Object[]) null)); assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo", (Object[]) null, (Class<?>[]) null)); assertEquals("foo(String)", MethodUtils.invokeExactMethod(testBean, "foo", "")); assertEquals("foo(Object)", MethodUtils.invokeExactMethod(testBean, "foo", new Object())); assertEquals("foo(Integer)", MethodUtils.invokeExactMethod(testBean, "foo", NumberUtils.INTEGER_ONE)); assertEquals("foo(double)", MethodUtils.invokeExactMethod(testBean, "foo", new Object[] { NumberUtils.DOUBLE_ONE }, new Class[] { Double.TYPE })); try { MethodUtils .invokeExactMethod(testBean, "foo", NumberUtils.BYTE_ONE); fail("should throw NoSuchMethodException"); } catch (final NoSuchMethodException e) { } try { MethodUtils .invokeExactMethod(testBean, "foo", NumberUtils.LONG_ONE); fail("should throw NoSuchMethodException"); } catch (final NoSuchMethodException e) { } try { MethodUtils.invokeExactMethod(testBean, "foo", Boolean.TRUE); fail("should throw NoSuchMethodException"); } catch (final NoSuchMethodException e) { } } @Test public void testInvokeStaticMethod() throws Exception { assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class, "bar", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY)); assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class, "bar", (Object[]) null)); assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class, "bar", (Object[]) null, (Class<?>[]) null)); assertEquals("bar(String)", MethodUtils.invokeStaticMethod( TestBean.class, "bar", "")); assertEquals("bar(Object)", MethodUtils.invokeStaticMethod( TestBean.class, "bar", new Object())); assertEquals("bar(Object)", MethodUtils.invokeStaticMethod( TestBean.class, "bar", Boolean.TRUE)); assertEquals("bar(Integer)", MethodUtils.invokeStaticMethod( TestBean.class, "bar", NumberUtils.INTEGER_ONE)); assertEquals("bar(int)", MethodUtils.invokeStaticMethod(TestBean.class, "bar", NumberUtils.BYTE_ONE)); assertEquals("bar(double)", MethodUtils.invokeStaticMethod( TestBean.class, "bar", NumberUtils.LONG_ONE)); assertEquals("bar(double)", MethodUtils.invokeStaticMethod( TestBean.class, "bar", NumberUtils.DOUBLE_ONE)); try { MethodUtils.invokeStaticMethod(TestBean.class, "does_not_exist"); fail("should throw NoSuchMethodException"); } catch (final NoSuchMethodException e) { } } @Test public void testInvokeExactStaticMethod() throws Exception { assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY)); assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", (Object[]) null)); assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", (Object[]) null, (Class<?>[]) null)); assertEquals("bar(String)", MethodUtils.invokeExactStaticMethod( TestBean.class, "bar", "")); assertEquals("bar(Object)", MethodUtils.invokeExactStaticMethod( TestBean.class, "bar", new Object())); assertEquals("bar(Integer)", MethodUtils.invokeExactStaticMethod( TestBean.class, "bar", NumberUtils.INTEGER_ONE)); assertEquals("bar(double)", MethodUtils.invokeExactStaticMethod( TestBean.class, "bar", new Object[] { NumberUtils.DOUBLE_ONE }, new Class[] { Double.TYPE })); try { MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", NumberUtils.BYTE_ONE); fail("should throw NoSuchMethodException"); } catch (final NoSuchMethodException e) { } try { MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", NumberUtils.LONG_ONE); fail("should throw NoSuchMethodException"); } catch (final NoSuchMethodException e) { } try { MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", Boolean.TRUE); fail("should throw NoSuchMethodException"); } catch (final NoSuchMethodException e) { } } @Test public void testGetAccessibleInterfaceMethod() throws Exception { final Class<?>[][] p = { ArrayUtils.EMPTY_CLASS_ARRAY, null }; for (final Class<?>[] element : p) { final Method method = TestMutable.class.getMethod("getValue", element); final Method accessibleMethod = MethodUtils.getAccessibleMethod(method); assertNotSame(accessibleMethod, method); assertSame(Mutable.class, accessibleMethod.getDeclaringClass()); } } @Test public void testGetAccessibleMethodPrivateInterface() throws Exception { final Method expected = TestBeanWithInterfaces.class.getMethod("foo"); assertNotNull(expected); final Method actual = MethodUtils.getAccessibleMethod(TestBeanWithInterfaces.class, "foo"); assertNull(actual); } @Test public void testGetAccessibleInterfaceMethodFromDescription() throws Exception { final Class<?>[][] p = { ArrayUtils.EMPTY_CLASS_ARRAY, null }; for (final Class<?>[] element : p) { final Method accessibleMethod = MethodUtils.getAccessibleMethod( TestMutable.class, "getValue", element); assertSame(Mutable.class, accessibleMethod.getDeclaringClass()); } } @Test public void testGetAccessiblePublicMethod() throws Exception { assertSame(MutableObject.class, MethodUtils.getAccessibleMethod( MutableObject.class.getMethod("getValue", ArrayUtils.EMPTY_CLASS_ARRAY)).getDeclaringClass()); } @Test public void testGetAccessiblePublicMethodFromDescription() throws Exception { assertSame(MutableObject.class, MethodUtils.getAccessibleMethod( MutableObject.class, "getValue", ArrayUtils.EMPTY_CLASS_ARRAY) .getDeclaringClass()); } @Test public void testGetAccessibleMethodInaccessible() throws Exception { final Method expected = TestBean.class.getDeclaredMethod("privateStuff"); final Method actual = MethodUtils.getAccessibleMethod(expected); assertNull(actual); } @Test public void testGetMatchingAccessibleMethod() throws Exception { expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", ArrayUtils.EMPTY_CLASS_ARRAY, ArrayUtils.EMPTY_CLASS_ARRAY); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", null, ArrayUtils.EMPTY_CLASS_ARRAY); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(String.class), singletonArray(String.class)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Object.class), singletonArray(Object.class)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Boolean.class), singletonArray(Object.class)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Byte.class), singletonArray(Integer.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Byte.TYPE), singletonArray(Integer.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Short.class), singletonArray(Integer.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Short.TYPE), singletonArray(Integer.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Character.class), singletonArray(Integer.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Character.TYPE), singletonArray(Integer.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Integer.class), singletonArray(Integer.class)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Integer.TYPE), singletonArray(Integer.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Long.class), singletonArray(Double.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Long.TYPE), singletonArray(Double.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Float.class), singletonArray(Double.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Float.TYPE), singletonArray(Double.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Double.class), singletonArray(Double.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Double.TYPE), singletonArray(Double.TYPE)); expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", singletonArray(Double.TYPE), singletonArray(Double.TYPE)); expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testOne", singletonArray(ParentObject.class), singletonArray(ParentObject.class)); expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testOne", singletonArray(ChildObject.class), singletonArray(ParentObject.class)); expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testTwo", singletonArray(ParentObject.class), singletonArray(GrandParentObject.class)); expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testTwo", singletonArray(ChildObject.class), singletonArray(ChildInterface.class)); } @Test public void testNullArgument() { expectMatchingAccessibleMethodParameterTypes(TestBean.class, "oneParameter", singletonArray(null), singletonArray(String.class)); } @Test public void testGetOverrideHierarchyIncludingInterfaces() { final Method method = MethodUtils.getAccessibleMethod(StringParameterizedChild.class, "consume", String.class); final Iterator<MethodDescriptor> expected = Arrays.asList(new MethodDescriptor(StringParameterizedChild.class, "consume", String.class), new MethodDescriptor(GenericParent.class, "consume", GenericParent.class.getTypeParameters()[0]), new MethodDescriptor(GenericConsumer.class, "consume", GenericConsumer.class.getTypeParameters()[0])) .iterator(); for (Method m : MethodUtils.getOverrideHierarchy(method, Interfaces.INCLUDE)) { assertTrue(expected.hasNext()); final MethodDescriptor md = expected.next(); assertEquals(md.declaringClass, m.getDeclaringClass()); assertEquals(md.name, m.getName()); assertEquals(md.parameterTypes.length, m.getParameterTypes().length); for (int i = 0; i < md.parameterTypes.length; i++) { assertTrue(TypeUtils.equals(md.parameterTypes[i], m.getGenericParameterTypes()[i])); } } assertFalse(expected.hasNext()); } @Test public void testGetOverrideHierarchyExcludingInterfaces() { final Method method = MethodUtils.getAccessibleMethod(StringParameterizedChild.class, "consume", String.class); final Iterator<MethodDescriptor> expected = Arrays.asList(new MethodDescriptor(StringParameterizedChild.class, "consume", String.class), new MethodDescriptor(GenericParent.class, "consume", GenericParent.class.getTypeParameters()[0])) .iterator(); for (Method m : MethodUtils.getOverrideHierarchy(method, Interfaces.EXCLUDE)) { assertTrue(expected.hasNext()); final MethodDescriptor md = expected.next(); assertEquals(md.declaringClass, m.getDeclaringClass()); assertEquals(md.name, m.getName()); assertEquals(md.parameterTypes.length, m.getParameterTypes().length); for (int i = 0; i < md.parameterTypes.length; i++) { assertTrue(TypeUtils.equals(md.parameterTypes[i], m.getGenericParameterTypes()[i])); } } assertFalse(expected.hasNext()); } private void expectMatchingAccessibleMethodParameterTypes(final Class<?> cls, final String methodName, final Class<?>[] requestTypes, final Class<?>[] actualTypes) { final Method m = MethodUtils.getMatchingAccessibleMethod(cls, methodName, requestTypes); assertTrue(toString(m.getParameterTypes()) + " not equals " + toString(actualTypes), Arrays.equals(actualTypes, m .getParameterTypes())); } private String toString(final Class<?>[] c) { return Arrays.asList(c).toString(); } private Class<?>[] singletonArray(final Class<?> c) { Class<?>[] result = classCache.get(c); if (result == null) { result = new Class[] { c }; classCache.put(c, result); } return result; } public static class InheritanceBean { public void testOne(final Object obj) {} public void testOne(final GrandParentObject obj) {} public void testOne(final ParentObject obj) {} public void testTwo(final Object obj) {} public void testTwo(final GrandParentObject obj) {} public void testTwo(final ChildInterface obj) {} } interface ChildInterface {} public static class GrandParentObject {} public static class ParentObject extends GrandParentObject {} public static class ChildObject extends ParentObject implements ChildInterface {} private static class MethodDescriptor { final Class<?> declaringClass; final String name; final Type[] parameterTypes; MethodDescriptor(Class<?> declaringClass, String name, Type... parameterTypes) { this.declaringClass = declaringClass; this.name = name; this.parameterTypes = parameterTypes; } } }