1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.seasar.cubby.spi.beans.impl;
17
18 import static org.seasar.cubby.internal.util.ReflectionUtils.findAllDeclaredField;
19
20 import java.beans.BeanInfo;
21 import java.beans.IntrospectionException;
22 import java.beans.Introspector;
23 import java.beans.PropertyDescriptor;
24 import java.lang.annotation.Annotation;
25 import java.lang.annotation.Inherited;
26 import java.lang.reflect.Array;
27 import java.lang.reflect.Field;
28 import java.lang.reflect.GenericArrayType;
29 import java.lang.reflect.InvocationTargetException;
30 import java.lang.reflect.Method;
31 import java.lang.reflect.Modifier;
32 import java.lang.reflect.ParameterizedType;
33 import java.lang.reflect.Proxy;
34 import java.lang.reflect.Type;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.LinkedHashMap;
39 import java.util.LinkedHashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Set;
43 import java.util.concurrent.ConcurrentHashMap;
44
45 import org.seasar.cubby.spi.BeanDescProvider;
46 import org.seasar.cubby.spi.beans.Attribute;
47 import org.seasar.cubby.spi.beans.AttributeNotFoundException;
48 import org.seasar.cubby.spi.beans.BeanDesc;
49 import org.seasar.cubby.spi.beans.IllegalAttributeException;
50 import org.seasar.cubby.spi.beans.ParameterizedClassDesc;
51
52
53
54
55
56
57
58
59
60 public class DefaultBeanDescProvider implements BeanDescProvider {
61
62
63 private static final Map<Class<?>, Object> PRIMITIVE_TYPE_DEFAULT_VALUES;
64 static {
65 final Map<Class<?>, Object> map = new HashMap<Class<?>, Object>();
66 map.put(boolean.class, Boolean.FALSE);
67 map.put(char.class, Character.valueOf('\u0000'));
68 map.put(byte.class, Byte.valueOf((byte) 0));
69 map.put(short.class, Short.valueOf((short) 0));
70 map.put(int.class, Integer.valueOf(0));
71 map.put(long.class, Long.valueOf(0L));
72 map.put(float.class, Float.valueOf(0F));
73 map.put(double.class, Double.valueOf(0D));
74 PRIMITIVE_TYPE_DEFAULT_VALUES = Collections.unmodifiableMap(map);
75 }
76
77
78 protected final Map<Class<?>, BeanDesc> beanDescCache = new ConcurrentHashMap<Class<?>, BeanDesc>(
79 1024);
80
81
82
83
84 public BeanDesc getBeanDesc(final Class<?> clazz) {
85 if (beanDescCache.containsKey(clazz)) {
86 return beanDescCache.get(clazz);
87 }
88
89 synchronized (clazz) {
90 if (beanDescCache.containsKey(clazz)) {
91 return beanDescCache.get(clazz);
92 }
93
94 final BeanDesc beanDesc = createBeanDesc(clazz);
95 beanDescCache.put(clazz, beanDesc);
96 return beanDesc;
97 }
98 }
99
100
101
102
103
104
105
106
107 protected BeanDesc createBeanDesc(final Class<?> clazz) {
108 return new BeanDescImpl(clazz);
109 }
110
111
112
113
114
115
116 protected static class BeanDescImpl implements BeanDesc {
117
118
119 private final Class<?> clazz;
120
121
122 private final Map<String, Attribute> propertyAttributeMap;
123
124
125 private final Map<String, List<Attribute>> fieldAttributesMap;
126
127
128
129
130
131
132
133 public BeanDescImpl(final Class<?> clazz) {
134 this.clazz = clazz;
135 this.propertyAttributeMap = collectPropertyAttributeMap(clazz);
136 this.fieldAttributesMap = collectFieldAttributesMap(clazz);
137 }
138
139
140
141
142
143
144
145
146 protected Map<String, Attribute> collectPropertyAttributeMap(
147 final Class<?> clazz) {
148 final Map<String, Attribute> propertyAttributes = new LinkedHashMap<String, Attribute>();
149 final BeanInfo beanInfo;
150 try {
151 beanInfo = Introspector.getBeanInfo(clazz);
152 } catch (final IntrospectionException e) {
153 throw new IllegalStateException(e);
154 }
155 for (final PropertyDescriptor propertyDescriptor : beanInfo
156 .getPropertyDescriptors()) {
157 final String propertyName = propertyDescriptor.getName();
158 final Attribute propertyDesc = new PropertyAttribute(clazz,
159 propertyDescriptor);
160 propertyAttributes.put(propertyName, propertyDesc);
161 }
162 return propertyAttributes;
163 }
164
165
166
167
168
169
170
171
172 protected Map<String, List<Attribute>> collectFieldAttributesMap(
173 final Class<?> clazz) {
174 final Map<String, List<Attribute>> fieldAttributes = new LinkedHashMap<String, List<Attribute>>();
175 for (final Field field : findAllDeclaredField(clazz)) {
176 final String fieldName = field.getName();
177 List<Attribute> fieldDescs;
178 if (!fieldAttributes.containsKey(fieldName)) {
179 fieldDescs = new ArrayList<Attribute>();
180 fieldAttributes.put(fieldName, fieldDescs);
181 } else {
182 fieldDescs = fieldAttributes.get(fieldName);
183 }
184 final Attribute attributes = new FieldAttribute(clazz, field);
185 fieldDescs.add(attributes);
186 }
187 return fieldAttributes;
188 }
189
190
191
192
193 public boolean hasPropertyAttribute(final String name) {
194 return propertyAttributeMap.containsKey(name);
195 }
196
197
198
199
200 public Attribute getPropertyAttribute(final String name)
201 throws AttributeNotFoundException {
202 if (!propertyAttributeMap.containsKey(name)) {
203 throw new AttributeNotFoundException(clazz, name);
204 }
205 return propertyAttributeMap.get(name);
206 }
207
208
209
210
211 public Set<Attribute> findtPropertyAttributes() {
212 final Set<Attribute> attributes = new LinkedHashSet<Attribute>();
213 attributes.addAll(propertyAttributeMap.values());
214 return Collections.unmodifiableSet(attributes);
215 }
216
217
218
219
220 public Attribute getFieldAttribute(final String fieldName) {
221 if (!fieldAttributesMap.containsKey(fieldName)) {
222 throw new AttributeNotFoundException(clazz, fieldName);
223 }
224 return fieldAttributesMap.get(fieldName).get(0);
225 }
226
227
228
229
230 public boolean hasFieldAttribute(final String fieldName) {
231 return fieldAttributesMap.containsKey(fieldName);
232 }
233
234
235
236
237 public Set<Attribute> findFieldAttributes() {
238 final Set<Attribute> fieldAttributes = new LinkedHashSet<Attribute>();
239 for (final List<Attribute> attributes : fieldAttributesMap.values()) {
240 fieldAttributes.addAll(attributes);
241 }
242 return Collections.unmodifiableSet(fieldAttributes);
243 }
244
245
246
247
248 public Set<Attribute> findAllAttributes() {
249 final Set<Attribute> attributes = new LinkedHashSet<Attribute>();
250 attributes.addAll(this.findtPropertyAttributes());
251 attributes.addAll(this.findFieldAttributes());
252 return Collections.unmodifiableSet(attributes);
253 }
254
255
256
257
258 public Set<Attribute> findAttributesAnnotatedWith(
259 final Class<? extends Annotation> annotationClass) {
260 final Set<Attribute> attributes = new LinkedHashSet<Attribute>();
261 for (final Attribute attribute : findAllAttributes()) {
262 if (attribute.isAnnotationPresent(annotationClass)) {
263 attributes.add(attribute);
264 }
265 }
266 return Collections.unmodifiableSet(attributes);
267 }
268
269 }
270
271
272
273
274
275
276 protected static class PropertyAttribute implements Attribute {
277
278
279 private final Class<?> clazz;
280
281
282 private final PropertyDescriptor propertyDescriptor;
283
284
285 private final ParameterizedClassDesc parameterizedClassDesc;
286
287
288 private final Map<Class<? extends Annotation>, Annotation> annotationCache = new HashMap<Class<? extends Annotation>, Annotation>();
289
290
291
292
293
294
295
296
297
298 PropertyAttribute(final Class<?> clazz,
299 final PropertyDescriptor propertyDescriptor) {
300 this.clazz = clazz;
301 this.propertyDescriptor = propertyDescriptor;
302
303 if (propertyDescriptor.getReadMethod() != null) {
304 this.parameterizedClassDesc = createParameterizedClassDesc(propertyDescriptor
305 .getReadMethod().getGenericReturnType());
306 } else if (propertyDescriptor.getWriteMethod() != null) {
307 this.parameterizedClassDesc = createParameterizedClassDesc(propertyDescriptor
308 .getWriteMethod().getParameterTypes()[0]);
309 } else {
310 this.parameterizedClassDesc = null;
311 }
312 }
313
314
315
316
317 public String getName() {
318 return propertyDescriptor.getName();
319 }
320
321
322
323
324 public Class<?> getType() {
325 return propertyDescriptor.getPropertyType();
326 }
327
328
329
330
331 public boolean isReadable() {
332 return propertyDescriptor.getReadMethod() != null;
333 }
334
335
336
337
338 public boolean isWritable() {
339 return propertyDescriptor.getWriteMethod() != null;
340 }
341
342
343
344
345 public Object getValue(final Object target)
346 throws IllegalAttributeException {
347 final Method method = propertyDescriptor.getReadMethod();
348 if (method == null) {
349 throw new IllegalAttributeException(clazz, propertyDescriptor
350 .getName(), new IllegalStateException(
351 propertyDescriptor.getName() + " is not readable."));
352 }
353 try {
354 return method.invoke(target);
355 } catch (final IllegalAccessException e) {
356 throw new IllegalAttributeException(clazz, propertyDescriptor
357 .getName(), e);
358 } catch (final InvocationTargetException e) {
359 final Throwable t = e.getTargetException();
360 if (t instanceof Error) {
361 throw (Error) t;
362 }
363 throw new IllegalAttributeException(clazz, propertyDescriptor
364 .getName(), e);
365 }
366 }
367
368
369
370
371 public void setValue(final Object target, final Object value)
372 throws IllegalAttributeException {
373 final Method method = propertyDescriptor.getWriteMethod();
374 if (method == null) {
375 throw new IllegalAttributeException(clazz, propertyDescriptor
376 .getName(), new IllegalStateException(
377 propertyDescriptor.getName() + " is not writable."));
378 }
379 try {
380 final Class<?> propertyType = propertyDescriptor
381 .getPropertyType();
382 if (value == null && propertyType.isPrimitive()) {
383 method.invoke(target, PRIMITIVE_TYPE_DEFAULT_VALUES
384 .get(propertyType));
385 } else {
386 method.invoke(target, value);
387 }
388 } catch (final IllegalArgumentException e) {
389 throw new IllegalAttributeException(clazz, propertyDescriptor
390 .getName(), e);
391 } catch (final IllegalAccessException e) {
392 throw new IllegalAttributeException(clazz, propertyDescriptor
393 .getName(), e);
394 } catch (final InvocationTargetException e) {
395 final Throwable t = e.getTargetException();
396 if (t instanceof Error) {
397 throw (Error) t;
398 }
399 throw new IllegalAttributeException(clazz, propertyDescriptor
400 .getName(), e);
401 }
402 }
403
404
405
406
407 public ParameterizedClassDesc getParameterizedClassDesc() {
408 return parameterizedClassDesc;
409 }
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433 public <T extends Annotation> T getAnnotation(
434 final Class<T> annotationClass) {
435 if (annotationCache.containsKey(annotationClass)) {
436 return annotationClass.cast(annotationCache
437 .get(annotationClass));
438 }
439
440 final Method readMethod = propertyDescriptor.getReadMethod();
441 if (readMethod != null) {
442 final T annotation = findAnnotation(annotationClass, readMethod);
443 if (annotation != null) {
444 annotationCache.put(annotationClass, annotation);
445 return annotation;
446 }
447 }
448
449 final Method writeMethod = propertyDescriptor.getWriteMethod();
450 if (writeMethod != null) {
451 final T annotation = findAnnotation(annotationClass,
452 writeMethod);
453 if (annotation != null) {
454 annotationCache.put(annotationClass, annotation);
455 return annotation;
456 }
457 }
458
459 annotationCache.put(annotationClass, null);
460 return null;
461 }
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478 private static <T extends Annotation> T findAnnotation(
479 final Class<T> annotationClass, final Method method) {
480 final String methodName = method.getName();
481 final Class<?>[] parameterTypes = method.getParameterTypes();
482 for (Class<?> target = method.getDeclaringClass(); !target
483 .equals(Object.class); target = target.getSuperclass()) {
484 final T annotation = getAnnotation(annotationClass, target,
485 methodName, parameterTypes);
486 if (annotation != null) {
487 return annotation;
488 }
489 final T annotationOfInterfaces = getAnnotationOfInterfaces(
490 annotationClass, target, methodName, parameterTypes);
491 if (annotationOfInterfaces != null) {
492 return annotationOfInterfaces;
493 }
494 }
495 return null;
496 }
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514 private static <T extends Annotation> T getAnnotationOfInterfaces(
515 final Class<T> annotationClass, final Class<?> clazz,
516 final String methodName, final Class<?>[] parameterTypes) {
517 for (final Class<?> interfaceClass : clazz.getInterfaces()) {
518 final T annotation = getAnnotation(annotationClass,
519 interfaceClass, methodName, parameterTypes);
520 if (annotation != null) {
521 return annotation;
522 }
523 }
524 return null;
525 }
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543 private static <T extends Annotation> T getAnnotation(
544 final Class<T> annotationClass, final Class<?> clazz,
545 final String methodName,
546 @SuppressWarnings("unchecked") final Class[] parameterTypes) {
547 try {
548 final Method method = clazz.getDeclaredMethod(methodName,
549 parameterTypes);
550 if (method.isAnnotationPresent(annotationClass)) {
551 return method.getAnnotation(annotationClass);
552 }
553 } catch (final NoSuchMethodException e) {
554
555 }
556
557 return null;
558 }
559
560
561
562
563 public boolean isAnnotationPresent(
564 final Class<? extends Annotation> annotationClass) {
565 return this.getAnnotation(annotationClass) != null;
566 }
567
568
569
570
571 @Override
572 public int hashCode() {
573 final int prime = 31;
574 int result = 1;
575 result = prime
576 * result
577 + ((propertyDescriptor == null) ? 0 : propertyDescriptor
578 .hashCode());
579 return result;
580 }
581
582
583
584
585 @Override
586 public boolean equals(final Object obj) {
587 if (this == obj) {
588 return true;
589 }
590 if (obj == null) {
591 return false;
592 }
593 if (getClass() != obj.getClass()) {
594 return false;
595 }
596 final PropertyAttribute other = (PropertyAttribute) obj;
597 if (propertyDescriptor == null) {
598 if (other.propertyDescriptor != null) {
599 return false;
600 }
601 } else if (!propertyDescriptor.equals(other.propertyDescriptor)) {
602 return false;
603 }
604 return true;
605 }
606
607 }
608
609
610
611
612
613
614 protected static class FieldAttribute implements Attribute {
615
616
617 private final Class<?> clazz;
618
619
620 private final Field field;
621
622
623 private final boolean writable;
624
625
626 private final ParameterizedClassDesc parameterizedClassDesc;
627
628
629
630
631
632
633
634
635
636 public FieldAttribute(final Class<?> clazz, final Field field) {
637 this.clazz = clazz;
638 this.field = field;
639 this.writable = (field.getModifiers() & Modifier.FINAL) == 0;
640 this.parameterizedClassDesc = createParameterizedClassDesc(field
641 .getGenericType());
642 }
643
644
645
646
647 public String getName() {
648 return field.getName();
649 }
650
651
652
653
654 public Class<?> getType() {
655 return field.getType();
656 }
657
658
659
660
661 public Object getValue(final Object target) {
662 try {
663 if (this.isReadable() && !field.isAccessible()) {
664 field.setAccessible(true);
665 final Object value = field.get(target);
666 field.setAccessible(false);
667 return value;
668 } else {
669 final Object value = field.get(target);
670 return value;
671 }
672 } catch (final IllegalAccessException e) {
673 throw new IllegalAttributeException(clazz, field.getName(), e);
674 }
675 }
676
677
678
679
680 public void setValue(final Object target, final Object value) {
681 try {
682 if (this.isWritable() && !field.isAccessible()) {
683 field.setAccessible(true);
684 field.set(target, value);
685 field.setAccessible(false);
686 } else {
687 field.set(target, value);
688 }
689 } catch (final IllegalAccessException e) {
690 throw new IllegalAttributeException(clazz, field.getName(), e);
691 }
692 }
693
694
695
696
697 public boolean isReadable() {
698 return true;
699 }
700
701
702
703
704 public boolean isWritable() {
705 return writable;
706 }
707
708
709
710
711 public ParameterizedClassDesc getParameterizedClassDesc() {
712 return parameterizedClassDesc;
713 }
714
715
716
717
718 public <T extends Annotation> T getAnnotation(
719 final Class<T> annotationClass) {
720 return field.getAnnotation(annotationClass);
721 }
722
723
724
725
726 public boolean isAnnotationPresent(
727 final Class<? extends Annotation> annotationClass) {
728 return field.isAnnotationPresent(annotationClass);
729 }
730
731
732
733
734 @Override
735 public int hashCode() {
736 final int prime = 31;
737 int result = 1;
738 result = prime * result + ((field == null) ? 0 : field.hashCode());
739 return result;
740 }
741
742
743
744
745 @Override
746 public boolean equals(final Object obj) {
747 if (this == obj) {
748 return true;
749 }
750 if (obj == null) {
751 return false;
752 }
753 if (getClass() != obj.getClass()) {
754 return false;
755 }
756 final FieldAttribute other = (FieldAttribute) obj;
757 if (field == null) {
758 if (other.field != null) {
759 return false;
760 }
761 } else if (!field.equals(other.field)) {
762 return false;
763 }
764 return true;
765 }
766
767 }
768
769
770
771
772
773
774 protected static class ParameterizedClassDescImpl implements
775 ParameterizedClassDesc {
776
777
778 protected Class<?> rawClass;
779
780
781 protected ParameterizedClassDesc[] arguments;
782
783
784
785
786 public ParameterizedClassDescImpl() {
787 }
788
789
790
791
792
793
794
795 public ParameterizedClassDescImpl(final Class<?> rawClass) {
796 this.rawClass = rawClass;
797 }
798
799
800
801
802
803
804
805
806
807 public ParameterizedClassDescImpl(final Class<?> rawClass,
808 final ParameterizedClassDesc[] arguments) {
809 this.rawClass = rawClass;
810 this.arguments = arguments;
811 }
812
813
814
815
816 public boolean isParameterizedClass() {
817 return arguments != null;
818 }
819
820
821
822
823 public Class<?> getRawClass() {
824 return rawClass;
825 }
826
827
828
829
830 public ParameterizedClassDesc[] getArguments() {
831 return arguments;
832 }
833
834 }
835
836
837
838
839
840
841
842
843 protected static ParameterizedClassDesc createParameterizedClassDesc(
844 final Type type) {
845 final Class<?> rowClass = getRawClass(type);
846 if (rowClass == null) {
847 return null;
848 }
849 final Type[] parameterTypes = getGenericParameter(type);
850 if (parameterTypes == null) {
851 final ParameterizedClassDescImpl desc = new ParameterizedClassDescImpl(
852 rowClass);
853 return desc;
854 } else {
855 final ParameterizedClassDesc[] parameterDescs = new ParameterizedClassDesc[parameterTypes.length];
856 for (int i = 0; i < parameterTypes.length; ++i) {
857 parameterDescs[i] = createParameterizedClassDesc(parameterTypes[i]);
858 }
859 final ParameterizedClassDescImpl desc = new ParameterizedClassDescImpl(
860 rowClass, parameterDescs);
861 return desc;
862 }
863 }
864
865
866
867
868
869
870
871
872
873
874
875 protected static Class<?> getRawClass(final Type type) {
876 if (Class.class.isInstance(type)) {
877 return Class.class.cast(type);
878 }
879 if (ParameterizedType.class.isInstance(type)) {
880 final ParameterizedType parameterizedType = ParameterizedType.class
881 .cast(type);
882 return getRawClass(parameterizedType.getRawType());
883 }
884 if (GenericArrayType.class.isInstance(type)) {
885 final GenericArrayType genericArrayType = GenericArrayType.class
886 .cast(type);
887 final Class<?> rawClass = getRawClass(genericArrayType
888 .getGenericComponentType());
889 return Array.newInstance(rawClass, 0).getClass();
890 }
891 return null;
892 }
893
894
895
896
897
898
899
900
901
902
903
904 protected static Type[] getGenericParameter(final Type type) {
905 if (ParameterizedType.class.isInstance(type)) {
906 return ParameterizedType.class.cast(type).getActualTypeArguments();
907 }
908 if (GenericArrayType.class.isInstance(type)) {
909 return getGenericParameter(GenericArrayType.class.cast(type)
910 .getGenericComponentType());
911 }
912 return null;
913 }
914
915 }