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