// Copyright (c) 2003-present, Jodd Team (http://jodd.org) // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. package jodd.introspector; import jodd.bean.BeanUtil; import jodd.introspector.fixtures.Abean; import jodd.introspector.fixtures.Ac; import jodd.introspector.fixtures.Bbean; import jodd.introspector.fixtures.Bc; import jodd.introspector.fixtures.Cbean; import jodd.introspector.fixtures.Mojo; import jodd.introspector.fixtures.One; import jodd.introspector.fixtures.OneSub; import jodd.introspector.fixtures.Overload; import jodd.introspector.fixtures.TwoSub; import org.junit.Test; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.Comparator; import static org.junit.Assert.*; public class IntrospectorTest { @Test public void testBasic() { ClassDescriptor cd = ClassIntrospector.lookup(Abean.class); assertNotNull(cd); PropertyDescriptor[] properties = cd.getAllPropertyDescriptors(); int c = 0; for (PropertyDescriptor property : properties) { if (property.isFieldOnly()) continue; if (property.isPublic()) c++; } assertEquals(2, c); Arrays.sort(properties, new Comparator<PropertyDescriptor>() { public int compare(PropertyDescriptor o1, PropertyDescriptor o2) { return o1.getName().compareTo(o2.getName()); } }); PropertyDescriptor pd = properties[0]; assertEquals("fooProp", pd.getName()); assertNotNull(pd.getReadMethodDescriptor()); assertNotNull(pd.getWriteMethodDescriptor()); assertNotNull(pd.getFieldDescriptor()); pd = properties[1]; assertEquals("shared", pd.getName()); assertNull(pd.getReadMethodDescriptor()); assertNull(pd.getWriteMethodDescriptor()); assertNotNull(pd.getFieldDescriptor()); pd = properties[2]; assertEquals("something", pd.getName()); assertNotNull(pd.getReadMethodDescriptor()); assertNull(pd.getWriteMethodDescriptor()); assertNull(pd.getFieldDescriptor()); assertNotNull(cd.getPropertyDescriptor("fooProp", false)); assertNotNull(cd.getPropertyDescriptor("something", false)); assertNull(cd.getPropertyDescriptor("FooProp", false)); assertNull(cd.getPropertyDescriptor("Something", false)); assertNull(cd.getPropertyDescriptor("notExisting", false)); } @Test public void testExtends() { ClassDescriptor cd = ClassIntrospector.lookup(Bbean.class); assertNotNull(cd); PropertyDescriptor[] properties = cd.getAllPropertyDescriptors(); int c = 0; for (PropertyDescriptor property : properties) { if (property.isFieldOnly()) continue; if (property.isPublic()) c++; } assertEquals(2, c); c = 0; for (PropertyDescriptor property : properties) { if (property.isFieldOnly()) continue; c++; } assertEquals(3, c); assertEquals(4, properties.length); Arrays.sort(properties, new Comparator<PropertyDescriptor>() { public int compare(PropertyDescriptor o1, PropertyDescriptor o2) { return o1.getName().compareTo(o2.getName()); } }); PropertyDescriptor pd = properties[0]; assertEquals("boo", pd.getName()); assertNotNull(pd.getReadMethodDescriptor()); assertNotNull(pd.getWriteMethodDescriptor()); assertNotNull(pd.getFieldDescriptor()); assertFalse(pd.isFieldOnly()); pd = properties[1]; assertEquals("fooProp", pd.getName()); assertNotNull(pd.getReadMethodDescriptor()); assertNotNull(pd.getWriteMethodDescriptor()); assertNotNull(pd.getFieldDescriptor()); assertFalse(pd.isFieldOnly()); pd = properties[2]; assertEquals("shared", pd.getName()); assertNull(pd.getReadMethodDescriptor()); assertNull(pd.getWriteMethodDescriptor()); assertNotNull(pd.getFieldDescriptor()); assertTrue(pd.isFieldOnly()); pd = properties[3]; assertEquals("something", pd.getName()); assertNotNull(pd.getReadMethodDescriptor()); assertNull(pd.getWriteMethodDescriptor()); assertNull(pd.getFieldDescriptor()); assertFalse(pd.isFieldOnly()); assertNotNull(cd.getPropertyDescriptor("fooProp", false)); assertNotNull(cd.getPropertyDescriptor("something", false)); assertNull(cd.getPropertyDescriptor("FooProp", false)); assertNull(cd.getPropertyDescriptor("Something", false)); assertNull(cd.getPropertyDescriptor("notExisting", false)); assertNotNull(cd.getPropertyDescriptor("boo", true)); assertNull(cd.getPropertyDescriptor("boo", false)); } @Test public void testCtors() { ClassDescriptor cd = ClassIntrospector.lookup(Ac.class); CtorDescriptor[] ctors = cd.getAllCtorDescriptors(); int c = 0; for (CtorDescriptor ctor : ctors) { if (ctor.isPublic()) c++; } assertEquals(1, c); ctors = cd.getAllCtorDescriptors(); assertEquals(2, ctors.length); assertNotNull(cd.getDefaultCtorDescriptor(true)); assertNull(cd.getDefaultCtorDescriptor(false)); Constructor ctor = cd.getCtorDescriptor(new Class[] {Integer.class}, true).getConstructor(); assertNotNull(ctor); cd = ClassIntrospector.lookup(Bc.class); ctors = cd.getAllCtorDescriptors(); c = 0; for (CtorDescriptor ccc : ctors) { if (ccc.isPublic()) c++; } assertEquals(1, c); ctors = cd.getAllCtorDescriptors(); assertEquals(1, ctors.length); assertNull(cd.getDefaultCtorDescriptor(false)); assertNull(cd.getDefaultCtorDescriptor(true)); CtorDescriptor ctorDescriptor = cd.getCtorDescriptor(new Class[] {Integer.class}, true); assertNull(ctorDescriptor); ctor = cd.getCtorDescriptor(new Class[] {String.class}, true).getConstructor(); assertNotNull(ctor); } @Test public void testSameFieldDifferentClass() { ClassDescriptor cd = ClassIntrospector.lookup(Abean.class); FieldDescriptor fd = cd.getFieldDescriptor("shared", false); assertNull(fd); fd = cd.getFieldDescriptor("shared", true); assertNotNull(fd); ClassDescriptor cd2 = ClassIntrospector.lookup(Bbean.class); FieldDescriptor fd2 = cd2.getFieldDescriptor("shared", true); assertNotEquals(fd, fd2); assertEquals(fd.getField(), fd2.getField()); } @Test public void testPropertyMatches() { ClassDescriptor cd = ClassIntrospector.lookup(Cbean.class); PropertyDescriptor pd; pd = cd.getPropertyDescriptor("s1", false); assertNull(pd); pd = cd.getPropertyDescriptor("s1", true); assertFalse(pd.isPublic()); assertTrue(pd.getReadMethodDescriptor().isPublic()); assertFalse(pd.getWriteMethodDescriptor().isPublic()); assertNotNull(getPropertyGetterDescriptor(cd, "s1", false)); assertNull(getPropertySetterDescriptor(cd, "s1", false)); pd = cd.getPropertyDescriptor("s2", false); assertNull(pd); pd = cd.getPropertyDescriptor("s2", true); assertFalse(pd.isPublic()); assertFalse(pd.getReadMethodDescriptor().isPublic()); assertTrue(pd.getWriteMethodDescriptor().isPublic()); assertNull(getPropertyGetterDescriptor(cd, "s2", false)); assertNotNull(getPropertySetterDescriptor(cd, "s2", false)); pd = cd.getPropertyDescriptor("s3", false); assertNotNull(pd); pd = cd.getPropertyDescriptor("s3", true); assertTrue(pd.isPublic()); assertTrue(pd.getReadMethodDescriptor().isPublic()); assertTrue(pd.getWriteMethodDescriptor().isPublic()); assertNotNull(getPropertyGetterDescriptor(cd, "s3", false)); assertNotNull(getPropertySetterDescriptor(cd, "s3", false)); } @Test public void testOverload() { ClassDescriptor cd = ClassIntrospector.lookup(Overload.class); PropertyDescriptor[] pds = cd.getAllPropertyDescriptors(); assertEquals(1, pds.length); PropertyDescriptor pd = pds[0]; assertNotNull(pd.getFieldDescriptor()); assertNotNull(pd.getReadMethodDescriptor()); assertNull(pd.getWriteMethodDescriptor()); } @Test public void testSerialUid() { ClassDescriptor cd = ClassIntrospector.lookup(Bbean.class); assertNull(cd.getFieldDescriptor("serialVersionUID", true)); } @Test public void testStaticFieldsForProperties() { ClassDescriptor cd = ClassIntrospector.lookup(Mojo.class); FieldDescriptor[] fieldDescriptors = cd.getAllFieldDescriptors(); assertEquals(3, fieldDescriptors.length); MethodDescriptor[] methodDescriptors = cd.getAllMethodDescriptors(); assertEquals(2, methodDescriptors.length); PropertyDescriptor[] propertyDescriptor = cd.getAllPropertyDescriptors(); assertEquals(3, propertyDescriptor.length); int count = 0; for (PropertyDescriptor pd : propertyDescriptor) { if (pd.isFieldOnly()) { continue; } count++; } assertEquals(1, count); } @Test public void testPropertiesOneClass() throws InvocationTargetException, IllegalAccessException { ClassDescriptor cd = ClassIntrospector.lookup(One.class); PropertyDescriptor[] propertyDescriptors = cd.getAllPropertyDescriptors(); assertEquals(3, propertyDescriptors.length); assertEquals("fone", propertyDescriptors[0].getName()); assertEquals("ftwo", propertyDescriptors[1].getName()); assertEquals("not", propertyDescriptors[2].getName()); for (int i = 0; i < 3; i++) { assertNull(propertyDescriptors[i].getWriteMethodDescriptor()); if (i != 2) { assertNotNull(propertyDescriptors[i].getReadMethodDescriptor()); } else { assertNull(propertyDescriptors[i].getReadMethodDescriptor()); } assertNotNull(propertyDescriptors[i].getFieldDescriptor()); } // change value One one = new One(); Setter setter = propertyDescriptors[0].getSetter(true); setter.invokeSetter(one, "one!"); assertEquals("one!", one.getFone()); // fields FieldDescriptor[] fieldDescriptors = cd.getAllFieldDescriptors(); assertEquals(3, fieldDescriptors.length); // beanutil BeanUtil.declared.setProperty(one, "fone", "!!!"); assertEquals("!!!", one.getFone()); // change value 2 setter = propertyDescriptors[2].getSetter(true); setter.invokeSetter(one, Long.valueOf("99")); assertEquals(99, one.whynot()); } @Test public void testPropertiesOneSubClass() throws InvocationTargetException, IllegalAccessException { ClassDescriptor cd = ClassIntrospector.lookup(OneSub.class); PropertyDescriptor[] propertyDescriptors = cd.getAllPropertyDescriptors(); assertEquals(2, propertyDescriptors.length); assertEquals("fone", propertyDescriptors[0].getName()); assertEquals("ftwo", propertyDescriptors[1].getName()); for (int i = 0; i < 2; i++) { assertNull(propertyDescriptors[i].getWriteMethodDescriptor()); assertNotNull(propertyDescriptors[i].getReadMethodDescriptor()); assertNotNull(propertyDescriptors[i].getFieldDescriptor()); } // change value OneSub one = new OneSub(); Setter setter = propertyDescriptors[0].getSetter(true); setter.invokeSetter(one, "one!"); assertEquals("one!", one.getFone()); // fields FieldDescriptor[] fieldDescriptors = cd.getAllFieldDescriptors(); assertEquals(1, fieldDescriptors.length); assertEquals("ftwo", fieldDescriptors[0].getName()); // beanutil BeanUtil.declared.setProperty(one, "fone", "!!!"); assertEquals("!!!", one.getFone()); } @Test public void testPropertiesTwoSubClass() throws InvocationTargetException, IllegalAccessException { ClassDescriptor cd = ClassIntrospector.lookup(TwoSub.class); PropertyDescriptor[] propertyDescriptors = cd.getAllPropertyDescriptors(); assertEquals(2, propertyDescriptors.length); assertEquals("fone", propertyDescriptors[0].getName()); assertEquals("ftwo", propertyDescriptors[1].getName()); for (int i = 0; i < 2; i++) { assertNull(propertyDescriptors[i].getWriteMethodDescriptor()); assertNotNull(propertyDescriptors[i].getReadMethodDescriptor()); assertNotNull(propertyDescriptors[i].getFieldDescriptor()); } // change value TwoSub one = new TwoSub(); Setter setter = propertyDescriptors[0].getSetter(true); setter.invokeSetter(one, "one!"); assertEquals("one!", one.getFone()); // fields FieldDescriptor[] fieldDescriptors = cd.getAllFieldDescriptors(); assertEquals(1, fieldDescriptors.length); assertEquals("ftwo", fieldDescriptors[0].getName()); // beanutil BeanUtil.declared.setProperty(one, "fone", "!!!"); assertEquals("!!!", one.getFone()); } MethodDescriptor getPropertySetterDescriptor(ClassDescriptor cd, String name, boolean declared) { PropertyDescriptor propertyDescriptor = cd.getPropertyDescriptor(name, true); if (propertyDescriptor != null) { MethodDescriptor setter = propertyDescriptor.getWriteMethodDescriptor(); if ((setter != null) && setter.matchDeclared(declared)) { return setter; } } return null; } MethodDescriptor getPropertyGetterDescriptor(ClassDescriptor cd, String name, boolean declared) { PropertyDescriptor propertyDescriptor = cd.getPropertyDescriptor(name, true); if (propertyDescriptor != null) { MethodDescriptor getter = propertyDescriptor.getReadMethodDescriptor(); if ((getter != null) && getter.matchDeclared(declared)) { return getter; } } return null; } }