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 java.beans.BeanInfo;
19 import java.beans.IntrospectionException;
20 import java.beans.Introspector;
21 import java.beans.PropertyDescriptor;
22 import java.lang.annotation.Annotation;
23 import java.lang.reflect.Array;
24 import java.lang.reflect.GenericArrayType;
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Method;
27 import java.lang.reflect.ParameterizedType;
28 import java.lang.reflect.Type;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.LinkedHashMap;
32 import java.util.Map;
33 import java.util.concurrent.ConcurrentHashMap;
34
35 import org.seasar.cubby.spi.BeanDescProvider;
36 import org.seasar.cubby.spi.beans.BeanDesc;
37 import org.seasar.cubby.spi.beans.IllegalPropertyException;
38 import org.seasar.cubby.spi.beans.ParameterizedClassDesc;
39 import org.seasar.cubby.spi.beans.PropertyDesc;
40 import org.seasar.cubby.spi.beans.PropertyNotFoundException;
41
42
43
44
45
46
47
48
49
50
51 public class DefaultBeanDescProvider implements BeanDescProvider {
52
53
54 private static final Map<Class<?>, Object> PRIMITIVE_TYPE_DEFAULT_VALUES;
55 static {
56 final Map<Class<?>, Object> map = new HashMap<Class<?>, Object>();
57 map.put(boolean.class, Boolean.FALSE);
58 map.put(char.class, Character.valueOf('\u0000'));
59 map.put(byte.class, Byte.valueOf((byte) 0));
60 map.put(short.class, Short.valueOf((short) 0));
61 map.put(int.class, Integer.valueOf(0));
62 map.put(long.class, Long.valueOf(0L));
63 map.put(float.class, Float.valueOf(0F));
64 map.put(double.class, Double.valueOf(0D));
65 PRIMITIVE_TYPE_DEFAULT_VALUES = Collections.unmodifiableMap(map);
66 }
67
68
69 private final Map<Class<?>, BeanDesc> beanDescCache = new ConcurrentHashMap<Class<?>, BeanDesc>(
70 1024);
71
72
73
74
75 public BeanDesc getBeanDesc(final Class<?> clazz) {
76 if (beanDescCache.containsKey(clazz)) {
77 return beanDescCache.get(clazz);
78 }
79
80 synchronized (clazz) {
81 if (beanDescCache.containsKey(clazz)) {
82 return beanDescCache.get(clazz);
83 }
84
85 try {
86 final BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
87 final BeanDesc beanDesc = new BeanDescImpl(clazz, beanInfo);
88 beanDescCache.put(clazz, beanDesc);
89 return beanDesc;
90 } catch (final IntrospectionException e) {
91 throw new IllegalStateException(e);
92 }
93 }
94 }
95
96
97
98
99
100
101
102
103
104
105 private static class BeanDescImpl implements BeanDesc {
106
107
108 private final Class<?> clazz;
109
110
111 private final Map<String, PropertyDesc> propertyDescMap = new LinkedHashMap<String, PropertyDesc>();
112
113
114
115
116
117
118
119
120
121 public BeanDescImpl(final Class<?> clazz, final BeanInfo beanInfo) {
122 this.clazz = clazz;
123 for (final PropertyDescriptor propertyDescriptor : beanInfo
124 .getPropertyDescriptors()) {
125 propertyDescMap.put(propertyDescriptor.getName(),
126 new PropertyDescImpl(clazz, propertyDescriptor));
127 }
128 }
129
130
131
132
133 public boolean hasPropertyDesc(final String propertyName) {
134 return propertyDescMap.containsKey(propertyName);
135 }
136
137
138
139
140 public PropertyDesc getPropertyDesc(final String propertyName)
141 throws PropertyNotFoundException {
142 if (!propertyDescMap.containsKey(propertyName)) {
143 throw new PropertyNotFoundException(clazz, propertyName);
144 }
145 return propertyDescMap.get(propertyName);
146 }
147
148
149
150
151 public PropertyDesc[] getPropertyDescs() {
152 return propertyDescMap.values().toArray(new PropertyDesc[0]);
153 }
154
155 }
156
157
158
159
160
161
162
163
164
165
166 private static class PropertyDescImpl implements PropertyDesc {
167
168
169 private final Class<?> clazz;
170
171
172 private final PropertyDescriptor propertyDescriptor;
173
174
175 private final ParameterizedClassDesc parameterizedClassDesc;
176
177
178 private final Map<Class<? extends Annotation>, Annotation> annotationCache = new HashMap<Class<? extends Annotation>, Annotation>();
179
180
181
182
183
184
185
186
187
188 PropertyDescImpl(final Class<?> clazz,
189 final PropertyDescriptor propertyDescriptor) {
190 this.clazz = clazz;
191 this.propertyDescriptor = propertyDescriptor;
192
193 if (propertyDescriptor.getReadMethod() != null) {
194 parameterizedClassDesc = createParameterizedClassDesc(propertyDescriptor
195 .getReadMethod().getGenericReturnType());
196 } else if (propertyDescriptor.getWriteMethod() != null) {
197 parameterizedClassDesc = createParameterizedClassDesc(propertyDescriptor
198 .getWriteMethod().getParameterTypes()[0]);
199 } else {
200 parameterizedClassDesc = null;
201 }
202 }
203
204
205
206
207 public String getPropertyName() {
208 return propertyDescriptor.getName();
209 }
210
211
212
213
214 public Class<?> getPropertyType() {
215 return propertyDescriptor.getPropertyType();
216 }
217
218
219
220
221 public Method getReadMethod() {
222 return propertyDescriptor.getReadMethod();
223 }
224
225
226
227
228 public boolean hasReadMethod() {
229 return this.getReadMethod() != null;
230 }
231
232
233
234
235 public Method getWriteMethod() {
236 return propertyDescriptor.getWriteMethod();
237 }
238
239
240
241
242 public boolean hasWriteMethod() {
243 return this.getWriteMethod() != null;
244 }
245
246
247
248
249 public boolean isReadable() {
250 return propertyDescriptor.getReadMethod() != null;
251 }
252
253
254
255
256 public boolean isWritable() {
257 return propertyDescriptor.getWriteMethod() != null;
258 }
259
260
261
262
263 public Object getValue(final Object target)
264 throws IllegalPropertyException {
265 final Method method = this.getReadMethod();
266 if (method == null) {
267 throw new IllegalPropertyException(clazz, propertyDescriptor
268 .getName(), new IllegalStateException(
269 propertyDescriptor.getName() + " is not readable."));
270 }
271 try {
272 return method.invoke(target);
273 } catch (final IllegalAccessException e) {
274 throw new IllegalPropertyException(clazz, propertyDescriptor
275 .getName(), e);
276 } catch (final InvocationTargetException e) {
277 final Throwable t = e.getTargetException();
278 if (t instanceof Error) {
279 throw (Error) t;
280 }
281 throw new IllegalPropertyException(clazz, propertyDescriptor
282 .getName(), e);
283 }
284 }
285
286
287
288
289 public void setValue(final Object target, final Object value)
290 throws IllegalPropertyException {
291 final Method method = this.getWriteMethod();
292 if (method == null) {
293 throw new IllegalPropertyException(clazz, propertyDescriptor
294 .getName(), new IllegalStateException(
295 propertyDescriptor.getName() + " is not writable."));
296 }
297 try {
298 final Class<?> propertyType = propertyDescriptor
299 .getPropertyType();
300 if (value == null && propertyType.isPrimitive()) {
301 method.invoke(target, PRIMITIVE_TYPE_DEFAULT_VALUES
302 .get(propertyType));
303 } else {
304 method.invoke(target, value);
305 }
306 } catch (final IllegalArgumentException e) {
307 throw new IllegalPropertyException(clazz, propertyDescriptor
308 .getName(), e);
309 } catch (final IllegalAccessException e) {
310 throw new IllegalPropertyException(clazz, propertyDescriptor
311 .getName(), e);
312 } catch (final InvocationTargetException e) {
313 final Throwable t = e.getTargetException();
314 if (t instanceof Error) {
315 throw (Error) t;
316 }
317 throw new IllegalPropertyException(clazz, propertyDescriptor
318 .getName(), e);
319 }
320 }
321
322
323
324
325 public boolean isParameterized() {
326 return parameterizedClassDesc != null
327 && parameterizedClassDesc.isParameterizedClass();
328 }
329
330
331
332
333 public ParameterizedClassDesc getParameterizedClassDesc() {
334 return parameterizedClassDesc;
335 }
336
337
338
339
340 public <T extends Annotation> T getAnnotation(
341 final Class<T> annotationClass) {
342 if (annotationCache.containsKey(annotationClass)) {
343 return annotationClass.cast(annotationCache
344 .get(annotationClass));
345 }
346
347 final Method readMethod = this.getReadMethod();
348 if (readMethod != null) {
349 final T annotation = findAnnotation(annotationClass, readMethod);
350 if (annotation != null) {
351 annotationCache.put(annotationClass, annotation);
352 return annotation;
353 }
354 }
355
356 final Method writeMethod = this.getWriteMethod();
357 if (writeMethod != null) {
358 final T annotation = findAnnotation(annotationClass,
359 writeMethod);
360 if (annotation != null) {
361 annotationCache.put(annotationClass, annotation);
362 return annotation;
363 }
364 }
365
366 annotationCache.put(annotationClass, null);
367 return null;
368 }
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385 private static <T extends Annotation> T findAnnotation(
386 final Class<T> annotationClass, final Method method) {
387 final String methodName = method.getName();
388 final Class<?>[] parameterTypes = method.getParameterTypes();
389 for (Class<?> target = method.getDeclaringClass(); !target
390 .equals(Object.class); target = target.getSuperclass()) {
391 final T annotation = getAnnotation(annotationClass, target,
392 methodName, parameterTypes);
393 if (annotation != null) {
394 return annotation;
395 }
396 final T annotationOfInterfaces = getAnnotationOfInterfaces(
397 annotationClass, target, methodName, parameterTypes);
398 if (annotationOfInterfaces != null) {
399 return annotationOfInterfaces;
400 }
401 }
402 return null;
403 }
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421 private static <T extends Annotation> T getAnnotationOfInterfaces(
422 final Class<T> annotationClass, final Class<?> clazz,
423 final String methodName, final Class<?>[] parameterTypes) {
424 for (final Class<?> interfaceClass : clazz.getInterfaces()) {
425 final T annotation = getAnnotation(annotationClass,
426 interfaceClass, methodName, parameterTypes);
427 if (annotation != null) {
428 return annotation;
429 }
430 }
431 return null;
432 }
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450 private static <T extends Annotation> T getAnnotation(
451 final Class<T> annotationClass, final Class<?> clazz,
452 final String methodName,
453 @SuppressWarnings("unchecked") final Class[] parameterTypes) {
454 try {
455 final Method method = clazz.getDeclaredMethod(methodName,
456 parameterTypes);
457 if (method.isAnnotationPresent(annotationClass)) {
458 return method.getAnnotation(annotationClass);
459 }
460 } catch (final NoSuchMethodException e) {
461
462 }
463
464 return null;
465 }
466
467
468
469
470 public boolean isAnnotationPresent(
471 final Class<? extends Annotation> annotationClass) {
472 return this.getAnnotation(annotationClass) != null;
473 }
474
475 }
476
477
478
479
480
481
482
483 private static class ParameterizedClassDescImpl implements
484 ParameterizedClassDesc {
485
486
487 protected Class<?> rawClass;
488
489
490 protected ParameterizedClassDesc[] arguments;
491
492
493
494
495 public ParameterizedClassDescImpl() {
496 }
497
498
499
500
501
502
503
504 public ParameterizedClassDescImpl(final Class<?> rawClass) {
505 this.rawClass = rawClass;
506 }
507
508
509
510
511
512
513
514
515
516 public ParameterizedClassDescImpl(final Class<?> rawClass,
517 final ParameterizedClassDesc[] arguments) {
518 this.rawClass = rawClass;
519 this.arguments = arguments;
520 }
521
522
523
524
525 public boolean isParameterizedClass() {
526 return arguments != null;
527 }
528
529
530
531
532 public Class<?> getRawClass() {
533 return rawClass;
534 }
535
536
537
538
539 public ParameterizedClassDesc[] getArguments() {
540 return arguments;
541 }
542
543 }
544
545
546
547
548
549
550
551
552 private static ParameterizedClassDesc createParameterizedClassDesc(
553 final Type type) {
554 final Class<?> rowClass = getRawClass(type);
555 if (rowClass == null) {
556 return null;
557 }
558 final Type[] parameterTypes = getGenericParameter(type);
559 if (parameterTypes == null) {
560 final ParameterizedClassDescImpl desc = new ParameterizedClassDescImpl(
561 rowClass);
562 return desc;
563 } else {
564 final ParameterizedClassDesc[] parameterDescs = new ParameterizedClassDesc[parameterTypes.length];
565 for (int i = 0; i < parameterTypes.length; ++i) {
566 parameterDescs[i] = createParameterizedClassDesc(parameterTypes[i]);
567 }
568 final ParameterizedClassDescImpl desc = new ParameterizedClassDescImpl(
569 rowClass, parameterDescs);
570 return desc;
571 }
572 }
573
574
575
576
577
578
579
580
581
582
583
584 private static Class<?> getRawClass(final Type type) {
585 if (Class.class.isInstance(type)) {
586 return Class.class.cast(type);
587 }
588 if (ParameterizedType.class.isInstance(type)) {
589 final ParameterizedType parameterizedType = ParameterizedType.class
590 .cast(type);
591 return getRawClass(parameterizedType.getRawType());
592 }
593 if (GenericArrayType.class.isInstance(type)) {
594 final GenericArrayType genericArrayType = GenericArrayType.class
595 .cast(type);
596 final Class<?> rawClass = getRawClass(genericArrayType
597 .getGenericComponentType());
598 return Array.newInstance(rawClass, 0).getClass();
599 }
600 return null;
601 }
602
603
604
605
606
607
608
609
610
611
612
613 private static Type[] getGenericParameter(final Type type) {
614 if (ParameterizedType.class.isInstance(type)) {
615 return ParameterizedType.class.cast(type).getActualTypeArguments();
616 }
617 if (GenericArrayType.class.isInstance(type)) {
618 return getGenericParameter(GenericArrayType.class.cast(type)
619 .getGenericComponentType());
620 }
621 return null;
622 }
623
624 }