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.io.Serializable;
20 import java.lang.reflect.Array;
21 import java.math.BigDecimal;
22 import java.math.BigInteger;
23 import java.util.ArrayList;
24 import java.util.Date;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114 public class LazyDynaBean implements DynaBean, Serializable {
115
116
117
118
119
120 private transient Log logger = LogFactory.getLog(LazyDynaBean.class);
121
122
123 protected static final BigInteger BigInteger_ZERO = new BigInteger("0");
124
125 protected static final BigDecimal BigDecimal_ZERO = new BigDecimal("0");
126
127 protected static final Character Character_SPACE = new Character(' ');
128
129 protected static final Byte Byte_ZERO = new Byte((byte)0);
130
131 protected static final Short Short_ZERO = new Short((short)0);
132
133 protected static final Integer Integer_ZERO = new Integer(0);
134
135 protected static final Long Long_ZERO = new Long(0);
136
137 protected static final Float Float_ZERO = new Float((byte)0);
138
139 protected static final Double Double_ZERO = new Double((byte)0);
140
141
142
143
144
145 protected Map<String, Object> values;
146
147
148 private transient Map<String, Object> mapDecorator;
149
150
151
152
153
154 protected MutableDynaClass dynaClass;
155
156
157
158
159
160
161
162 public LazyDynaBean() {
163 this(new LazyDynaClass());
164 }
165
166
167
168
169
170
171 public LazyDynaBean(final String name) {
172 this(new LazyDynaClass(name));
173 }
174
175
176
177
178
179
180
181
182 public LazyDynaBean(final DynaClass dynaClass) {
183
184 values = newMap();
185
186 if (dynaClass instanceof MutableDynaClass) {
187 this.dynaClass = (MutableDynaClass)dynaClass;
188 } else {
189 this.dynaClass = new LazyDynaClass(dynaClass.getName(), dynaClass.getDynaProperties());
190 }
191
192 }
193
194
195
196
197
198
199
200
201
202
203
204
205
206 public Map<String, Object> getMap() {
207
208 if (mapDecorator == null) {
209 mapDecorator = new DynaBeanPropertyMapDecorator(this);
210 }
211 return mapDecorator;
212 }
213
214
215
216
217
218
219
220
221 public int size(final String name) {
222
223 if (name == null) {
224 throw new IllegalArgumentException("No property name specified");
225 }
226
227 final Object value = values.get(name);
228 if (value == null) {
229 return 0;
230 }
231
232 if (value instanceof Map) {
233 return ((Map<?, ?>)value).size();
234 }
235
236 if (value instanceof List) {
237 return ((List<?>)value).size();
238 }
239
240 if ((value.getClass().isArray())) {
241 return Array.getLength(value);
242 }
243
244 return 0;
245
246 }
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261 public boolean contains(final String name, final String key) {
262
263 if (name == null) {
264 throw new IllegalArgumentException("No property name specified");
265 }
266
267 final Object value = values.get(name);
268 if (value == null) {
269 return false;
270 }
271
272 if (value instanceof Map) {
273 return (((Map<?, ?>) value).containsKey(key));
274 }
275
276 return false;
277
278 }
279
280
281
282
283
284
285
286
287
288
289
290 public Object get(final String name) {
291
292 if (name == null) {
293 throw new IllegalArgumentException("No property name specified");
294 }
295
296
297 Object value = values.get(name);
298 if (value != null) {
299 return value;
300 }
301
302
303 if (!isDynaProperty(name)) {
304 return null;
305 }
306
307
308 value = createProperty(name, dynaClass.getDynaProperty(name).getType());
309
310 if (value != null) {
311 set(name, value);
312 }
313
314 return value;
315
316 }
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333 public Object get(final String name, final int index) {
334
335
336 if (!isDynaProperty(name)) {
337 set(name, defaultIndexedProperty(name));
338 }
339
340
341 Object indexedProperty = get(name);
342
343
344 if (!dynaClass.getDynaProperty(name).isIndexed()) {
345 throw new IllegalArgumentException
346 ("Non-indexed property for '" + name + "[" + index + "]' "
347 + dynaClass.getDynaProperty(name).getName());
348 }
349
350
351 indexedProperty = growIndexedProperty(name, indexedProperty, index);
352
353
354 if (indexedProperty.getClass().isArray()) {
355 return Array.get(indexedProperty, index);
356 } else if (indexedProperty instanceof List) {
357 return ((List<?>)indexedProperty).get(index);
358 } else {
359 throw new IllegalArgumentException
360 ("Non-indexed property for '" + name + "[" + index + "]' "
361 + indexedProperty.getClass().getName());
362 }
363
364 }
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379 public Object get(final String name, final String key) {
380
381
382 if (!isDynaProperty(name)) {
383 set(name, defaultMappedProperty(name));
384 }
385
386
387 final Object mappedProperty = get(name);
388
389
390 if (!dynaClass.getDynaProperty(name).isMapped()) {
391 throw new IllegalArgumentException
392 ("Non-mapped property for '" + name + "(" + key + ")' "
393 + dynaClass.getDynaProperty(name).getType().getName());
394 }
395
396
397 if (mappedProperty instanceof Map) {
398 return (((Map<?, ?>) mappedProperty).get(key));
399 } else {
400 throw new IllegalArgumentException
401 ("Non-mapped property for '" + name + "(" + key + ")'"
402 + mappedProperty.getClass().getName());
403 }
404
405 }
406
407
408
409
410
411
412
413
414 public DynaClass getDynaClass() {
415 return dynaClass;
416 }
417
418
419
420
421
422
423
424
425
426
427
428
429 public void remove(final String name, final String key) {
430
431 if (name == null) {
432 throw new IllegalArgumentException("No property name specified");
433 }
434
435 final Object value = values.get(name);
436 if (value == null) {
437 return;
438 }
439
440 if (value instanceof Map) {
441 ((Map<?, ?>) value).remove(key);
442 } else {
443 throw new IllegalArgumentException
444 ("Non-mapped property for '" + name + "(" + key + ")'"
445 + value.getClass().getName());
446 }
447
448 }
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463 public void set(final String name, final Object value) {
464
465
466 if (!isDynaProperty(name)) {
467
468 if (dynaClass.isRestricted()) {
469 throw new IllegalArgumentException
470 ("Invalid property name '" + name + "' (DynaClass is restricted)");
471 }
472 if (value == null) {
473 dynaClass.add(name);
474 } else {
475 dynaClass.add(name, value.getClass());
476 }
477
478 }
479
480 final DynaProperty descriptor = dynaClass.getDynaProperty(name);
481
482 if (value == null) {
483 if (descriptor.getType().isPrimitive()) {
484 throw new NullPointerException
485 ("Primitive value for '" + name + "'");
486 }
487 } else if (!isAssignable(descriptor.getType(), value.getClass())) {
488 throw new ConversionException
489 ("Cannot assign value of type '" +
490 value.getClass().getName() +
491 "' to property '" + name + "' of type '" +
492 descriptor.getType().getName() + "'");
493 }
494
495
496 values.put(name, value);
497
498 }
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516 public void set(final String name, final int index, final Object value) {
517
518
519 if (!isDynaProperty(name)) {
520 set(name, defaultIndexedProperty(name));
521 }
522
523
524 Object indexedProperty = get(name);
525
526
527 if (!dynaClass.getDynaProperty(name).isIndexed()) {
528 throw new IllegalArgumentException
529 ("Non-indexed property for '" + name + "[" + index + "]'"
530 + dynaClass.getDynaProperty(name).getType().getName());
531 }
532
533
534 indexedProperty = growIndexedProperty(name, indexedProperty, index);
535
536
537 if (indexedProperty.getClass().isArray()) {
538 Array.set(indexedProperty, index, value);
539 } else if (indexedProperty instanceof List) {
540 @SuppressWarnings("unchecked")
541 final
542
543 List<Object> values = (List<Object>) indexedProperty;
544 values.set(index, value);
545 } else {
546 throw new IllegalArgumentException
547 ("Non-indexed property for '" + name + "[" + index + "]' "
548 + indexedProperty.getClass().getName());
549 }
550
551 }
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567 public void set(final String name, final String key, final Object value) {
568
569
570 if (!isDynaProperty(name)) {
571 set(name, defaultMappedProperty(name));
572 }
573
574
575 final Object mappedProperty = get(name);
576
577
578 if (!dynaClass.getDynaProperty(name).isMapped()) {
579 throw new IllegalArgumentException
580 ("Non-mapped property for '" + name + "(" + key + ")'"
581 + dynaClass.getDynaProperty(name).getType().getName());
582 }
583
584
585 @SuppressWarnings("unchecked")
586 final
587
588 Map<String, Object> valuesMap = (Map<String, Object>) mappedProperty;
589 valuesMap.put(key, value);
590
591 }
592
593
594
595
596
597
598
599
600
601
602
603 protected Object growIndexedProperty(final String name, Object indexedProperty, final int index) {
604
605
606 if (indexedProperty instanceof List) {
607
608 @SuppressWarnings("unchecked")
609 final
610
611 List<Object> list = (List<Object>)indexedProperty;
612 while (index >= list.size()) {
613 final Class<?> contentType = getDynaClass().getDynaProperty(name).getContentType();
614 Object value = null;
615 if (contentType != null) {
616 value = createProperty(name+"["+list.size()+"]", contentType);
617 }
618 list.add(value);
619 }
620
621 }
622
623
624 if ((indexedProperty.getClass().isArray())) {
625
626 final int length = Array.getLength(indexedProperty);
627 if (index >= length) {
628 final Class<?> componentType = indexedProperty.getClass().getComponentType();
629 final Object newArray = Array.newInstance(componentType, (index + 1));
630 System.arraycopy(indexedProperty, 0, newArray, 0, length);
631 indexedProperty = newArray;
632 set(name, indexedProperty);
633 final int newLength = Array.getLength(indexedProperty);
634 for (int i = length; i < newLength; i++) {
635 Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType));
636 }
637 }
638 }
639
640 return indexedProperty;
641
642 }
643
644
645
646
647
648
649
650 protected Object createProperty(final String name, final Class<?> type) {
651 if (type == null) {
652 return null;
653 }
654
655
656 if (type.isArray() || List.class.isAssignableFrom(type)) {
657 return createIndexedProperty(name, type);
658 }
659
660 if (Map.class.isAssignableFrom(type)) {
661 return createMappedProperty(name, type);
662 }
663
664 if (DynaBean.class.isAssignableFrom(type)) {
665 return createDynaBeanProperty(name, type);
666 }
667
668 if (type.isPrimitive()) {
669 return createPrimitiveProperty(name, type);
670 }
671
672 if (Number.class.isAssignableFrom(type)) {
673 return createNumberProperty(name, type);
674 }
675
676 return createOtherProperty(name, type);
677
678 }
679
680
681
682
683
684
685
686 protected Object createIndexedProperty(final String name, final Class<?> type) {
687
688
689 Object indexedProperty = null;
690
691 if (type == null) {
692
693 indexedProperty = defaultIndexedProperty(name);
694
695 } else if (type.isArray()) {
696
697 indexedProperty = Array.newInstance(type.getComponentType(), 0);
698
699 } else if (List.class.isAssignableFrom(type)) {
700 if (type.isInterface()) {
701 indexedProperty = defaultIndexedProperty(name);
702 } else {
703 try {
704 indexedProperty = type.newInstance();
705 }
706 catch (final Exception ex) {
707 throw new IllegalArgumentException
708 ("Error instantiating indexed property of type '" +
709 type.getName() + "' for '" + name + "' " + ex);
710 }
711 }
712 } else {
713
714 throw new IllegalArgumentException
715 ("Non-indexed property of type '" + type.getName() + "' for '" + name + "'");
716 }
717
718 return indexedProperty;
719
720 }
721
722
723
724
725
726
727
728 protected Object createMappedProperty(final String name, final Class<?> type) {
729
730
731 Object mappedProperty = null;
732
733 if (type == null) {
734
735 mappedProperty = defaultMappedProperty(name);
736
737 } else if (type.isInterface()) {
738
739 mappedProperty = defaultMappedProperty(name);
740
741 } else if (Map.class.isAssignableFrom(type)) {
742 try {
743 mappedProperty = type.newInstance();
744 }
745 catch (final Exception ex) {
746 throw new IllegalArgumentException
747 ("Error instantiating mapped property of type '" +
748 type.getName() + "' for '" + name + "' " + ex);
749 }
750 } else {
751
752 throw new IllegalArgumentException
753 ("Non-mapped property of type '" + type.getName() + "' for '" + name + "'");
754 }
755
756 return mappedProperty;
757
758 }
759
760
761
762
763
764
765
766 protected Object createDynaBeanProperty(final String name, final Class<?> type) {
767 try {
768 return type.newInstance();
769 }
770 catch (final Exception ex) {
771 if (logger().isWarnEnabled()) {
772 logger().warn("Error instantiating DynaBean property of type '" +
773 type.getName() + "' for '" + name + "' " + ex);
774 }
775 return null;
776 }
777 }
778
779
780
781
782
783
784
785 protected Object createPrimitiveProperty(final String name, final Class<?> type) {
786
787 if (type == Boolean.TYPE) {
788 return Boolean.FALSE;
789 } else if (type == Integer.TYPE) {
790 return Integer_ZERO;
791 } else if (type == Long.TYPE) {
792 return Long_ZERO;
793 } else if (type == Double.TYPE) {
794 return Double_ZERO;
795 } else if (type == Float.TYPE) {
796 return Float_ZERO;
797 } else if (type == Byte.TYPE) {
798 return Byte_ZERO;
799 } else if (type == Short.TYPE) {
800 return Short_ZERO;
801 } else if (type == Character.TYPE) {
802 return Character_SPACE;
803 } else {
804 return null;
805 }
806
807 }
808
809
810
811
812
813
814
815 protected Object createNumberProperty(final String name, final Class<?> type) {
816
817 return null;
818
819 }
820
821
822
823
824
825
826
827 protected Object createOtherProperty(final String name, final Class<?> type) {
828
829 if (type == Object.class ||
830 type == String.class ||
831 type == Boolean.class ||
832 type == Character.class ||
833 Date.class.isAssignableFrom(type)) {
834
835 return null;
836
837 }
838
839 try {
840 return type.newInstance();
841 }
842 catch (final Exception ex) {
843 if (logger().isWarnEnabled()) {
844 logger().warn("Error instantiating property of type '" + type.getName() + "' for '" + name + "' " + ex);
845 }
846 return null;
847 }
848 }
849
850
851
852
853
854
855
856
857
858
859
860 protected Object defaultIndexedProperty(final String name) {
861 return new ArrayList<Object>();
862 }
863
864
865
866
867
868
869
870
871
872
873
874 protected Map<String, Object> defaultMappedProperty(final String name) {
875 return new HashMap<String, Object>();
876 }
877
878
879
880
881
882
883
884 protected boolean isDynaProperty(final String name) {
885
886 if (name == null) {
887 throw new IllegalArgumentException("No property name specified");
888 }
889
890
891 if (dynaClass instanceof LazyDynaClass) {
892 return ((LazyDynaClass)dynaClass).isDynaProperty(name);
893 }
894
895
896 return dynaClass.getDynaProperty(name) == null ? false : true;
897
898 }
899
900
901
902
903
904
905
906
907
908 protected boolean isAssignable(final Class<?> dest, final Class<?> source) {
909
910 if (dest.isAssignableFrom(source) ||
911 ((dest == Boolean.TYPE) && (source == Boolean.class)) ||
912 ((dest == Byte.TYPE) && (source == Byte.class)) ||
913 ((dest == Character.TYPE) && (source == Character.class)) ||
914 ((dest == Double.TYPE) && (source == Double.class)) ||
915 ((dest == Float.TYPE) && (source == Float.class)) ||
916 ((dest == Integer.TYPE) && (source == Integer.class)) ||
917 ((dest == Long.TYPE) && (source == Long.class)) ||
918 ((dest == Short.TYPE) && (source == Short.class))) {
919 return (true);
920 } else {
921 return (false);
922 }
923
924 }
925
926
927
928
929
930 protected Map<String, Object> newMap() {
931 return new HashMap<String, Object>();
932 }
933
934
935
936
937 private Log logger() {
938 if (logger == null) {
939 logger = LogFactory.getLog(LazyDynaBean.class);
940 }
941 return logger;
942 }
943
944 }