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 }