1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
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
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
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
296 Object value = values.get(name);
297 if (value != null) {
298 return value;
299 }
300
301
302 if (!isDynaProperty(name)) {
303 return null;
304 }
305
306
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
335 if (!isDynaProperty(name)) {
336 set(name, defaultIndexedProperty(name));
337 }
338
339
340 Object indexedProperty = get(name);
341
342
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
350 indexedProperty = growIndexedProperty(name, indexedProperty, index);
351
352
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
381 if (!isDynaProperty(name)) {
382 set(name, defaultMappedProperty(name));
383 }
384
385
386 Object mappedProperty = get(name);
387
388
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
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
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
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
518 if (!isDynaProperty(name)) {
519 set(name, defaultIndexedProperty(name));
520 }
521
522
523 Object indexedProperty = get(name);
524
525
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
533 indexedProperty = growIndexedProperty(name, indexedProperty, index);
534
535
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
565 if (!isDynaProperty(name)) {
566 set(name, defaultMappedProperty(name));
567 }
568
569
570 Object mappedProperty = get(name);
571
572
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
580 ((Map)mappedProperty).put(key, value);
581
582 }
583
584
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
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
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
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
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
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
879 if (dynaClass instanceof LazyDynaClass) {
880 return ((LazyDynaClass)dynaClass).isDynaProperty(name);
881 }
882
883
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 }