View Javadoc

1   /*
2    * Copyright 2004 The Apache Software Foundation.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.beanutils;
17  
18  import java.util.List;
19  import java.util.ArrayList;
20  import java.util.Map;
21  import java.util.HashMap;
22  import java.util.Date;
23  import java.lang.reflect.Array;
24  import java.math.BigDecimal;
25  import java.math.BigInteger;
26  import java.io.Serializable;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  /**
31   * <p>DynaBean which automatically adds properties to the <code>DynaClass</code>
32   *   and provides <i>Lazy List</i> and <i>Lazy Map</i> features.</p>
33   *
34   * <p>DynaBeans deal with three types of properties - <i>simple</i>, <i>indexed</i> and <i>mapped</i> and
35   *    have the following <code>get()</code> and <code>set()</code> methods for
36   *    each of these types:</p>
37   *    <ul>
38   *        <li><i>Simple</i> property methods - <code>get(name)</code> and <code>set(name, value)</code></li>
39   *        <li><i>Indexed</i> property methods - <code>get(name, index)</code> and <code>set(name, index, value)</code></li>
40   *        <li><i>Mapped</i> property methods - <code>get(name, key)</code> and <code>set(name, key, value)</code></li>
41   *    </ul>
42   *
43   * <p><b><u>Getting Property Values</u></b></p>
44   * <p>Calling any of the <code>get()</code> methods, for a property which
45   *    doesn't exist, returns <code>null</code> in this implementation.</p>
46   *
47   * <p><b><u>Setting Simple Properties</u></b></p>
48   *    <p>The <code>LazyDynaBean</code> will automatically add a property to the <code>DynaClass</code>
49   *       if it doesn't exist when the <code>set(name, value)</code> method is called.</p>
50   *
51   *     <code>DynaBean myBean = new LazyDynaBean();</code></br>
52   *     <code>myBean.set("myProperty", "myValue");</code></br>
53   *
54   * <p><b><u>Setting Indexed Properties</u></b></p>
55   *    <p>If the property <b>doesn't</b> exist, the <code>LazyDynaBean</code> will automatically add
56   *       a property with an <code>ArrayList</code> type to the <code>DynaClass</code> when
57   *       the <code>set(name, index, value)</code> method is called.
58   *       It will also instantiate a new <code>ArrayList</code> and automatically <i>grow</i>
59   *       the <code>List</code> so that it is big enough to accomodate the index being set.
60   *       <code>ArrayList</code> is the default indexed property that LazyDynaBean uses but
61   *       this can be easily changed by overriding the <code>newIndexedProperty(name)</code>
62   *       method.</p>
63   *
64   *     <code>DynaBean myBean = new LazyDynaBean();</code></br>
65   *     <code>myBean.set("myIndexedProperty", 0, "myValue1");</code></br>
66   *     <code>myBean.set("myIndexedProperty", 1, "myValue2");</code></br>
67   *
68   *    <p>If the indexed property <b>does</b> exist in the <code>DynaClass</code> but is set to
69   *      <code>null</code> in the <code>LazyDynaBean</code>, then it will instantiate a
70   *      new <code>List</code> or <code>Array</code> as specified by the property's type
71   *      in the <code>DynaClass</code> and automatically <i>grow</i> the <code>List</code>
72   *      or <code>Array</code> so that it is big enough to accomodate the index being set.</p>
73   *
74   *     <code>DynaBean myBean = new LazyDynaBean();</code></br>
75   *     <code>MutableDynaClass myClass = (MutableDynaClass)myBean.getDynaClass();</code></br>
76   *     <code>myClass.add("myIndexedProperty", int[].class);</code></br>
77   *     <code>myBean.set("myIndexedProperty", 0, new Integer(10));</code></br>
78   *     <code>myBean.set("myIndexedProperty", 1, new Integer(20));</code></br>
79   *
80   * <p><b><u>Setting Mapped Properties</u></b></p>
81   *    <p>If the property <b>doesn't</b> exist, the <code>LazyDynaBean</code> will automatically add
82   *       a property with a <code>HashMap</code> type to the <code>DynaClass</code> and
83   *       instantiate a new <code>HashMap</code> in the DynaBean when the
84   *       <code>set(name, key, value)</code> method is called. <code>HashMap</code> is the default
85   *       mapped property that LazyDynaBean uses but this can be easily changed by overriding
86   *       the <code>newMappedProperty(name)</code> method.</p>
87   *
88   *     <code>DynaBean myBean = new LazyDynaBean();</code></br>
89   *     <code>myBean.set("myMappedProperty", "myKey", "myValue");</code></br>
90   *
91   *    <p>If the mapped property <b>does</b> exist in the <code>DynaClass</code> but is set to
92   *      <code>null</code> in the <code>LazyDynaBean</code>, then it will instantiate a
93   *      new <code>Map</code> as specified by the property's type in the <code>DynaClass</code>.</p>
94   *
95   *     <code>DynaBean myBean = new LazyDynaBean();</code></br>
96   *     <code>MutableDynaClass myClass = (MutableDynaClass)myBean.getDynaClass();</code></br>
97   *     <code>myClass.add("myMappedProperty", TreeMap.class);</code></br>
98   *     <code>myBean.set("myMappedProperty", "myKey", "myValue");</code></br>
99   *
100  * <p><b><u><i>Restricted</i> DynaClass</u></b></p>
101  *    <p><code>MutableDynaClass</code> have a facility to <i>restrict</i> the <code>DynaClass</code>
102  *       so that its properties cannot be modified. If the <code>MutableDynaClass</code> is
103  *       restricted then calling any of the <code>set()</code> methods for a property which
104  *       doesn't exist will result in a <code>IllegalArgumentException</code> being thrown.</p>
105  *
106  * @see LazyDynaClass
107  * @author Niall Pemberton
108  */
109 public class LazyDynaBean implements DynaBean, Serializable {
110 
111 
112    /**
113     * Commons Logging
114     */
115     private static Log logger = LogFactory.getLog(LazyDynaBean.class);
116 
117     protected static final BigInteger BigInteger_ZERO = new BigInteger("0");
118     protected static final BigDecimal BigDecimal_ZERO = new BigDecimal("0");
119     protected static final Character  Character_SPACE = new Character(' ');
120     protected static final Byte       Byte_ZERO       = new Byte((byte)0);
121     protected static final Short      Short_ZERO      = new Short((short)0);
122     protected static final Integer    Integer_ZERO    = new Integer(0);
123     protected static final Long       Long_ZERO       = new Long((long)0);
124     protected static final Float      Float_ZERO      = new Float((byte)0);
125     protected static final Double     Double_ZERO     = new Double((byte)0);
126 
127     /**
128      * The <code>MutableDynaClass</code> "base class" that this DynaBean
129      * is associated with.
130      */
131     protected Map values;
132 
133     /**
134      * The <code>MutableDynaClass</code> "base class" that this DynaBean
135      * is associated with.
136      */
137     protected MutableDynaClass dynaClass;
138 
139 
140     // ------------------- Constructors ----------------------------------
141 
142     /**
143      * Construct a new <code>LazyDynaBean</code> with a <code>LazyDynaClass</code> instance.
144      */
145     public LazyDynaBean() {
146         this(new LazyDynaClass());
147     }
148 
149     /**
150      * Construct a new <code>LazyDynaBean</code> with a <code>LazyDynaClass</code> instance.
151      *
152      * @param name Name of this DynaBean class
153      */
154     public LazyDynaBean(String name) {
155         this(new LazyDynaClass(name));
156     }
157 
158     /**
159      * Construct a new <code>DynaBean</code> associated with the specified
160      * <code>DynaClass</code> instance - if its not a <code>MutableDynaClass</code>
161      * then a new <code>LazyDynaClass</code> is created and the properties copied.
162      *
163      * @param dynaClass The DynaClass we are associated with
164      */
165     public LazyDynaBean(DynaClass dynaClass) {
166 
167         values = newMap();
168 
169         if (dynaClass instanceof MutableDynaClass) {
170             this.dynaClass = (MutableDynaClass)dynaClass;
171         } else {
172             this.dynaClass = new LazyDynaClass(dynaClass.getName(), dynaClass.getDynaProperties());
173         }
174 
175     }
176 
177 
178     // ------------------- Public Methods ----------------------------------
179 
180     /**
181      * Return the Map backing this <code>DynaBean</code>
182      */
183     public Map getMap() {
184         return values;
185     }
186 
187     /**
188      * <p>Return the size of an indexed or mapped property.</p>
189      *
190      * @param name Name of the property
191      * @exception IllegalArgumentException if no property name is specified
192      */
193     public int size(String name) {
194 
195         if (name == null) {
196             throw new IllegalArgumentException("No property name specified");
197         }
198 
199         Object value = values.get(name);
200         if (value == null) {
201             return 0;
202         }
203 
204         if (value instanceof Map) {
205             return ((Map)value).size();
206         }
207 
208         if (value instanceof List) {
209             return ((List)value).size();
210         }
211 
212         if ((value.getClass().isArray())) {
213             return Array.getLength(value);
214         }
215 
216         return 0;
217 
218     }
219 
220     // ------------------- DynaBean Methods ----------------------------------
221 
222     /**
223      * Does the specified mapped property contain a value for the specified
224      * key value?
225      *
226      * @param name Name of the property to check
227      * @param key Name of the key to check
228      *
229      * @exception IllegalArgumentException if no property name is specified
230      */
231     public boolean contains(String name, String key) {
232 
233         if (name == null) {
234             throw new IllegalArgumentException("No property name specified");
235         }
236 
237         Object value = values.get(name);
238         if (value == null) {
239             return false;
240         }
241 
242         if (value instanceof Map) {
243             return (((Map) value).containsKey(key));
244         }
245 
246         return false;
247 
248     }
249 
250     /**
251      * <p>Return the value of a simple property with the specified name.</p>
252      *
253      * <p><strong>N.B.</strong> Returns <code>null</code> if there is no property
254      *  of the specified name.</p>
255      *
256      * @param name Name of the property whose value is to be retrieved.
257      * @exception IllegalArgumentException if no property name is specified
258      */
259     public Object get(String name) {
260 
261         if (name == null) {
262             throw new IllegalArgumentException("No property name specified");
263         }
264 
265         // Value found
266         Object value = values.get(name);
267         if (value != null) {
268             return value;
269         }
270 
271         // Property doesn't exist
272         if (!isDynaProperty(name)) {
273             return null;
274         }
275 
276         // Property doesn't exist
277         value = createProperty(name, dynaClass.getDynaProperty(name).getType());
278 
279         if (value != null) {
280             set(name, value);
281         }
282 
283         return value;
284 
285     }
286 
287     /**
288      * <p>Return the value of an indexed property with the specified name.</p>
289      *
290      * <p><strong>N.B.</strong> Returns <code>null</code> if there is no 'indexed'
291      * property of the specified name.</p>
292      *
293      * @param name Name of the property whose value is to be retrieved
294      * @param index Index of the value to be retrieved
295      *
296      * @exception IllegalArgumentException if the specified property
297      *  exists, but is not indexed
298      * @exception IndexOutOfBoundsException if the specified index
299      *  is outside the range of the underlying property
300      */
301     public Object get(String name, int index) {
302 
303         // If its not a property, then create default indexed property
304         if (!isDynaProperty(name)) {
305             set(name, defaultIndexedProperty(name));
306         }
307 
308         // Get the indexed property
309         Object indexedProperty = get(name);
310 
311         // Check that the property is indexed
312         if (!dynaClass.getDynaProperty(name).isIndexed()) {
313             throw new IllegalArgumentException
314                 ("Non-indexed property for '" + name + "[" + index + "]' "
315                                       + dynaClass.getDynaProperty(name).getName());
316         }
317 
318         // Grow indexed property to appropriate size
319         indexedProperty = growIndexedProperty(name, indexedProperty, index);
320 
321         // Return the indexed value
322         if (indexedProperty.getClass().isArray()) {
323             return Array.get(indexedProperty, index);
324         } else if (indexedProperty instanceof List) {
325             return ((List)indexedProperty).get(index);
326         } else {
327             throw new IllegalArgumentException
328                 ("Non-indexed property for '" + name + "[" + index + "]' "
329                                   + indexedProperty.getClass().getName());
330         }
331 
332     }
333 
334     /**
335      * <p>Return the value of a mapped property with the specified name.</p>
336      *
337      * <p><strong>N.B.</strong> Returns <code>null</code> if there is no 'mapped'
338      * property of the specified name.</p>
339      *
340      * @param name Name of the property whose value is to be retrieved
341      * @param key Key of the value to be retrieved
342      *
343      * @exception IllegalArgumentException if the specified property
344      *  exists, but is not mapped
345      */
346     public Object get(String name, String key) {
347 
348         // If its not a property, then create default mapped property
349         if (!isDynaProperty(name)) {
350             set(name, defaultMappedProperty(name));
351         }
352 
353         // Get the mapped property
354         Object mappedProperty = get(name);
355 
356         // Check that the property is mapped
357         if (!dynaClass.getDynaProperty(name).isMapped()) {
358             throw new IllegalArgumentException
359                 ("Non-mapped property for '" + name + "(" + key + ")' "
360                             + dynaClass.getDynaProperty(name).getType().getName());
361         }
362 
363         // Get the value from the Map
364         if (mappedProperty instanceof Map) {
365             return (((Map) mappedProperty).get(key));
366         } else {
367             throw new IllegalArgumentException
368               ("Non-mapped property for '" + name + "(" + key + ")'"
369                                   + mappedProperty.getClass().getName());
370         }
371 
372     }
373 
374 
375     /**
376      * Return the <code>DynaClass</code> instance that describes the set of
377      * properties available for this DynaBean.
378      */
379     public DynaClass getDynaClass() {
380         return (DynaClass)dynaClass;
381     }
382 
383     /**
384      * Remove any existing value for the specified key on the
385      * specified mapped property.
386      *
387      * @param name Name of the property for which a value is to
388      *  be removed
389      * @param key Key of the value to be removed
390      *
391      * @exception IllegalArgumentException if there is no property
392      *  of the specified name
393      */
394     public void remove(String name, String key) {
395 
396         if (name == null) {
397             throw new IllegalArgumentException("No property name specified");
398         }
399 
400         Object value = values.get(name);
401         if (value == null) {
402             return;
403         }
404 
405         if (value instanceof Map) {
406             ((Map) value).remove(key);
407         } else {
408             throw new IllegalArgumentException
409                     ("Non-mapped property for '" + name + "(" + key + ")'"
410                             + value.getClass().getName());
411         }
412 
413     }
414 
415     /**
416      * Set the value of a simple property with the specified name.
417      *
418      * @param name Name of the property whose value is to be set
419      * @param value Value to which this property is to be set
420      *
421      * @exception IllegalArgumentException if this is not an existing property
422      *  name for our DynaClass and the MutableDynaClass is restricted
423      * @exception ConversionException if the specified value cannot be
424      *  converted to the type required for this property
425      * @exception NullPointerException if an attempt is made to set a
426      *  primitive property to null
427      */
428     public void set(String name, Object value) {
429 
430         // If the property doesn't exist, then add it
431         if (!isDynaProperty(name)) {
432 
433             if (dynaClass.isRestricted()) {
434                 throw new IllegalArgumentException
435                     ("Invalid property name '" + name + "' (DynaClass is restricted)");
436             }
437             if (value == null) {
438                 dynaClass.add(name);
439             } else {
440                 dynaClass.add(name, value.getClass());
441             }
442 
443         }
444 
445         DynaProperty descriptor = dynaClass.getDynaProperty(name);
446 
447         if (value == null) {
448             if (descriptor.getType().isPrimitive()) {
449                 throw new NullPointerException
450                         ("Primitive value for '" + name + "'");
451             }
452         } else if (!isAssignable(descriptor.getType(), value.getClass())) {
453             throw new ConversionException
454                     ("Cannot assign value of type '" +
455                     value.getClass().getName() +
456                     "' to property '" + name + "' of type '" +
457                     descriptor.getType().getName() + "'");
458         }
459 
460         // Set the property's value
461         values.put(name, value);
462 
463     }
464 
465     /**
466      * Set the value of an indexed property with the specified name.
467      *
468      * @param name Name of the property whose value is to be set
469      * @param index Index of the property to be set
470      * @param value Value to which this property is to be set
471      *
472      * @exception ConversionException if the specified value cannot be
473      *  converted to the type required for this property
474      * @exception IllegalArgumentException if there is no property
475      *  of the specified name
476      * @exception IllegalArgumentException if the specified property
477      *  exists, but is not indexed
478      * @exception IndexOutOfBoundsException if the specified index
479      *  is outside the range of the underlying property
480      */
481     public void set(String name, int index, Object value) {
482 
483         // If its not a property, then create default indexed property
484         if (!isDynaProperty(name)) {
485             set(name, defaultIndexedProperty(name));
486         }
487 
488         // Get the indexed property
489         Object indexedProperty = get(name);
490 
491         // Check that the property is indexed
492         if (!dynaClass.getDynaProperty(name).isIndexed()) {
493             throw new IllegalArgumentException
494                 ("Non-indexed property for '" + name + "[" + index + "]'"
495                             + dynaClass.getDynaProperty(name).getType().getName());
496         }
497 
498         // Grow indexed property to appropriate size
499         indexedProperty = growIndexedProperty(name, indexedProperty, index);
500 
501         // Set the value in an array
502         if (indexedProperty.getClass().isArray()) {
503             Array.set(indexedProperty, index, value);
504         } else if (indexedProperty instanceof List) {
505             ((List)indexedProperty).set(index, value);
506         } else {
507             throw new IllegalArgumentException
508                 ("Non-indexed property for '" + name + "[" + index + "]' "
509                             + indexedProperty.getClass().getName());
510         }
511 
512     }
513 
514     /**
515      * Set the value of a mapped property with the specified name.
516      *
517      * @param name Name of the property whose value is to be set
518      * @param key Key of the property to be set
519      * @param value Value to which this property is to be set
520      *
521      * @exception ConversionException if the specified value cannot be
522      *  converted to the type required for this property
523      * @exception IllegalArgumentException if there is no property
524      *  of the specified name
525      * @exception IllegalArgumentException if the specified property
526      *  exists, but is not mapped
527      */
528     public void set(String name, String key, Object value) {
529 
530         // If the 'mapped' property doesn't exist, then add it
531         if (!isDynaProperty(name)) {
532             set(name, defaultMappedProperty(name));
533         }
534 
535         // Get the mapped property
536         Object mappedProperty = get(name);
537 
538         // Check that the property is mapped
539         if (!dynaClass.getDynaProperty(name).isMapped()) {
540             throw new IllegalArgumentException
541                 ("Non-mapped property for '" + name + "(" + key + ")'"
542                             + dynaClass.getDynaProperty(name).getType().getName());
543         }
544 
545         // Set the value in the Map
546         ((Map)mappedProperty).put(key, value);
547 
548     }
549 
550     // ------------------- protected Methods ----------------------------------
551 
552     protected Object growIndexedProperty(String name, Object indexedProperty, int index) {
553 
554         // Grow a List to the appropriate size
555         if (indexedProperty instanceof List) {
556 
557             List list = (List)indexedProperty;
558             while (index >= list.size()) {
559                 list.add(null);
560             }
561 
562         }
563 
564         // Grow an Array to the appropriate size
565         if ((indexedProperty.getClass().isArray())) {
566 
567             int length = Array.getLength(indexedProperty);
568             if (index >= length) {
569                 Class componentType = indexedProperty.getClass().getComponentType();
570                 Object newArray = Array.newInstance(componentType, (index + 1));
571                 System.arraycopy(indexedProperty, 0, newArray, 0, length);
572                 indexedProperty = newArray;
573                 set(name, indexedProperty);
574                 int newLength = Array.getLength(indexedProperty);
575                 for (int i = length; i < newLength; i++) {
576                     Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType));
577                 }
578             }
579         }
580 
581         return indexedProperty;
582 
583     }
584 
585     /**
586      * Create a new Instance of a Property
587      */
588     protected Object createProperty(String name, Class type) {
589 
590         // Create Lists, arrays or DynaBeans
591         if (type.isArray() || List.class.isAssignableFrom(type)) {
592             return createIndexedProperty(name, type);
593         }
594 
595         if (Map.class.isAssignableFrom(type)) {
596             return createMappedProperty(name, type);
597         }
598 
599         if (DynaBean.class.isAssignableFrom(type)) {
600             return createDynaBeanProperty(name, type);
601         }
602 
603         if (type.isPrimitive()) {
604             return createPrimitiveProperty(name, type);
605         }
606 
607         if (Number.class.isAssignableFrom(type)) {
608             return createNumberProperty(name, type);
609         }
610 
611         return createOtherProperty(name, type);
612 
613     }
614 
615     /**
616      * Create a new Instance of an 'Indexed' Property
617      */
618     protected Object createIndexedProperty(String name, Class type) {
619 
620         // Create the indexed object
621         Object indexedProperty = null;
622 
623         if (type == null) {
624 
625             indexedProperty = defaultIndexedProperty(name);
626 
627         } else if (type.isArray()) {
628 
629             indexedProperty = Array.newInstance(type.getComponentType(), 0);
630 
631         } else if (List.class.isAssignableFrom(type)) {
632             if (type.isInterface()) {
633                 indexedProperty = defaultIndexedProperty(name);
634             } else {
635                 try {
636                     indexedProperty = type.newInstance();
637                 }
638                 catch (Exception ex) {
639                     throw new IllegalArgumentException
640                         ("Error instantiating indexed property of type '" +
641                                    type.getName() + "' for '" + name + "' " + ex);
642                 }
643             }
644         } else {
645 
646             throw new IllegalArgumentException
647                     ("Non-indexed property of type '" + type.getName() + "' for '" + name + "'");
648         }
649 
650         return indexedProperty;
651 
652     }
653 
654     /**
655      * Create a new Instance of a 'Mapped' Property
656      */
657     protected Object createMappedProperty(String name, Class type) {
658 
659         // Create the mapped object
660         Object mappedProperty = null;
661 
662         if (type == null) {
663 
664             mappedProperty = defaultMappedProperty(name);
665 
666         } else if (type.isInterface()) {
667 
668             mappedProperty = defaultMappedProperty(name);
669 
670         } else if (Map.class.isAssignableFrom(type)) {
671             try {
672                 mappedProperty = type.newInstance();
673             }
674             catch (Exception ex) {
675                 throw new IllegalArgumentException
676                     ("Error instantiating mapped property of type '" + type.getName() + "' for '" + name + "' " + ex);
677             }
678         } else {
679 
680             throw new IllegalArgumentException
681                     ("Non-mapped property of type '" + type.getName() + "' for '" + name + "'");
682         }
683 
684         return mappedProperty;
685 
686     }
687 
688     /**
689      * Create a new Instance of a 'Mapped' Property
690      */
691     protected Object createDynaBeanProperty(String name, Class type) {
692         try {
693             return type.newInstance();
694         }
695         catch (Exception ex) {
696             if (logger.isWarnEnabled()) {
697                 logger.warn("Error instantiating DynaBean property of type '" + type.getName() + "' for '" + name + "' " + ex);
698             }
699             return null;
700         }
701     }
702 
703     /**
704      * Create a new Instance of a 'Primitive' Property
705      */
706     protected Object createPrimitiveProperty(String name, Class type) {
707 
708         if (type == Boolean.TYPE) {
709             return Boolean.FALSE;
710         } else if (type == Integer.TYPE) {
711             return Integer_ZERO;
712         } else if (type == Long.TYPE) {
713             return Long_ZERO;
714         } else if (type == Double.TYPE) {
715             return Double_ZERO;
716         } else if (type == Float.TYPE) {
717             return Float_ZERO;
718         } else if (type == Byte.TYPE) {
719             return Byte_ZERO;
720         } else if (type == Short.TYPE) {
721             return Short_ZERO;
722         } else if (type == Character.TYPE) {
723             return Character_SPACE;
724         } else {
725             return null;
726         }
727 
728     }
729 
730     /**
731      * Create a new Instance of a 'Primitive' Property
732      */
733     protected Object createNumberProperty(String name, Class type) {
734 
735         return null;
736 
737     }
738 
739     /**
740      * Create a new Instance of a 'Mapped' Property
741      */
742     protected Object createOtherProperty(String name, Class type) {
743 
744         if (type == String.class || type == Boolean.class ||
745             type == Character.class || Date.class.isAssignableFrom(type)) {
746             return null;
747         }
748 
749         try {
750             return type.newInstance();
751         }
752         catch (Exception ex) {
753             if (logger.isWarnEnabled()) {
754                 logger.warn("Error instantiating property of type '" + type.getName() + "' for '" + name + "' " + ex);
755             }
756             return null;
757         }
758     }
759 
760     /**
761      * <p>Creates a new <code>ArrayList</code> for an 'indexed' property
762      *    which doesn't exist.</p>
763      *
764      * <p>This method shouls be overriden if an alternative <code>List</code>
765      *    or <code>Array</code> implementation is required for 'indexed' properties.</p>
766      *
767      * @param name Name of the 'indexed property.
768      */
769     protected Object defaultIndexedProperty(String name) {
770         return new ArrayList();
771     }
772 
773     /**
774      * <p>Creates a new <code>HashMap</code> for a 'mapped' property
775      *    which doesn't exist.</p>
776      *
777      * <p>This method can be overriden if an alternative <code>Map</code>
778      *    implementation is required for 'mapped' properties.</p>
779      *
780      * @param name Name of the 'mapped property.
781      */
782     protected Map defaultMappedProperty(String name) {
783         return new HashMap();
784     }
785 
786     /**
787      * Indicates if there is a property with the specified name.
788      */
789     protected boolean isDynaProperty(String name) {
790 
791         if (name == null) {
792             throw new IllegalArgumentException("No property name specified");
793         }
794 
795         // Handle LazyDynaClasses
796         if (dynaClass instanceof LazyDynaClass) {
797             return ((LazyDynaClass)dynaClass).isDynaProperty(name);
798         }
799 
800         // Handle other MutableDynaClass
801         return dynaClass.getDynaProperty(name) == null ? false : true;
802 
803     }
804 
805     /**
806      * Is an object of the source class assignable to the destination class?
807      *
808      * @param dest Destination class
809      * @param source Source class
810      */
811     protected boolean isAssignable(Class dest, Class source) {
812 
813         if (dest.isAssignableFrom(source) ||
814                 ((dest == Boolean.TYPE) && (source == Boolean.class)) ||
815                 ((dest == Byte.TYPE) && (source == Byte.class)) ||
816                 ((dest == Character.TYPE) && (source == Character.class)) ||
817                 ((dest == Double.TYPE) && (source == Double.class)) ||
818                 ((dest == Float.TYPE) && (source == Float.class)) ||
819                 ((dest == Integer.TYPE) && (source == Integer.class)) ||
820                 ((dest == Long.TYPE) && (source == Long.class)) ||
821                 ((dest == Short.TYPE) && (source == Short.class))) {
822             return (true);
823         } else {
824             return (false);
825         }
826 
827     }
828 
829     /**
830      * <p>Creates a new instance of the <code>Map</code>.</p>
831      */
832     protected Map newMap() {
833         return new HashMap();
834     }
835 
836 }