View Javadoc
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.io.Serializable;
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import junit.framework.Test;
26  import junit.textui.TestRunner;
27  
28  import org.apache.commons.beanutils.bugs.other.Jira87BeanFactory;
29  import org.apache.commons.collections.BulkTest;
30  import org.apache.commons.collections.Transformer;
31  import org.apache.commons.collections.map.AbstractTestMap;
32  
33  /**
34   * Test cases for BeanMap
35   *
36   * @version $Id$
37   */
38  @SuppressWarnings("deprecation")
39  public class BeanMapTestCase extends AbstractTestMap {
40  
41      public BeanMapTestCase(final String testName) {
42          super(testName);
43      }
44  
45      public static void main(final String[] args) {
46          TestRunner.run(suite());
47      }
48  
49      public static Test suite() {
50          return BulkTest.makeSuite(BeanMapTestCase.class);
51      }
52  
53  /*
54    note to self.  The getter and setter methods were generated by copying the
55    field declarations and using the following regular expression search and
56    replace:
57  
58    From:
59          private \(.*\) some\(.*\);
60    To:
61          public \1 getSome\2Value() {
62              return some\2;
63          }
64          public void setSome\2Value(\1 value) {
65              some\2 = value;
66          }
67  
68    Also note:  The sample keys and mappings were generated manually.
69  */
70  
71  
72      public static class BeanWithProperties implements Serializable {
73          private int someInt;
74          private long someLong;
75          private double someDouble;
76          private float someFloat;
77          private short someShort;
78          private byte someByte;
79          private char someChar;
80          private Integer someInteger;
81          private String someString;
82          private Object someObject;
83  
84          public int getSomeIntValue() {
85              return someInt;
86          }
87          public void setSomeIntValue(final int value) {
88              someInt = value;
89          }
90  
91          public long getSomeLongValue() {
92              return someLong;
93          }
94          public void setSomeLongValue(final long value) {
95              someLong = value;
96          }
97  
98          public double getSomeDoubleValue() {
99              return someDouble;
100         }
101         public void setSomeDoubleValue(final double value) {
102             someDouble = value;
103         }
104 
105         public float getSomeFloatValue() {
106             return someFloat;
107         }
108         public void setSomeFloatValue(final float value) {
109             someFloat = value;
110         }
111 
112         public short getSomeShortValue() {
113             return someShort;
114         }
115         public void setSomeShortValue(final short value) {
116             someShort = value;
117         }
118 
119         public byte getSomeByteValue() {
120             return someByte;
121         }
122         public void setSomeByteValue(final byte value) {
123             someByte = value;
124         }
125 
126         public char getSomeCharValue() {
127             return someChar;
128         }
129         public void setSomeCharValue(final char value) {
130             someChar = value;
131         }
132 
133         public String getSomeStringValue() {
134             return someString;
135         }
136         public void setSomeStringValue(final String value) {
137             someString = value;
138         }
139 
140         public Integer getSomeIntegerValue() {
141             return someInteger;
142         }
143         public void setSomeIntegerValue(final Integer value) {
144             someInteger = value;
145         }
146 
147         public Object getSomeObjectValue() {
148             return someObject;
149         }
150         public void setSomeObjectValue(final Object value) {
151             someObject = value;
152         }
153     }
154 
155     public static class BeanThrowingExceptions extends BeanWithProperties {
156         private static final long serialVersionUID = 1L;
157         public void setValueThrowingException(final String value) {
158             throw new TestException();
159         }
160         public String getValueThrowingException() {
161             throw new TestException();
162         }
163     }
164 
165     /**
166      * Exception for testing exception handling.
167      */
168     public static class TestException extends RuntimeException {
169         private static final long serialVersionUID = 1L;
170     }
171 
172     // note to self.  The Sample keys were generated by copying the field
173     // declarations and using the following regular expression search and replace:
174     //
175     // From:
176     //    private \(.*\) some\(.*\);
177     // To:
178     //    "some\2Value",
179     //
180     // Then, I manually added the "class" key, which is a property that exists for
181     // all beans (and all objects for that matter.
182     @Override
183     public Object[] getSampleKeys() {
184         final Object[] keys = new Object[] {
185             "someIntValue",
186             "someLongValue",
187             "someDoubleValue",
188             "someFloatValue",
189             "someShortValue",
190             "someByteValue",
191             "someCharValue",
192             "someIntegerValue",
193             "someStringValue",
194             "someObjectValue",
195             "class",
196         };
197         return keys;
198     }
199 
200     /**
201      *  An object value that will be stored in the bean map as a value.  Need
202      *  to save this externally so that we can make sure the object instances
203      *  are equivalent since getSampleValues() would otherwise construct a new
204      *  and different Object each time.
205      **/
206     private final Object objectInFullMap = new Object();
207 
208     // note to self: the sample values were created manually
209     @Override
210     public Object[] getSampleValues() {
211         final Object[] values = new Object[] {
212             new Integer(1234),
213             new Long(1298341928234L),
214             new Double(123423.34),
215             new Float(1213332.12f),
216             new Short((short)134),
217             new Byte((byte)10),
218             new Character('a'),
219             new Integer(1432),
220             "SomeStringValue",
221             objectInFullMap,
222             BeanWithProperties.class,
223         };
224         return values;
225     }
226 
227     @Override
228     public Object[] getNewSampleValues() {
229         final Object[] values = new Object[] {
230             new Integer(223),
231             new Long(23341928234L),
232             new Double(23423.34),
233             new Float(213332.12f),
234             new Short((short)234),
235             new Byte((byte)20),
236             new Character('b'),
237             new Integer(232),
238             "SomeNewStringValue",
239             new Object(),
240             null,
241         };
242         return values;
243     }
244 
245     /**
246      * Values is a dead copy in BeanMap, so refresh each time.
247      */
248     @Override
249     public void verifyValues() {
250         values = map.values();
251         super.verifyValues();
252     }
253 
254     /**
255      * The mappings in a BeanMap are fixed on the properties the underlying
256      * bean has.  Adding and removing mappings is not possible, thus this
257      * method is overridden to return false.
258      */
259     @Override
260     public boolean isPutAddSupported() {
261         return false;
262     }
263 
264     /**
265      * The mappings in a BeanMap are fixed on the properties the underlying
266      * bean has.  Adding and removing mappings is not possible, thus this
267      * method is overridden to return false.
268      */
269     @Override
270     public boolean isRemoveSupported() {
271         return false;
272     }
273 
274     @Override
275     public Map<Object, Object> makeFullMap() {
276         // note: These values must match (i.e. .equals() must return true)
277         // those returned from getSampleValues().
278         final BeanWithProperties bean = new BeanWithProperties();
279         bean.setSomeIntValue(1234);
280         bean.setSomeLongValue(1298341928234L);
281         bean.setSomeDoubleValue(123423.34);
282         bean.setSomeFloatValue(1213332.12f);
283         bean.setSomeShortValue((short)134);
284         bean.setSomeByteValue((byte)10);
285         bean.setSomeCharValue('a');
286         bean.setSomeIntegerValue(new Integer(1432));
287         bean.setSomeStringValue("SomeStringValue");
288         bean.setSomeObjectValue(objectInFullMap);
289         return new BeanMap(bean);
290     }
291 
292     @Override
293     public Map<Object, Object> makeEmptyMap() {
294         return new BeanMap();
295     }
296 
297     @Override
298     public String[] ignoredTests() {
299         // Ignore the serialization tests on collection views.
300         return new String[] {
301          "TestBeanMap.bulkTestMapEntrySet.testCanonicalEmptyCollectionExists",
302          "TestBeanMap.bulkTestMapEntrySet.testCanonicalFullCollectionExists",
303          "TestBeanMap.bulkTestMapKeySet.testCanonicalEmptyCollectionExists",
304          "TestBeanMap.bulkTestMapKeySet.testCanonicalFullCollectionExists",
305          "TestBeanMap.bulkTestMapValues.testCanonicalEmptyCollectionExists",
306          "TestBeanMap.bulkTestMapValues.testCanonicalFullCollectionExists",
307          "TestBeanMap.bulkTestMapEntrySet.testSimpleSerialization",
308          "TestBeanMap.bulkTestMapKeySet.testSimpleSerialization",
309          "TestBeanMap.bulkTestMapEntrySet.testSerializeDeserializeThenCompare",
310          "TestBeanMap.bulkTestMapKeySet.testSerializeDeserializeThenCompare"
311         };
312     }
313 
314     /**
315      * Need to override this method because the "clear()" method on the bean
316      * map just returns the bean properties to their default states.  It does
317      * not actually remove the mappings as per the map contract.  The default
318      * testClear() methods checks that the clear method throws an
319      * UnsupportedOperationException since this class is not add/remove
320      * modifiable.  In our case though, we do not always throw that exception.
321      */
322     @Override
323     public void testMapClear() {
324         //TODO: make sure a call to BeanMap.clear returns the bean to its
325         //default initialization values.
326     }
327 
328     /**
329      * Need to override this method because the "put()" method on the bean
330      * doesn't work for this type of Map.
331      */
332     @Override
333     public void testMapPut() {
334         // see testBeanMapPutAllWriteable
335     }
336 
337     public void testBeanMapClone() {
338         final BeanMap map = (BeanMap)makeFullMap();
339         try {
340             final BeanMap map2 = (BeanMap)map.clone();
341 
342             // make sure containsKey is working to verify the bean was cloned
343             // ok, and the read methods were properly initialized
344             final Object[] keys = getSampleKeys();
345             for (Object key : keys) {
346                 assertTrue("Cloned BeanMap should contain the same keys",
347                            map2.containsKey(key));
348             }
349         } catch (final CloneNotSupportedException exception) {
350             fail("BeanMap.clone() should not throw a " +
351                  "CloneNotSupportedException when clone should succeed.");
352         }
353     }
354 
355     public void testBeanMapPutAllWriteable() {
356         final BeanMap map1 = (BeanMap)makeFullMap();
357         final BeanMap map2 = (BeanMap)makeFullMap();
358         map2.put("someIntValue", new Integer(0));
359         map1.putAllWriteable(map2);
360         assertEquals(map1.get("someIntValue"), new Integer(0));
361     }
362 
363     public void testMethodAccessor() throws Exception {
364         final BeanMap map = (BeanMap) makeFullMap();
365         final Method method = BeanWithProperties.class.getDeclaredMethod("getSomeIntegerValue");
366         assertEquals(method, map.getReadMethod("someIntegerValue"));
367     }
368 
369     public void testMethodMutator() throws Exception {
370         final BeanMap map = (BeanMap) makeFullMap();
371         final Method method = BeanWithProperties.class.getDeclaredMethod("setSomeIntegerValue", new Class[] {Integer.class});
372         assertEquals(method, map.getWriteMethod("someIntegerValue"));
373     }
374 
375     /**
376      *  Test the default transformers using the getTypeTransformer() method
377      */
378     public void testGetTypeTransformerMethod() {
379         final BeanMap beanMap = new BeanMap();
380         assertEquals("Boolean.TYPE",   Boolean.TRUE,        beanMap.getTypeTransformer(Boolean.TYPE).transform("true"));
381         assertEquals("Character.TYPE", new Character('B'),  beanMap.getTypeTransformer(Character.TYPE).transform("BCD"));
382         assertEquals("Byte.TYPE",      new Byte((byte)1),   beanMap.getTypeTransformer(Byte.TYPE).transform("1"));
383         assertEquals("Short.TYPE",     new Short((short)2), beanMap.getTypeTransformer(Short.TYPE).transform("2"));
384         assertEquals("Integer.TYPE",   new Integer(3),      beanMap.getTypeTransformer(Integer.TYPE).transform("3"));
385         assertEquals("Long.TYPE",      new Long(4),         beanMap.getTypeTransformer(Long.TYPE).transform("4"));
386         assertEquals("Float.TYPE",     new Float("5"),      beanMap.getTypeTransformer(Float.TYPE).transform("5"));
387         assertEquals("Double.TYPE",    new Double("6"),     beanMap.getTypeTransformer(Double.TYPE).transform("6"));
388     }
389 
390     /**
391      *  Test the default transformers via the public static Map instance
392      */
393     public void testGetDefaultTransformersMap() {
394         assertEquals("Boolean.TYPE",   Boolean.TRUE,        ((Transformer)BeanMap.defaultTransformers.get(Boolean.TYPE)).transform("true"));
395         assertEquals("Character.TYPE", new Character('B'),  ((Transformer)BeanMap.defaultTransformers.get(Character.TYPE)).transform("BCD"));
396         assertEquals("Byte.TYPE",      new Byte((byte)1),   ((Transformer)BeanMap.defaultTransformers.get(Byte.TYPE)).transform("1"));
397         assertEquals("Short.TYPE",     new Short((short)2), ((Transformer)BeanMap.defaultTransformers.get(Short.TYPE)).transform("2"));
398         assertEquals("Integer.TYPE",   new Integer(3),      ((Transformer)BeanMap.defaultTransformers.get(Integer.TYPE)).transform("3"));
399         assertEquals("Long.TYPE",      new Long(4),         ((Transformer)BeanMap.defaultTransformers.get(Long.TYPE)).transform("4"));
400         assertEquals("Float.TYPE",     new Float("5"),      ((Transformer)BeanMap.defaultTransformers.get(Float.TYPE)).transform("5"));
401         assertEquals("Double.TYPE",    new Double("6"),     ((Transformer)BeanMap.defaultTransformers.get(Double.TYPE)).transform("6"));
402     }
403 
404     /**
405      *  Test the default transformers HashMap
406      */
407     public void testDefaultTransformersMap() {
408         assertEquals("Size",     8, BeanMap.defaultTransformers.size());
409         assertEquals("entrySet", 8, BeanMap.defaultTransformers.entrySet().size());
410         assertEquals("keySet",   8, BeanMap.defaultTransformers.keySet().size());
411         assertEquals("values",   8, BeanMap.defaultTransformers.values().size());
412         assertFalse("isEmpty",      BeanMap.defaultTransformers.isEmpty());
413         assertTrue("containsKey(Double)",    BeanMap.defaultTransformers.containsKey(Double.TYPE));
414         assertFalse("containsKey(Object)",   BeanMap.defaultTransformers.containsKey(Object.class));
415         assertTrue("containsValue(double)",  BeanMap.defaultTransformers.containsValue(BeanMap.defaultTransformers.get(Double.TYPE)));
416         assertFalse("containsValue(Object)", BeanMap.defaultTransformers.containsValue(Object.class));
417 
418         try {
419             BeanMap.defaultTransformers.clear();
420             fail("clear() - expected UnsupportedOperationException");
421         } catch(final UnsupportedOperationException e) {
422             // expected result
423         }
424         try {
425             BeanMap.defaultTransformers.put("FOO", null);
426             fail("put() - expected UnsupportedOperationException");
427         } catch(final UnsupportedOperationException e) {
428             // expected result
429         }
430         try {
431             BeanMap.defaultTransformers.putAll(new HashMap<Object, Object>());
432             fail("putAll() - expected UnsupportedOperationException");
433         } catch(final UnsupportedOperationException e) {
434             // expected result
435         }
436         try {
437             BeanMap.defaultTransformers.remove("FOO");
438             fail("remove() - expected UnsupportedOperationException");
439         } catch(final UnsupportedOperationException e) {
440             // expected result
441         }
442     }
443 
444     /**
445      * Test that the cause of exception thrown by a clone() is initialised.
446      */
447     public void testExceptionThrowFromClone() {
448 
449         if (BeanUtilsTestCase.isPre14JVM()) {
450             System.out.println("testExceptionThrowFromClone() skipped on pre 1.4 JVM");
451             return;
452         }
453 
454         // Test cloning a non-public bean (instantiation exception)
455         try {
456             final Object bean = Jira87BeanFactory.createMappedPropertyBean();
457             final BeanMap map = new BeanMap(bean);
458             map.clone();
459             fail("Non-public bean clone() - expected CloneNotSupportedException");
460         } catch (final CloneNotSupportedException e) {
461             Throwable cause = null;
462             try {
463                 cause = (Throwable)PropertyUtils.getProperty(e, "cause");
464             } catch (final Exception e2) {
465                 fail("Non-public bean - retrieving the cause threw " + e2);
466             }
467             assertNotNull("Non-public bean cause null", cause);
468             assertEquals("Non-public bean cause", IllegalAccessException.class, cause.getClass());
469         }
470 
471         // Test cloning a bean that throws exception
472         try {
473             final BeanMap map = new BeanMap(new BeanThrowingExceptions());
474             map.clone();
475             fail("Setter Exception clone() - expected CloneNotSupportedException");
476         } catch (final CloneNotSupportedException e) {
477             Throwable cause = null;
478             try {
479                 cause = (Throwable)PropertyUtils.getProperty(e, "cause");
480             } catch (final Exception e2) {
481                 fail("Setter Exception - retrieving the cause threw " + e2);
482             }
483             assertNotNull("Setter Exception cause null", cause);
484             assertEquals("Setter Exception cause", IllegalArgumentException.class, cause.getClass());
485         }
486     }
487 
488     /**
489      * Test that the cause of exception thrown by clear() is initialised.
490      */
491     public void testExceptionThrowFromClear() {
492 
493         if (BeanUtilsTestCase.isPre14JVM()) {
494             System.out.println("testExceptionThrowFromClear() skipped on pre 1.4 JVM");
495             return;
496         }
497 
498         try {
499             final Object bean = Jira87BeanFactory.createMappedPropertyBean();
500             final BeanMap map = new BeanMap(bean);
501             map.clear();
502             fail("clear() - expected UnsupportedOperationException");
503         } catch (final UnsupportedOperationException e) {
504             Throwable cause = null;
505             try {
506                 cause = (Throwable)PropertyUtils.getProperty(e, "cause");
507             } catch (final Exception e2) {
508                 fail("Retrieving the cause threw " + e2);
509             }
510             assertNotNull("Cause null", cause);
511             assertEquals("Cause", IllegalAccessException.class, cause.getClass());
512         }
513     }
514 
515     /**
516      * Test that the cause of exception thrown by put() is initialized.
517      */
518     public void testExceptionThrowFromPut() {
519 
520         if (BeanUtilsTestCase.isPre14JVM()) {
521             System.out.println("testExceptionThrowFromPut() skipped on pre 1.4 JVM");
522             return;
523         }
524 
525         try {
526             final Map<Object, Object> map = new BeanMap(new BeanThrowingExceptions());
527             map.put("valueThrowingException", "value");
528             fail("Setter exception - expected IllegalArgumentException");
529         } catch (final IllegalArgumentException e) {
530             Throwable cause1 = null;
531             Throwable cause2 = null;
532             try {
533                 cause1 = (Throwable)PropertyUtils.getProperty(e, "cause");
534                 cause2 = (Throwable)PropertyUtils.getProperty(e, "cause.cause");
535             } catch (final Exception e2) {
536                 fail("Setter exception - retrieving the cause threw " + e2);
537             }
538             assertNotNull("Setter exception cause 1 null", cause1);
539             assertEquals("Setter exception cause 1", InvocationTargetException.class, cause1.getClass());
540             assertNotNull("Setter exception cause 2 null", cause2);
541             assertEquals("Setter exception cause 2", TestException.class, cause2.getClass());
542         }
543     }
544 }