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