1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.beanutils;
18  
19  import java.util.HashMap;
20  import java.util.TreeMap;
21  import java.util.ArrayList;
22  import java.util.LinkedList;
23  import java.lang.reflect.InvocationTargetException;
24  import junit.framework.TestCase;
25  import junit.framework.Test;
26  import junit.framework.TestSuite;
27  
28  /***
29   * <p>Test Case for the <code>LazyDynaBean</code> implementation class.</p>
30   *
31   * @author Niall Pemberton
32   */
33  public class LazyDynaBeanTestCase extends TestCase {
34  
35      protected LazyDynaBean  bean      = null;
36      protected LazyDynaClass dynaClass = null;
37      protected String testProperty     = "myProperty";
38      protected String testPropertyA    = "myProperty-A";
39      protected String testPropertyB    = "myProperty-B";
40      protected String testString1      = "myStringValue-1";
41      protected String testString2      = "myStringValue-2";
42      protected Integer testInteger1    = new Integer(30);
43      protected Integer testInteger2    = new Integer(40);
44      protected String testKey          = "myKey";
45  
46      // ---------------------------------------------------------- Constructors
47  
48      /***
49       * Construct a new instance of this test case.
50       *
51       * @param name Name of the test case
52       */
53      public LazyDynaBeanTestCase(String name) {
54          super(name);
55      }
56  
57      // -------------------------------------------------- Overall Test Methods
58  
59      /***
60       * Run thus Test
61       */
62      public static void main(String[] args) {
63          junit.textui.TestRunner.run(suite());
64      }
65  
66      /***
67       * Return the tests included in this test suite.
68       */
69      public static Test suite() {
70          return (new TestSuite(LazyDynaBeanTestCase.class));
71      }
72  
73      /***
74       * Set up instance variables required by this test case.
75       */
76      public void setUp() throws Exception {
77          bean = new LazyDynaBean();
78          dynaClass = (LazyDynaClass)bean.getDynaClass();
79          dynaClass.setReturnNull(true);
80      }
81  
82      /***
83       * Tear down instance variables required by this test case.
84       */
85      public void tearDown() {
86        bean = null;
87      }
88  
89      // ------------------------------------------------ Individual Test Methods
90  
91      /***
92       * Test Getting/Setting a Simple Property
93       */
94      public void testSimpleProperty() {
95  
96          // Check the property & value doesn't exist
97          assertNull("Check Property doesn't exist", dynaClass.getDynaProperty(testProperty));
98          assertNull("Check Value is null", bean.get(testProperty));
99  
100         // Set a new property - should add new property and set value
101         bean.set(testProperty, testInteger1);
102         assertEquals("Check First Value is correct", testInteger1, bean.get(testProperty));
103         assertEquals("Check Property type is correct", Integer.class, dynaClass.getDynaProperty(testProperty).getType());
104 
105         // Set the property again - should set the new value
106         bean.set(testProperty, testInteger2);
107         assertEquals("Check Second Value is correct", testInteger2, bean.get(testProperty));
108 
109         // Set the property again - with a different type, should fail
110         try {
111             bean.set(testProperty, testString1);
112             fail("expected ConversionException trying to set an Integer property to a String");
113         } catch (ConversionException expected) {
114             // expected result
115         }
116 
117     }
118 
119     /***
120      * Test Getting/Setting a 'null' Property
121      */
122     public void testNullProperty() {
123 
124         // Check the property & value doesn't exist
125         assertNull("Check Property doesn't exist", dynaClass.getDynaProperty(testProperty));
126         assertNull("Check Value is null", bean.get(testProperty));
127 
128         // Set a new property to null
129         bean.set(testProperty, null);
130         assertNull("Check Value is still null", bean.get(testProperty));
131 
132     }
133 
134     /***
135      * Test Setting a Simple Property when MutableDynaClass is set to restricted
136      */
137     public void testSimplePropertyRestricted() {
138 
139         // Set the MutableDyanClass to 'restricted' (i.e. no new properties cab be added
140         dynaClass.setRestricted(true);
141         assertTrue("Check MutableDynaClass is restricted", dynaClass.isRestricted());
142 
143         // Check the property & value doesn't exist
144         assertNull("Check Property doesn't exist", dynaClass.getDynaProperty(testProperty));
145         assertNull("Check Value is null", bean.get(testProperty));
146 
147         // Set the property - should fail because property doesn't exist and MutableDynaClass is restricted
148         try {
149             bean.set(testProperty, testString1);
150             fail("expected IllegalArgumentException trying to add new property to restricted DynaClass");
151         } catch (IllegalArgumentException expected) {
152             // expected result
153         }
154 
155     }
156 
157     /***
158      * Test Getting/Setting a 'Mapped' Property - default HashMap property
159      */
160     public void testMappedPropertyDefault() {
161 
162         // Check the property & value doesn't exist
163         assertNull("Check Mapped Property doesn't exist", dynaClass.getDynaProperty(testProperty));
164         assertNull("Check Map is null", bean.get(testProperty));
165         assertNull("Check Mapped Value is null", bean.get(testProperty, testKey));
166 
167         // Set a new mapped property - should add new HashMap property and set the mapped value
168         bean.set(testProperty, testKey, testInteger1);
169         assertEquals("Check Mapped Property exists", HashMap.class, bean.get(testProperty).getClass());
170         assertEquals("Check First Mapped Value is correct(a)", testInteger1, bean.get(testProperty, testKey));
171         assertEquals("Check First Mapped Value is correct(b)", testInteger1, ((HashMap)bean.get(testProperty)).get(testKey));
172 
173         // Set the property again - should set the new value
174         bean.set(testProperty, testKey, testInteger2);
175         assertEquals("Check Second Mapped Value is correct(a)", testInteger2, bean.get(testProperty, testKey));
176         assertEquals("Check Second Mapped Value is correct(b)", testInteger2, ((HashMap)bean.get(testProperty)).get(testKey));
177     }
178 
179     /***
180      * Test Getting/Setting a 'Mapped' Property - use TreeMap property
181      */
182     public void testMappedPropertyTreeMap() {
183 
184         // Check the property & value doesn't exist
185         assertNull("Check Mapped Property doesn't exist", dynaClass.getDynaProperty(testProperty));
186 
187         // Add a 'TreeMap' property to the DynaClass
188         dynaClass.add(testProperty, TreeMap.class);
189         assertTrue("Check Property is mapped", dynaClass.getDynaProperty(testProperty).isMapped());
190         assertEquals("Check Property is correct type", TreeMap.class, dynaClass.getDynaProperty(testProperty).getType());
191         assertEquals("Check Mapped Property exists", TreeMap.class, bean.get(testProperty).getClass());
192 //        assertNull("Check mapped property is null", bean.get(testProperty));
193 
194         // Set a new mapped property - should instatiate a new TreeMap property and set the mapped value
195         bean.set(testProperty, testKey, testInteger1);
196         assertEquals("Check Mapped Property exists", TreeMap.class, bean.get(testProperty).getClass());
197         assertEquals("Check First Mapped Value is correct(a)", testInteger1, bean.get(testProperty, testKey));
198         assertEquals("Check First Mapped Value is correct(b)", testInteger1, ((TreeMap)bean.get(testProperty)).get(testKey));
199 
200         // Set the property again - should set the new value
201         bean.set(testProperty, testKey, testInteger2);
202         assertEquals("Check Second Mapped Value is correct(a)", testInteger2, bean.get(testProperty, testKey));
203         assertEquals("Check Second Mapped Value is correct(b)", testInteger2, ((TreeMap)bean.get(testProperty)).get(testKey));
204     }
205 
206     /***
207      * Test Setting a 'Mapped' Property using PropertyUtils
208      */
209     public void testMappedPropertyUtils() {
210 
211         dynaClass.setReturnNull(false);
212 
213         // Check the property & value doesn't exist
214         assertFalse("Check Mapped Property doesn't exist", dynaClass.isDynaProperty(testProperty));
215         assertNull("Check Map is null", bean.get(testProperty));
216         assertNull("Check Mapped Value is null", bean.get(testProperty, testKey));
217 
218         // Set the mapped property using PropertyUtils
219         try {
220           PropertyUtils.setProperty(bean, testProperty+"("+testKey+")", testString1);
221         }
222         catch (NoSuchMethodException ex) {
223             fail("testIndexedPropertyUtils threw "+ex);
224         }
225         catch (InvocationTargetException ex) {
226             fail("testIndexedPropertyUtils threw "+ex);
227         }
228         catch (IllegalAccessException ex) {
229             fail("testIndexedPropertyUtils threw "+ex);
230         }
231 
232         // Check property value correctly set
233         assertEquals("Check Mapped Bean Value is correct", testString1, bean.get(testProperty, testKey));
234 
235     }
236 
237     /***
238      * Test Setting a Mapped Property when MutableDynaClass is set to restricted
239      */
240     public void testMappedPropertyRestricted() {
241 
242         // Set the MutableDyanClass to 'restricted' (i.e. no new properties cab be added
243         dynaClass.setRestricted(true);
244         assertTrue("Check MutableDynaClass is restricted", dynaClass.isRestricted());
245 
246         // Check the property & value doesn't exist
247         assertNull("Check Property doesn't exist", dynaClass.getDynaProperty(testProperty));
248         assertNull("Check Value is null", bean.get(testProperty));
249 
250         // Set the property - should fail because property doesn't exist and MutableDynaClass is restricted
251         try {
252             bean.set(testProperty, testKey, testInteger1);
253             fail("expected IllegalArgumentException trying to add new property to restricted MutableDynaClass");
254         } catch (IllegalArgumentException expected) {
255             // expected result
256         }
257 
258     }
259 
260     /***
261      * Test setting mapped property for type which is not Map
262      */
263     public void testMappedInvalidType() {
264         dynaClass.add(testProperty, String.class);
265         assertFalse("Check Property is not mapped", dynaClass.getDynaProperty(testProperty).isMapped());
266         try {
267             bean.set(testProperty, testKey, testInteger1);
268             fail("set(property, key, value) should have thrown IllegalArgumentException");
269         } catch (IllegalArgumentException expected) {
270             // expected result
271         }
272     }
273 
274     /***
275      * Test Getting/Setting an 'Indexed' Property - default ArrayList property
276      */
277     public void testIndexedPropertyDefault() {
278 
279         int index = 3;
280 
281         // Check the property & value doesn't exist
282         assertNull("Check Indexed Property doesn't exist", dynaClass.getDynaProperty(testProperty));
283         assertNull("Check Indexed Property is null", bean.get(testProperty));
284         assertNull("Check Indexed value is null", bean.get(testProperty, index));
285 
286         // Set the property, should create new ArrayList and set appropriate indexed value
287         bean.set(testProperty, index, testInteger1);
288         assertNotNull("Check Indexed Property is not null", bean.get(testProperty));
289         assertEquals("Check Indexed Property is correct type", ArrayList.class, bean.get(testProperty).getClass());
290         assertEquals("Check First Indexed Value is correct", testInteger1, bean.get(testProperty, index));
291         assertEquals("Check First Array length is correct", new Integer(index+1),  new Integer(((ArrayList)bean.get(testProperty)).size()));
292 
293         // Set a second indexed value, should automatically grow the ArrayList and set appropriate indexed value
294         index = index + 2;
295         bean.set(testProperty, index, testString1);
296         assertEquals("Check Second Indexed Value is correct", testString1, bean.get(testProperty, index));
297         assertEquals("Check Second Array length is correct", new Integer(index+1),  new Integer(((ArrayList)bean.get(testProperty)).size()));
298     }
299 
300     /***
301      * Test Getting/Setting a List 'Indexed' Property - use alternative List (LinkedList)
302      */
303     public void testIndexedLinkedList() {
304 
305         int   index     = 3;
306 
307         // Check the property & value doesn't exist
308         assertNull("Check Indexed Property doesn't exist", dynaClass.getDynaProperty(testProperty));
309         assertNull("Check Indexed Property is null", bean.get(testProperty));
310 
311         // Add a 'LinkedList' property to the DynaClass
312         dynaClass.add(testProperty, LinkedList.class);
313         assertTrue("Check Property is indexed", dynaClass.getDynaProperty(testProperty).isIndexed());
314         assertEquals("Check Property is correct type", LinkedList.class, dynaClass.getDynaProperty(testProperty).getType());
315         assertEquals("Check Property type is correct", LinkedList.class, bean.get(testProperty).getClass());
316 
317         // Set the property, should instantiate a new LinkedList and set appropriate indexed value
318         bean.set(testProperty, index, testString1);
319         assertEquals("Check Property type is correct", LinkedList.class, bean.get(testProperty).getClass());
320         assertEquals("Check First Indexed Value is correct", testString1, bean.get(testProperty, index));
321         assertEquals("Check First Array length is correct", new Integer(index+1),  new Integer(((LinkedList)bean.get(testProperty)).size()));
322 
323         // Set a second indexed value, should automatically grow the LinkedList and set appropriate indexed value
324         index = index + 2;
325         bean.set(testProperty, index, testInteger1);
326         assertEquals("Check Second Indexed Value is correct", testInteger1, bean.get(testProperty, index));
327         assertEquals("Check Second Array length is correct", new Integer(index+1),  new Integer(((LinkedList)bean.get(testProperty)).size()));
328     }
329 
330     /***
331      * Test Getting/Setting a primitive array 'Indexed' Property - use int[]
332      */
333     public void testIndexedPrimitiveArray() {
334 
335         int   index     = 3;
336         int[] primitiveArray = new int[0];
337 
338         // Check the property & value doesn't exist
339         assertNull("Check Indexed Property doesn't exist", dynaClass.getDynaProperty(testProperty));
340         assertNull("Check Indexed Property is null", bean.get(testProperty));
341 
342         // Add a DynaProperty of type int[]
343         dynaClass.add(testProperty, primitiveArray.getClass());
344         assertEquals("Check Indexed Property exists", primitiveArray.getClass(), dynaClass.getDynaProperty(testProperty).getType());
345         assertEquals("Check Indexed Property is correct type", primitiveArray.getClass(), bean.get(testProperty).getClass());
346 
347         // Set an indexed value
348         bean.set(testProperty, index, testInteger1);
349         assertNotNull("Check Indexed Property is not null", bean.get(testProperty));
350         assertEquals("Check Indexed Property is correct type", primitiveArray.getClass(), bean.get(testProperty).getClass());
351         assertEquals("Check First Indexed Value is correct(a)", testInteger1, bean.get(testProperty, index));
352         assertEquals("Check First Indexed Value is correct(b)", testInteger1, new Integer(((int[])bean.get(testProperty))[index]));
353         assertEquals("Check Array length is correct", new Integer(index+1),  new Integer(((int[])bean.get(testProperty)).length));
354 
355         // Set a second indexed value, should automatically grow the int[] and set appropriate indexed value
356         index = index + 2;
357         bean.set(testProperty, index, testInteger2);
358         assertEquals("Check Second Indexed Value is correct(a)", testInteger2, bean.get(testProperty, index));
359         assertEquals("Check Second Indexed Value is correct(b)", testInteger2, new Integer(((int[])bean.get(testProperty))[index]));
360         assertEquals("Check Second Array length is correct", new Integer(index+1),  new Integer(((int[])bean.get(testProperty)).length));
361 
362     }
363 
364     /***
365      * Test Getting/Setting an Object array 'Indexed' Property - use String[]
366      */
367     public void testIndexedObjectArray() {
368 
369         int   index     = 3;
370         Object objectArray = new String[0];
371 
372         // Check the property & value doesn't exist
373         assertNull("Check Indexed Property doesn't exist", dynaClass.getDynaProperty(testProperty));
374         assertNull("Check Indexed Property is null", bean.get(testProperty));
375 
376         // Add a DynaProperty of type String[]
377         dynaClass.add(testProperty, objectArray.getClass());
378         assertEquals("Check Indexed Property exists", objectArray.getClass(), dynaClass.getDynaProperty(testProperty).getType());
379         assertEquals("Check Indexed Property is correct type", objectArray.getClass(), bean.get(testProperty).getClass());
380 
381         // Set an indexed value
382         bean.set(testProperty, index, testString1);
383         assertNotNull("Check Indexed Property is not null", bean.get(testProperty));
384         assertEquals("Check Indexed Property is correct type", objectArray.getClass(), bean.get(testProperty).getClass());
385         assertEquals("Check First Indexed Value is correct(a)", testString1, bean.get(testProperty, index));
386         assertEquals("Check First Indexed Value is correct(b)", testString1, ((String[])bean.get(testProperty))[index]);
387         assertEquals("Check Array length is correct", new Integer(index+1),  new Integer(((String[])bean.get(testProperty)).length));
388 
389         // Set a second indexed value, should automatically grow the String[] and set appropriate indexed value
390         index = index + 2;
391         bean.set(testProperty, index, testString2);
392         assertEquals("Check Second Indexed Value is correct(a)", testString2, bean.get(testProperty, index));
393         assertEquals("Check Second Indexed Value is correct(b)", testString2, ((String[])bean.get(testProperty))[index]);
394         assertEquals("Check Second Array length is correct", new Integer(index+1),  new Integer(((String[])bean.get(testProperty)).length));
395     }
396 
397     /***
398      * Test Getting/Setting an DynaBean[] array
399      */
400     public void testIndexedDynaBeanArray() {
401 
402         int   index     = 3;
403         Object objectArray = new LazyDynaMap[0];
404 
405         // Check the property & value doesn't exist
406         assertNull("Check Indexed Property doesn't exist", dynaClass.getDynaProperty(testProperty));
407         assertNull("Check Indexed Property is null", bean.get(testProperty));
408 
409         // Add a DynaProperty of type String[]
410         dynaClass.add(testProperty, objectArray.getClass());
411         assertEquals("Check Indexed Property exists", objectArray.getClass(), dynaClass.getDynaProperty(testProperty).getType());
412         assertEquals("Check Indexed Property is correct type", objectArray.getClass(), bean.get(testProperty).getClass());
413 
414         // Retrieving from Array should initialize DynaBean
415         for (int i = index; i >= 0; i--) {
416             assertEquals("Check Array Components initialized", LazyDynaMap.class, bean.get(testProperty, index).getClass());
417         }
418 
419         dynaClass.add(testPropertyB, objectArray.getClass());
420         LazyDynaMap newMap = new LazyDynaMap();
421         newMap.set(testPropertyB, testString2);
422         bean.set(testPropertyA, index, newMap);
423         assertEquals("Check Indexed Value is correct(a)", testString2, ((DynaBean)bean.get(testPropertyA, index)).get(testPropertyB));
424 
425     }
426 
427     /***
428      * Test Setting an 'Indexed' Property using PropertyUtils
429      */
430     public void testIndexedPropertyUtils() {
431 
432         int   index     = 3;
433         dynaClass.setReturnNull(false);
434 
435         // Check the property & value doesn't exist
436         assertFalse("Check Indexed Property doesn't exist", dynaClass.isDynaProperty(testProperty));
437         assertNull("Check Indexed Property is null", bean.get(testProperty));
438         assertNull("Check Indexed value is null", bean.get(testProperty, index));
439 
440         // Use PropertyUtils to set the indexed value
441         try {
442           PropertyUtils.setProperty(bean, testProperty+"["+index+"]", testString1);
443         }
444         catch (NoSuchMethodException ex) {
445             fail("testIndexedPropertyUtils threw "+ex);
446         }
447         catch (InvocationTargetException ex) {
448             fail("testIndexedPropertyUtils threw "+ex);
449         }
450         catch (IllegalAccessException ex) {
451             fail("testIndexedPropertyUtils threw "+ex);
452         }
453 
454         // Check property value correctly set
455         assertEquals("Check Indexed Bean Value is correct", testString1, bean.get(testProperty, index));
456 
457     }
458 
459     /***
460      * Test Setting an Indexed Property when MutableDynaClass is set to restricted
461      */
462     public void testIndexedPropertyRestricted() {
463 
464         int   index     = 3;
465 
466         // Set the MutableDyanClass to 'restricted' (i.e. no new properties cab be added
467         dynaClass.setRestricted(true);
468         assertTrue("Check MutableDynaClass is restricted", dynaClass.isRestricted());
469 
470         // Check the property & value doesn't exist
471         assertNull("Check Property doesn't exist", dynaClass.getDynaProperty(testProperty));
472         assertNull("Check Value is null", bean.get(testProperty));
473 
474         // Set the property - should fail because property doesn't exist and MutableDynaClass is restricted
475         try {
476             bean.set(testProperty, index, testInteger1);
477             fail("expected IllegalArgumentException trying to add new property to restricted MutableDynaClass");
478         } catch (IllegalArgumentException expected) {
479             // expected result
480         }
481 
482     }
483 
484     /***
485      * Test setting indexed property for type which is not List or Array
486      */
487     public void testIndexedInvalidType() {
488         int   index     = 3;
489         dynaClass.add(testProperty, String.class);
490         assertFalse("Check Property is not indexed", dynaClass.getDynaProperty(testProperty).isIndexed());
491         try {
492             bean.set(testProperty, index, testString1);
493             fail("set(property, index, value) should have thrown IllegalArgumentException");
494         } catch (IllegalArgumentException expected) {
495             // expected result
496         }
497     }
498 
499 }