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.beans.BeanInfo;
20 import java.beans.IntrospectionException;
21 import java.beans.Introspector;
22 import java.beans.PropertyDescriptor;
23 import java.lang.reflect.Constructor;
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.util.AbstractMap;
27 import java.util.AbstractSet;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.Map;
34 import java.util.Set;
35
36 import org.apache.commons.collections.Transformer;
37 import org.apache.commons.collections.keyvalue.AbstractMapEntry;
38
39
40
41
42
43
44
45
46
47
48 public class BeanMap extends AbstractMap<Object, Object> implements Cloneable {
49
50 private transient Object bean;
51
52 private transient HashMap<String, Method> readMethods = new HashMap<String, Method>();
53 private transient HashMap<String, Method> writeMethods = new HashMap<String, Method>();
54 private transient HashMap<String, Class<? extends Object>> types = new HashMap<String, Class<? extends Object>>();
55
56
57
58
59 public static final Object[] NULL_ARGUMENTS = {};
60
61
62
63
64
65
66
67 private static final Map<Class<? extends Object>, Transformer> typeTransformers =
68 Collections.unmodifiableMap(createTypeTransformers());
69
70
71
72
73
74
75
76
77 @Deprecated
78 public static HashMap defaultTransformers = new HashMap() {
79 @Override
80 public void clear() {
81 throw new UnsupportedOperationException();
82 }
83 @Override
84 public boolean containsKey(final Object key) {
85 return typeTransformers.containsKey(key);
86 }
87 @Override
88 public boolean containsValue(final Object value) {
89 return typeTransformers.containsValue(value);
90 }
91 @Override
92 public Set entrySet() {
93 return typeTransformers.entrySet();
94 }
95 @Override
96 public Object get(final Object key) {
97 return typeTransformers.get(key);
98 }
99 @Override
100 public boolean isEmpty() {
101 return false;
102 }
103 @Override
104 public Set keySet() {
105 return typeTransformers.keySet();
106 }
107 @Override
108 public Object put(final Object key, final Object value) {
109 throw new UnsupportedOperationException();
110 }
111 @Override
112 public void putAll(final Map m) {
113 throw new UnsupportedOperationException();
114 }
115 @Override
116 public Object remove(final Object key) {
117 throw new UnsupportedOperationException();
118 }
119 @Override
120 public int size() {
121 return typeTransformers.size();
122 }
123 @Override
124 public Collection values() {
125 return typeTransformers.values();
126 }
127 };
128
129 private static Map<Class<? extends Object>, Transformer> createTypeTransformers() {
130 final Map<Class<? extends Object>, Transformer> defaultTransformers =
131 new HashMap<Class<? extends Object>, Transformer>();
132 defaultTransformers.put(
133 Boolean.TYPE,
134 new Transformer() {
135 public Object transform( final Object input ) {
136 return Boolean.valueOf( input.toString() );
137 }
138 }
139 );
140 defaultTransformers.put(
141 Character.TYPE,
142 new Transformer() {
143 public Object transform( final Object input ) {
144 return new Character( input.toString().charAt( 0 ) );
145 }
146 }
147 );
148 defaultTransformers.put(
149 Byte.TYPE,
150 new Transformer() {
151 public Object transform( final Object input ) {
152 return Byte.valueOf( input.toString() );
153 }
154 }
155 );
156 defaultTransformers.put(
157 Short.TYPE,
158 new Transformer() {
159 public Object transform( final Object input ) {
160 return Short.valueOf( input.toString() );
161 }
162 }
163 );
164 defaultTransformers.put(
165 Integer.TYPE,
166 new Transformer() {
167 public Object transform( final Object input ) {
168 return Integer.valueOf( input.toString() );
169 }
170 }
171 );
172 defaultTransformers.put(
173 Long.TYPE,
174 new Transformer() {
175 public Object transform( final Object input ) {
176 return Long.valueOf( input.toString() );
177 }
178 }
179 );
180 defaultTransformers.put(
181 Float.TYPE,
182 new Transformer() {
183 public Object transform( final Object input ) {
184 return Float.valueOf( input.toString() );
185 }
186 }
187 );
188 defaultTransformers.put(
189 Double.TYPE,
190 new Transformer() {
191 public Object transform( final Object input ) {
192 return Double.valueOf( input.toString() );
193 }
194 }
195 );
196 return defaultTransformers;
197 }
198
199
200
201
202
203
204
205
206 public BeanMap() {
207 }
208
209
210
211
212
213
214
215
216 public BeanMap(final Object bean) {
217 this.bean = bean;
218 initialise();
219 }
220
221
222
223
224
225
226
227
228 @Override
229 public String toString() {
230 return "BeanMap<" + String.valueOf(bean) + ">";
231 }
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260 @Override
261 public Object clone() throws CloneNotSupportedException {
262 final BeanMap newMap = (BeanMap)super.clone();
263
264 if(bean == null) {
265
266
267 return newMap;
268 }
269
270 Object newBean = null;
271 final Class<? extends Object> beanClass = bean.getClass();
272 try {
273 newBean = beanClass.newInstance();
274 } catch (final Exception e) {
275
276 final CloneNotSupportedException cnse = new CloneNotSupportedException
277 ("Unable to instantiate the underlying bean \"" +
278 beanClass.getName() + "\": " + e);
279 BeanUtils.initCause(cnse, e);
280 throw cnse;
281 }
282
283 try {
284 newMap.setBean(newBean);
285 } catch (final Exception exception) {
286 final CloneNotSupportedException cnse = new CloneNotSupportedException
287 ("Unable to set bean in the cloned bean map: " +
288 exception);
289 BeanUtils.initCause(cnse, exception);
290 throw cnse;
291 }
292
293 try {
294
295
296
297 final Iterator<?> readableKeys = readMethods.keySet().iterator();
298 while(readableKeys.hasNext()) {
299 final Object key = readableKeys.next();
300 if(getWriteMethod(key) != null) {
301 newMap.put(key, get(key));
302 }
303 }
304 } catch (final Exception exception) {
305 final CloneNotSupportedException cnse = new CloneNotSupportedException
306 ("Unable to copy bean values to cloned bean map: " +
307 exception);
308 BeanUtils.initCause(cnse, exception);
309 throw cnse;
310 }
311
312 return newMap;
313 }
314
315
316
317
318
319
320
321 public void putAllWriteable(final BeanMap map) {
322 final Iterator<?> readableKeys = map.readMethods.keySet().iterator();
323 while (readableKeys.hasNext()) {
324 final Object key = readableKeys.next();
325 if (getWriteMethod(key) != null) {
326 this.put(key, map.get(key));
327 }
328 }
329 }
330
331
332
333
334
335
336
337
338
339
340 @Override
341 public void clear() {
342 if(bean == null) {
343 return;
344 }
345
346 Class<? extends Object> beanClass = null;
347 try {
348 beanClass = bean.getClass();
349 bean = beanClass.newInstance();
350 }
351 catch (final Exception e) {
352 final UnsupportedOperationException uoe =
353 new UnsupportedOperationException("Could not create new instance of class: " + beanClass);
354 BeanUtils.initCause(uoe, e);
355 throw uoe;
356 }
357 }
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374 @Override
375 public boolean containsKey(final Object name) {
376 final Method method = getReadMethod(name);
377 return method != null;
378 }
379
380
381
382
383
384
385
386
387
388 @Override
389 public boolean containsValue(final Object value) {
390
391 return super.containsValue(value);
392 }
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409 @Override
410 public Object get(final Object name) {
411 if ( bean != null ) {
412 final Method method = getReadMethod( name );
413 if ( method != null ) {
414 try {
415 return method.invoke( bean, NULL_ARGUMENTS );
416 }
417 catch ( final IllegalAccessException e ) {
418 logWarn( e );
419 }
420 catch ( final IllegalArgumentException e ) {
421 logWarn( e );
422 }
423 catch ( final InvocationTargetException e ) {
424 logWarn( e );
425 }
426 catch ( final NullPointerException e ) {
427 logWarn( e );
428 }
429 }
430 }
431 return null;
432 }
433
434
435
436
437
438
439
440
441
442
443
444
445
446 @Override
447 public Object put(final Object name, final Object value) throws IllegalArgumentException, ClassCastException {
448 if ( bean != null ) {
449 final Object oldValue = get( name );
450 final Method method = getWriteMethod( name );
451 if ( method == null ) {
452 throw new IllegalArgumentException( "The bean of type: "+
453 bean.getClass().getName() + " has no property called: " + name );
454 }
455 try {
456 final Object[] arguments = createWriteMethodArguments( method, value );
457 method.invoke( bean, arguments );
458
459 final Object newValue = get( name );
460 firePropertyChange( name, oldValue, newValue );
461 }
462 catch ( final InvocationTargetException e ) {
463 final IllegalArgumentException iae = new IllegalArgumentException(e.getMessage());
464 if (BeanUtils.initCause(iae, e) == false) {
465 logInfo(e);
466 }
467 throw iae;
468 }
469 catch ( final IllegalAccessException e ) {
470 final IllegalArgumentException iae = new IllegalArgumentException(e.getMessage());
471 if (BeanUtils.initCause(iae, e) == false) {
472 logInfo(e);
473 }
474 throw iae;
475 }
476 return oldValue;
477 }
478 return null;
479 }
480
481
482
483
484
485
486 @Override
487 public int size() {
488 return readMethods.size();
489 }
490
491
492
493
494
495
496
497
498
499
500
501
502 @SuppressWarnings({ "unchecked", "rawtypes" })
503
504
505 @Override
506 public Set<Object> keySet() {
507 return Collections.unmodifiableSet((Set) readMethods.keySet());
508 }
509
510
511
512
513
514
515
516
517 @Override
518 public Set<Map.Entry<Object, Object>> entrySet() {
519 return Collections.unmodifiableSet(new AbstractSet<Map.Entry<Object, Object>>() {
520 @Override
521 public Iterator<Map.Entry<Object, Object>> iterator() {
522 return entryIterator();
523 }
524 @Override
525 public int size() {
526 return BeanMap.this.readMethods.size();
527 }
528 });
529 }
530
531
532
533
534
535
536
537 @Override
538 public Collection<Object> values() {
539 final ArrayList<Object> answer = new ArrayList<Object>( readMethods.size() );
540 for ( final Iterator<Object> iter = valueIterator(); iter.hasNext(); ) {
541 answer.add( iter.next() );
542 }
543 return Collections.unmodifiableList(answer);
544 }
545
546
547
548
549
550
551
552
553
554
555
556
557 public Class<?> getType(final String name) {
558 return types.get( name );
559 }
560
561
562
563
564
565
566
567
568 public Iterator<String> keyIterator() {
569 return readMethods.keySet().iterator();
570 }
571
572
573
574
575
576
577 public Iterator<Object> valueIterator() {
578 final Iterator<?> iter = keyIterator();
579 return new Iterator<Object>() {
580 public boolean hasNext() {
581 return iter.hasNext();
582 }
583 public Object next() {
584 final Object key = iter.next();
585 return get(key);
586 }
587 public void remove() {
588 throw new UnsupportedOperationException( "remove() not supported for BeanMap" );
589 }
590 };
591 }
592
593
594
595
596
597
598 public Iterator<Map.Entry<Object, Object>> entryIterator() {
599 final Iterator<String> iter = keyIterator();
600 return new Iterator<Map.Entry<Object, Object>>() {
601 public boolean hasNext() {
602 return iter.hasNext();
603 }
604 public Map.Entry<Object, Object> next() {
605 final Object key = iter.next();
606 final Object value = get(key);
607 @SuppressWarnings("unchecked")
608 final
609
610
611 Map.Entry<Object, Object> tmpEntry = new Entry( BeanMap.this, key, value );
612 return tmpEntry;
613 }
614 public void remove() {
615 throw new UnsupportedOperationException( "remove() not supported for BeanMap" );
616 }
617 };
618 }
619
620
621
622
623
624
625
626
627
628
629
630 public Object getBean() {
631 return bean;
632 }
633
634
635
636
637
638
639
640 public void setBean( final Object newBean ) {
641 bean = newBean;
642 reinitialise();
643 }
644
645
646
647
648
649
650
651 public Method getReadMethod(final String name) {
652 return readMethods.get(name);
653 }
654
655
656
657
658
659
660
661 public Method getWriteMethod(final String name) {
662 return writeMethods.get(name);
663 }
664
665
666
667
668
669
670
671
672
673
674
675
676
677 protected Method getReadMethod( final Object name ) {
678 return readMethods.get( name );
679 }
680
681
682
683
684
685
686
687
688
689 protected Method getWriteMethod( final Object name ) {
690 return writeMethods.get( name );
691 }
692
693
694
695
696
697 protected void reinitialise() {
698 readMethods.clear();
699 writeMethods.clear();
700 types.clear();
701 initialise();
702 }
703
704 private void initialise() {
705 if(getBean() == null) {
706 return;
707 }
708
709 final Class<? extends Object> beanClass = getBean().getClass();
710 try {
711
712 final BeanInfo beanInfo = Introspector.getBeanInfo( beanClass );
713 final PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
714 if ( propertyDescriptors != null ) {
715 for (final PropertyDescriptor propertyDescriptor : propertyDescriptors) {
716 if ( propertyDescriptor != null ) {
717 final String name = propertyDescriptor.getName();
718 final Method readMethod = propertyDescriptor.getReadMethod();
719 final Method writeMethod = propertyDescriptor.getWriteMethod();
720 final Class<? extends Object> aType = propertyDescriptor.getPropertyType();
721
722 if ( readMethod != null ) {
723 readMethods.put( name, readMethod );
724 }
725 if ( writeMethod != null ) {
726 writeMethods.put( name, writeMethod );
727 }
728 types.put( name, aType );
729 }
730 }
731 }
732 }
733 catch ( final IntrospectionException e ) {
734 logWarn( e );
735 }
736 }
737
738
739
740
741
742
743
744
745
746
747 protected void firePropertyChange( final Object key, final Object oldValue, final Object newValue ) {
748 }
749
750
751
752
753
754
755
756 protected static class Entry extends AbstractMapEntry {
757 private final BeanMap owner;
758
759
760
761
762
763
764
765
766 protected Entry( final BeanMap owner, final Object key, final Object value ) {
767 super( key, value );
768 this.owner = owner;
769 }
770
771
772
773
774
775
776
777 @Override
778 public Object setValue(final Object value) {
779 final Object key = getKey();
780 final Object oldValue = owner.get( key );
781
782 owner.put( key, value );
783 final Object newValue = owner.get( key );
784 super.setValue( newValue );
785 return oldValue;
786 }
787 }
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804 protected Object[] createWriteMethodArguments( final Method method, Object value )
805 throws IllegalAccessException, ClassCastException {
806 try {
807 if ( value != null ) {
808 final Class<? extends Object>[] types = method.getParameterTypes();
809 if ( types != null && types.length > 0 ) {
810 final Class<? extends Object> paramType = types[0];
811 if ( ! paramType.isAssignableFrom( value.getClass() ) ) {
812 value = convertType( paramType, value );
813 }
814 }
815 }
816 final Object[] answer = { value };
817 return answer;
818 }
819 catch ( final InvocationTargetException e ) {
820 final IllegalArgumentException iae = new IllegalArgumentException(e.getMessage());
821 if (BeanUtils.initCause(iae, e) == false) {
822 logInfo(e);
823 }
824 throw iae;
825 }
826 catch ( final InstantiationException e ) {
827 final IllegalArgumentException iae = new IllegalArgumentException(e.getMessage());
828 if (BeanUtils.initCause(iae, e) == false) {
829 logInfo(e);
830 }
831 BeanUtils.initCause(iae, e);
832 throw iae;
833 }
834 }
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867 protected Object convertType( final Class<?> newType, final Object value )
868 throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
869
870
871 final Class<?>[] types = { value.getClass() };
872 try {
873 final Constructor<?> constructor = newType.getConstructor( types );
874 final Object[] arguments = { value };
875 return constructor.newInstance( arguments );
876 }
877 catch ( final NoSuchMethodException e ) {
878
879 final Transformer transformer = getTypeTransformer( newType );
880 if ( transformer != null ) {
881 return transformer.transform( value );
882 }
883 return value;
884 }
885 }
886
887
888
889
890
891
892
893
894 protected Transformer getTypeTransformer( final Class<?> aType ) {
895 return typeTransformers.get( aType );
896 }
897
898
899
900
901
902
903
904 protected void logInfo(final Exception ex) {
905
906 System.out.println( "INFO: Exception: " + ex );
907 }
908
909
910
911
912
913
914
915 protected void logWarn(final Exception ex) {
916
917 System.out.println( "WARN: Exception: " + ex );
918 ex.printStackTrace();
919 }
920 }