| /* |
| * 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 |
| * |
| * https://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.beanutils2; |
| |
| import static org.junit.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertFalse; |
| import static org.junit.jupiter.api.Assertions.assertInstanceOf; |
| import static org.junit.jupiter.api.Assertions.assertNotNull; |
| import static org.junit.jupiter.api.Assertions.assertNull; |
| import static org.junit.jupiter.api.Assertions.assertThrows; |
| import static org.junit.jupiter.api.Assertions.assertTrue; |
| |
| import java.beans.IntrospectionException; |
| import java.beans.PropertyDescriptor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.apache.commons.beanutils2.priv.PrivateBeanFactory; |
| import org.apache.commons.beanutils2.priv.PrivateDirect; |
| import org.apache.commons.beanutils2.priv.PublicSubBean; |
| import org.apache.commons.lang3.StringUtils; |
| import org.junit.jupiter.api.AfterEach; |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.Test; |
| |
| /** |
| * <p> |
| * Test Case for the PropertyUtils class. The majority of these tests use instances of the TestBean class, so be sure to update the tests if you change the |
| * characteristics of that class. |
| * </p> |
| * |
| * <p> |
| * So far, this test case has tests for the following methods of the {@code PropertyUtils} class: |
| * </p> |
| * <ul> |
| * <li>getIndexedProperty(Object,String)</li> |
| * <li>getIndexedProperty(Object,String,int)</li> |
| * <li>getMappedProperty(Object,String)</li> |
| * <li>getMappedProperty(Object,String,String</li> |
| * <li>getNestedProperty(Object,String)</li> |
| * <li>getPropertyDescriptor(Object,String)</li> |
| * <li>getPropertyDescriptors(Object)</li> |
| * <li>getPropertyType(Object,String)</li> |
| * <li>getSimpleProperty(Object,String)</li> |
| * <li>setIndexedProperty(Object,String,Object)</li> |
| * <li>setIndexedProperty(Object,String,String,Object)</li> |
| * <li>setMappedProperty(Object,String,Object)</li> |
| * <li>setMappedProperty(Object,String,String,Object)</li> |
| * <li>setNestedProperty(Object,String,Object)</li> |
| * <li>setSimpleProperty(Object,String,Object)</li> |
| * </ul> |
| */ |
| class PropertyUtilsTest { |
| |
| /** |
| * The fully qualified class name of our private directly implemented interface. |
| */ |
| private static final String PRIVATE_DIRECT_CLASS = "org.apache.commons.beanutils2.priv.PrivateDirect"; |
| |
| /** |
| * The fully qualified class name of our private indirectly implemented interface. |
| */ |
| private static final String PRIVATE_INDIRECT_CLASS = "org.apache.commons.beanutils2.priv.PrivateIndirect"; |
| |
| /** |
| * The fully qualified class name of our test bean class. |
| */ |
| private static final String TEST_BEAN_CLASS = "org.apache.commons.beanutils2.TestBean"; |
| |
| /** |
| * The set of property names we expect to have returned when calling {@code getPropertyDescriptors()}. You should update this list when new properties are |
| * added to TestBean. |
| */ |
| protected static final String[] properties = { "booleanProperty", "booleanSecond", "doubleProperty", "dupProperty", "floatProperty", "intArray", |
| "intIndexed", "intProperty", "listIndexed", "longProperty", "nested", "nullProperty", "readOnlyProperty", "shortProperty", "stringArray", |
| "stringIndexed", "stringProperty", "writeOnlyProperty", }; |
| |
| /** |
| * Finds the descriptor of the name property. |
| * |
| * @param desc The array with descriptors |
| * @return The found descriptor or null |
| */ |
| private static PropertyDescriptor findNameDescriptor(final PropertyDescriptor[] desc) { |
| for (final PropertyDescriptor element : desc) { |
| if (element.getName().equals("name")) { |
| return element; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * The basic test bean for each test. |
| */ |
| protected TestBean bean; |
| |
| /** |
| * The "package private subclass" test bean for each test. |
| */ |
| protected TestBeanPackageSubclass beanPackageSubclass; |
| |
| /** |
| * The test bean for private access tests. |
| */ |
| protected PrivateDirect beanPrivate; |
| |
| /** |
| * The test bean for private access tests of subclasses. |
| */ |
| protected PrivateDirect beanPrivateSubclass; |
| |
| /** |
| * The "public subclass" test bean for each test. |
| */ |
| protected TestBeanPublicSubclass beanPublicSubclass; |
| |
| /** |
| * The set of properties that should be described. |
| */ |
| protected String[] describes = { "booleanProperty", "booleanSecond", "doubleProperty", "floatProperty", "intArray", |
| // "intIndexed", |
| "intProperty", "listIndexed", "longProperty", |
| // "mappedObjects", |
| // "mappedProperty", |
| // "mappedIntProperty", |
| "nested", "nullProperty", |
| // "readOnlyProperty", |
| "shortProperty", "stringArray", |
| // "stringIndexed", |
| "stringProperty" }; |
| |
| /** |
| * Returns a single string containing all the keys in the map, sorted in alphabetical order and separated by ", ". |
| * <p> |
| * If there are no keys, an empty string is returned. |
| */ |
| private String keysToString(final Map<String, ?> map) { |
| return StringUtils.join(new TreeSet<>(map.keySet()), ", "); |
| } |
| |
| /** |
| * Sets up instance variables required by this test case. |
| */ |
| @BeforeEach |
| public void setUp() { |
| |
| bean = new TestBean(); |
| beanPackageSubclass = new TestBeanPackageSubclass(); |
| beanPrivate = PrivateBeanFactory.create(); |
| beanPrivateSubclass = PrivateBeanFactory.createSubclass(); |
| beanPublicSubclass = new TestBeanPublicSubclass(); |
| |
| final DynaProperty[] properties = { new DynaProperty("stringProperty", String.class), new DynaProperty("nestedBean", TestBean.class), |
| new DynaProperty("nullDynaBean", DynaBean.class) }; |
| final BasicDynaClass dynaClass = new BasicDynaClass("nestedDynaBean", BasicDynaBean.class, properties); |
| final BasicDynaBean nestedDynaBean = new BasicDynaBean(dynaClass); |
| nestedDynaBean.set("nestedBean", bean); |
| bean.setNestedDynaBean(nestedDynaBean); |
| PropertyUtils.clearDescriptors(); |
| } |
| |
| /** |
| * Tear down instance variables required by this test case. |
| */ |
| @AfterEach |
| public void tearDown() { |
| |
| bean = null; |
| beanPackageSubclass = null; |
| beanPrivate = null; |
| beanPrivateSubclass = null; |
| beanPublicSubclass = null; |
| |
| PropertyUtils.resetBeanIntrospectors(); |
| } |
| |
| /** |
| * Tries to add a null BeanIntrospector. |
| */ |
| @Test |
| void testAddBeanIntrospectorNull() { |
| assertThrows(NullPointerException.class, () -> PropertyUtils.addBeanIntrospector(null)); |
| } |
| |
| /** |
| * Test copyProperties() when the origin is a {@code Map}. |
| */ |
| @Test |
| void testCopyPropertiesMap() throws Exception { |
| |
| final Map<String, Object> map = new HashMap<>(); |
| map.put("booleanProperty", Boolean.FALSE); |
| map.put("doubleProperty", Double.valueOf(333.0)); |
| map.put("dupProperty", new String[] { "New 0", "New 1", "New 2" }); |
| map.put("floatProperty", Float.valueOf((float) 222.0)); |
| map.put("intArray", new int[] { 0, 100, 200 }); |
| map.put("intProperty", Integer.valueOf(111)); |
| map.put("longProperty", Long.valueOf(444)); |
| map.put("shortProperty", Short.valueOf((short) 555)); |
| map.put("stringProperty", "New String Property"); |
| |
| PropertyUtils.copyProperties(bean, map); |
| |
| // Scalar properties |
| assertEquals(false, bean.getBooleanProperty(), "booleanProperty"); |
| assertEquals(333.0, bean.getDoubleProperty(), 0.005, "doubleProperty"); |
| assertEquals((float) 222.0, bean.getFloatProperty(), (float) 0.005, "floatProperty"); |
| assertEquals(111, bean.getIntProperty(), "intProperty"); |
| assertEquals(444, bean.getLongProperty(), "longProperty"); |
| assertEquals((short) 555, bean.getShortProperty(), "shortProperty"); |
| assertEquals("New String Property", bean.getStringProperty(), "stringProperty"); |
| |
| // Indexed Properties |
| final String[] dupProperty = bean.getDupProperty(); |
| assertNotNull(dupProperty, "dupProperty present"); |
| assertEquals(3, dupProperty.length, "dupProperty length"); |
| assertEquals("New 0", dupProperty[0], "dupProperty[0]"); |
| assertEquals("New 1", dupProperty[1], "dupProperty[1]"); |
| assertEquals("New 2", dupProperty[2], "dupProperty[2]"); |
| final int[] intArray = bean.getIntArray(); |
| assertNotNull(intArray, "intArray present"); |
| assertEquals(3, intArray.length, "intArray length"); |
| assertEquals(0, intArray[0], "intArray[0]"); |
| assertEquals(100, intArray[1], "intArray[1]"); |
| assertEquals(200, intArray[2], "intArray[2]"); |
| |
| } |
| |
| /** |
| * Tests whether the default introspection mechanism can be replaced by a custom BeanIntrospector. |
| */ |
| @Test |
| void testCustomIntrospection() { |
| final PropertyDescriptor[] desc1 = PropertyUtils.getPropertyDescriptors(AlphaBean.class); |
| PropertyDescriptor nameDescriptor = findNameDescriptor(desc1); |
| assertNotNull(nameDescriptor.getWriteMethod(), "No write method"); |
| |
| final BeanIntrospector bi = icontext -> { |
| final Set<String> names = icontext.propertyNames(); |
| final PropertyDescriptor[] newDescs = new PropertyDescriptor[names.size()]; |
| int idx = 0; |
| for (final Iterator<String> it = names.iterator(); it.hasNext(); idx++) { |
| final String propName = it.next(); |
| final PropertyDescriptor pd = icontext.getPropertyDescriptor(propName); |
| newDescs[idx] = new PropertyDescriptor(pd.getName(), pd.getReadMethod(), null); |
| } |
| icontext.addPropertyDescriptors(newDescs); |
| }; |
| PropertyUtils.clearDescriptors(); |
| PropertyUtils.addBeanIntrospector(bi); |
| final PropertyDescriptor[] desc2 = PropertyUtils.getPropertyDescriptors(AlphaBean.class); |
| assertEquals(desc1.length, desc2.length, "Different number of properties"); |
| nameDescriptor = findNameDescriptor(desc2); |
| assertNull(nameDescriptor.getWriteMethod(), "Got a write method"); |
| PropertyUtils.removeBeanIntrospector(bi); |
| } |
| |
| /** |
| * Tests whether exceptions during custom introspection are handled. |
| */ |
| @Test |
| void testCustomIntrospectionEx() { |
| final BeanIntrospector bi = icontext -> { |
| throw new IntrospectionException("TestException"); |
| }; |
| PropertyUtils.clearDescriptors(); |
| PropertyUtils.addBeanIntrospector(bi); |
| final PropertyDescriptor[] desc = PropertyUtils.getPropertyDescriptors(AlphaBean.class); |
| assertNotNull(findNameDescriptor(desc), "Introspection did not work"); |
| PropertyUtils.removeBeanIntrospector(bi); |
| } |
| |
| /** |
| * A mapped property removed by a {@link SuppressPropertiesBeanIntrospector} must stay hidden. The mapped-descriptor fallback in |
| * {@code getPropertyDescriptor} rebuilt the descriptor directly from the class methods without consulting the introspectors, so a suppressed mapped |
| * property was still readable and writable through {@code name(key)} access. |
| */ |
| @Test |
| void testCustomIntrospectionSuppressedMappedProperty() throws Exception { |
| final PropertyUtilsBean pub = new PropertyUtilsBean(); |
| pub.addBeanIntrospector(new SuppressPropertiesBeanIntrospector(Arrays.asList("mappedProperty"))); |
| |
| assertNull(pub.getPropertyDescriptor(bean, "mappedProperty"), "Suppressed mapped property should have no descriptor"); |
| assertThrows(NoSuchMethodException.class, () -> pub.getProperty(bean, "mappedProperty(First Key)"), "Suppressed mapped property must not be readable"); |
| assertThrows(NoSuchMethodException.class, () -> pub.setProperty(bean, "mappedProperty(First Key)", "changed"), |
| "Suppressed mapped property must not be writable"); |
| assertEquals("First Value", bean.getMappedProperty("First Key"), "Suppressed mapped property must be unchanged"); |
| |
| // A mapped property that is not suppressed is still accessible. |
| final PropertyUtilsBean unsuppressed = new PropertyUtilsBean(); |
| assertEquals("First Value", unsuppressed.getProperty(bean, "mappedProperty(First Key)"), "Mapped property should be readable when not suppressed"); |
| } |
| |
| /** |
| * Test the describe() method. |
| */ |
| @Test |
| void testDescribe() throws Exception { |
| |
| final Map<String, Object> map = PropertyUtils.describe(bean); |
| |
| // Verify existence of all the properties that should be present |
| for (final String describe : describes) { |
| assertTrue(map.containsKey(describe), "Property '" + describe + "' is present"); |
| } |
| assertFalse(map.containsKey("writeOnlyProperty"), "Property 'writeOnlyProperty' is not present"); |
| |
| // Verify the values of scalar properties |
| assertEquals(Boolean.TRUE, map.get("booleanProperty"), "Value of 'booleanProperty'"); |
| assertEquals(Double.valueOf(321.0), map.get("doubleProperty"), "Value of 'doubleProperty'"); |
| assertEquals(Float.valueOf((float) 123.0), map.get("floatProperty"), "Value of 'floatProperty'"); |
| assertEquals(Integer.valueOf(123), map.get("intProperty"), "Value of 'intProperty'"); |
| assertEquals(Long.valueOf(321), map.get("longProperty"), "Value of 'longProperty'"); |
| assertEquals(Short.valueOf((short) 987), map.get("shortProperty"), "Value of 'shortProperty'"); |
| assertEquals("This is a string", (String) map.get("stringProperty"), "Value of 'stringProperty'"); |
| |
| } |
| |
| /** |
| * Test {@link PropertyUtilsBean}'s invoke method throwing an IllegalArgumentException and check that the "cause" has been properly initialized for JDK 1.4+ |
| * See BEANUTILS-266 for changes and reason for test |
| */ |
| @Test |
| void testExceptionFromInvoke() throws Exception { |
| try { |
| PropertyUtils.setSimpleProperty(bean, "intProperty", "XXX"); |
| } catch (final IllegalArgumentException t) { |
| final Throwable cause = (Throwable) PropertyUtils.getProperty(t, "cause"); |
| assertNotNull(cause, "Cause not found"); |
| assertInstanceOf(IllegalArgumentException.class, cause, "Expected cause to be IllegalArgumentException, but was: " + cause.getClass()); |
| // JDK 1.6 doesn't have "argument type mismatch" message |
| // assertEquals("Check error message", "argument type mismatch", cause.getMessage()); |
| } |
| } |
| |
| /** |
| * Corner cases on getPropertyDescriptor invalid arguments. |
| */ |
| @Test |
| void testGetDescriptorArguments() { |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getPropertyDescriptor(null, "stringProperty")); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getPropertyDescriptor(bean, null)); |
| } |
| |
| /** |
| * Base for testGetDescriptorXxxxx() series of tests. |
| * |
| * @param name Name of the property to be retrieved |
| * @param read Expected name of the read method (or null) |
| * @param write Expected name of the write method (or null) |
| * @throws NoSuchMethodException |
| * @throws InvocationTargetException |
| * @throws IllegalAccessException |
| */ |
| private void testGetDescriptorBase(final String name, final String read, final String write) |
| throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { |
| final PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor(bean, name); |
| if (read == null && write == null) { |
| assertNull(pd, "Got descriptor"); |
| return; |
| } |
| assertNotNull(pd, "Got descriptor"); |
| final Method rm = pd.getReadMethod(); |
| if (read != null) { |
| assertNotNull(rm, "Got read method"); |
| assertEquals(rm.getName(), read, "Got correct read method"); |
| } else { |
| assertNull(rm, "Got read method"); |
| } |
| final Method wm = pd.getWriteMethod(); |
| if (write != null) { |
| assertNotNull(wm, "Got write method"); |
| assertEquals(wm.getName(), write, "Got correct write method"); |
| } else { |
| assertNull(wm, "Got write method"); |
| } |
| } |
| |
| /** |
| * Positive getPropertyDescriptor on property {@code booleanProperty}. |
| */ |
| @Test |
| void testGetDescriptorBoolean() throws Exception { |
| testGetDescriptorBase("booleanProperty", "getBooleanProperty", "setBooleanProperty"); |
| } |
| |
| /** |
| * Positive getPropertyDescriptor on property {@code doubleProperty}. |
| */ |
| @Test |
| void testGetDescriptorDouble() throws Exception { |
| testGetDescriptorBase("doubleProperty", "getDoubleProperty", "setDoubleProperty"); |
| } |
| |
| /** |
| * Positive getPropertyDescriptor on property {@code floatProperty}. |
| */ |
| @Test |
| void testGetDescriptorFloat() throws Exception { |
| testGetDescriptorBase("floatProperty", "getFloatProperty", "setFloatProperty"); |
| } |
| |
| /** |
| * Positive getPropertyDescriptor on property {@code intProperty}. |
| */ |
| @Test |
| void testGetDescriptorInt() throws Exception { |
| testGetDescriptorBase("intProperty", "getIntProperty", "setIntProperty"); |
| } |
| |
| /** |
| * <p> |
| * Negative tests on an invalid property with two different boolean getters (which is fine, according to the JavaBeans spec) but a String setter instead of |
| * a boolean setter. |
| * </p> |
| * |
| * <p> |
| * Although one could logically argue that this combination of method signatures should not identify a property at all, there is a sentence in Section 8.3.1 |
| * making it clear that the behavior tested for here is correct: "If we find only one of these methods, then we regard it as defining either a read-only or |
| * write-only property called <em><property-name></em>. |
| * </p> |
| */ |
| @Test |
| void testGetDescriptorInvalidBoolean() throws Exception { |
| |
| final PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor(bean, "invalidBoolean"); |
| assertNotNull(pd, "invalidBoolean is a property"); |
| assertNotNull(pd.getReadMethod(), "invalidBoolean has a getter method"); |
| assertNull(pd.getWriteMethod(), "invalidBoolean has no write method"); |
| assertTrue(Arrays.asList("isInvalidBoolean", "getInvalidBoolean").contains(pd.getReadMethod().getName()), |
| "invalidBoolean getter method is isInvalidBoolean or getInvalidBoolean"); |
| } |
| |
| /** |
| * Positive getPropertyDescriptor on property {@code longProperty}. |
| */ |
| @Test |
| void testGetDescriptorLong() throws Exception { |
| testGetDescriptorBase("longProperty", "getLongProperty", "setLongProperty"); |
| } |
| |
| /** |
| * Test getting mapped descriptor with periods in the key. |
| */ |
| @Test |
| void testGetDescriptorMappedPeriods() throws Exception { |
| |
| bean.getMappedIntProperty("xyz"); // initializes mappedIntProperty |
| |
| PropertyDescriptor desc; |
| final Integer testIntegerValue = Integer.valueOf(1234); |
| |
| bean.setMappedIntProperty("key.with.a.dot", testIntegerValue.intValue()); |
| assertEquals(testIntegerValue, Integer.valueOf(bean.getMappedIntProperty("key.with.a.dot")), "Can retrieve directly"); |
| desc = PropertyUtils.getPropertyDescriptor(bean, "mappedIntProperty(key.with.a.dot)"); |
| assertEquals(Integer.TYPE, ((MappedPropertyDescriptor) desc).getMappedPropertyType(), "Check descriptor type (A)"); |
| |
| bean.setMappedObjects("nested.property", new TestBean(testIntegerValue.intValue())); |
| assertEquals(testIntegerValue, Integer.valueOf(((TestBean) bean.getMappedObjects("nested.property")).getIntProperty()), "Can retrieve directly"); |
| desc = PropertyUtils.getPropertyDescriptor(bean, "mappedObjects(nested.property).intProperty"); |
| assertEquals(Integer.TYPE, desc.getPropertyType(), "Check descriptor type (B)"); |
| } |
| |
| /** |
| * Positive getPropertyDescriptor on property {@code readOnlyProperty}. |
| */ |
| @Test |
| void testGetDescriptorReadOnly() throws Exception { |
| testGetDescriptorBase("readOnlyProperty", "getReadOnlyProperty", null); |
| } |
| |
| /** |
| * Positive test for getPropertyDescriptors(). Each property name listed in {@code properties} should be returned exactly once. |
| */ |
| @Test |
| void testGetDescriptors() { |
| |
| final PropertyDescriptor[] pd = PropertyUtils.getPropertyDescriptors(bean); |
| assertNotNull(pd, "Got descriptors"); |
| final int[] count = new int[properties.length]; |
| for (final PropertyDescriptor element : pd) { |
| final String name = element.getName(); |
| for (int j = 0; j < properties.length; j++) { |
| if (name.equals(properties[j])) { |
| count[j]++; |
| } |
| } |
| } |
| for (int j = 0; j < properties.length; j++) { |
| assertFalse(count[j] < 0, "Missing property " + properties[j]); |
| assertFalse(count[j] > 1, "Missing property " + properties[j]); |
| } |
| |
| } |
| |
| /** |
| * Corner cases on getPropertyDescriptors invalid arguments. |
| */ |
| @Test |
| void testGetDescriptorsArguments() { |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getPropertyDescriptors(null)); |
| } |
| |
| /** |
| * Positive getPropertyDescriptor on property {@code booleanSecond} that uses an "is" method as the getter. |
| */ |
| @Test |
| void testGetDescriptorSecond() throws Exception { |
| testGetDescriptorBase("booleanSecond", "isBooleanSecond", "setBooleanSecond"); |
| } |
| |
| /** |
| * Positive getPropertyDescriptor on property {@code shortProperty}. |
| */ |
| @Test |
| void testGetDescriptorShort() throws Exception { |
| testGetDescriptorBase("shortProperty", "getShortProperty", "setShortProperty"); |
| } |
| |
| /** |
| * Positive getPropertyDescriptor on property {@code stringProperty}. |
| */ |
| @Test |
| void testGetDescriptorString() throws Exception { |
| testGetDescriptorBase("stringProperty", "getStringProperty", "setStringProperty"); |
| } |
| |
| /** |
| * Negative getPropertyDescriptor on property {@code unknown}. |
| */ |
| @Test |
| void testGetDescriptorUnknown() throws Exception { |
| testGetDescriptorBase("unknown", null, null); |
| } |
| |
| /** |
| * Positive getPropertyDescriptor on property {@code writeOnlyProperty}. |
| */ |
| @Test |
| void testGetDescriptorWriteOnly() throws Exception { |
| testGetDescriptorBase("writeOnlyProperty", null, "setWriteOnlyProperty"); |
| } |
| |
| /** |
| * Corner cases on getIndexedProperty invalid arguments. |
| */ |
| @Test |
| void testGetIndexedArguments() { |
| // Use explicit index argument |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getIndexedProperty(null, "intArray", 0)); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getIndexedProperty(bean, null, 0)); |
| // Use index expression |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getIndexedProperty(null, "intArray[0]")); |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.getIndexedProperty(bean, "[0]")); |
| assertThrows(IllegalArgumentException.class, () -> PropertyUtils.getIndexedProperty(bean, "intArray")); |
| // Use explicit index argument |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getIndexedProperty(null, "intIndexed", 0)); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getIndexedProperty(bean, null, 0)); |
| // Use index expression |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getIndexedProperty(null, "intIndexed[0]")); |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.getIndexedProperty(bean, "[0]")); |
| assertThrows(IllegalArgumentException.class, () -> PropertyUtils.getIndexedProperty(bean, "intIndexed")); |
| } |
| |
| /** |
| * Test getting an indexed value out of a multi-dimensional array |
| */ |
| @Test |
| void testGetIndexedArray() throws Exception { |
| final String[] firstArray = { "FIRST-1", "FIRST-2", "FIRST-3" }; |
| final String[] secondArray = { "SECOND-1", "SECOND-2", "SECOND-3", "SECOND-4" }; |
| final String[][] mainArray = { firstArray, secondArray }; |
| final TestBean bean = new TestBean(mainArray); |
| assertEquals(firstArray[0], PropertyUtils.getProperty(bean, "string2dArray[0][0]"), "firstArray[0]"); |
| assertEquals(firstArray[1], PropertyUtils.getProperty(bean, "string2dArray[0][1]"), "firstArray[1]"); |
| assertEquals(firstArray[2], PropertyUtils.getProperty(bean, "string2dArray[0][2]"), "firstArray[2]"); |
| assertEquals(secondArray[0], PropertyUtils.getProperty(bean, "string2dArray[1][0]"), "secondArray[0]"); |
| assertEquals(secondArray[1], PropertyUtils.getProperty(bean, "string2dArray[1][1]"), "secondArray[1]"); |
| assertEquals(secondArray[2], PropertyUtils.getProperty(bean, "string2dArray[1][2]"), "secondArray[2]"); |
| assertEquals(secondArray[3], PropertyUtils.getProperty(bean, "string2dArray[1][3]"), "secondArray[3]"); |
| } |
| |
| /** |
| * Test getting an indexed value out of List of Lists |
| */ |
| @Test |
| void testGetIndexedList() throws Exception { |
| final String[] firstArray = { "FIRST-1", "FIRST-2", "FIRST-3" }; |
| final String[] secondArray = { "SECOND-1", "SECOND-2", "SECOND-3", "SECOND-4" }; |
| final List<Object> mainList = new ArrayList<>(); |
| mainList.add(Arrays.asList(firstArray)); |
| mainList.add(Arrays.asList(secondArray)); |
| final TestBean bean = new TestBean(mainList); |
| assertEquals(firstArray[0], PropertyUtils.getProperty(bean, "listIndexed[0][0]"), "firstArray[0]"); |
| assertEquals(firstArray[1], PropertyUtils.getProperty(bean, "listIndexed[0][1]"), "firstArray[1]"); |
| assertEquals(firstArray[2], PropertyUtils.getProperty(bean, "listIndexed[0][2]"), "firstArray[2]"); |
| assertEquals(secondArray[0], PropertyUtils.getProperty(bean, "listIndexed[1][0]"), "secondArray[0]"); |
| assertEquals(secondArray[1], PropertyUtils.getProperty(bean, "listIndexed[1][1]"), "secondArray[1]"); |
| assertEquals(secondArray[2], PropertyUtils.getProperty(bean, "listIndexed[1][2]"), "secondArray[2]"); |
| assertEquals(secondArray[3], PropertyUtils.getProperty(bean, "listIndexed[1][3]"), "secondArray[3]"); |
| } |
| |
| /** |
| * Test getting a value out of a mapped Map |
| */ |
| @Test |
| void testGetIndexedMap() throws Exception { |
| final Map<String, Object> firstMap = new HashMap<>(); |
| firstMap.put("FIRST-KEY-1", "FIRST-VALUE-1"); |
| firstMap.put("FIRST-KEY-2", "FIRST-VALUE-2"); |
| final Map<String, Object> secondMap = new HashMap<>(); |
| secondMap.put("SECOND-KEY-1", "SECOND-VALUE-1"); |
| secondMap.put("SECOND-KEY-2", "SECOND-VALUE-2"); |
| |
| final List<Object> mainList = new ArrayList<>(); |
| mainList.add(firstMap); |
| mainList.add(secondMap); |
| final TestBean bean = new TestBean(mainList); |
| assertEquals("FIRST-VALUE-1", PropertyUtils.getProperty(bean, "listIndexed[0](FIRST-KEY-1)"), "listIndexed[0](FIRST-KEY-1)"); |
| assertEquals("FIRST-VALUE-2", PropertyUtils.getProperty(bean, "listIndexed[0](FIRST-KEY-2)"), "listIndexed[0](FIRST-KEY-2)"); |
| assertEquals("SECOND-VALUE-1", PropertyUtils.getProperty(bean, "listIndexed[1](SECOND-KEY-1)"), "listIndexed[1](SECOND-KEY-1)"); |
| assertEquals("SECOND-VALUE-2", PropertyUtils.getProperty(bean, "listIndexed[1](SECOND-KEY-2)"), "listIndexed[1](SECOND-KEY-2)"); |
| } |
| |
| /** |
| * Positive and negative tests on getIndexedProperty valid arguments. |
| */ |
| @Test |
| void testGetIndexedValues() throws Exception { |
| Object value = null; |
| // Use explicit key argument |
| for (int i = 0; i < 5; i++) { |
| value = PropertyUtils.getIndexedProperty(bean, "dupProperty", i); |
| assertNotNull(value, "dupProperty returned value " + i); |
| assertInstanceOf(String.class, value, "dupProperty returned String " + i); |
| assertEquals("Dup " + i, (String) value, "dupProperty returned correct " + i); |
| |
| value = PropertyUtils.getIndexedProperty(bean, "intArray", i); |
| assertNotNull(value, "intArray returned value " + i); |
| assertInstanceOf(Integer.class, value, "intArray returned Integer " + i); |
| assertEquals(i * 10, ((Integer) value).intValue(), "intArray returned correct " + i); |
| |
| value = PropertyUtils.getIndexedProperty(bean, "intIndexed", i); |
| assertNotNull(value, "intIndexed returned value " + i); |
| assertInstanceOf(Integer.class, value, "intIndexed returned Integer " + i); |
| assertEquals(i * 10, ((Integer) value).intValue(), "intIndexed returned correct " + i); |
| |
| value = PropertyUtils.getIndexedProperty(bean, "listIndexed", i); |
| assertNotNull(value, "listIndexed returned value " + i); |
| assertInstanceOf(String.class, value, "list returned String " + i); |
| assertEquals("String " + i, (String) value, "listIndexed returned correct " + i); |
| |
| value = PropertyUtils.getIndexedProperty(bean, "stringArray", i); |
| assertNotNull(value, "stringArray returned value " + i); |
| assertInstanceOf(String.class, value, "stringArray returned String " + i); |
| assertEquals("String " + i, (String) value, "stringArray returned correct " + i); |
| |
| value = PropertyUtils.getIndexedProperty(bean, "stringIndexed", i); |
| assertNotNull(value, "stringIndexed returned value " + i); |
| assertInstanceOf(String.class, value, "stringIndexed returned String " + i); |
| assertEquals("String " + i, (String) value, "stringIndexed returned correct " + i); |
| |
| } |
| |
| // Use key expression |
| |
| for (int i = 0; i < 5; i++) { |
| |
| value = PropertyUtils.getIndexedProperty(bean, "dupProperty[" + i + "]"); |
| assertNotNull(value, "dupProperty returned value " + i); |
| assertInstanceOf(String.class, value, "dupProperty returned String " + i); |
| assertEquals("Dup " + i, (String) value, "dupProperty returned correct " + i); |
| |
| value = PropertyUtils.getIndexedProperty(bean, "intArray[" + i + "]"); |
| assertNotNull(value, "intArray returned value " + i); |
| assertInstanceOf(Integer.class, value, "intArray returned Integer " + i); |
| assertEquals(i * 10, ((Integer) value).intValue(), "intArray returned correct " + i); |
| |
| value = PropertyUtils.getIndexedProperty(bean, "intIndexed[" + i + "]"); |
| assertNotNull(value, "intIndexed returned value " + i); |
| assertInstanceOf(Integer.class, value, "intIndexed returned Integer " + i); |
| assertEquals(i * 10, ((Integer) value).intValue(), "intIndexed returned correct " + i); |
| |
| value = PropertyUtils.getIndexedProperty(bean, "listIndexed[" + i + "]"); |
| assertNotNull(value, "listIndexed returned value " + i); |
| assertInstanceOf(String.class, value, "listIndexed returned String " + i); |
| assertEquals("String " + i, (String) value, "listIndexed returned correct " + i); |
| |
| value = PropertyUtils.getIndexedProperty(bean, "stringArray[" + i + "]"); |
| assertNotNull(value, "stringArray returned value " + i); |
| assertInstanceOf(String.class, value, "stringArray returned String " + i); |
| assertEquals("String " + i, (String) value, "stringArray returned correct " + i); |
| |
| value = PropertyUtils.getIndexedProperty(bean, "stringIndexed[" + i + "]"); |
| assertNotNull(value, "stringIndexed returned value " + i); |
| assertInstanceOf(String.class, value, "stringIndexed returned String " + i); |
| assertEquals("String " + i, (String) value, "stringIndexed returned correct " + i); |
| |
| } |
| |
| // Index out of bounds tests |
| |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "dupProperty", -1)); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "dupProperty", 5)); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "intArray", -1)); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "intArray", 5)); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "intIndexed", -1)); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "intIndexed", 5)); |
| assertThrows(IndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "listIndexed", -1)); |
| assertThrows(IndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "listIndexed", 5)); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "stringArray", -1)); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "stringArray", 5)); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "stringIndexed", -1)); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.getIndexedProperty(bean, "stringIndexed", 5)); |
| } |
| |
| /** |
| * Corner cases on getMappedProperty invalid arguments. |
| */ |
| @Test |
| void testGetMappedArguments() { |
| // Use explicit key argument |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getMappedProperty(null, "mappedProperty", "First Key")); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getMappedProperty(bean, null, "First Key")); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getMappedProperty(bean, "mappedProperty", null)); |
| // Use key expression |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getMappedProperty(null, "mappedProperty(First Key)")); |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.getMappedProperty(bean, "(Second Key)")); |
| assertThrows(IllegalArgumentException.class, () -> PropertyUtils.getMappedProperty(bean, "mappedProperty")); |
| } |
| |
| /** |
| * Test getting an indexed value out of a mapped array |
| */ |
| @Test |
| void testGetMappedArray() throws Exception { |
| final TestBean bean = new TestBean(); |
| final String[] array = { "abc", "def", "ghi" }; |
| bean.getMapProperty().put("mappedArray", array); |
| assertEquals("abc", PropertyUtils.getProperty(bean, "mapProperty(mappedArray)[0]")); |
| assertEquals("def", PropertyUtils.getProperty(bean, "mapProperty(mappedArray)[1]")); |
| assertEquals("ghi", PropertyUtils.getProperty(bean, "mapProperty(mappedArray)[2]")); |
| } |
| |
| /** |
| * Test getting an indexed value out of a mapped List |
| */ |
| @Test |
| void testGetMappedList() throws Exception { |
| final TestBean bean = new TestBean(); |
| final List<Object> list = new ArrayList<>(); |
| list.add("klm"); |
| list.add("nop"); |
| list.add("qrs"); |
| bean.getMapProperty().put("mappedList", list); |
| assertEquals("klm", PropertyUtils.getProperty(bean, "mapProperty(mappedList)[0]")); |
| assertEquals("nop", PropertyUtils.getProperty(bean, "mapProperty(mappedList)[1]")); |
| assertEquals("qrs", PropertyUtils.getProperty(bean, "mapProperty(mappedList)[2]")); |
| } |
| |
| /** |
| * Test getting a value out of a mapped Map |
| */ |
| @Test |
| void testGetMappedMap() throws Exception { |
| final TestBean bean = new TestBean(); |
| final Map<String, Object> map = new HashMap<>(); |
| map.put("sub-key-1", "sub-value-1"); |
| map.put("sub-key-2", "sub-value-2"); |
| map.put("sub-key-3", "sub-value-3"); |
| bean.getMapProperty().put("mappedMap", map); |
| assertEquals("sub-value-1", PropertyUtils.getProperty(bean, "mapProperty(mappedMap)(sub-key-1)")); |
| assertEquals("sub-value-2", PropertyUtils.getProperty(bean, "mapProperty(mappedMap)(sub-key-2)")); |
| assertEquals("sub-value-3", PropertyUtils.getProperty(bean, "mapProperty(mappedMap)(sub-key-3)")); |
| } |
| |
| /** |
| * Test getting mapped values with periods in the key. |
| */ |
| @Test |
| void testGetMappedPeriods() throws Exception { |
| |
| bean.setMappedProperty("key.with.a.dot", "Special Value"); |
| assertEquals("Special Value", bean.getMappedProperty("key.with.a.dot"), "Can retrieve directly"); |
| assertEquals("Special Value", PropertyUtils.getMappedProperty(bean, "mappedProperty", "key.with.a.dot"), "Can retrieve via getMappedProperty"); |
| assertEquals("Special Value", PropertyUtils.getNestedProperty(bean, "mappedProperty(key.with.a.dot)"), "Can retrieve via getNestedProperty"); |
| |
| bean.setMappedObjects("nested.property", new TestBean()); |
| assertNotNull(bean.getMappedObjects("nested.property"), "Can retrieve directly"); |
| assertEquals("This is a string", PropertyUtils.getNestedProperty(bean, "mappedObjects(nested.property).stringProperty"), "Can retrieve nested"); |
| |
| assertEquals("Mapped Value", PropertyUtils.getNestedProperty(bean, "mappedNested.value(Mapped Key)"), "Can't retrieved nested with mapped property"); |
| } |
| |
| /** |
| * Test getting mapped values with slashes in the key. This is different from periods because slashes are not syntactically significant. |
| */ |
| @Test |
| void testGetMappedSlashes() throws Exception { |
| |
| bean.setMappedProperty("key/with/a/slash", "Special Value"); |
| assertEquals("Special Value", bean.getMappedProperty("key/with/a/slash"), "Can retrieve directly"); |
| assertEquals("Special Value", PropertyUtils.getMappedProperty(bean, "mappedProperty", "key/with/a/slash"), "Can retrieve via getMappedProperty"); |
| assertEquals("Special Value", PropertyUtils.getNestedProperty(bean, "mappedProperty(key/with/a/slash)"), "Can retrieve via getNestedProperty"); |
| |
| bean.setMappedObjects("nested/property", new TestBean()); |
| assertNotNull(bean.getMappedObjects("nested/property"), "Can retrieve directly"); |
| assertEquals("This is a string", PropertyUtils.getNestedProperty(bean, "mappedObjects(nested/property).stringProperty"), "Can retrieve nested"); |
| } |
| |
| /** |
| * Positive and negative tests on getMappedProperty valid arguments. |
| */ |
| @Test |
| void testGetMappedValues() throws Exception { |
| Object value = null; |
| // Use explicit key argument |
| value = PropertyUtils.getMappedProperty(bean, "mappedProperty", "First Key"); |
| assertEquals("First Value", value, "Can find first value"); |
| |
| value = PropertyUtils.getMappedProperty(bean, "mappedProperty", "Second Key"); |
| assertEquals("Second Value", value, "Can find second value"); |
| |
| value = PropertyUtils.getMappedProperty(bean, "mappedProperty", "Third Key"); |
| assertNull(value, "Can not find third value"); |
| |
| // Use key expression with parentheses |
| value = PropertyUtils.getMappedProperty(bean, "mappedProperty(First Key)"); |
| assertEquals("First Value", value, "Can find first value"); |
| |
| value = PropertyUtils.getMappedProperty(bean, "mappedProperty(Second Key)"); |
| assertEquals("Second Value", value, "Can find second value"); |
| |
| value = PropertyUtils.getMappedProperty(bean, "mappedProperty(Third Key)"); |
| assertNull(value, "Can not find third value"); |
| |
| // Use key expression with dotted syntax |
| value = PropertyUtils.getNestedProperty(bean, "mapProperty.First Key"); |
| assertEquals("First Value", value, "Can find first value"); |
| |
| value = PropertyUtils.getNestedProperty(bean, "mapProperty.Second Key"); |
| assertEquals("Second Value", value, "Can find second value"); |
| |
| value = PropertyUtils.getNestedProperty(bean, "mapProperty.Third Key"); |
| assertNull(value, "Can not find third value"); |
| } |
| |
| /** |
| * Corner cases on getNestedProperty invalid arguments. |
| */ |
| @Test |
| void testGetNestedArguments() { |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getNestedProperty(null, "stringProperty")); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getNestedProperty(bean, null)); |
| } |
| |
| /** |
| * Test getNestedProperty on a boolean property. |
| */ |
| @Test |
| void testGetNestedBoolean() throws Exception { |
| final Object value = PropertyUtils.getNestedProperty(bean, "nested.booleanProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Boolean.class, value, "Got correct type"); |
| assertEquals(((Boolean) value).booleanValue(), bean.getNested().getBooleanProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Test getNestedProperty on a double property. |
| */ |
| @Test |
| void testGetNestedDouble() throws Exception { |
| final Object value = PropertyUtils.getNestedProperty(bean, "nested.doubleProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Double.class, value, "Got correct type"); |
| assertEquals(((Double) value).doubleValue(), bean.getNested().getDoubleProperty(), 0.005, "Got correct value"); |
| } |
| |
| /** |
| * Test getNestedProperty on a float property. |
| */ |
| @Test |
| void testGetNestedFloat() throws Exception { |
| final Object value = PropertyUtils.getNestedProperty(bean, "nested.floatProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Float.class, value, "Got correct type"); |
| assertEquals(((Float) value).floatValue(), bean.getNested().getFloatProperty(), (float) 0.005, "Got correct value"); |
| } |
| |
| /** |
| * Test getNestedProperty on an int property. |
| */ |
| @Test |
| void testGetNestedInt() throws Exception { |
| final Object value = PropertyUtils.getNestedProperty(bean, "nested.intProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Integer.class, value, "Got correct type"); |
| assertEquals(((Integer) value).intValue(), bean.getNested().getIntProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Test getNestedProperty on a long property. |
| */ |
| @Test |
| void testGetNestedLong() throws Exception { |
| final Object value = PropertyUtils.getNestedProperty(bean, "nested.longProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Long.class, value, "Got correct type"); |
| assertEquals(((Long) value).longValue(), bean.getNested().getLongProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Test getNestedProperty on a read-only String property. |
| */ |
| @Test |
| void testGetNestedReadOnly() throws Exception { |
| final Object value = PropertyUtils.getNestedProperty(bean, "nested.readOnlyProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(String.class, value, "Got correct type"); |
| assertEquals((String) value, bean.getReadOnlyProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Test getNestedProperty on a short property. |
| */ |
| @Test |
| void testGetNestedShort() throws Exception { |
| final Object value = PropertyUtils.getNestedProperty(bean, "nested.shortProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Short.class, value, "Got correct type"); |
| assertEquals(((Short) value).shortValue(), bean.getNested().getShortProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Test getNestedProperty on a String property. |
| */ |
| @Test |
| void testGetNestedString() throws Exception { |
| final Object value = PropertyUtils.getNestedProperty(bean, "nested.stringProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(String.class, value, "Got correct type"); |
| assertEquals((String) value, bean.getNested().getStringProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Negative test getNestedProperty on an unknown property. |
| */ |
| @Test |
| void testGetNestedUnknown() throws Exception { |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.getNestedProperty(bean, "nested.unknown")); |
| } |
| |
| /** |
| * Test getNestedProperty on a write-only String property. |
| */ |
| @Test |
| void testGetNestedWriteOnly() throws Exception { |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.getNestedProperty(bean, "writeOnlyProperty")); |
| } |
| |
| /** |
| * Test getPropertyType() on all kinds of properties. |
| */ |
| @Test |
| void testGetPropertyType() throws Exception { |
| |
| Class<?> clazz = null; |
| final int[] intArray = {}; |
| final String[] stringArray = {}; |
| |
| // Scalar and Indexed Properties |
| clazz = PropertyUtils.getPropertyType(bean, "booleanProperty"); |
| assertEquals(Boolean.TYPE, clazz, "booleanProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "booleanSecond"); |
| assertEquals(Boolean.TYPE, clazz, "booleanSecond type"); |
| clazz = PropertyUtils.getPropertyType(bean, "doubleProperty"); |
| assertEquals(Double.TYPE, clazz, "doubleProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "dupProperty"); |
| assertEquals(String.class, clazz, "dupProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "floatProperty"); |
| assertEquals(Float.TYPE, clazz, "floatProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "intArray"); |
| assertEquals(intArray.getClass(), clazz, "intArray type"); |
| clazz = PropertyUtils.getPropertyType(bean, "intIndexed"); |
| assertEquals(Integer.TYPE, clazz, "intIndexed type"); |
| clazz = PropertyUtils.getPropertyType(bean, "intProperty"); |
| assertEquals(Integer.TYPE, clazz, "intProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "listIndexed"); |
| assertEquals(List.class, clazz, "listIndexed type"); |
| clazz = PropertyUtils.getPropertyType(bean, "longProperty"); |
| assertEquals(Long.TYPE, clazz, "longProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "mappedProperty"); |
| assertEquals(String.class, clazz, "mappedProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "mappedIntProperty"); |
| assertEquals(Integer.TYPE, clazz, "mappedIntProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "readOnlyProperty"); |
| assertEquals(String.class, clazz, "readOnlyProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "shortProperty"); |
| assertEquals(Short.TYPE, clazz, "shortProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "stringArray"); |
| assertEquals(stringArray.getClass(), clazz, "stringArray type"); |
| clazz = PropertyUtils.getPropertyType(bean, "stringIndexed"); |
| assertEquals(String.class, clazz, "stringIndexed type"); |
| clazz = PropertyUtils.getPropertyType(bean, "stringProperty"); |
| assertEquals(String.class, clazz, "stringProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "writeOnlyProperty"); |
| assertEquals(String.class, clazz, "writeOnlyProperty type"); |
| |
| // Nested Properties |
| clazz = PropertyUtils.getPropertyType(bean, "nested.booleanProperty"); |
| assertEquals(Boolean.TYPE, clazz, "booleanProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.booleanSecond"); |
| assertEquals(Boolean.TYPE, clazz, "booleanSecond type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.doubleProperty"); |
| assertEquals(Double.TYPE, clazz, "doubleProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.dupProperty"); |
| assertEquals(String.class, clazz, "dupProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.floatProperty"); |
| assertEquals(Float.TYPE, clazz, "floatProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.intArray"); |
| assertEquals(intArray.getClass(), clazz, "intArray type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.intIndexed"); |
| assertEquals(Integer.TYPE, clazz, "intIndexed type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.intProperty"); |
| assertEquals(Integer.TYPE, clazz, "intProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.listIndexed"); |
| assertEquals(List.class, clazz, "listIndexed type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.longProperty"); |
| assertEquals(Long.TYPE, clazz, "longProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.mappedProperty"); |
| assertEquals(String.class, clazz, "mappedProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.mappedIntProperty"); |
| assertEquals(Integer.TYPE, clazz, "mappedIntProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.readOnlyProperty"); |
| assertEquals(String.class, clazz, "readOnlyProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.shortProperty"); |
| assertEquals(Short.TYPE, clazz, "shortProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.stringArray"); |
| assertEquals(stringArray.getClass(), clazz, "stringArray type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.stringIndexed"); |
| assertEquals(String.class, clazz, "stringIndexed type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.stringProperty"); |
| assertEquals(String.class, clazz, "stringProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nested.writeOnlyProperty"); |
| assertEquals(String.class, clazz, "writeOnlyProperty type"); |
| |
| // Nested DynaBean |
| clazz = PropertyUtils.getPropertyType(bean, "nestedDynaBean"); |
| assertEquals(DynaBean.class, clazz, "nestedDynaBean type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nestedDynaBean.stringProperty"); |
| assertEquals(String.class, clazz, "nestedDynaBean.stringProperty type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nestedDynaBean.nestedBean"); |
| assertEquals(TestBean.class, clazz, "nestedDynaBean.nestedBean type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nestedDynaBean.nestedBean.nestedDynaBean"); |
| assertEquals(DynaBean.class, clazz, "nestedDynaBean.nestedBean.nestedDynaBean type"); |
| clazz = PropertyUtils.getPropertyType(bean, "nestedDynaBean.nestedBean.nestedDynaBean.stringProperty"); |
| assertEquals(String.class, clazz, "nestedDynaBean.nestedBean.nestedDynaBean.stringPropert type"); |
| |
| // test Null |
| clazz = PropertyUtils.getPropertyType(bean, "nestedDynaBean.nullDynaBean"); |
| assertEquals(DynaBean.class, clazz, "nestedDynaBean.nullDynaBean type"); |
| assertThrows(NestedNullException.class, () -> PropertyUtils.getPropertyType(bean, "nestedDynaBean.nullDynaBean.foo")); |
| } |
| |
| /** |
| * Test accessing a public sub-bean of a package scope bean |
| */ |
| @Test |
| void testGetPublicSubBean_of_PackageBean() throws Exception { |
| |
| final PublicSubBean bean = new PublicSubBean(); |
| bean.setFoo("foo-start"); |
| bean.setBar("bar-start"); |
| Object result = null; |
| |
| // Get Foo |
| result = PropertyUtils.getProperty(bean, "foo"); |
| assertEquals("foo-start", result, "foo property"); |
| |
| // Get Bar |
| result = PropertyUtils.getProperty(bean, "bar"); |
| assertEquals("bar-start", result, "bar property"); |
| } |
| |
| /** |
| * Base for testGetReadMethod() series of tests. |
| * |
| * @param bean Bean for which to retrieve read methods. |
| * @param properties Property names to search for |
| * @param className Class name where this method should be defined |
| * @throws InvocationTargetException |
| * @throws IllegalArgumentException |
| * @throws IllegalAccessException |
| */ |
| private void testGetReadMethod(final Object bean, final String[] properties, final String className) |
| throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { |
| |
| final PropertyDescriptor[] pd = PropertyUtils.getPropertyDescriptors(bean); |
| for (final String propertie : properties) { |
| |
| // Identify the property descriptor for this property |
| switch (propertie) { |
| case "intIndexed": |
| continue; |
| case "stringIndexed": |
| continue; |
| case "writeOnlyProperty": |
| continue; |
| default: |
| break; |
| } |
| int n = -1; |
| for (int j = 0; j < pd.length; j++) { |
| if (propertie.equals(pd[j].getName())) { |
| n = j; |
| break; |
| } |
| } |
| assertTrue(n >= 0, "PropertyDescriptor for " + propertie); |
| |
| // Locate an accessible property reader method for it |
| final Method reader = PropertyUtils.getReadMethod(pd[n]); |
| assertNotNull(reader, "Reader for " + propertie); |
| final Class<?> clazz = reader.getDeclaringClass(); |
| assertNotNull(clazz, "Declaring class for " + propertie); |
| assertEquals(clazz.getName(), className, "Correct declaring class for " + propertie); |
| |
| // Actually call the reader method we received |
| reader.invoke(bean, (Object[]) new Class<?>[0]); |
| } |
| |
| } |
| |
| /** |
| * Test getting accessible property reader methods for a specified list of properties of our standard test bean. |
| */ |
| @Test |
| void testGetReadMethodBasic() throws Exception { |
| testGetReadMethod(bean, properties, TEST_BEAN_CLASS); |
| |
| } |
| |
| /** |
| * Test getting accessible property reader methods for a specified list of properties of a package private subclass of our standard test bean. |
| */ |
| @Test |
| void testGetReadMethodPackageSubclass() throws Exception { |
| testGetReadMethod(beanPackageSubclass, properties, TEST_BEAN_CLASS); |
| |
| } |
| |
| /** |
| * Test getting accessible property reader methods for a specified list of properties that are declared either directly or via implemented interfaces. |
| */ |
| @Test |
| void testGetReadMethodPublicInterface() throws Exception { |
| |
| // Properties "bar" and "baz" are visible via implemented interfaces |
| // (one direct and one indirect) |
| testGetReadMethod(beanPrivate, new String[] { "bar" }, PRIVATE_DIRECT_CLASS); |
| testGetReadMethod(beanPrivate, new String[] { "baz" }, PRIVATE_INDIRECT_CLASS); |
| |
| // Properties "bar" and "baz" are visible via implemented interfaces |
| // (one direct and one indirect). The interface is implemented in |
| // a superclass |
| testGetReadMethod(beanPrivateSubclass, new String[] { "bar" }, PRIVATE_DIRECT_CLASS); |
| testGetReadMethod(beanPrivateSubclass, new String[] { "baz" }, PRIVATE_INDIRECT_CLASS); |
| |
| // Property "foo" is not accessible because the underlying |
| // class has package scope |
| final PropertyDescriptor[] pd = PropertyUtils.getPropertyDescriptors(beanPrivate); |
| int n = -1; |
| for (int i = 0; i < pd.length; i++) { |
| if ("foo".equals(pd[i].getName())) { |
| n = i; |
| break; |
| } |
| } |
| assertTrue(n >= 0, "Found foo descriptor"); |
| final Method reader = pd[n].getReadMethod(); |
| assertNotNull(reader, "Found foo read method"); |
| assertThrows(IllegalAccessException.class, () -> reader.invoke(beanPrivate, (Object[]) new Class<?>[0])); |
| } |
| |
| /** |
| * Test getting accessible property reader methods for a specified list of properties of a public subclass of our standard test bean. |
| */ |
| @Test |
| void testGetReadMethodPublicSubclass() throws Exception { |
| testGetReadMethod(beanPublicSubclass, properties, TEST_BEAN_CLASS); |
| } |
| |
| /** Text case for setting properties on inner classes */ |
| @Test |
| void testGetSetInnerBean() throws Exception { |
| final BeanWithInnerBean bean = new BeanWithInnerBean(); |
| |
| PropertyUtils.setProperty(bean, "innerBean.fish(loiterTimer)", "5"); |
| String out = (String) PropertyUtils.getProperty(bean.getInnerBean(), "fish(loiterTimer)"); |
| assertEquals("5", out, "(1) Inner class property set/get property failed."); |
| |
| out = (String) PropertyUtils.getProperty(bean, "innerBean.fish(loiterTimer)"); |
| |
| assertEquals("5", out, "(2) Inner class property set/get property failed."); |
| } |
| |
| /** Text case for setting properties on parent */ |
| @Test |
| void testGetSetParentBean() throws Exception { |
| |
| final SonOfAlphaBean bean = new SonOfAlphaBean("Roger"); |
| |
| final String out = (String) PropertyUtils.getProperty(bean, "name"); |
| assertEquals("Roger", out, "(1) Get/Set On Parent."); |
| |
| PropertyUtils.setProperty(bean, "name", "abcd"); |
| assertEquals("abcd", bean.getName(), "(2) Get/Set On Parent."); |
| } |
| |
| /** |
| * Corner cases on getSimpleProperty invalid arguments. |
| */ |
| @Test |
| void testGetSimpleArguments() { |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getSimpleProperty(null, "stringProperty")); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.getSimpleProperty(bean, null)); |
| } |
| |
| /** |
| * Test getSimpleProperty on a boolean property. |
| */ |
| @Test |
| void testGetSimpleBoolean() throws Exception { |
| final Object value = PropertyUtils.getSimpleProperty(bean, "booleanProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Boolean.class, value, "Got correct type"); |
| assertEquals(((Boolean) value).booleanValue(), bean.getBooleanProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a double property. |
| */ |
| @Test |
| void testGetSimpleDouble() throws Exception { |
| final Object value = PropertyUtils.getSimpleProperty(bean, "doubleProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Double.class, value, "Got correct type"); |
| assertEquals(((Double) value).doubleValue(), bean.getDoubleProperty(), 0.005, "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a float property. |
| */ |
| @Test |
| void testGetSimpleFloat() throws Exception { |
| final Object value = PropertyUtils.getSimpleProperty(bean, "floatProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Float.class, value, "Got correct type"); |
| assertEquals(((Float) value).floatValue(), bean.getFloatProperty(), (float) 0.005, "Got correct value"); |
| } |
| |
| /** |
| * Negative test getSimpleProperty on an indexed property. |
| */ |
| @Test |
| void testGetSimpleIndexed() throws Exception { |
| assertThrows(IllegalArgumentException.class, () -> PropertyUtils.getSimpleProperty(bean, "intIndexed[0]")); |
| } |
| |
| /** |
| * Test getSimpleProperty on an int property. |
| */ |
| @Test |
| void testGetSimpleInt() throws Exception { |
| final Object value = PropertyUtils.getSimpleProperty(bean, "intProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Integer.class, value, "Got correct type"); |
| assertEquals(((Integer) value).intValue(), bean.getIntProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a long property. |
| */ |
| @Test |
| void testGetSimpleLong() throws Exception { |
| final Object value = PropertyUtils.getSimpleProperty(bean, "longProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Long.class, value, "Got correct type"); |
| assertEquals(((Long) value).longValue(), bean.getLongProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Negative test getSimpleProperty on a nested property. |
| */ |
| @Test |
| void testGetSimpleNested() throws Exception { |
| assertThrows(IllegalArgumentException.class, () -> PropertyUtils.getSimpleProperty(bean, "nested.stringProperty")); |
| } |
| |
| /** |
| * Test getSimpleProperty on a read-only String property. |
| */ |
| @Test |
| void testGetSimpleReadOnly() throws Exception { |
| final Object value = PropertyUtils.getSimpleProperty(bean, "readOnlyProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(String.class, value, "Got correct type"); |
| assertEquals((String) value, bean.getReadOnlyProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a short property. |
| */ |
| @Test |
| void testGetSimpleShort() throws Exception { |
| final Object value = PropertyUtils.getSimpleProperty(bean, "shortProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(Short.class, value, "Got correct type"); |
| assertEquals(((Short) value).shortValue(), bean.getShortProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Test getSimpleProperty on a String property. |
| */ |
| @Test |
| void testGetSimpleString() throws Exception { |
| final Object value = PropertyUtils.getSimpleProperty(bean, "stringProperty"); |
| assertNotNull(value, "Got a value"); |
| assertInstanceOf(String.class, value, "Got correct type"); |
| assertEquals((String) value, bean.getStringProperty(), "Got correct value"); |
| } |
| |
| /** |
| * Negative test getSimpleProperty on an unknown property. |
| */ |
| @Test |
| void testGetSimpleUnknown() throws Exception { |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.getSimpleProperty(bean, "unknown")); |
| } |
| |
| /** |
| * Test getSimpleProperty on a write-only String property. |
| */ |
| @Test |
| void testGetSimpleWriteOnly() throws Exception { |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.getSimpleProperty(bean, "writeOnlyProperty")); |
| |
| } |
| |
| /** |
| * Base for testGetWriteMethod() series of tests. |
| * |
| * @param bean Bean for which to retrieve write methods. |
| * @param properties Property names to search for |
| * @param className Class name where this method should be defined |
| */ |
| private void testGetWriteMethod(final Object bean, final String[] properties, final String className) { |
| |
| final PropertyDescriptor[] pd = PropertyUtils.getPropertyDescriptors(bean); |
| for (final String property : properties) { |
| |
| // Identify the property descriptor for this property |
| switch (property) { |
| case "intIndexed": |
| continue; |
| case "listIndexed": |
| continue; |
| case "nested": |
| continue; // This property is read only |
| case "readOnlyProperty": |
| continue; |
| case "stringIndexed": |
| continue; |
| default: |
| break; |
| } |
| int n = -1; |
| for (int j = 0; j < pd.length; j++) { |
| if (property.equals(pd[j].getName())) { |
| n = j; |
| break; |
| } |
| } |
| assertTrue(n >= 0, "PropertyDescriptor for " + property); |
| |
| // Locate an accessible property reader method for it |
| final Method writer = PropertyUtils.getWriteMethod(pd[n]); |
| assertNotNull(writer, "Writer for " + property); |
| final Class<?> clazz = writer.getDeclaringClass(); |
| assertNotNull(clazz, "Declaring class for " + property); |
| assertEquals(clazz.getName(), className, "Correct declaring class for " + property); |
| |
| } |
| |
| } |
| |
| /** |
| * Test getting accessible property writer methods for a specified list of properties of our standard test bean. |
| */ |
| @Test |
| void testGetWriteMethodBasic() { |
| testGetWriteMethod(bean, properties, TEST_BEAN_CLASS); |
| } |
| |
| /** |
| * Test getting accessible property writer methods for a specified list of properties of a package private subclass of our standard test bean. |
| */ |
| @Test |
| void testGetWriteMethodPackageSubclass() { |
| testGetWriteMethod(beanPackageSubclass, properties, TEST_BEAN_CLASS); |
| } |
| |
| /** |
| * Test getting accessible property writer methods for a specified list of properties of a public subclass of our standard test bean. |
| */ |
| @Test |
| void testGetWriteMethodPublicSubclass() { |
| testGetWriteMethod(beanPublicSubclass, properties, TEST_BEAN_CLASS); |
| } |
| |
| /** |
| * Test isReadable() method. |
| */ |
| @Test |
| void testIsReadable() throws Exception { |
| String property = null; |
| property = "stringProperty"; |
| assertTrue(PropertyUtils.isReadable(bean, property), "Property " + property + " isReadable expected TRUE"); |
| property = "stringIndexed"; |
| assertTrue(PropertyUtils.isReadable(bean, property), "Property " + property + " isReadable expected TRUE"); |
| property = "mappedProperty"; |
| assertTrue(PropertyUtils.isReadable(bean, property), "Property " + property + " isReadable expected TRUE"); |
| property = "nestedDynaBean"; |
| assertTrue(PropertyUtils.isReadable(bean, property), "Property " + property + " isReadable expected TRUE"); |
| property = "nestedDynaBean.stringProperty"; |
| assertTrue(PropertyUtils.isReadable(bean, property), "Property " + property + " isReadable expected TRUE"); |
| property = "nestedDynaBean.nestedBean"; |
| assertTrue(PropertyUtils.isReadable(bean, property), "Property " + property + " isReadable expected TRUE"); |
| property = "nestedDynaBean.nestedBean.nestedDynaBean"; |
| assertTrue(PropertyUtils.isReadable(bean, property), "Property " + property + " isReadable expected TRUE"); |
| property = "nestedDynaBean.nestedBean.nestedDynaBean.stringProperty"; |
| assertTrue(PropertyUtils.isReadable(bean, property), "Property " + property + " isReadable expected TRUE"); |
| property = "nestedDynaBean.nullDynaBean"; |
| assertTrue(PropertyUtils.isReadable(bean, property), "Property " + property + " isReadable expected TRUE"); |
| |
| assertThrows(NestedNullException.class, () -> PropertyUtils.isReadable(bean, "nestedDynaBean.nullDynaBean.foo")); |
| } |
| |
| /** |
| * Test isWriteable() method. |
| */ |
| @Test |
| void testIsWriteable() throws Exception { |
| String property = null; |
| property = "stringProperty"; |
| assertTrue(PropertyUtils.isWriteable(bean, property), "Property " + property + " isWriteable expected TRUE"); |
| property = "stringIndexed"; |
| assertTrue(PropertyUtils.isWriteable(bean, property), "Property " + property + " isWriteable expected TRUE"); |
| property = "mappedProperty"; |
| assertTrue(PropertyUtils.isWriteable(bean, property), "Property " + property + " isWriteable expected TRUE"); |
| property = "nestedDynaBean"; |
| assertTrue(PropertyUtils.isWriteable(bean, property), "Property " + property + " isWriteable expected TRUE"); |
| property = "nestedDynaBean.stringProperty"; |
| assertTrue(PropertyUtils.isWriteable(bean, property), "Property " + property + " isWriteable expected TRUE"); |
| property = "nestedDynaBean.nestedBean"; |
| assertTrue(PropertyUtils.isWriteable(bean, property), "Property " + property + " isWriteable expected TRUE"); |
| property = "nestedDynaBean.nestedBean.nestedDynaBean"; |
| assertTrue(PropertyUtils.isWriteable(bean, property), "Property " + property + " isWriteable expected TRUE"); |
| property = "nestedDynaBean.nestedBean.nestedDynaBean.stringProperty"; |
| assertTrue(PropertyUtils.isWriteable(bean, property), "Property " + property + " isWriteable expected TRUE"); |
| property = "nestedDynaBean.nullDynaBean"; |
| assertTrue(PropertyUtils.isWriteable(bean, property), "Property " + property + " isWriteable expected TRUE"); |
| assertThrows(NestedNullException.class, () -> PropertyUtils.isWriteable(bean, "nestedDynaBean.nullDynaBean.foo")); |
| } |
| |
| /** |
| * This tests to see that it is possible to subclass PropertyUtilsBean and change the behavior of setNestedProperty/getNestedProperty when dealing with |
| * objects that implement Map. |
| */ |
| @Test |
| void testMapExtensionCustom() throws Exception { |
| final PropsFirstPropertyUtilsBean utilsBean = new PropsFirstPropertyUtilsBean(); |
| final ExtendMapBean bean = new ExtendMapBean(); |
| |
| // hardly worth testing this, really :-) |
| bean.setUnusuallyNamedProperty("bean value"); |
| assertEquals("bean value", bean.getUnusuallyNamedProperty(), "Set property direct failed"); |
| |
| // setSimpleProperty should affect the simple property |
| utilsBean.setSimpleProperty(bean, "unusuallyNamedProperty", "new value"); |
| assertEquals("new value", bean.getUnusuallyNamedProperty(), "Set property on map failed (1)"); |
| |
| // setNestedProperty with setter should affect the simple property |
| // getNestedProperty with getter should obtain the simple property |
| utilsBean.setProperty(bean, "unusuallyNamedProperty", "next value"); |
| assertEquals("next value", bean.getUnusuallyNamedProperty(), "Set property on map failed (2)"); |
| assertEquals("next value", utilsBean.getNestedProperty(bean, "unusuallyNamedProperty"), "setNestedProperty on non-simple property failed"); |
| |
| // setting property without setter should update the map |
| // getting property without setter should fetch from the map |
| utilsBean.setProperty(bean, "mapProperty", "value1"); |
| assertEquals("value1", utilsBean.getNestedProperty(bean, "mapProperty"), "setNestedProperty on non-simple property failed"); |
| |
| final HashMap<String, Object> myMap = new HashMap<>(); |
| myMap.put("thebean", bean); |
| utilsBean.getNestedProperty(myMap, "thebean.mapitem"); |
| utilsBean.getNestedProperty(myMap, "thebean(mapitem)"); |
| } |
| |
| /** |
| * This tests to see that classes that implement Map always have their custom properties ignored. |
| * <p> |
| * Note that this behavior has changed several times over past releases of beanutils, breaking backwards compatibility each time. Here's hoping that the |
| * current 1.7.1 release is the last time this behavior changes! |
| */ |
| @Test |
| void testMapExtensionDefault() throws Exception { |
| final ExtendMapBean bean = new ExtendMapBean(); |
| |
| // setting property direct should work, and not affect map |
| bean.setUnusuallyNamedProperty("bean value"); |
| assertEquals("bean value", bean.getUnusuallyNamedProperty(), "Set property direct failed"); |
| assertNull(PropertyUtils.getNestedProperty(bean, "unusuallyNamedProperty"), "Get on unset map property failed"); |
| |
| // setting simple property should call the setter method only, and not |
| // affect the map. |
| PropertyUtils.setSimpleProperty(bean, "unusuallyNamedProperty", "new value"); |
| assertEquals("new value", bean.getUnusuallyNamedProperty(), "Set property on map failed (1)"); |
| assertNull(PropertyUtils.getNestedProperty(bean, "unusuallyNamedProperty"), "Get on unset map property failed"); |
| |
| // setting via setNestedProperty should affect the map only, and not |
| // call the setter method. |
| PropertyUtils.setProperty(bean, "unusuallyNamedProperty", "next value"); |
| assertEquals("next value", PropertyUtils.getNestedProperty(bean, "unusuallyNamedProperty"), |
| "setNestedProperty on map not visible to getNestedProperty"); |
| assertEquals("new value", bean.getUnusuallyNamedProperty(), "Set nested property on map unexpected affected simple property"); |
| } |
| |
| /** |
| * Test the mappedPropertyType of MappedPropertyDescriptor. |
| */ |
| @Test |
| void testMappedPropertyType() throws Exception { |
| |
| MappedPropertyDescriptor desc; |
| |
| // Check a String property |
| desc = (MappedPropertyDescriptor) PropertyUtils.getPropertyDescriptor(bean, "mappedProperty"); |
| assertEquals(String.class, desc.getMappedPropertyType()); |
| |
| // Check an int property |
| desc = (MappedPropertyDescriptor) PropertyUtils.getPropertyDescriptor(bean, "mappedIntProperty"); |
| assertEquals(Integer.TYPE, desc.getMappedPropertyType()); |
| |
| } |
| |
| /** |
| * There is an issue in setNestedProperty/getNestedProperty when the target bean is a map and the name string requests mapped or indexed operations on a |
| * field. These are not supported for fields of a Map, but it's an easy mistake to make and this test case ensures that an appropriate exception is thrown |
| * when a user does this. |
| * <p> |
| * The problem is with passing strings of form "a(b)" or "a[3]" to setNestedProperty or getNestedProperty when the target bean they are applied to |
| * implements Map. These strings are actually requesting "the result of calling mapped method a on the target object with a parameter of b" or "the result |
| * of calling indexed method a on the target object with a parameter of 3". And these requests are not valid when the target is a Map as a Map only supports |
| * calling get(fieldName) or put(fieldName), neither of which can be further indexed with a string or an integer. |
| * <p> |
| * However it is likely that some users will assume that "a[3]" when applied to a map will be equivalent to (map.get("a"))[3] with the appropriate |
| * typecasting, or for "a(b)" to be equivalent to map.get("a").get("b"). |
| * <p> |
| * Here we verify that an exception is thrown if the user makes this mistake. |
| */ |
| @Test |
| void testNestedPropertyKeyOrIndexOnBeanImplementingMap() throws Exception { |
| final HashMap<String, Object> map = new HashMap<>(); |
| final HashMap<String, Object> submap = new HashMap<>(); |
| final BetaBean betaBean1 = new BetaBean("test1"); |
| final BetaBean betaBean2 = new BetaBean("test2"); |
| |
| // map.put("submap", submap) |
| PropertyUtils.setNestedProperty(map, "submap", submap); |
| |
| // map.get("submap").put("beta1", betaBean1) |
| PropertyUtils.setNestedProperty(map, "submap.beta1", betaBean1); |
| assertEquals("submap", keysToString(map), "Unexpected keys in map"); |
| assertEquals("beta1", keysToString(submap), "Unexpected keys in submap"); |
| |
| // One would expect that the command below would be equivalent to |
| // Map m = (Map) map.get("submap"); |
| // m.put("beta2", betaBean2) |
| // However this isn't how javabeans property methods work. A map |
| // only effectively has "simple" properties, even when the |
| // returned object is a Map or Array. |
| final IllegalArgumentException e1 = assertThrows(IllegalArgumentException.class, |
| () -> PropertyUtils.setNestedProperty(map, "submap(beta2)", betaBean2)); |
| // What, no exception? In that case, setNestedProperties has |
| // probably just tried to do |
| // map.set("submap(beta2)", betaBean2) |
| // which is almost certainly not what the used expected. This is |
| // what BeanUtils 1.5.0 to 1.7.1 did.... |
| // ok, getting an exception was expected. As it is of a generic |
| // type, let's check the message string to make sure it really |
| // was caused by the issue we expected. |
| assertTrue(e1.getMessage().contains("Indexed or mapped properties are not supported"), "Unexpected exception message"); |
| |
| // One would expect that "submap[3]" would be equivalent to |
| // Object[] objects = (Object[]) map.get("submap"); |
| // return objects[3]; |
| // However this isn't how javabeans property methods work. A map |
| // only effectively has "simple" properties, even when the |
| // returned object is a Map or Array. |
| final IllegalArgumentException e2 = assertThrows(IllegalArgumentException.class, () -> PropertyUtils.getNestedProperty(map, "submap[3]")); |
| // What, no exception? In that case, getNestedProperties has |
| // probably just tried to do |
| // map.get("submap[3]") |
| // which is almost certainly not what the used expected. This is |
| // what BeanUtils 1.5.0 to 1.7.1 did.... |
| // ok, getting an exception was expected. As it is of a generic |
| // type, let's check the message string to make sure it really |
| // was caused by the issue we expected. |
| final int index = e2.getMessage().indexOf("Indexed or mapped properties are not supported"); |
| assertTrue(index >= 0, "Unexpected exception message"); |
| } |
| |
| @Test |
| void testNestedWithIndex() throws Exception { |
| final NestedTestBean nestedBean = new NestedTestBean("base"); |
| nestedBean.init(); |
| nestedBean.getSimpleBeanProperty().init(); |
| |
| NestedTestBean |
| |
| // test first calling properties on indexed beans |
| |
| value = (NestedTestBean) PropertyUtils.getProperty(nestedBean, "indexedProperty[0]"); |
| assertEquals("Bean@0", value.getName(), "Cannot get simple index(1)"); |
| assertEquals("NOT SET", value.getTestString(), "Bug in NestedTestBean"); |
| |
| value = (NestedTestBean) PropertyUtils.getProperty(nestedBean, "indexedProperty[1]"); |
| assertEquals("Bean@1", value.getName(), "Cannot get simple index(1)"); |
| assertEquals("NOT SET", value.getTestString(), "Bug in NestedTestBean"); |
| |
| String prop = (String) PropertyUtils.getProperty(nestedBean, "indexedProperty[0].testString"); |
| assertEquals("NOT SET", prop, "Get property on indexes failed (1)"); |
| |
| prop = (String) PropertyUtils.getProperty(nestedBean, "indexedProperty[1].testString"); |
| assertEquals("NOT SET", prop, "Get property on indexes failed (2)"); |
| |
| PropertyUtils.setProperty(nestedBean, "indexedProperty[0].testString", "Test#1"); |
| assertEquals("Test#1", nestedBean.getIndexedProperty(0).getTestString(), "Cannot set property on indexed bean (1)"); |
| |
| PropertyUtils.setProperty(nestedBean, "indexedProperty[1].testString", "Test#2"); |
| assertEquals("Test#2", nestedBean.getIndexedProperty(1).getTestString(), "Cannot set property on indexed bean (2)"); |
| |
| // test first calling indexed properties on a simple property |
| |
| value = (NestedTestBean) PropertyUtils.getProperty(nestedBean, "simpleBeanProperty"); |
| assertEquals("Simple Property Bean", value.getName(), "Cannot get simple bean"); |
| assertEquals("NOT SET", value.getTestString(), "Bug in NestedTestBean"); |
| |
| value = (NestedTestBean) PropertyUtils.getProperty(nestedBean, "simpleBeanProperty.indexedProperty[3]"); |
| assertEquals("Bean@3", value.getName(), "Cannot get index property on property"); |
| assertEquals("NOT SET", value.getTestString(), "Bug in NestedTestBean"); |
| |
| PropertyUtils.setProperty(nestedBean, "simpleBeanProperty.indexedProperty[3].testString", "Test#3"); |
| assertEquals("Test#3", nestedBean.getSimpleBeanProperty().getIndexedProperty(3).getTestString(), "Cannot set property on indexed property on property"); |
| } |
| |
| /** |
| * Tests whether a BeanIntrospector can be removed. |
| */ |
| @Test |
| void testRemoveBeanIntrospector() { |
| assertTrue(PropertyUtils.removeBeanIntrospector(DefaultBeanIntrospector.INSTANCE), "Wrong result"); |
| final PropertyDescriptor[] desc = PropertyUtils.getPropertyDescriptors(AlphaBean.class); |
| assertEquals(0, desc.length, "Got descriptors"); |
| PropertyUtils.addBeanIntrospector(DefaultBeanIntrospector.INSTANCE); |
| } |
| |
| /** |
| * Tests whether a reset of the registered BeanIntrospectors can be performed. |
| */ |
| @Test |
| void testResetBeanIntrospectors() { |
| assertTrue(PropertyUtils.removeBeanIntrospector(DefaultBeanIntrospector.INSTANCE), "Wrong result"); |
| PropertyUtils.resetBeanIntrospectors(); |
| final PropertyDescriptor[] desc = PropertyUtils.getPropertyDescriptors(AlphaBean.class); |
| assertTrue(desc.length > 0, "Got no descriptors"); |
| } |
| |
| /** |
| * Corner cases on setIndexedProperty invalid arguments. |
| */ |
| @Test |
| void testSetIndexedArguments() { |
| // Use explicit index argument |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setIndexedProperty(null, "intArray", 0, Integer.valueOf(1))); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setIndexedProperty(bean, null, 0, Integer.valueOf(1))); |
| // Use index expression |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setIndexedProperty(null, "intArray[0]", Integer.valueOf(1))); |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.setIndexedProperty(bean, "[0]", Integer.valueOf(1))); |
| assertThrows(IllegalArgumentException.class, () -> PropertyUtils.setIndexedProperty(bean, "intArray", Integer.valueOf(1))); |
| // Use explicit index argument |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setIndexedProperty(null, "intIndexed", 0, Integer.valueOf(1))); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setIndexedProperty(bean, null, 0, Integer.valueOf(1))); |
| // Use index expression |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setIndexedProperty(null, "intIndexed[0]", Integer.valueOf(1))); |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.setIndexedProperty(bean, "[0]", Integer.valueOf(1))); |
| assertThrows(IllegalArgumentException.class, () -> PropertyUtils.setIndexedProperty(bean, "intIndexed", Integer.valueOf(1))); |
| } |
| |
| /** |
| * Test setting an indexed value out of a multi-dimensional array |
| */ |
| @Test |
| void testSetIndexedArray() throws Exception { |
| final String[] firstArray = { "FIRST-1", "FIRST-2", "FIRST-3" }; |
| final String[] secondArray = { "SECOND-1", "SECOND-2", "SECOND-3", "SECOND-4" }; |
| final String[][] mainArray = { firstArray, secondArray }; |
| final TestBean bean = new TestBean(mainArray); |
| assertEquals("SECOND-3", bean.getString2dArray(1)[2], "BEFORE"); |
| PropertyUtils.setProperty(bean, "string2dArray[1][2]", "SECOND-3-UPDATED"); |
| assertEquals("SECOND-3-UPDATED", bean.getString2dArray(1)[2], "AFTER"); |
| } |
| |
| /** |
| * Test setting an indexed value out of List of Lists |
| */ |
| @Test |
| void testSetIndexedList() throws Exception { |
| final String[] firstArray = { "FIRST-1", "FIRST-2", "FIRST-3" }; |
| final String[] secondArray = { "SECOND-1", "SECOND-2", "SECOND-3", "SECOND-4" }; |
| final List<Object> mainList = new ArrayList<>(); |
| mainList.add(Arrays.asList(firstArray)); |
| mainList.add(Arrays.asList(secondArray)); |
| final TestBean bean = new TestBean(mainList); |
| assertEquals("SECOND-4", ((List<?>) bean.getListIndexed().get(1)).get(3), "BEFORE"); |
| PropertyUtils.setProperty(bean, "listIndexed[1][3]", "SECOND-4-UPDATED"); |
| assertEquals("SECOND-4-UPDATED", ((List<?>) bean.getListIndexed().get(1)).get(3), "AFTER"); |
| } |
| |
| /** |
| * Test setting a value out of a mapped Map |
| */ |
| @Test |
| void testSetIndexedMap() throws Exception { |
| final Map<String, Object> firstMap = new HashMap<>(); |
| firstMap.put("FIRST-KEY-1", "FIRST-VALUE-1"); |
| firstMap.put("FIRST-KEY-2", "FIRST-VALUE-2"); |
| final Map<String, Object> secondMap = new HashMap<>(); |
| secondMap.put("SECOND-KEY-1", "SECOND-VALUE-1"); |
| secondMap.put("SECOND-KEY-2", "SECOND-VALUE-2"); |
| |
| final List<Object> mainList = new ArrayList<>(); |
| mainList.add(firstMap); |
| mainList.add(secondMap); |
| final TestBean bean = new TestBean(mainList); |
| |
| assertEquals(null, ((Map<?, ?>) bean.getListIndexed().get(0)).get("FIRST-NEW-KEY"), "BEFORE"); |
| assertEquals("SECOND-VALUE-1", ((Map<?, ?>) bean.getListIndexed().get(1)).get("SECOND-KEY-1"), "BEFORE"); |
| PropertyUtils.setProperty(bean, "listIndexed[0](FIRST-NEW-KEY)", "FIRST-NEW-VALUE"); |
| PropertyUtils.setProperty(bean, "listIndexed[1](SECOND-KEY-1)", "SECOND-VALUE-1-UPDATED"); |
| assertEquals("FIRST-NEW-VALUE", ((Map<?, ?>) bean.getListIndexed().get(0)).get("FIRST-NEW-KEY"), "BEFORE"); |
| assertEquals("SECOND-VALUE-1-UPDATED", ((Map<?, ?>) bean.getListIndexed().get(1)).get("SECOND-KEY-1"), "AFTER"); |
| } |
| |
| /** |
| * Positive and negative tests on setIndexedProperty valid arguments. |
| */ |
| @Test |
| void testSetIndexedValues() throws Exception { |
| Object value = null; |
| // Use explicit index argument |
| PropertyUtils.setIndexedProperty(bean, "dupProperty", 0, "New 0"); |
| value = PropertyUtils.getIndexedProperty(bean, "dupProperty", 0); |
| assertNotNull(value, "Returned new value 0"); |
| assertInstanceOf(String.class, value, "Returned String new value 0"); |
| assertEquals("New 0", (String) value, "Returned correct new value 0"); |
| |
| PropertyUtils.setIndexedProperty(bean, "intArray", 0, Integer.valueOf(1)); |
| value = PropertyUtils.getIndexedProperty(bean, "intArray", 0); |
| assertNotNull(value, "Returned new value 0"); |
| assertInstanceOf(Integer.class, value, "Returned Integer new value 0"); |
| assertEquals(1, ((Integer) value).intValue(), "Returned correct new value 0"); |
| |
| PropertyUtils.setIndexedProperty(bean, "intIndexed", 1, Integer.valueOf(11)); |
| value = PropertyUtils.getIndexedProperty(bean, "intIndexed", 1); |
| assertNotNull(value, "Returned new value 1"); |
| assertInstanceOf(Integer.class, value, "Returned Integer new value 1"); |
| assertEquals(11, ((Integer) value).intValue(), "Returned correct new value 1"); |
| |
| PropertyUtils.setIndexedProperty(bean, "listIndexed", 2, "New Value 2"); |
| value = PropertyUtils.getIndexedProperty(bean, "listIndexed", 2); |
| assertNotNull(value, "Returned new value 2"); |
| assertInstanceOf(String.class, value, "Returned String new value 2"); |
| assertEquals("New Value 2", (String) value, "Returned correct new value 2"); |
| |
| PropertyUtils.setIndexedProperty(bean, "stringArray", 2, "New Value 2"); |
| value = PropertyUtils.getIndexedProperty(bean, "stringArray", 2); |
| assertNotNull(value, "Returned new value 2"); |
| assertInstanceOf(String.class, value, "Returned String new value 2"); |
| assertEquals("New Value 2", (String) value, "Returned correct new value 2"); |
| |
| PropertyUtils.setIndexedProperty(bean, "stringArray", 3, "New Value 3"); |
| value = PropertyUtils.getIndexedProperty(bean, "stringArray", 3); |
| assertNotNull(value, "Returned new value 3"); |
| assertInstanceOf(String.class, value, "Returned String new value 3"); |
| assertEquals("New Value 3", (String) value, "Returned correct new value 3"); |
| |
| // Use index expression |
| |
| PropertyUtils.setIndexedProperty(bean, "dupProperty[4]", "New 4"); |
| value = PropertyUtils.getIndexedProperty(bean, "dupProperty[4]"); |
| assertNotNull(value, "Returned new value 4"); |
| assertInstanceOf(String.class, value, "Returned String new value 4"); |
| assertEquals("New 4", (String) value, "Returned correct new value 4"); |
| |
| PropertyUtils.setIndexedProperty(bean, "intArray[4]", Integer.valueOf(1)); |
| value = PropertyUtils.getIndexedProperty(bean, "intArray[4]"); |
| assertNotNull(value, "Returned new value 4"); |
| assertInstanceOf(Integer.class, value, "Returned Integer new value 4"); |
| assertEquals(1, ((Integer) value).intValue(), "Returned correct new value 4"); |
| |
| PropertyUtils.setIndexedProperty(bean, "intIndexed[3]", Integer.valueOf(11)); |
| value = PropertyUtils.getIndexedProperty(bean, "intIndexed[3]"); |
| assertNotNull(value, "Returned new value 5"); |
| assertInstanceOf(Integer.class, value, "Returned Integer new value 5"); |
| assertEquals(11, ((Integer) value).intValue(), "Returned correct new value 5"); |
| |
| PropertyUtils.setIndexedProperty(bean, "listIndexed[1]", "New Value 2"); |
| value = PropertyUtils.getIndexedProperty(bean, "listIndexed[1]"); |
| assertNotNull(value, "Returned new value 6"); |
| assertInstanceOf(String.class, value, "Returned String new value 6"); |
| assertEquals("New Value 2", (String) value, "Returned correct new value 6"); |
| |
| PropertyUtils.setIndexedProperty(bean, "stringArray[1]", "New Value 2"); |
| value = PropertyUtils.getIndexedProperty(bean, "stringArray[2]"); |
| assertNotNull(value, "Returned new value 6"); |
| assertInstanceOf(String.class, value, "Returned String new value 6"); |
| assertEquals("New Value 2", (String) value, "Returned correct new value 6"); |
| |
| PropertyUtils.setIndexedProperty(bean, "stringArray[0]", "New Value 3"); |
| value = PropertyUtils.getIndexedProperty(bean, "stringArray[0]"); |
| assertNotNull(value, "Returned new value 7"); |
| assertInstanceOf(String.class, value, "Returned String new value 7"); |
| assertEquals("New Value 3", (String) value, "Returned correct new value 7"); |
| |
| // Index out of bounds tests |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "dupProperty", -1, "New -1")); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "dupProperty", 5, "New 5")); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "intArray", -1, Integer.valueOf(0))); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "intArray", 5, Integer.valueOf(0))); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "intIndexed", -1, Integer.valueOf(0))); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "intIndexed", 5, Integer.valueOf(0))); |
| assertThrows(IndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "listIndexed", 5, "New String")); |
| assertThrows(IndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "listIndexed", -1, "New String")); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "stringArray", -1, "New String")); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "stringArray", 5, "New String")); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "stringIndexed", -1, "New String")); |
| assertThrows(ArrayIndexOutOfBoundsException.class, () -> PropertyUtils.setIndexedProperty(bean, "stringIndexed", 5, "New String")); |
| } |
| |
| /** |
| * Corner cases on getMappedProperty invalid arguments. |
| */ |
| @Test |
| void testSetMappedArguments() { |
| // Use explicit key argument |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setMappedProperty(null, "mappedProperty", "First Key", "First Value")); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setMappedProperty(bean, null, "First Key", "First Value")); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setMappedProperty(bean, "mappedProperty", null, "First Value")); |
| // Use key expression |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setMappedProperty(null, "mappedProperty(First Key)", "First Value")); |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.setMappedProperty(bean, "(Second Key)", "Second Value")); |
| assertThrows(IllegalArgumentException.class, () -> PropertyUtils.setMappedProperty(bean, "mappedProperty", "Third Value")); |
| } |
| |
| /** |
| * Test setting an indexed value out of a mapped array |
| */ |
| @Test |
| void testSetMappedArray() throws Exception { |
| final TestBean bean = new TestBean(); |
| final String[] array = { "abc", "def", "ghi" }; |
| bean.getMapProperty().put("mappedArray", array); |
| |
| assertEquals("def", ((String[]) bean.getMapProperty().get("mappedArray"))[1], "BEFORE"); |
| PropertyUtils.setProperty(bean, "mapProperty(mappedArray)[1]", "DEF-UPDATED"); |
| assertEquals("DEF-UPDATED", ((String[]) bean.getMapProperty().get("mappedArray"))[1], "AFTER"); |
| } |
| |
| /** |
| * Test setting an indexed value out of a mapped List |
| */ |
| @Test |
| void testSetMappedList() throws Exception { |
| final TestBean bean = new TestBean(); |
| final List<Object> list = new ArrayList<>(); |
| list.add("klm"); |
| list.add("nop"); |
| list.add("qrs"); |
| bean.getMapProperty().put("mappedList", list); |
| |
| assertEquals("klm", ((List<?>) bean.getMapProperty().get("mappedList")).get(0), "BEFORE"); |
| PropertyUtils.setProperty(bean, "mapProperty(mappedList)[0]", "KLM-UPDATED"); |
| assertEquals("KLM-UPDATED", ((List<?>) bean.getMapProperty().get("mappedList")).get(0), "AFTER"); |
| } |
| |
| /** |
| * Test setting a value out of a mapped Map |
| */ |
| @Test |
| void testSetMappedMap() throws Exception { |
| final TestBean bean = new TestBean(); |
| final Map<String, Object> map = new HashMap<>(); |
| map.put("sub-key-1", "sub-value-1"); |
| map.put("sub-key-2", "sub-value-2"); |
| map.put("sub-key-3", "sub-value-3"); |
| bean.getMapProperty().put("mappedMap", map); |
| |
| assertEquals("sub-value-3", ((Map<?, ?>) bean.getMapProperty().get("mappedMap")).get("sub-key-3"), "BEFORE"); |
| PropertyUtils.setProperty(bean, "mapProperty(mappedMap)(sub-key-3)", "SUB-KEY-3-UPDATED"); |
| assertEquals("SUB-KEY-3-UPDATED", ((Map<?, ?>) bean.getMapProperty().get("mappedMap")).get("sub-key-3"), "AFTER"); |
| } |
| |
| /** |
| * Test setting mapped values with periods in the key. |
| */ |
| @Test |
| void testSetMappedPeriods() throws Exception { |
| |
| // PropertyUtils.setMappedProperty()-------- |
| bean.setMappedProperty("key.with.a.dot", "Special Value"); |
| assertEquals("Special Value", bean.getMappedProperty("key.with.a.dot"), "Can retrieve directly (A)"); |
| |
| PropertyUtils.setMappedProperty(bean, "mappedProperty", "key.with.a.dot", "Updated Special Value"); |
| assertEquals("Updated Special Value", bean.getMappedProperty("key.with.a.dot"), "Check set via setMappedProperty"); |
| |
| // PropertyUtils.setNestedProperty() |
| bean.setMappedProperty("key.with.a.dot", "Special Value"); |
| assertEquals("Special Value", bean.getMappedProperty("key.with.a.dot"), "Can retrieve directly (B)"); |
| PropertyUtils.setNestedProperty(bean, "mappedProperty(key.with.a.dot)", "Updated Special Value"); |
| assertEquals("Updated Special Value", bean.getMappedProperty("key.with.a.dot"), "Check set via setNestedProperty (B)"); |
| |
| // PropertyUtils.setNestedProperty() |
| final TestBean testBean = new TestBean(); |
| bean.setMappedObjects("nested.property", testBean); |
| assertEquals("This is a string", testBean.getStringProperty(), "Can retrieve directly (C)"); |
| PropertyUtils.setNestedProperty(bean, "mappedObjects(nested.property).stringProperty", "Updated String Value"); |
| assertEquals("Updated String Value", testBean.getStringProperty(), "Check set via setNestedProperty (C)"); |
| |
| // PropertyUtils.setNestedProperty() |
| bean.getNested().setMappedProperty("Mapped Key", "Nested Mapped Value"); |
| assertEquals("Nested Mapped Value", PropertyUtils.getNestedProperty(bean, "nested.mappedProperty(Mapped Key)"), |
| "Can retrieve via getNestedProperty (D)"); |
| PropertyUtils.setNestedProperty(bean, "nested.mappedProperty(Mapped Key)", "Updated Nested Mapped Value"); |
| assertEquals("Updated Nested Mapped Value", PropertyUtils.getNestedProperty(bean, "nested.mappedProperty(Mapped Key)"), |
| "Check set via setNestedProperty (D)"); |
| } |
| |
| /** |
| * Positive and negative tests on setMappedProperty valid arguments. |
| */ |
| @Test |
| void testSetMappedValues() throws Exception { |
| Object value = null; |
| // Use explicit key argument |
| value = PropertyUtils.getMappedProperty(bean, "mappedProperty", "Fourth Key"); |
| assertNull(value, "Can not find fourth value"); |
| |
| PropertyUtils.setMappedProperty(bean, "mappedProperty", "Fourth Key", "Fourth Value"); |
| |
| value = PropertyUtils.getMappedProperty(bean, "mappedProperty", "Fourth Key"); |
| assertEquals("Fourth Value", value, "Can find fourth value"); |
| |
| // Use key expression with parentheses |
| value = PropertyUtils.getMappedProperty(bean, "mappedProperty(Fifth Key)"); |
| assertNull(value, "Can not find fifth value"); |
| |
| PropertyUtils.setMappedProperty(bean, "mappedProperty(Fifth Key)", "Fifth Value"); |
| |
| value = PropertyUtils.getMappedProperty(bean, "mappedProperty(Fifth Key)"); |
| assertEquals("Fifth Value", value, "Can find fifth value"); |
| |
| // Use key expression with dotted expression |
| |
| value = PropertyUtils.getNestedProperty(bean, "mapProperty.Sixth Key"); |
| assertNull(value, "Can not find sixth value"); |
| |
| PropertyUtils.setNestedProperty(bean, "mapProperty.Sixth Key", "Sixth Value"); |
| |
| value = PropertyUtils.getNestedProperty(bean, "mapProperty.Sixth Key"); |
| assertEquals("Sixth Value", value, "Can find sixth value"); |
| } |
| |
| /** |
| * Corner cases on setNestedProperty invalid arguments. |
| */ |
| @Test |
| void testSetNestedArguments() { |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setNestedProperty(null, "stringProperty", "")); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setNestedProperty(bean, null, "")); |
| } |
| |
| /** |
| * Test setNextedProperty on a boolean property. |
| */ |
| @Test |
| void testSetNestedBoolean() throws Exception { |
| final boolean oldValue = bean.getNested().getBooleanProperty(); |
| final boolean newValue = !oldValue; |
| PropertyUtils.setNestedProperty(bean, "nested.booleanProperty", Boolean.valueOf(newValue)); |
| assertEquals(newValue, bean.getNested().getBooleanProperty(), "Matched new value"); |
| } |
| |
| /** |
| * Test setNestedProperty on a double property. |
| */ |
| @Test |
| void testSetNestedDouble() throws Exception { |
| final double oldValue = bean.getNested().getDoubleProperty(); |
| final double newValue = oldValue + 1.0; |
| PropertyUtils.setNestedProperty(bean, "nested.doubleProperty", Double.valueOf(newValue)); |
| assertEquals(newValue, bean.getNested().getDoubleProperty(), 0.005, "Matched new value"); |
| } |
| |
| /** |
| * Test setNestedProperty on a float property. |
| */ |
| @Test |
| void testSetNestedFloat() throws Exception { |
| final float oldValue = bean.getNested().getFloatProperty(); |
| final float newValue = oldValue + (float) 1.0; |
| PropertyUtils.setNestedProperty(bean, "nested.floatProperty", Float.valueOf(newValue)); |
| assertEquals(newValue, bean.getNested().getFloatProperty(), (float) 0.005, "Matched new value"); |
| } |
| |
| /** |
| * Test setNestedProperty on a int property. |
| */ |
| @Test |
| void testSetNestedInt() throws Exception { |
| final int oldValue = bean.getNested().getIntProperty(); |
| final int newValue = oldValue + 1; |
| PropertyUtils.setNestedProperty(bean, "nested.intProperty", Integer.valueOf(newValue)); |
| assertEquals(newValue, bean.getNested().getIntProperty(), "Matched new value"); |
| } |
| |
| /** |
| * Test setNestedProperty on a long property. |
| */ |
| @Test |
| void testSetNestedLong() throws Exception { |
| final long oldValue = bean.getNested().getLongProperty(); |
| final long newValue = oldValue + 1; |
| PropertyUtils.setNestedProperty(bean, "nested.longProperty", Long.valueOf(newValue)); |
| assertEquals(newValue, bean.getNested().getLongProperty(), "Matched new value"); |
| } |
| |
| /** |
| * Test setNestedProperty on a read-only String property. |
| */ |
| @Test |
| void testSetNestedReadOnly() throws Exception { |
| final String oldValue = bean.getNested().getWriteOnlyPropertyValue(); |
| final String newValue = oldValue + " Extra Value"; |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.setNestedProperty(bean, "nested.readOnlyProperty", newValue)); |
| } |
| |
| /** |
| * Test setNestedProperty on a short property. |
| */ |
| @Test |
| void testSetNestedShort() throws Exception { |
| final short oldValue = bean.getNested().getShortProperty(); |
| short newValue = oldValue; |
| newValue++; |
| PropertyUtils.setNestedProperty(bean, "nested.shortProperty", Short.valueOf(newValue)); |
| assertEquals(newValue, bean.getNested().getShortProperty(), "Matched new value"); |
| } |
| |
| /** |
| * Test setNestedProperty on a String property. |
| */ |
| @Test |
| void testSetNestedString() throws Exception { |
| final String oldValue = bean.getNested().getStringProperty(); |
| final String newValue = oldValue + " Extra Value"; |
| PropertyUtils.setNestedProperty(bean, "nested.stringProperty", newValue); |
| assertEquals(newValue, bean.getNested().getStringProperty(), "Matched new value"); |
| } |
| |
| /** |
| * Test setNestedProperty on an unknown property name. |
| */ |
| @Test |
| void testSetNestedUnknown() throws Exception { |
| final String newValue = "New String Value"; |
| assertThrows(NoSuchMethodException.class, () -> PropertyUtils.setNestedProperty(bean, "nested.unknown", newValue)); |
| } |
| |
| /** |
| * Test setNestedProperty on a write-only String property. |
| */ |
| @Test |
| void testSetNestedWriteOnly() throws Exception { |
| final String oldValue = bean.getNested().getWriteOnlyPropertyValue(); |
| final String newValue = oldValue + " Extra Value"; |
| PropertyUtils.setNestedProperty(bean, "nested.writeOnlyProperty", newValue); |
| assertEquals(newValue, bean.getNested().getWriteOnlyPropertyValue(), "Matched new value"); |
| } |
| |
| @Test |
| void testSetNoGetter() throws Exception { |
| final BetaBean bean = new BetaBean("Cedric"); |
| |
| // test standard no getter |
| bean.setNoGetterProperty("Sigma"); |
| assertEquals("Sigma", bean.getSecret(), "BetaBean test failed"); |
| |
| assertNotNull(PropertyUtils.getPropertyDescriptor(bean, "noGetterProperty"), "Descriptor is null"); |
| |
| BeanUtils.setProperty(bean, "noGetterProperty", "Omega"); |
| assertEquals("Omega", bean.getSecret(), "Cannot set no-getter property"); |
| |
| // test mapped no getter descriptor |
| assertNotNull(PropertyUtils.getPropertyDescriptor(bean, "noGetterMappedProperty"), "Map Descriptor is null"); |
| |
| PropertyUtils.setMappedProperty(bean, "noGetterMappedProperty", "Epsilon", "Epsilon"); |
| assertEquals("MAP:Epsilon", bean.getSecret(), "Cannot set mapped no-getter property"); |
| } |
| |
| /** |
| * Test accessing a public sub-bean of a package scope bean |
| */ |
| @Test |
| void testSetPublicSubBean_of_PackageBean() throws Exception { |
| |
| final PublicSubBean bean = new PublicSubBean(); |
| bean.setFoo("foo-start"); |
| bean.setBar("bar-start"); |
| // Set Foo |
| PropertyUtils.setProperty(bean, "foo", "foo-updated"); |
| assertEquals("foo-updated", bean.getFoo(), "foo property"); |
| // Set Bar |
| PropertyUtils.setProperty(bean, "bar", "bar-updated"); |
| assertEquals("bar-updated", bean.getBar(), "bar property"); |
| } |
| |
| /** |
| * Corner cases on setSimpleProperty invalid arguments. |
| */ |
| @Test |
| void testSetSimpleArguments() { |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setSimpleProperty(null, "stringProperty", "")); |
| assertThrows(NullPointerException.class, () -> PropertyUtils.setSimpleProperty(bean, null, "")); |
| } |
| |
| /** |
| * Test setSimpleProperty on a boolean property. |
| */ |
| @Test |
| void testSetSimpleBoolean() throws Exception { |
| final boolean oldValue = bean.getBooleanProperty(); |
| final boolean newValue = !oldValue; |
| PropertyUtils.setSimpleProperty(bean, "booleanProperty", Boolean.valueOf(newValue)); |
| assertEquals(newValue, bean.getBooleanProperty(), "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a double property. |
| */ |
| @Test |
| void testSetSimpleDouble() throws Exception { |
| final double oldValue = bean.getDoubleProperty(); |
| final double newValue = oldValue + 1.0; |
| PropertyUtils.setSimpleProperty(bean, "doubleProperty", Double.valueOf(newValue)); |
| assertEquals(newValue, bean.getDoubleProperty(), 0.005, "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a float property. |
| */ |
| @Test |
| void testSetSimpleFloat() throws Exception { |
| final float oldValue = bean.getFloatProperty(); |
| final float newValue = oldValue + (float) 1.0; |
| PropertyUtils.setSimpleProperty(bean, "floatProperty", Float.valueOf(newValue)); |
| assertEquals(newValue, bean.getFloatProperty(), (float) 0.005, "Matched new value"); |
| } |
| |
| /** |
| * Negative test setSimpleProperty on an indexed property. |
| */ |
| @Test |
| void testSetSimpleIndexed() throws Exception { |
| assertThrows(IllegalArgumentException.class, () -> PropertyUtils.setSimpleProperty(bean, "stringIndexed[0]", "New String Value")); |
| } |
| |
| /** |
| * Test setSimpleProperty on a int property. |
| */ |
| @Test |
| void testSetSimpleInt() throws Exception { |
| final int oldValue = bean.getIntProperty(); |
| final int newValue = oldValue + 1; |
| PropertyUtils.setSimpleProperty(bean, "intProperty", Integer.valueOf(newValue)); |
| assertEquals(newValue, bean.getIntProperty(), "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a long property. |
| */ |
| @Test |
| void testSetSimpleLong() throws Exception { |
| final long oldValue = bean.getLongProperty(); |
| final long newValue = oldValue + 1; |
| PropertyUtils.setSimpleProperty(bean, "longProperty", Long.valueOf(newValue)); |
| assertEquals(newValue, bean.getLongProperty(), "Matched new value"); |
| } |
| |
| /** |
| * Negative test setSimpleProperty on a nested property. |
| */ |
| @Test |
| void testSetSimpleNested() throws Exception { |
| assertThrows(IllegalArgumentException.class, () -> PropertyUtils.setSimpleProperty(bean, "nested.stringProperty", "New String Value")); |
| } |
| |
| /** |
| * Test setSimpleProperty on a read-only String property. |
| */ |
| @Test |
| void testSetSimpleReadOnly() throws Exception { |
| final String oldValue = bean.getWriteOnlyPropertyValue(); |
| final String newValue = oldValue + " Extra Value"; |
| final NoSuchMethodException e = assertThrows(NoSuchMethodException.class, () -> PropertyUtils.setSimpleProperty(bean, "readOnlyProperty", newValue)); |
| assertEquals("Property 'readOnlyProperty' has no setter method in class '" + bean.getClass() + "'", e.getMessage()); |
| } |
| |
| /** |
| * Test setSimpleProperty on a short property. |
| */ |
| @Test |
| void testSetSimpleShort() throws Exception { |
| final short oldValue = bean.getShortProperty(); |
| short newValue = oldValue; |
| newValue++; |
| PropertyUtils.setSimpleProperty(bean, "shortProperty", Short.valueOf(newValue)); |
| assertEquals(newValue, bean.getShortProperty(), "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on a String property. |
| */ |
| @Test |
| void testSetSimpleString() throws Exception { |
| final String oldValue = bean.getStringProperty(); |
| final String newValue = oldValue + " Extra Value"; |
| PropertyUtils.setSimpleProperty(bean, "stringProperty", newValue); |
| assertEquals(newValue, bean.getStringProperty(), "Matched new value"); |
| } |
| |
| /** |
| * Test setSimpleProperty on an unknown property name. |
| */ |
| @Test |
| void testSetSimpleUnknown() throws Exception { |
| final String newValue = "New String Value"; |
| final NoSuchMethodException e = assertThrows(NoSuchMethodException.class, () -> PropertyUtils.setSimpleProperty(bean, "unknown", newValue)); |
| assertEquals("Unknown property 'unknown' on class '" + bean.getClass() + "'", e.getMessage()); |
| } |
| |
| /** |
| * Test setSimpleProperty on a write-only String property. |
| */ |
| @Test |
| void testSetSimpleWriteOnly() throws Exception { |
| final String oldValue = bean.getWriteOnlyPropertyValue(); |
| final String newValue = oldValue + " Extra Value"; |
| PropertyUtils.setSimpleProperty(bean, "writeOnlyProperty", newValue); |
| assertEquals(newValue, bean.getWriteOnlyPropertyValue(), "Matched new value"); |
| } |
| |
| /** |
| * When a bean has a null property which is reference by the standard access language, this should throw a NestedNullException. |
| */ |
| @Test |
| void testThrowNestedNull() throws Exception { |
| final NestedTestBean nestedBean = new NestedTestBean("base"); |
| // don't init! |
| assertThrows(NestedNullException.class, () -> PropertyUtils.getProperty(nestedBean, "simpleBeanProperty.indexedProperty[0]")); |
| } |
| } |