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