001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.beanutils;
018    
019    import java.util.List;
020    import java.util.ArrayList;
021    import java.util.Map;
022    import java.util.HashMap;
023    import java.util.Date;
024    import java.lang.reflect.Array;
025    import java.math.BigDecimal;
026    import java.math.BigInteger;
027    import java.io.Serializable;
028    import org.apache.commons.logging.Log;
029    import org.apache.commons.logging.LogFactory;
030    
031    /**
032     * <p>DynaBean which automatically adds properties to the <code>DynaClass</code>
033     *   and provides <i>Lazy List</i> and <i>Lazy Map</i> features.</p>
034     *
035     * <p>DynaBeans deal with three types of properties - <i>simple</i>, <i>indexed</i> and <i>mapped</i> and
036     *    have the following <code>get()</code> and <code>set()</code> methods for
037     *    each of these types:</p>
038     *    <ul>
039     *        <li><i>Simple</i> property methods - <code>get(name)</code> and
040     *                          <code>set(name, value)</code></li>
041     *        <li><i>Indexed</i> property methods - <code>get(name, index)</code> and
042     *                          <code>set(name, index, value)</code></li>
043     *        <li><i>Mapped</i> property methods - <code>get(name, key)</code> and
044     *                          <code>set(name, key, value)</code></li>
045     *    </ul>
046     *
047     * <p><b><u>Getting Property Values</u></b></p>
048     * <p>Calling any of the <code>get()</code> methods, for a property which
049     *    doesn't exist, returns <code>null</code> in this implementation.</p>
050     *
051     * <p><b><u>Setting Simple Properties</u></b></p>
052     *    <p>The <code>LazyDynaBean</code> will automatically add a property to the <code>DynaClass</code>
053     *       if it doesn't exist when the <code>set(name, value)</code> method is called.</p>
054     *
055     *     <code>DynaBean myBean = new LazyDynaBean();</code></br>
056     *     <code>myBean.set("myProperty", "myValue");</code></br>
057     *
058     * <p><b><u>Setting Indexed Properties</u></b></p>
059     *    <p>If the property <b>doesn't</b> exist, the <code>LazyDynaBean</code> will automatically add
060     *       a property with an <code>ArrayList</code> type to the <code>DynaClass</code> when
061     *       the <code>set(name, index, value)</code> method is called.
062     *       It will also instantiate a new <code>ArrayList</code> and automatically <i>grow</i>
063     *       the <code>List</code> so that it is big enough to accomodate the index being set.
064     *       <code>ArrayList</code> is the default indexed property that LazyDynaBean uses but
065     *       this can be easily changed by overriding the <code>defaultIndexedProperty(name)</code>
066     *       method.</p>
067     *
068     *     <code>DynaBean myBean = new LazyDynaBean();</code></br>
069     *     <code>myBean.set("myIndexedProperty", 0, "myValue1");</code></br>
070     *     <code>myBean.set("myIndexedProperty", 1, "myValue2");</code></br>
071     *
072     *    <p>If the indexed property <b>does</b> exist in the <code>DynaClass</code> but is set to
073     *      <code>null</code> in the <code>LazyDynaBean</code>, then it will instantiate a
074     *      new <code>List</code> or <code>Array</code> as specified by the property's type
075     *      in the <code>DynaClass</code> and automatically <i>grow</i> the <code>List</code>
076     *      or <code>Array</code> so that it is big enough to accomodate the index being set.</p>
077     *
078     *     <code>DynaBean myBean = new LazyDynaBean();</code></br>
079     *     <code>MutableDynaClass myClass = (MutableDynaClass)myBean.getDynaClass();</code></br>
080     *     <code>myClass.add("myIndexedProperty", int[].class);</code></br>
081     *     <code>myBean.set("myIndexedProperty", 0, new Integer(10));</code></br>
082     *     <code>myBean.set("myIndexedProperty", 1, new Integer(20));</code></br>
083     *
084     * <p><b><u>Setting Mapped Properties</u></b></p>
085     *    <p>If the property <b>doesn't</b> exist, the <code>LazyDynaBean</code> will automatically add
086     *       a property with a <code>HashMap</code> type to the <code>DynaClass</code> and
087     *       instantiate a new <code>HashMap</code> in the DynaBean when the
088     *       <code>set(name, key, value)</code> method is called. <code>HashMap</code> is the default
089     *       mapped property that LazyDynaBean uses but this can be easily changed by overriding
090     *       the <code>defaultMappedProperty(name)</code> method.</p>
091     *
092     *     <code>DynaBean myBean = new LazyDynaBean();</code></br>
093     *     <code>myBean.set("myMappedProperty", "myKey", "myValue");</code></br>
094     *
095     *    <p>If the mapped property <b>does</b> exist in the <code>DynaClass</code> but is set to
096     *      <code>null</code> in the <code>LazyDynaBean</code>, then it will instantiate a
097     *      new <code>Map</code> as specified by the property's type in the <code>DynaClass</code>.</p>
098     *
099     *     <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    }