/* * Copyright 2002-2014 the original author or authors. * * Licensed 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.springframework.util; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.MatcherAssert.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.LinkedList; import java.util.List; import junit.framework.TestCase; import org.springframework.tests.sample.objects.TestObject; import android.os.RemoteException; /** * @author Rob Harrop * @author Juergen Hoeller * @author Sam Brannen * @author Arjen Poutsma * @author Roy Clarkson */ public class ReflectionUtilsTests extends TestCase { public void testFindField() { Field field = ReflectionUtils.findField(TestObjectSubclassWithPublicField.class, "publicField", String.class); assertNotNull(field); assertEquals("publicField", field.getName()); assertEquals(String.class, field.getType()); assertTrue("Field should be public.", Modifier.isPublic(field.getModifiers())); field = ReflectionUtils.findField(TestObjectSubclassWithNewField.class, "prot", String.class); assertNotNull(field); assertEquals("prot", field.getName()); assertEquals(String.class, field.getType()); assertTrue("Field should be protected.", Modifier.isProtected(field.getModifiers())); field = ReflectionUtils.findField(TestObjectSubclassWithNewField.class, "name", String.class); assertNotNull(field); assertEquals("name", field.getName()); assertEquals(String.class, field.getType()); assertTrue("Field should be private.", Modifier.isPrivate(field.getModifiers())); } public void testSetField() { final TestObjectSubclassWithNewField testBean = new TestObjectSubclassWithNewField(); final Field field = ReflectionUtils.findField(TestObjectSubclassWithNewField.class, "name", String.class); ReflectionUtils.makeAccessible(field); ReflectionUtils.setField(field, testBean, "FooBar"); assertNotNull(testBean.getName()); assertEquals("FooBar", testBean.getName()); ReflectionUtils.setField(field, testBean, null); assertNull(testBean.getName()); } public void testSetFieldIllegal() { boolean success = false; try { final TestObjectSubclassWithNewField testBean = new TestObjectSubclassWithNewField(); final Field field = ReflectionUtils.findField(TestObjectSubclassWithNewField.class, "name", String.class); ReflectionUtils.setField(field, testBean, "FooBar"); } catch (IllegalStateException e) { success = true; } assertTrue("Expected IllegalStateException", success); } public void testInvokeMethod() throws Exception { String rob = "Rob Harrop"; TestObject bean = new TestObject(); bean.setName(rob); Method getName = TestObject.class.getMethod("getName", (Class[]) null); Method setName = TestObject.class.getMethod("setName", new Class[] { String.class }); Object name = ReflectionUtils.invokeMethod(getName, bean); assertEquals("Incorrect name returned", rob, name); String juergen = "Juergen Hoeller"; ReflectionUtils.invokeMethod(setName, bean, new Object[] { juergen }); assertEquals("Incorrect name set", juergen, bean.getName()); } public void testDeclaresException() throws Exception { Method remoteExMethod = A.class.getDeclaredMethod("foo", new Class[] { Integer.class }); assertFalse(ReflectionUtils.declaresException(remoteExMethod, NoSuchMethodException.class)); assertFalse(ReflectionUtils.declaresException(remoteExMethod, Exception.class)); Method illegalExMethod = B.class.getDeclaredMethod("bar", new Class[] { String.class }); assertTrue(ReflectionUtils.declaresException(illegalExMethod, IllegalArgumentException.class)); assertTrue(ReflectionUtils.declaresException(illegalExMethod, NumberFormatException.class)); assertFalse(ReflectionUtils.declaresException(illegalExMethod, IllegalStateException.class)); assertFalse(ReflectionUtils.declaresException(illegalExMethod, Exception.class)); } public void testCopySrcToDestinationOfIncorrectClass() { TestObject src = new TestObject(); String dest = new String(); try { ReflectionUtils.shallowCopyFieldState(src, dest); fail(); } catch (IllegalArgumentException ex) { // Ok } } public void testRejectsNullSrc() { TestObject src = null; String dest = new String(); try { ReflectionUtils.shallowCopyFieldState(src, dest); fail(); } catch (IllegalArgumentException ex) { // Ok } } public void testRejectsNullDest() { TestObject src = new TestObject(); String dest = null; try { ReflectionUtils.shallowCopyFieldState(src, dest); fail(); } catch (IllegalArgumentException ex) { // Ok } } public void testValidCopy() { TestObject src = new TestObject(); TestObject dest = new TestObject(); confirmValidCopy(src, dest); } public void testValidCopyOnSubTypeWithNewField() { TestObjectSubclassWithNewField src = new TestObjectSubclassWithNewField(); TestObjectSubclassWithNewField dest = new TestObjectSubclassWithNewField(); src.magic = 11; // Will check inherited fields are copied confirmValidCopy(src, dest); // Check subclass fields were copied assertEquals(src.magic, dest.magic); assertEquals(src.prot, dest.prot); } public void testValidCopyToSubType() { TestObject src = new TestObject(); TestObjectSubclassWithNewField dest = new TestObjectSubclassWithNewField(); dest.magic = 11; confirmValidCopy(src, dest); // Should have left this one alone assertEquals(11, dest.magic); } public void testValidCopyToSubTypeWithFinalField() { TestObjectSubclassWithFinalField src = new TestObjectSubclassWithFinalField(); TestObjectSubclassWithFinalField dest = new TestObjectSubclassWithFinalField(); // Check that this doesn't fail due to attempt to assign final confirmValidCopy(src, dest); } private void confirmValidCopy(TestObject src, TestObject dest) { src.setName("freddie"); src.setAge(15); src.setSpouse(new TestObject()); assertFalse(src.getAge() == dest.getAge()); ReflectionUtils.shallowCopyFieldState(src, dest); assertEquals(src.getAge(), dest.getAge()); assertEquals(src.getSpouse(), dest.getSpouse()); } public void testDoWithProtectedMethods() { ListSavingMethodCallback mc = new ListSavingMethodCallback(); ReflectionUtils.doWithMethods(TestObject.class, mc, new ReflectionUtils.MethodFilter() { @Override public boolean matches(Method m) { return Modifier.isProtected(m.getModifiers()); } }); assertFalse(mc.getMethodNames().isEmpty()); assertTrue("Must find protected method on Object", mc.getMethodNames().contains("clone")); assertTrue("Must find protected method on Object", mc.getMethodNames().contains("finalize")); assertFalse("Public, not protected", mc.getMethodNames().contains("hashCode")); assertFalse("Public, not protected", mc.getMethodNames().contains("absquatulate")); } public void testDuplicatesFound() { ListSavingMethodCallback mc = new ListSavingMethodCallback(); ReflectionUtils.doWithMethods(TestObjectSubclass.class, mc); int absquatulateCount = 0; for (String name : mc.getMethodNames()) { if (name.equals("absquatulate")) { ++absquatulateCount; } } assertEquals("Found 2 absquatulates", 2, absquatulateCount); } public void testFindMethod() throws Exception { assertNotNull(ReflectionUtils.findMethod(B.class, "bar", String.class)); assertNotNull(ReflectionUtils.findMethod(B.class, "foo", Integer.class)); assertNotNull(ReflectionUtils.findMethod(B.class, "getClass")); } // @Ignore("[SPR-8644] findMethod() does not currently support var-args") // @Test // public void findMethodWithVarArgs() throws Exception { // assertNotNull(ReflectionUtils.findMethod(B.class, "add", int.class, int.class, int.class)); // } public void testGetAllDeclaredMethods() throws Exception { class Foo { @Override public String toString() { return super.toString(); } } int toStringMethodCount = 0; for (Method method : ReflectionUtils.getAllDeclaredMethods(Foo.class)) { if (method.getName().equals("toString")) { toStringMethodCount++; } } assertThat(toStringMethodCount, is(2)); } public void testGetUniqueDeclaredMethods() throws Exception { class Foo { @Override public String toString() { return super.toString(); } } int toStringMethodCount = 0; for (Method method : ReflectionUtils.getUniqueDeclaredMethods(Foo.class)) { if (method.getName().equals("toString")) { toStringMethodCount++; } } assertThat(toStringMethodCount, is(1)); } public void testGetUniqueDeclaredMethods_withCovariantReturnType() throws Exception { class Parent { @SuppressWarnings("unused") public Number m1() { return new Integer(42); } } class Leaf extends Parent { @Override public Integer m1() { return new Integer(42); } } int m1MethodCount = 0; Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(Leaf.class); for (Method method : methods) { if (method.getName().equals("m1")) { m1MethodCount++; } } assertThat(m1MethodCount, is(1)); assertTrue(ObjectUtils.containsElement(methods, Leaf.class.getMethod("m1"))); assertFalse(ObjectUtils.containsElement(methods, Parent.class.getMethod("m1"))); } private static class ListSavingMethodCallback implements ReflectionUtils.MethodCallback { private List<String> methodNames = new LinkedList<String>(); private List<Method> methods = new LinkedList<Method>(); @Override public void doWith(Method m) throws IllegalArgumentException, IllegalAccessException { this.methodNames.add(m.getName()); this.methods.add(m); } public List<String> getMethodNames() { return this.methodNames; } @SuppressWarnings("unused") public List<Method> getMethods() { return this.methods; } } private static class TestObjectSubclass extends TestObject { @Override public void absquatulate() { throw new UnsupportedOperationException(); } } private static class TestObjectSubclassWithPublicField extends TestObject { @SuppressWarnings("unused") public String publicField = "foo"; } private static class TestObjectSubclassWithNewField extends TestObject { private int magic; protected String prot = "foo"; } private static class TestObjectSubclassWithFinalField extends TestObject { @SuppressWarnings("unused") private final String foo = "will break naive copy that doesn't exclude statics"; } private static class A { @SuppressWarnings("unused") private void foo(Integer i) throws RemoteException { } } @SuppressWarnings("unused") private static class B extends A { void bar(String s) throws IllegalArgumentException { } int add(int... args) { int sum = 0; for (int i = 0; i < args.length; i++) { sum += args[i]; } return sum; } } }