Coverage Report - org.apache.commons.beanutils.LazyDynaBean
 
Classes in this File Line Coverage Branch Coverage Complexity
LazyDynaBean
61%
136/220
47%
88/184
6.25
 
 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  91
     private transient Log logger = LogFactory.getLog(LazyDynaBean.class);
 120  
 
 121  
     /** BigInteger Zero */
 122  1
     protected static final BigInteger BigInteger_ZERO = new BigInteger("0");
 123  
     /** BigDecimal Zero */
 124  1
     protected static final BigDecimal BigDecimal_ZERO = new BigDecimal("0");
 125  
     /** Character Space */
 126  1
     protected static final Character  Character_SPACE = new Character(' ');
 127  
     /** Byte Zero */
 128  1
     protected static final Byte       Byte_ZERO       = new Byte((byte)0);
 129  
     /** Short Zero */
 130  1
     protected static final Short      Short_ZERO      = new Short((short)0);
 131  
     /** Integer Zero */
 132  1
     protected static final Integer    Integer_ZERO    = new Integer(0);
 133  
     /** Long Zero */
 134  1
     protected static final Long       Long_ZERO       = new Long(0);
 135  
     /** Float Zero */
 136  1
     protected static final Float      Float_ZERO      = new Float((byte)0);
 137  
     /** Double Zero */
 138  1
     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  81
         this(new LazyDynaClass());
 163  81
     }
 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  0
         this(new LazyDynaClass(name));
 172  0
     }
 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  91
     public LazyDynaBean(DynaClass dynaClass) {
 182  
 
 183  91
         values = newMap();
 184  
 
 185  91
         if (dynaClass instanceof MutableDynaClass) {
 186  91
             this.dynaClass = (MutableDynaClass)dynaClass;
 187  
         } else {
 188  0
             this.dynaClass = new LazyDynaClass(dynaClass.getName(), dynaClass.getDynaProperties());
 189  
         }
 190  
 
 191  91
     }
 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  0
         if (mapDecorator == null) {
 208  0
             mapDecorator = new DynaBeanMapDecorator(this);
 209  
         }
 210  0
         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  0
         if (name == null) {
 223  0
             throw new IllegalArgumentException("No property name specified");
 224  
         }
 225  
 
 226  0
         Object value = values.get(name);
 227  0
         if (value == null) {
 228  0
             return 0;
 229  
         }
 230  
 
 231  0
         if (value instanceof Map) {
 232  0
             return ((Map)value).size();
 233  
         }
 234  
 
 235  0
         if (value instanceof List) {
 236  0
             return ((List)value).size();
 237  
         }
 238  
 
 239  0
         if ((value.getClass().isArray())) {
 240  0
             return Array.getLength(value);
 241  
         }
 242  
 
 243  0
         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  0
         if (name == null) {
 263  0
             throw new IllegalArgumentException("No property name specified");
 264  
         }
 265  
 
 266  0
         Object value = values.get(name);
 267  0
         if (value == null) {
 268  0
             return false;
 269  
         }
 270  
 
 271  0
         if (value instanceof Map) {
 272  0
             return (((Map) value).containsKey(key));
 273  
         }
 274  
 
 275  0
         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  192
         if (name == null) {
 292  0
             throw new IllegalArgumentException("No property name specified");
 293  
         }
 294  
 
 295  
         // Value found
 296  192
         Object value = values.get(name);
 297  192
         if (value != null) {
 298  155
             return value;
 299  
         }
 300  
 
 301  
         // Property doesn't exist
 302  37
         if (!isDynaProperty(name)) {
 303  28
             return null;
 304  
         }
 305  
 
 306  
         // Property doesn't exist
 307  9
         value = createProperty(name, dynaClass.getDynaProperty(name).getType());
 308  
 
 309  9
         if (value != null) {
 310  5
             set(name, value);
 311  
         }
 312  
 
 313  9
         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  32
         if (!isDynaProperty(name)) {
 336  4
             set(name, defaultIndexedProperty(name));
 337  
         }
 338  
 
 339  
         // Get the indexed property
 340  32
         Object indexedProperty = get(name);
 341  
 
 342  
         // Check that the property is indexed
 343  32
         if (!dynaClass.getDynaProperty(name).isIndexed()) {
 344  0
             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  32
         indexedProperty = growIndexedProperty(name, indexedProperty, index);
 351  
 
 352  
         // Return the indexed value
 353  32
         if (indexedProperty.getClass().isArray()) {
 354  16
             return Array.get(indexedProperty, index);
 355  16
         } else if (indexedProperty instanceof List) {
 356  16
             return ((List)indexedProperty).get(index);
 357  
         } else {
 358  0
             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  14
         if (!isDynaProperty(name)) {
 382  4
             set(name, defaultMappedProperty(name));
 383  
         }
 384  
 
 385  
         // Get the mapped property
 386  14
         Object mappedProperty = get(name);
 387  
 
 388  
         // Check that the property is mapped
 389  14
         if (!dynaClass.getDynaProperty(name).isMapped()) {
 390  0
             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  14
         if (mappedProperty instanceof Map) {
 397  14
             return (((Map) mappedProperty).get(key));
 398  
         } else {
 399  0
             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  65
         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  0
         if (name == null) {
 431  0
             throw new IllegalArgumentException("No property name specified");
 432  
         }
 433  
 
 434  0
         Object value = values.get(name);
 435  0
         if (value == null) {
 436  0
             return;
 437  
         }
 438  
 
 439  0
         if (value instanceof Map) {
 440  0
             ((Map) value).remove(key);
 441  
         } else {
 442  0
             throw new IllegalArgumentException
 443  
                     ("Non-mapped property for '" + name + "(" + key + ")'"
 444  
                             + value.getClass().getName());
 445  
         }
 446  
 
 447  0
     }
 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  33
         if (!isDynaProperty(name)) {
 466  
 
 467  14
             if (dynaClass.isRestricted()) {
 468  3
                 throw new IllegalArgumentException
 469  
                     ("Invalid property name '" + name + "' (DynaClass is restricted)");
 470  
             }
 471  11
             if (value == null) {
 472  1
                 dynaClass.add(name);
 473  
             } else {
 474  10
                 dynaClass.add(name, value.getClass());
 475  
             }
 476  
 
 477  
         }
 478  
 
 479  30
         DynaProperty descriptor = dynaClass.getDynaProperty(name);
 480  
 
 481  30
         if (value == null) {
 482  1
             if (descriptor.getType().isPrimitive()) {
 483  0
                 throw new NullPointerException
 484  
                         ("Primitive value for '" + name + "'");
 485  
             }
 486  29
         } else if (!isAssignable(descriptor.getType(), value.getClass())) {
 487  1
             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  29
         values.put(name, value);
 496  
 
 497  29
     }
 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  26
         if (!isDynaProperty(name)) {
 519  5
             set(name, defaultIndexedProperty(name));
 520  
         }
 521  
 
 522  
         // Get the indexed property
 523  24
         Object indexedProperty = get(name);
 524  
 
 525  
         // Check that the property is indexed
 526  24
         if (!dynaClass.getDynaProperty(name).isIndexed()) {
 527  2
             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  22
         indexedProperty = growIndexedProperty(name, indexedProperty, index);
 534  
 
 535  
         // Set the value in an array
 536  22
         if (indexedProperty.getClass().isArray()) {
 537  8
             Array.set(indexedProperty, index, value);
 538  14
         } else if (indexedProperty instanceof List) {
 539  14
             ((List)indexedProperty).set(index, value);
 540  
         } else {
 541  0
             throw new IllegalArgumentException
 542  
                 ("Non-indexed property for '" + name + "[" + index + "]' "
 543  
                             + indexedProperty.getClass().getName());
 544  
         }
 545  
 
 546  22
     }
 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  14
         if (!isDynaProperty(name)) {
 566  2
             set(name, defaultMappedProperty(name));
 567  
         }
 568  
 
 569  
         // Get the mapped property
 570  12
         Object mappedProperty = get(name);
 571  
 
 572  
         // Check that the property is mapped
 573  12
         if (!dynaClass.getDynaProperty(name).isMapped()) {
 574  2
             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  10
         ((Map)mappedProperty).put(key, value);
 581  
 
 582  10
     }
 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  54
         if (indexedProperty instanceof List) {
 598  
 
 599  30
             List list = (List)indexedProperty;
 600  72
             while (index >= list.size()) {
 601  42
                 Class contentType = getDynaClass().getDynaProperty(name).getContentType();
 602  42
                 Object value = null;
 603  42
                 if (contentType != null) {
 604  0
                     value = createProperty(name+"["+list.size()+"]", contentType);
 605  
                 }
 606  42
                 list.add(value);
 607  42
             }
 608  
 
 609  
         }
 610  
 
 611  
         // Grow an Array to the appropriate size
 612  54
         if ((indexedProperty.getClass().isArray())) {
 613  
 
 614  24
             int length = Array.getLength(indexedProperty);
 615  24
             if (index >= length) {
 616  10
                 Class componentType = indexedProperty.getClass().getComponentType();
 617  10
                 Object newArray = Array.newInstance(componentType, (index + 1));
 618  10
                 System.arraycopy(indexedProperty, 0, newArray, 0, length);
 619  10
                 indexedProperty = newArray;
 620  10
                 set(name, indexedProperty);
 621  10
                 int newLength = Array.getLength(indexedProperty);
 622  42
                 for (int i = length; i < newLength; i++) {
 623  32
                     Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType));
 624  
                 }
 625  
             }
 626  
         }
 627  
 
 628  54
         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  48
         if (type == null) {
 640  0
             return null;
 641  
         }
 642  
 
 643  
         // Create Lists, arrays or DynaBeans
 644  48
         if (type.isArray() || List.class.isAssignableFrom(type)) {
 645  10
             return createIndexedProperty(name, type);
 646  
         }
 647  
 
 648  38
         if (Map.class.isAssignableFrom(type)) {
 649  2
             return createMappedProperty(name, type);
 650  
         }
 651  
 
 652  36
         if (DynaBean.class.isAssignableFrom(type)) {
 653  8
             return createDynaBeanProperty(name, type);
 654  
         }
 655  
 
 656  28
         if (type.isPrimitive()) {
 657  12
             return createPrimitiveProperty(name, type);
 658  
         }
 659  
 
 660  16
         if (Number.class.isAssignableFrom(type)) {
 661  0
             return createNumberProperty(name, type);
 662  
         }
 663  
 
 664  16
         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  10
         Object indexedProperty = null;
 678  
 
 679  10
         if (type == null) {
 680  
 
 681  0
             indexedProperty = defaultIndexedProperty(name);
 682  
 
 683  10
         } else if (type.isArray()) {
 684  
 
 685  7
             indexedProperty = Array.newInstance(type.getComponentType(), 0);
 686  
 
 687  3
         } else if (List.class.isAssignableFrom(type)) {
 688  3
             if (type.isInterface()) {
 689  0
                 indexedProperty = defaultIndexedProperty(name);
 690  
             } else {
 691  
                 try {
 692  3
                     indexedProperty = type.newInstance();
 693  
                 }
 694  0
                 catch (Exception ex) {
 695  0
                     throw new IllegalArgumentException
 696  
                         ("Error instantiating indexed property of type '" +
 697  
                                    type.getName() + "' for '" + name + "' " + ex);
 698  3
                 }
 699  
             }
 700  
         } else {
 701  
 
 702  0
             throw new IllegalArgumentException
 703  
                     ("Non-indexed property of type '" + type.getName() + "' for '" + name + "'");
 704  
         }
 705  
 
 706  10
         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  2
         Object mappedProperty = null;
 720  
 
 721  2
         if (type == null) {
 722  
 
 723  0
             mappedProperty = defaultMappedProperty(name);
 724  
 
 725  2
         } else if (type.isInterface()) {
 726  
 
 727  0
             mappedProperty = defaultMappedProperty(name);
 728  
 
 729  2
         } else if (Map.class.isAssignableFrom(type)) {
 730  
             try {
 731  2
                 mappedProperty = type.newInstance();
 732  
             }
 733  0
             catch (Exception ex) {
 734  0
                 throw new IllegalArgumentException
 735  
                     ("Error instantiating mapped property of type '" +
 736  
                             type.getName() + "' for '" + name + "' " + ex);
 737  2
             }
 738  
         } else {
 739  
 
 740  0
             throw new IllegalArgumentException
 741  
                     ("Non-mapped property of type '" + type.getName() + "' for '" + name + "'");
 742  
         }
 743  
 
 744  2
         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  8
             return type.newInstance();
 757  
         }
 758  0
         catch (Exception ex) {
 759  0
             if (logger().isWarnEnabled()) {
 760  0
                 logger().warn("Error instantiating DynaBean property of type '" +
 761  
                         type.getName() + "' for '" + name + "' " + ex);
 762  
             }
 763  0
             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  12
         if (type == Boolean.TYPE) {
 776  0
             return Boolean.FALSE;
 777  12
         } else if (type == Integer.TYPE) {
 778  12
             return Integer_ZERO;
 779  0
         } else if (type == Long.TYPE) {
 780  0
             return Long_ZERO;
 781  0
         } else if (type == Double.TYPE) {
 782  0
             return Double_ZERO;
 783  0
         } else if (type == Float.TYPE) {
 784  0
             return Float_ZERO;
 785  0
         } else if (type == Byte.TYPE) {
 786  0
             return Byte_ZERO;
 787  0
         } else if (type == Short.TYPE) {
 788  0
             return Short_ZERO;
 789  0
         } else if (type == Character.TYPE) {
 790  0
             return Character_SPACE;
 791  
         } else {
 792  0
             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  0
         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  16
         if (type == Object.class    ||
 818  
             type == String.class    ||
 819  
             type == Boolean.class   ||
 820  
             type == Character.class ||
 821  
             Date.class.isAssignableFrom(type)) {
 822  
 
 823  16
             return null;
 824  
 
 825  
         }
 826  
 
 827  
         try {
 828  0
             return type.newInstance();
 829  
         }
 830  0
         catch (Exception ex) {
 831  0
             if (logger().isWarnEnabled()) {
 832  0
                 logger().warn("Error instantiating property of type '" + type.getName() + "' for '" + name + "' " + ex);
 833  
             }
 834  0
             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  9
         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  6
         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  98
         if (name == null) {
 875  0
             throw new IllegalArgumentException("No property name specified");
 876  
         }
 877  
 
 878  
         // Handle LazyDynaClasses
 879  98
         if (dynaClass instanceof LazyDynaClass) {
 880  98
             return ((LazyDynaClass)dynaClass).isDynaProperty(name);
 881  
         }
 882  
 
 883  
         // Handle other MutableDynaClass
 884  0
         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  29
         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  28
             return (true);
 908  
         } else {
 909  1
             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  114
         return new HashMap();
 920  
     }
 921  
 
 922  
     /**
 923  
      * <p>Returns the <code>Log</code>.
 924  
      */
 925  
     private Log logger() {
 926  0
         if (logger == null) {
 927  0
             logger = LogFactory.getLog(LazyDynaBean.class);
 928  
         }
 929  0
         return logger;
 930  
     }
 931  
 
 932  
 }