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