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 | 383 | public class DefaultBeanDescProvider implements BeanDescProvider { |
52 | |
|
53 | |
|
54 | |
private static final Map<Class<?>, Object> PRIMITIVE_TYPE_DEFAULT_VALUES; |
55 | |
static { |
56 | 1 | final Map<Class<?>, Object> map = new HashMap<Class<?>, Object>(); |
57 | 1 | map.put(boolean.class, Boolean.FALSE); |
58 | 1 | map.put(char.class, Character.valueOf('\u0000')); |
59 | 1 | map.put(byte.class, Byte.valueOf((byte) 0)); |
60 | 1 | map.put(short.class, Short.valueOf((short) 0)); |
61 | 1 | map.put(int.class, Integer.valueOf(0)); |
62 | 1 | map.put(long.class, Long.valueOf(0L)); |
63 | 1 | map.put(float.class, Float.valueOf(0F)); |
64 | 1 | map.put(double.class, Double.valueOf(0D)); |
65 | 1 | PRIMITIVE_TYPE_DEFAULT_VALUES = Collections.unmodifiableMap(map); |
66 | 1 | } |
67 | |
|
68 | |
|
69 | 86 | private final Map<Class<?>, BeanDesc> beanDescCache = new ConcurrentHashMap<Class<?>, BeanDesc>( |
70 | |
1024); |
71 | |
|
72 | |
|
73 | |
|
74 | |
|
75 | |
public BeanDesc getBeanDesc(final Class<?> clazz) { |
76 | 78 | if (beanDescCache.containsKey(clazz)) { |
77 | 43 | return beanDescCache.get(clazz); |
78 | |
} |
79 | |
|
80 | 35 | synchronized (clazz) { |
81 | 35 | if (beanDescCache.containsKey(clazz)) { |
82 | 0 | return beanDescCache.get(clazz); |
83 | |
} |
84 | |
|
85 | |
try { |
86 | 35 | final BeanInfo beanInfo = Introspector.getBeanInfo(clazz); |
87 | 35 | final BeanDesc beanDesc = new BeanDescImpl(clazz, beanInfo); |
88 | 35 | beanDescCache.put(clazz, beanDesc); |
89 | 35 | return beanDesc; |
90 | 0 | } catch (final IntrospectionException e) { |
91 | 0 | throw new IllegalStateException(e); |
92 | |
} |
93 | 0 | } |
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 | 35 | private final Map<String, PropertyDesc> propertyDescMap = new LinkedHashMap<String, PropertyDesc>(); |
112 | |
|
113 | |
|
114 | |
|
115 | |
|
116 | |
|
117 | |
|
118 | |
|
119 | |
|
120 | |
|
121 | 35 | public BeanDescImpl(final Class<?> clazz, final BeanInfo beanInfo) { |
122 | 35 | this.clazz = clazz; |
123 | 325 | for (final PropertyDescriptor propertyDescriptor : beanInfo |
124 | |
.getPropertyDescriptors()) { |
125 | 290 | propertyDescMap.put(propertyDescriptor.getName(), |
126 | |
new PropertyDescImpl(clazz, propertyDescriptor)); |
127 | |
} |
128 | 35 | } |
129 | |
|
130 | |
|
131 | |
|
132 | |
|
133 | |
public boolean hasPropertyDesc(final String propertyName) { |
134 | 8 | return propertyDescMap.containsKey(propertyName); |
135 | |
} |
136 | |
|
137 | |
|
138 | |
|
139 | |
|
140 | |
public PropertyDesc getPropertyDesc(final String propertyName) |
141 | |
throws PropertyNotFoundException { |
142 | 48 | if (!propertyDescMap.containsKey(propertyName)) { |
143 | 2 | throw new PropertyNotFoundException(clazz, propertyName); |
144 | |
} |
145 | 46 | return propertyDescMap.get(propertyName); |
146 | |
} |
147 | |
|
148 | |
|
149 | |
|
150 | |
|
151 | |
public PropertyDesc[] getPropertyDescs() { |
152 | 30 | 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 | 290 | 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 | 290 | final PropertyDescriptor propertyDescriptor) { |
190 | 290 | this.clazz = clazz; |
191 | 290 | this.propertyDescriptor = propertyDescriptor; |
192 | |
|
193 | 290 | if (propertyDescriptor.getReadMethod() != null) { |
194 | 273 | parameterizedClassDesc = createParameterizedClassDesc(propertyDescriptor |
195 | |
.getReadMethod().getGenericReturnType()); |
196 | 17 | } else if (propertyDescriptor.getWriteMethod() != null) { |
197 | 17 | parameterizedClassDesc = createParameterizedClassDesc(propertyDescriptor |
198 | |
.getWriteMethod().getParameterTypes()[0]); |
199 | |
} else { |
200 | 0 | parameterizedClassDesc = null; |
201 | |
} |
202 | 290 | } |
203 | |
|
204 | |
|
205 | |
|
206 | |
|
207 | |
public String getPropertyName() { |
208 | 214 | return propertyDescriptor.getName(); |
209 | |
} |
210 | |
|
211 | |
|
212 | |
|
213 | |
|
214 | |
public Class<?> getPropertyType() { |
215 | 104 | return propertyDescriptor.getPropertyType(); |
216 | |
} |
217 | |
|
218 | |
|
219 | |
|
220 | |
|
221 | |
public Method getReadMethod() { |
222 | 211 | return propertyDescriptor.getReadMethod(); |
223 | |
} |
224 | |
|
225 | |
|
226 | |
|
227 | |
|
228 | |
public boolean hasReadMethod() { |
229 | 0 | return this.getReadMethod() != null; |
230 | |
} |
231 | |
|
232 | |
|
233 | |
|
234 | |
|
235 | |
public Method getWriteMethod() { |
236 | 266 | return propertyDescriptor.getWriteMethod(); |
237 | |
} |
238 | |
|
239 | |
|
240 | |
|
241 | |
|
242 | |
public boolean hasWriteMethod() { |
243 | 0 | return this.getWriteMethod() != null; |
244 | |
} |
245 | |
|
246 | |
|
247 | |
|
248 | |
|
249 | |
public boolean isReadable() { |
250 | 7 | return propertyDescriptor.getReadMethod() != null; |
251 | |
} |
252 | |
|
253 | |
|
254 | |
|
255 | |
|
256 | |
public boolean isWritable() { |
257 | 0 | return propertyDescriptor.getWriteMethod() != null; |
258 | |
} |
259 | |
|
260 | |
|
261 | |
|
262 | |
|
263 | |
public Object getValue(final Object target) |
264 | |
throws IllegalPropertyException { |
265 | 51 | final Method method = this.getReadMethod(); |
266 | 51 | if (method == null) { |
267 | 2 | throw new IllegalPropertyException(clazz, propertyDescriptor |
268 | |
.getName(), new IllegalStateException( |
269 | |
propertyDescriptor.getName() + " is not readable.")); |
270 | |
} |
271 | |
try { |
272 | 49 | return method.invoke(target); |
273 | 0 | } catch (final IllegalAccessException e) { |
274 | 0 | throw new IllegalPropertyException(clazz, propertyDescriptor |
275 | |
.getName(), e); |
276 | 0 | } catch (final InvocationTargetException e) { |
277 | 0 | final Throwable t = e.getTargetException(); |
278 | 0 | if (t instanceof Error) { |
279 | 0 | throw (Error) t; |
280 | |
} |
281 | 0 | 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 | 106 | final Method method = this.getWriteMethod(); |
292 | 106 | if (method == null) { |
293 | 2 | throw new IllegalPropertyException(clazz, propertyDescriptor |
294 | |
.getName(), new IllegalStateException( |
295 | |
propertyDescriptor.getName() + " is not writable.")); |
296 | |
} |
297 | |
try { |
298 | 104 | final Class<?> propertyType = propertyDescriptor |
299 | |
.getPropertyType(); |
300 | 104 | if (value == null && propertyType.isPrimitive()) { |
301 | 7 | method.invoke(target, PRIMITIVE_TYPE_DEFAULT_VALUES |
302 | |
.get(propertyType)); |
303 | |
} else { |
304 | 97 | method.invoke(target, value); |
305 | |
} |
306 | 1 | } catch (final IllegalArgumentException e) { |
307 | 1 | throw new IllegalPropertyException(clazz, propertyDescriptor |
308 | |
.getName(), e); |
309 | 0 | } catch (final IllegalAccessException e) { |
310 | 0 | throw new IllegalPropertyException(clazz, propertyDescriptor |
311 | |
.getName(), e); |
312 | 0 | } catch (final InvocationTargetException e) { |
313 | 0 | final Throwable t = e.getTargetException(); |
314 | 0 | if (t instanceof Error) { |
315 | 0 | throw (Error) t; |
316 | |
} |
317 | 0 | throw new IllegalPropertyException(clazz, propertyDescriptor |
318 | |
.getName(), e); |
319 | 103 | } |
320 | 103 | } |
321 | |
|
322 | |
|
323 | |
|
324 | |
|
325 | |
public boolean isParameterized() { |
326 | 4 | return parameterizedClassDesc != null |
327 | |
&& parameterizedClassDesc.isParameterizedClass(); |
328 | |
} |
329 | |
|
330 | |
|
331 | |
|
332 | |
|
333 | |
public ParameterizedClassDesc getParameterizedClassDesc() { |
334 | 4 | return parameterizedClassDesc; |
335 | |
} |
336 | |
|
337 | |
|
338 | |
|
339 | |
|
340 | |
public <T extends Annotation> T getAnnotation( |
341 | |
final Class<T> annotationClass) { |
342 | 250 | if (annotationCache.containsKey(annotationClass)) { |
343 | 93 | return annotationClass.cast(annotationCache |
344 | |
.get(annotationClass)); |
345 | |
} |
346 | |
|
347 | 157 | final Method readMethod = this.getReadMethod(); |
348 | 157 | if (readMethod != null) { |
349 | 157 | final T annotation = findAnnotation(annotationClass, readMethod); |
350 | 157 | if (annotation != null) { |
351 | 0 | annotationCache.put(annotationClass, annotation); |
352 | 0 | return annotation; |
353 | |
} |
354 | |
} |
355 | |
|
356 | 157 | final Method writeMethod = this.getWriteMethod(); |
357 | 157 | if (writeMethod != null) { |
358 | 143 | final T annotation = findAnnotation(annotationClass, |
359 | |
writeMethod); |
360 | 143 | if (annotation != null) { |
361 | 8 | annotationCache.put(annotationClass, annotation); |
362 | 8 | return annotation; |
363 | |
} |
364 | |
} |
365 | |
|
366 | 149 | annotationCache.put(annotationClass, null); |
367 | 149 | 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 | 300 | final String methodName = method.getName(); |
388 | 300 | final Class<?>[] parameterTypes = method.getParameterTypes(); |
389 | 300 | for (Class<?> target = method.getDeclaringClass(); !target |
390 | 278 | .equals(Object.class); target = target.getSuperclass()) { |
391 | 286 | final T annotation = getAnnotation(annotationClass, target, |
392 | |
methodName, parameterTypes); |
393 | 286 | if (annotation != null) { |
394 | 8 | return annotation; |
395 | |
} |
396 | 278 | final T annotationOfInterfaces = getAnnotationOfInterfaces( |
397 | |
annotationClass, target, methodName, parameterTypes); |
398 | 278 | if (annotationOfInterfaces != null) { |
399 | 0 | return annotationOfInterfaces; |
400 | |
} |
401 | |
} |
402 | 292 | 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 | 278 | for (final Class<?> interfaceClass : clazz.getInterfaces()) { |
425 | 0 | final T annotation = getAnnotation(annotationClass, |
426 | |
interfaceClass, methodName, parameterTypes); |
427 | 0 | if (annotation != null) { |
428 | 0 | return annotation; |
429 | |
} |
430 | |
} |
431 | 278 | 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 | 286 | final Method method = clazz.getDeclaredMethod(methodName, |
456 | |
parameterTypes); |
457 | 286 | if (method.isAnnotationPresent(annotationClass)) { |
458 | 8 | return method.getAnnotation(annotationClass); |
459 | |
} |
460 | 0 | } catch (final NoSuchMethodException e) { |
461 | |
|
462 | 278 | } |
463 | |
|
464 | 278 | return null; |
465 | |
} |
466 | |
|
467 | |
|
468 | |
|
469 | |
|
470 | |
public boolean isAnnotationPresent( |
471 | |
final Class<? extends Annotation> annotationClass) { |
472 | 98 | return this.getAnnotation(annotationClass) != null; |
473 | |
} |
474 | |
|
475 | |
} |
476 | |
|
477 | |
|
478 | |
|
479 | |
|
480 | |
|
481 | |
|
482 | |
|
483 | 86 | private static class ParameterizedClassDescImpl implements |
484 | |
ParameterizedClassDesc { |
485 | |
|
486 | |
|
487 | |
protected Class<?> rawClass; |
488 | |
|
489 | |
|
490 | |
protected ParameterizedClassDesc[] arguments; |
491 | |
|
492 | |
|
493 | |
|
494 | |
|
495 | 0 | public ParameterizedClassDescImpl() { |
496 | 0 | } |
497 | |
|
498 | |
|
499 | |
|
500 | |
|
501 | |
|
502 | |
|
503 | |
|
504 | 258 | public ParameterizedClassDescImpl(final Class<?> rawClass) { |
505 | 258 | this.rawClass = rawClass; |
506 | 258 | } |
507 | |
|
508 | |
|
509 | |
|
510 | |
|
511 | |
|
512 | |
|
513 | |
|
514 | |
|
515 | |
|
516 | |
public ParameterizedClassDescImpl(final Class<?> rawClass, |
517 | 46 | final ParameterizedClassDesc[] arguments) { |
518 | 46 | this.rawClass = rawClass; |
519 | 46 | this.arguments = arguments; |
520 | 46 | } |
521 | |
|
522 | |
|
523 | |
|
524 | |
|
525 | |
public boolean isParameterizedClass() { |
526 | 4 | return arguments != null; |
527 | |
} |
528 | |
|
529 | |
|
530 | |
|
531 | |
|
532 | |
public Class<?> getRawClass() { |
533 | 4 | return rawClass; |
534 | |
} |
535 | |
|
536 | |
|
537 | |
|
538 | |
|
539 | |
public ParameterizedClassDesc[] getArguments() { |
540 | 4 | return arguments; |
541 | |
} |
542 | |
|
543 | |
} |
544 | |
|
545 | |
|
546 | |
|
547 | |
|
548 | |
|
549 | |
|
550 | |
|
551 | |
|
552 | |
private static ParameterizedClassDesc createParameterizedClassDesc( |
553 | |
final Type type) { |
554 | 339 | final Class<?> rowClass = getRawClass(type); |
555 | 339 | if (rowClass == null) { |
556 | 35 | return null; |
557 | |
} |
558 | 304 | final Type[] parameterTypes = getGenericParameter(type); |
559 | 304 | if (parameterTypes == null) { |
560 | 258 | final ParameterizedClassDescImpl desc = new ParameterizedClassDescImpl( |
561 | |
rowClass); |
562 | 258 | return desc; |
563 | |
} else { |
564 | 46 | final ParameterizedClassDesc[] parameterDescs = new ParameterizedClassDesc[parameterTypes.length]; |
565 | 95 | for (int i = 0; i < parameterTypes.length; ++i) { |
566 | 49 | parameterDescs[i] = createParameterizedClassDesc(parameterTypes[i]); |
567 | |
} |
568 | 46 | final ParameterizedClassDescImpl desc = new ParameterizedClassDescImpl( |
569 | |
rowClass, parameterDescs); |
570 | 46 | return desc; |
571 | |
} |
572 | |
} |
573 | |
|
574 | |
|
575 | |
|
576 | |
|
577 | |
|
578 | |
|
579 | |
|
580 | |
|
581 | |
|
582 | |
|
583 | |
|
584 | |
private static Class<?> getRawClass(final Type type) { |
585 | 386 | if (Class.class.isInstance(type)) { |
586 | 304 | return Class.class.cast(type); |
587 | |
} |
588 | 82 | if (ParameterizedType.class.isInstance(type)) { |
589 | 46 | final ParameterizedType parameterizedType = ParameterizedType.class |
590 | |
.cast(type); |
591 | 46 | return getRawClass(parameterizedType.getRawType()); |
592 | |
} |
593 | 36 | if (GenericArrayType.class.isInstance(type)) { |
594 | 1 | final GenericArrayType genericArrayType = GenericArrayType.class |
595 | |
.cast(type); |
596 | 1 | final Class<?> rawClass = getRawClass(genericArrayType |
597 | |
.getGenericComponentType()); |
598 | 1 | return Array.newInstance(rawClass, 0).getClass(); |
599 | |
} |
600 | 35 | return null; |
601 | |
} |
602 | |
|
603 | |
|
604 | |
|
605 | |
|
606 | |
|
607 | |
|
608 | |
|
609 | |
|
610 | |
|
611 | |
|
612 | |
|
613 | |
private static Type[] getGenericParameter(final Type type) { |
614 | 305 | if (ParameterizedType.class.isInstance(type)) { |
615 | 46 | return ParameterizedType.class.cast(type).getActualTypeArguments(); |
616 | |
} |
617 | 259 | if (GenericArrayType.class.isInstance(type)) { |
618 | 1 | return getGenericParameter(GenericArrayType.class.cast(type) |
619 | |
.getGenericComponentType()); |
620 | |
} |
621 | 258 | return null; |
622 | |
} |
623 | |
|
624 | |
} |