##### Coverage Report - org.seasar.cubby.spi.beans.impl.DefaultBeanDescProvider

Classes in this File | Line Coverage | Branch Coverage | Complexity
DefaultBeanDescProvider |
96% | 49/51
95% | 19/20
| 3.183673469387755;3.184
DefaultBeanDescProvider$BeanDescImpl |
92% | 46/50
87% | 14/16
| 3.183673469387755;3.184
DefaultBeanDescProvider$FieldAttribute |
58% | 28/48
33% | 8/24
| 3.183673469387755;3.184
DefaultBeanDescProvider$ParameterizedClassDescImpl |
83% | 10/12
50% | 1/2
| 3.183673469387755;3.184
DefaultBeanDescProvider$PropertyAttribute |
65% | 65/100
58% | 34/58
| 3.183673469387755;3.184

1 |  |
     /*
2 |  |
      * Copyright 2004-2009 the Seasar Foundation and the Others.
3 |  |
      *
4 |  |
      * Licensed under the Apache License, Version 2.0 (the "License");
5 |  |
      * you may not use this file except in compliance with the License.
6 |  |
      * You may obtain a copy of the License at
7 |  |
      *
8 |  |
      *     http://www.apache.org/licenses/LICENSE-2.0
9 |  |
      *
10 |  |
      * Unless required by applicable law or agreed to in writing, software
11 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
12 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13 |  |
      * either express or implied. See the License for the specific language
14 |  |
      * governing permissions and limitations under the License.
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 |  |
      * {@link BeanDesc} のプロバイダの標準的な実装です。
54 |  |
      * <p>
55 |  |
      * {@link Introspector} によって生成されるメタ情報とそのフィールドの情報を元に {@link BeanDesc} を構築します。
56 |  |
      * </p>
57 |  |
      *
58 |  |
      * @author baba
59 |  |
      */
60 | 95 |
     public class DefaultBeanDescProvider implements BeanDescProvider {
61 |  |
62 |  |
             /** プリミティブ型のデフォルト値の <code>Map</code>。 */
63 |  |
             private static final Map<Class<?>, Object> PRIMITIVE_TYPE_DEFAULT_VALUES;
64 |  |
             static {
65 | 1 |
                     final Map<Class<?>, Object> map = new HashMap<Class<?>, Object>();
66 | 1 |
                     map.put(boolean.class, Boolean.FALSE);
67 | 1 |
                     map.put(char.class, Character.valueOf('\u0000'));
68 | 1 |
                     map.put(byte.class, Byte.valueOf((byte) 0));
69 | 1 |
                     map.put(short.class, Short.valueOf((short) 0));
70 | 1 |
                     map.put(int.class, Integer.valueOf(0));
71 | 1 |
                     map.put(long.class, Long.valueOf(0L));
72 | 1 |
                     map.put(float.class, Float.valueOf(0F));
73 | 1 |
                     map.put(double.class, Double.valueOf(0D));
74 | 1 |
                     PRIMITIVE_TYPE_DEFAULT_VALUES = Collections.unmodifiableMap(map);
75 | 1 |
             }
76 |  |
77 |  |
             /** <code>BeanDesc</code> のキャッシュ。 */
78 | 88 |
             protected final Map<Class<?>, BeanDesc> beanDescCache = new ConcurrentHashMap<Class<?>, BeanDesc>(
79 |  |
                             1024);
80 |  |
81 |  |
             /**
82 |  |
              * {@inheritDoc}
83 |  |
              */
84 |  |
             public BeanDesc getBeanDesc(final Class<?> clazz) {
85 | 82 |
                     if (beanDescCache.containsKey(clazz)) {
86 | 45 |
                             return beanDescCache.get(clazz);
87 |  |
                     }
88 |  |
89 | 37 |
                     synchronized (clazz) {
90 | 37 |
                             if (beanDescCache.containsKey(clazz)) {
91 | 0 |
                                     return beanDescCache.get(clazz);
92 |  |
                             }
93 |  |
94 | 37 |
                             final BeanDesc beanDesc = createBeanDesc(clazz);
95 | 37 |
                             beanDescCache.put(clazz, beanDesc);
96 | 37 |
                             return beanDesc;
97 | 0 |
                     }
98 |  |
             }
99 |  |
100 |  |
             /**
101 |  |
              * {@link BeanDesc} を生成します。
102 |  |
              *
103 |  |
              * @param clazz
104 |  |
              *            操作対象のクラス
105 |  |
              * @return {@link BeanDesc}
106 |  |
              */
107 |  |
             protected BeanDesc createBeanDesc(final Class<?> clazz) {
108 | 37 |
                     return new BeanDescImpl(clazz);
109 |  |
             }
110 |  |
111 |  |
             /**
112 |  |
              * {@link BeanDesc} の実装です。
113 |  |
              *
114 |  |
              * @author baba
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 |  |
                      * @param clazz
131 |  |
                      *            操作対象のクラス
132 |  |
                      */
133 | 37 |
                     public BeanDescImpl(final Class<?> clazz) {
134 | 37 |
                             this.clazz = clazz;
135 | 37 |
                             this.propertyAttributeMap = collectPropertyAttributeMap(clazz);
136 | 37 |
                             this.fieldAttributesMap = collectFieldAttributesMap(clazz);
137 | 37 |
                     }
138 |  |
139 |  |
                     /**
140 |  |
                      * 指定されたクラスからプロパティの {@link Attribute} を生成します。
141 |  |
                      *
142 |  |
                      * @param clazz
143 |  |
                      *            対象のクラス
144 |  |
                      * @return {@link Attribute} の {@link Map}
145 |  |
                      */
146 |  |
                     protected Map<String, Attribute> collectPropertyAttributeMap(
147 |  |
                                     final Class<?> clazz) {
148 | 37 |
                             final Map<String, Attribute> propertyAttributes = new LinkedHashMap<String, Attribute>();
149 |  |
                             final BeanInfo beanInfo;
150 |  |
                             try {
151 | 37 |
                                     beanInfo = Introspector.getBeanInfo(clazz);
152 | 0 |
                             } catch (final IntrospectionException e) {
153 | 0 |
                                     throw new IllegalStateException(e);
154 | 37 |
                             }
155 | 331 |
                             for (final PropertyDescriptor propertyDescriptor : beanInfo
156 |  |
                                             .getPropertyDescriptors()) {
157 | 294 |
                                     final String propertyName = propertyDescriptor.getName();
158 | 294 |
                                     final Attribute propertyDesc = new PropertyAttribute(clazz,
159 |  |
                                                     propertyDescriptor);
160 | 294 |
                                     propertyAttributes.put(propertyName, propertyDesc);
161 |  |
                             }
162 | 37 |
                             return propertyAttributes;
163 |  |
                     }
164 |  |
165 |  |
                     /**
166 |  |
                      * 指定されたクラスからフィールドの {@link Attribute} を生成します。
167 |  |
                      *
168 |  |
                      * @param clazz
169 |  |
                      *            対象のクラス
170 |  |
                      * @return {@link Attribute} の {@link Map}
171 |  |
                      */
172 |  |
                     protected Map<String, List<Attribute>> collectFieldAttributesMap(
173 |  |
                                     final Class<?> clazz) {
174 | 37 |
                             final Map<String, List<Attribute>> fieldAttributes = new LinkedHashMap<String, List<Attribute>>();
175 | 37 |
                             for (final Field field : findAllDeclaredField(clazz)) {
176 | 232 |
                                     final String fieldName = field.getName();
177 |  |
                                     List<Attribute> fieldDescs;
178 | 232 |
                                     if (!fieldAttributes.containsKey(fieldName)) {
179 | 232 |
                                             fieldDescs = new ArrayList<Attribute>();
180 | 232 |
                                             fieldAttributes.put(fieldName, fieldDescs);
181 |  |
                                     } else {
182 | 0 |
                                             fieldDescs = fieldAttributes.get(fieldName);
183 |  |
                                     }
184 | 232 |
                                     final Attribute attributes = new FieldAttribute(clazz, field);
185 | 232 |
                                     fieldDescs.add(attributes);
186 | 232 |
                             }
187 | 37 |
                             return fieldAttributes;
188 |  |
                     }
189 |  |
190 |  |
                     /**
191 |  |
                      * {@inheritDoc}
192 |  |
                      */
193 |  |
                     public boolean hasPropertyAttribute(final String name) {
194 | 17 |
                             return propertyAttributeMap.containsKey(name);
195 |  |
                     }
196 |  |
197 |  |
                     /**
198 |  |
                      * {@inheritDoc}
199 |  |
                      */
200 |  |
                     public Attribute getPropertyAttribute(final String name)
201 |  |
                                     throws AttributeNotFoundException {
202 | 49 |
                             if (!propertyAttributeMap.containsKey(name)) {
203 | 1 |
                                     throw new AttributeNotFoundException(clazz, name);
204 |  |
                             }
205 | 48 |
                             return propertyAttributeMap.get(name);
206 |  |
                     }
207 |  |
208 |  |
                     /**
209 |  |
                      * {@inheritDoc}
210 |  |
                      */
211 |  |
                     public Set<Attribute> findtPropertyAttributes() {
212 | 41 |
                             final Set<Attribute> attributes = new LinkedHashSet<Attribute>();
213 | 41 |
                             attributes.addAll(propertyAttributeMap.values());
214 | 41 |
                             return Collections.unmodifiableSet(attributes);
215 |  |
                     }
216 |  |
217 |  |
                     /**
218 |  |
                      * {@inheritDoc}
219 |  |
                      */
220 |  |
                     public Attribute getFieldAttribute(final String fieldName) {
221 | 2 |
                             if (!fieldAttributesMap.containsKey(fieldName)) {
222 | 0 |
                                     throw new AttributeNotFoundException(clazz, fieldName);
223 |  |
                             }
224 | 2 |
                             return fieldAttributesMap.get(fieldName).get(0);
225 |  |
                     }
226 |  |
227 |  |
                     /**
228 |  |
                      * {@inheritDoc}
229 |  |
                      */
230 |  |
                     public boolean hasFieldAttribute(final String fieldName) {
231 | 5 |
                             return fieldAttributesMap.containsKey(fieldName);
232 |  |
                     }
233 |  |
234 |  |
                     /**
235 |  |
                      * {@inheritDoc}
236 |  |
                      */
237 |  |
                     public Set<Attribute> findFieldAttributes() {
238 | 31 |
                             final Set<Attribute> fieldAttributes = new LinkedHashSet<Attribute>();
239 | 31 |
                             for (final List<Attribute> attributes : fieldAttributesMap.values()) {
240 | 213 |
                                     fieldAttributes.addAll(attributes);
241 |  |
                             }
242 | 31 |
                             return Collections.unmodifiableSet(fieldAttributes);
243 |  |
                     }
244 |  |
245 |  |
                     /**
246 |  |
                      * {@inheritDoc}
247 |  |
                      */
248 |  |
                     public Set<Attribute> findAllAttributes() {
249 | 31 |
                             final Set<Attribute> attributes = new LinkedHashSet<Attribute>();
250 | 31 |
                             attributes.addAll(this.findtPropertyAttributes());
251 | 31 |
                             attributes.addAll(this.findFieldAttributes());
252 | 31 |
                             return Collections.unmodifiableSet(attributes);
253 |  |
                     }
254 |  |
255 |  |
                     /**
256 |  |
                      * {@inheritDoc}
257 |  |
                      */
258 |  |
                     public Set<Attribute> findAttributesAnnotatedWith(
259 |  |
                                     final Class<? extends Annotation> annotationClass) {
260 | 9 |
                             final Set<Attribute> attributes = new LinkedHashSet<Attribute>();
261 | 9 |
                             for (final Attribute attribute : findAllAttributes()) {
262 | 251 |
                                     if (attribute.isAnnotationPresent(annotationClass)) {
263 | 4 |
                                             attributes.add(attribute);
264 |  |
                                     }
265 |  |
                             }
266 | 9 |
                             return Collections.unmodifiableSet(attributes);
267 |  |
                     }
268 |  |
269 |  |
             }
270 |  |
271 |  |
             /**
272 |  |
              * プロパティに対する {@link Attribute} の実装です。
273 |  |
              *
274 |  |
              * @author baba
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 | 294 |
                     private final Map<Class<? extends Annotation>, Annotation> annotationCache = new HashMap<Class<? extends Annotation>, Annotation>();
289 |  |
290 |  |
                     /**
291 |  |
                      * インスタンス化します。
292 |  |
                      *
293 |  |
                      * @param clazz
294 |  |
                      *            操作対象のクラス
295 |  |
                      * @param propertyDescriptor
296 |  |
                      *            プロパティの記述
297 |  |
                      */
298 |  |
                     PropertyAttribute(final Class<?> clazz,
299 | 294 |
                                     final PropertyDescriptor propertyDescriptor) {
300 | 294 |
                             this.clazz = clazz;
301 | 294 |
                             this.propertyDescriptor = propertyDescriptor;
302 |  |
303 | 294 |
                             if (propertyDescriptor.getReadMethod() != null) {
304 | 277 |
                                     this.parameterizedClassDesc = createParameterizedClassDesc(propertyDescriptor
305 |  |
                                                     .getReadMethod().getGenericReturnType());
306 | 17 |
                             } else if (propertyDescriptor.getWriteMethod() != null) {
307 | 17 |
                                     this.parameterizedClassDesc = createParameterizedClassDesc(propertyDescriptor
308 |  |
                                                     .getWriteMethod().getParameterTypes()[0]);
309 |  |
                             } else {
310 | 0 |
                                     this.parameterizedClassDesc = null;
311 |  |
                             }
312 | 294 |
                     }
313 |  |
314 |  |
                     /**
315 |  |
                      * {@inheritDoc}
316 |  |
                      */
317 |  |
                     public String getName() {
318 | 222 |
                             return propertyDescriptor.getName();
319 |  |
                     }
320 |  |
321 |  |
                     /**
322 |  |
                      * {@inheritDoc}
323 |  |
                      */
324 |  |
                     public Class<?> getType() {
325 | 109 |
                             return propertyDescriptor.getPropertyType();
326 |  |
                     }
327 |  |
328 |  |
                     /**
329 |  |
                      * {@inheritDoc}
330 |  |
                      */
331 |  |
                     public boolean isReadable() {
332 | 9 |
                             return propertyDescriptor.getReadMethod() != null;
333 |  |
                     }
334 |  |
335 |  |
                     /**
336 |  |
                      * {@inheritDoc}
337 |  |
                      */
338 |  |
                     public boolean isWritable() {
339 | 2 |
                             return propertyDescriptor.getWriteMethod() != null;
340 |  |
                     }
341 |  |
342 |  |
                     /**
343 |  |
                      * {@inheritDoc}
344 |  |
                      */
345 |  |
                     public Object getValue(final Object target)
346 |  |
                                     throws IllegalAttributeException {
347 | 55 |
                             final Method method = propertyDescriptor.getReadMethod();
348 | 55 |
                             if (method == null) {
349 | 2 |
                                     throw new IllegalAttributeException(clazz, propertyDescriptor
350 |  |
                                                     .getName(), new IllegalStateException(
351 |  |
                                                     propertyDescriptor.getName() + " is not readable."));
352 |  |
                             }
353 |  |
                             try {
354 | 53 |
                                     return method.invoke(target);
355 | 0 |
                             } catch (final IllegalAccessException e) {
356 | 0 |
                                     throw new IllegalAttributeException(clazz, propertyDescriptor
357 |  |
                                                     .getName(), e);
358 | 0 |
                             } catch (final InvocationTargetException e) {
359 | 0 |
                                     final Throwable t = e.getTargetException();
360 | 0 |
                                     if (t instanceof Error) {
361 | 0 |
                                             throw (Error) t;
362 |  |
                                     }
363 | 0 |
                                     throw new IllegalAttributeException(clazz, propertyDescriptor
364 |  |
                                                     .getName(), e);
365 |  |
                             }
366 |  |
                     }
367 |  |
368 |  |
                     /**
369 |  |
                      * {@inheritDoc}
370 |  |
                      */
371 |  |
                     public void setValue(final Object target, final Object value)
372 |  |
                                     throws IllegalAttributeException {
373 | 112 |
                             final Method method = propertyDescriptor.getWriteMethod();
374 | 112 |
                             if (method == null) {
375 | 3 |
                                     throw new IllegalAttributeException(clazz, propertyDescriptor
376 |  |
                                                     .getName(), new IllegalStateException(
377 |  |
                                                     propertyDescriptor.getName() + " is not writable."));
378 |  |
                             }
379 |  |
                             try {
380 | 109 |
                                     final Class<?> propertyType = propertyDescriptor
381 |  |
                                                     .getPropertyType();
382 | 109 |
                                     if (value == null && propertyType.isPrimitive()) {
383 | 7 |
                                             method.invoke(target, PRIMITIVE_TYPE_DEFAULT_VALUES
384 |  |
                                                             .get(propertyType));
385 |  |
                                     } else {
386 | 102 |
                                             method.invoke(target, value);
387 |  |
                                     }
388 | 1 |
                             } catch (final IllegalArgumentException e) {
389 | 1 |
                                     throw new IllegalAttributeException(clazz, propertyDescriptor
390 |  |
                                                     .getName(), e);
391 | 0 |
                             } catch (final IllegalAccessException e) {
392 | 0 |
                                     throw new IllegalAttributeException(clazz, propertyDescriptor
393 |  |
                                                     .getName(), e);
394 | 0 |
                             } catch (final InvocationTargetException e) {
395 | 0 |
                                     final Throwable t = e.getTargetException();
396 | 0 |
                                     if (t instanceof Error) {
397 | 0 |
                                             throw (Error) t;
398 |  |
                                     }
399 | 0 |
                                     throw new IllegalAttributeException(clazz, propertyDescriptor
400 |  |
                                                     .getName(), e);
401 | 108 |
                             }
402 | 108 |
                     }
403 |  |
404 |  |
                     /**
405 |  |
                      * {@inheritDoc}
406 |  |
                      */
407 |  |
                     public ParameterizedClassDesc getParameterizedClassDesc() {
408 | 105 |
                             return parameterizedClassDesc;
409 |  |
                     }
410 |  |
411 |  |
                     /**
412 |  |
                      * {@inheritDoc}
413 |  |
                      * <p>
414 |  |
                      * 以下の順序でプロパティのメソッドの定義を検索し、最初に見つかったアノテーションを返します。
415 |  |
                      * <ol>
416 |  |
                      * <li>プロパティ値の読み込みに使用するメソッド</li>
417 |  |
                      * <li>プロパティ値の書き込みに使用するメソッド</li>
418 |  |
                      * </ol>
419 |  |
                      * </p>
420 |  |
                      * <p>
421 |  |
                      * また、クラスが {@link Proxy}
422 |  |
                      * になどよって動的に生成されている場合などは、メソッドからアノテーションを取得することができません。 (アノテーションが
423 |  |
                      * {@link Inherited} で修飾されている場合でも取得できません。)
424 |  |
                      * そのため、読み込み/書き込みメソッドの定義を以下のように検索し、アノテーションを取得します。
425 |  |
                      * <ul>
426 |  |
                      * <li>読み込み/書き込みメソッドが定義されたクラス ({@link Method#getDeclaringClass()})
427 |  |
                      * を検索対象クラスの起点とします。</li>
428 |  |
                      * <li>検索対象クラスと、そのインターフェイスから読み込み/書き込みメソッドの定義を検索します。
429 |  |
                      * <li>アノテーションが取得できなかった場合は、検索対象クラスをそのスーパークラスとし、再度検索を行います。</li>
430 |  |
                      * </ul>
431 |  |
                      * </p>
432 |  |
                      */
433 |  |
                     public <T extends Annotation> T getAnnotation(
434 |  |
                                     final Class<T> annotationClass) {
435 | 402 |
                             if (annotationCache.containsKey(annotationClass)) {
436 | 245 |
                                     return annotationClass.cast(annotationCache
437 |  |
                                                     .get(annotationClass));
438 |  |
                             }
439 |  |
440 | 157 |
                             final Method readMethod = propertyDescriptor.getReadMethod();
441 | 157 |
                             if (readMethod != null) {
442 | 157 |
                                     final T annotation = findAnnotation(annotationClass, readMethod);
443 | 157 |
                                     if (annotation != null) {
444 | 0 |
                                             annotationCache.put(annotationClass, annotation);
445 | 0 |
                                             return annotation;
446 |  |
                                     }
447 |  |
                             }
448 |  |
449 | 157 |
                             final Method writeMethod = propertyDescriptor.getWriteMethod();
450 | 157 |
                             if (writeMethod != null) {
451 | 143 |
                                     final T annotation = findAnnotation(annotationClass,
452 |  |
                                                     writeMethod);
453 | 143 |
                                     if (annotation != null) {
454 | 8 |
                                             annotationCache.put(annotationClass, annotation);
455 | 8 |
                                             return annotation;
456 |  |
                                     }
457 |  |
                             }
458 |  |
459 | 149 |
                             annotationCache.put(annotationClass, null);
460 | 149 |
                             return null;
461 |  |
                     }
462 |  |
463 |  |
                     /**
464 |  |
                      * 指定されたメソッドのアノテーションを検索します。
465 |  |
                      * <p>
466 |  |
                      * インターフェイスやスーパークラスに定義されたメソッドの定義からもアノテーションが見つかるまで検索します。
467 |  |
                      * アノテーションが見つからなかった場合は <code>null</code> を返します。
468 |  |
                      * </p>
469 |  |
                      *
470 |  |
                      * @param <T>
471 |  |
                      *            アノテーションの型
472 |  |
                      * @param annotationClass
473 |  |
                      *            アノテーションの型
474 |  |
                      * @param method
475 |  |
                      *            メソッド
476 |  |
                      * @return アノテーションが見つかった場合はそのアノテーション、見つからなかった場合は <code>null</code>
477 |  |
                      */
478 |  |
                     private static <T extends Annotation> T findAnnotation(
479 |  |
                                     final Class<T> annotationClass, final Method method) {
480 | 300 |
                             final String methodName = method.getName();
481 | 300 |
                             final Class<?>[] parameterTypes = method.getParameterTypes();
482 | 300 |
                             for (Class<?> target = method.getDeclaringClass(); !target
483 | 278 |
                                             .equals(Object.class); target = target.getSuperclass()) {
484 | 286 |
                                     final T annotation = getAnnotation(annotationClass, target,
485 |  |
                                                     methodName, parameterTypes);
486 | 286 |
                                     if (annotation != null) {
487 | 8 |
                                             return annotation;
488 |  |
                                     }
489 | 278 |
                                     final T annotationOfInterfaces = getAnnotationOfInterfaces(
490 |  |
                                                     annotationClass, target, methodName, parameterTypes);
491 | 278 |
                                     if (annotationOfInterfaces != null) {
492 | 0 |
                                             return annotationOfInterfaces;
493 |  |
                                     }
494 |  |
                             }
495 | 292 |
                             return null;
496 |  |
                     }
497 |  |
498 |  |
                     /**
499 |  |
                      * 指定されたクラスが実装するインターフェイスにメソッド名、パラメータ型でシグニチャを指定されたメソッドが定義されていれば、
500 |  |
                      * そのメソッドに定義されたアノテーションを返します。
501 |  |
                      *
502 |  |
                      * @param <T>
503 |  |
                      *            アノテーションの型
504 |  |
                      * @param annotationClass
505 |  |
                      *            アノテーションの型
506 |  |
                      * @param clazz
507 |  |
                      *            クラス
508 |  |
                      * @param methodName
509 |  |
                      *            メソッド名
510 |  |
                      * @param parameterTypes
511 |  |
                      *            パラメータの型
512 |  |
                      * @return アノテーション
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 | 278 |
                             for (final Class<?> interfaceClass : clazz.getInterfaces()) {
518 | 0 |
                                     final T annotation = getAnnotation(annotationClass,
519 |  |
                                                     interfaceClass, methodName, parameterTypes);
520 | 0 |
                                     if (annotation != null) {
521 | 0 |
                                             return annotation;
522 |  |
                                     }
523 |  |
                             }
524 | 278 |
                             return null;
525 |  |
                     }
526 |  |
527 |  |
                     /**
528 |  |
                      * 指定されたクラスにメソッド名、パラメータ型でシグニチャを指定されたメソッドが定義されていれば、
529 |  |
                      * そのメソッドに定義されたアノテーションを返します。
530 |  |
                      *
531 |  |
                      * @param <T>
532 |  |
                      *            アノテーションの型
533 |  |
                      * @param annotationClass
534 |  |
                      *            アノテーションの型
535 |  |
                      * @param clazz
536 |  |
                      *            クラス
537 |  |
                      * @param methodName
538 |  |
                      *            メソッド名
539 |  |
                      * @param parameterTypes
540 |  |
                      *            パラメータの型
541 |  |
                      * @return アノテーション
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 | 286 |
                                     final Method method = clazz.getDeclaredMethod(methodName,
549 |  |
                                                     parameterTypes);
550 | 286 |
                                     if (method.isAnnotationPresent(annotationClass)) {
551 | 8 |
                                             return method.getAnnotation(annotationClass);
552 |  |
                                     }
553 | 0 |
                             } catch (final NoSuchMethodException e) {
554 |  |
                                     // do nothing
555 | 278 |
                             }
556 |  |
557 | 278 |
                             return null;
558 |  |
                     }
559 |  |
560 |  |
                     /**
561 |  |
                      * {@inheritDoc}
562 |  |
                      */
563 |  |
                     public boolean isAnnotationPresent(
564 |  |
                                     final Class<? extends Annotation> annotationClass) {
565 | 238 |
                             return this.getAnnotation(annotationClass) != null;
566 |  |
                     }
567 |  |
568 |  |
                     /**
569 |  |
                      * {@inheritDoc}
570 |  |
                      */
571 |  |
                     @Override
572 |  |
                     public int hashCode() {
573 | 627 |
                             final int prime = 31;
574 | 627 |
                             int result = 1;
575 | 627 |
                             result = prime
576 |  |
                                             * result
577 |  |
                                             + ((propertyDescriptor == null) ? 0 : propertyDescriptor
578 |  |
                                                             .hashCode());
579 | 627 |
                             return result;
580 |  |
                     }
581 |  |
582 |  |
                     /**
583 |  |
                      * {@inheritDoc}
584 |  |
                      */
585 |  |
                     @Override
586 |  |
                     public boolean equals(final Object obj) {
587 | 0 |
                             if (this == obj) {
588 | 0 |
                                     return true;
589 |  |
                             }
590 | 0 |
                             if (obj == null) {
591 | 0 |
                                     return false;
592 |  |
                             }
593 | 0 |
                             if (getClass() != obj.getClass()) {
594 | 0 |
                                     return false;
595 |  |
                             }
596 | 0 |
                             final PropertyAttribute other = (PropertyAttribute) obj;
597 | 0 |
                             if (propertyDescriptor == null) {
598 | 0 |
                                     if (other.propertyDescriptor != null) {
599 | 0 |
                                             return false;
600 |  |
                                     }
601 | 0 |
                             } else if (!propertyDescriptor.equals(other.propertyDescriptor)) {
602 | 0 |
                                     return false;
603 |  |
                             }
604 | 0 |
                             return true;
605 |  |
                     }
606 |  |
607 |  |
             }
608 |  |
609 |  |
             /**
610 |  |
              * フィールドに対する {@link Attribute} の実装です。
611 |  |
              *
612 |  |
              * @author baba
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 |  |
                      * @param clazz
632 |  |
                      *            操作対象のクラス
633 |  |
                      * @param field
634 |  |
                      *            フィールド
635 |  |
                      */
636 | 232 |
                     public FieldAttribute(final Class<?> clazz, final Field field) {
637 | 232 |
                             this.clazz = clazz;
638 | 232 |
                             this.field = field;
639 | 232 |
                             this.writable = (field.getModifiers() & Modifier.FINAL) == 0;
640 | 232 |
                             this.parameterizedClassDesc = createParameterizedClassDesc(field
641 |  |
                                             .getGenericType());
642 | 232 |
                     }
643 |  |
644 |  |
                     /**
645 |  |
                      * {@inheritDoc}
646 |  |
                      */
647 |  |
                     public String getName() {
648 | 22 |
                             return field.getName();
649 |  |
                     }
650 |  |
651 |  |
                     /**
652 |  |
                      * {@inheritDoc}
653 |  |
                      */
654 |  |
                     public Class<?> getType() {
655 | 0 |
                             return field.getType();
656 |  |
                     }
657 |  |
658 |  |
                     /**
659 |  |
                      * {@inheritDoc}
660 |  |
                      */
661 |  |
                     public Object getValue(final Object target) {
662 |  |
                             try {
663 | 6 |
                                     if (this.isReadable() && !field.isAccessible()) {
664 | 6 |
                                             field.setAccessible(true);
665 | 6 |
                                             final Object value = field.get(target);
666 | 6 |
                                             field.setAccessible(false);
667 | 6 |
                                             return value;
668 |  |
                                     } else {
669 | 0 |
                                             final Object value = field.get(target);
670 | 0 |
                                             return value;
671 |  |
                                     }
672 | 0 |
                             } catch (final IllegalAccessException e) {
673 | 0 |
                                     throw new IllegalAttributeException(clazz, field.getName(), e);
674 |  |
                             }
675 |  |
                     }
676 |  |
677 |  |
                     /**
678 |  |
                      * {@inheritDoc}
679 |  |
                      */
680 |  |
                     public void setValue(final Object target, final Object value) {
681 |  |
                             try {
682 | 2 |
                                     if (this.isWritable() && !field.isAccessible()) {
683 | 1 |
                                             field.setAccessible(true);
684 | 1 |
                                             field.set(target, value);
685 | 1 |
                                             field.setAccessible(false);
686 |  |
                                     } else {
687 | 1 |
                                             field.set(target, value);
688 |  |
                                     }
689 | 1 |
                             } catch (final IllegalAccessException e) {
690 | 1 |
                                     throw new IllegalAttributeException(clazz, field.getName(), e);
691 | 1 |
                             }
692 | 1 |
                     }
693 |  |
694 |  |
                     /**
695 |  |
                      * {@inheritDoc}
696 |  |
                      */
697 |  |
                     public boolean isReadable() {
698 | 8 |
                             return true;
699 |  |
                     }
700 |  |
701 |  |
                     /**
702 |  |
                      * {@inheritDoc}
703 |  |
                      */
704 |  |
                     public boolean isWritable() {
705 | 4 |
                             return writable;
706 |  |
                     }
707 |  |
708 |  |
                     /**
709 |  |
                      * {@inheritDoc}
710 |  |
                      */
711 |  |
                     public ParameterizedClassDesc getParameterizedClassDesc() {
712 | 0 |
                             return parameterizedClassDesc;
713 |  |
                     }
714 |  |
715 |  |
                     /**
716 |  |
                      * {@inheritDoc}
717 |  |
                      */
718 |  |
                     public <T extends Annotation> T getAnnotation(
719 |  |
                                     final Class<T> annotationClass) {
720 | 0 |
                             return field.getAnnotation(annotationClass);
721 |  |
                     }
722 |  |
723 |  |
                     /**
724 |  |
                      * {@inheritDoc}
725 |  |
                      */
726 |  |
                     public boolean isAnnotationPresent(
727 |  |
                                     final Class<? extends Annotation> annotationClass) {
728 | 145 |
                             return field.isAnnotationPresent(annotationClass);
729 |  |
                     }
730 |  |
731 |  |
                     /**
732 |  |
                      * {@inheritDoc}
733 |  |
                      */
734 |  |
                     @Override
735 |  |
                     public int hashCode() {
736 | 426 |
                             final int prime = 31;
737 | 426 |
                             int result = 1;
738 | 426 |
                             result = prime * result + ((field == null) ? 0 : field.hashCode());
739 | 426 |
                             return result;
740 |  |
                     }
741 |  |
742 |  |
                     /**
743 |  |
                      * {@inheritDoc}
744 |  |
                      */
745 |  |
                     @Override
746 |  |
                     public boolean equals(final Object obj) {
747 | 0 |
                             if (this == obj) {
748 | 0 |
                                     return true;
749 |  |
                             }
750 | 0 |
                             if (obj == null) {
751 | 0 |
                                     return false;
752 |  |
                             }
753 | 0 |
                             if (getClass() != obj.getClass()) {
754 | 0 |
                                     return false;
755 |  |
                             }
756 | 0 |
                             final FieldAttribute other = (FieldAttribute) obj;
757 | 0 |
                             if (field == null) {
758 | 0 |
                                     if (other.field != null) {
759 | 0 |
                                             return false;
760 |  |
                                     }
761 | 0 |
                             } else if (!field.equals(other.field)) {
762 | 0 |
                                     return false;
763 |  |
                             }
764 | 0 |
                             return true;
765 |  |
                     }
766 |  |
767 |  |
             }
768 |  |
769 |  |
             /**
770 |  |
              * {@link ParameterizedClassDesc}の実装クラスです。
771 |  |
              *
772 |  |
              * @author baba
773 |  |
              */
774 | 88 |
             protected static class ParameterizedClassDescImpl implements
775 |  |
                             ParameterizedClassDesc {
776 |  |
777 |  |
                     /** 原型となるクラス */
778 |  |
                     protected Class<?> rawClass;
779 |  |
780 |  |
                     /** 型引数を表す{@link ParameterizedClassDesc}の配列 */
781 |  |
                     protected ParameterizedClassDesc[] arguments;
782 |  |
783 |  |
                     /**
784 |  |
                      * インスタンスを構築します。
785 |  |
                      */
786 | 0 |
                     public ParameterizedClassDescImpl() {
787 | 0 |
                     }
788 |  |
789 |  |
                     /**
790 |  |
                      * インスタンスを構築します。
791 |  |
                      *
792 |  |
                      * @param rawClass
793 |  |
                      *            原型となるクラス
794 |  |
                      */
795 | 495 |
                     public ParameterizedClassDescImpl(final Class<?> rawClass) {
796 | 495 |
                             this.rawClass = rawClass;
797 | 495 |
                     }
798 |  |
799 |  |
                     /**
800 |  |
                      * インスタンスを構築します。
801 |  |
                      *
802 |  |
                      * @param rawClass
803 |  |
                      *            原型となるクラス
804 |  |
                      * @param arguments
805 |  |
                      *            型引数を表す{@link ParameterizedClassDesc}の配列
806 |  |
                      */
807 |  |
                     public ParameterizedClassDescImpl(final Class<?> rawClass,
808 | 59 |
                                     final ParameterizedClassDesc[] arguments) {
809 | 59 |
                             this.rawClass = rawClass;
810 | 59 |
                             this.arguments = arguments;
811 | 59 |
                     }
812 |  |
813 |  |
                     /**
814 |  |
                      * {@inheritDoc}
815 |  |
                      */
816 |  |
                     public boolean isParameterizedClass() {
817 | 4 |
                             return arguments != null;
818 |  |
                     }
819 |  |
820 |  |
                     /**
821 |  |
                      * {@inheritDoc}
822 |  |
                      */
823 |  |
                     public Class<?> getRawClass() {
824 | 4 |
                             return rawClass;
825 |  |
                     }
826 |  |
827 |  |
                     /**
828 |  |
                      * {@inheritDoc}
829 |  |
                      */
830 |  |
                     public ParameterizedClassDesc[] getArguments() {
831 | 4 |
                             return arguments;
832 |  |
                     }
833 |  |
834 |  |
             }
835 |  |
836 |  |
             /**
837 |  |
              * {@link Type}を表現する{@link ParameterizedClassDesc}を作成して返します。
838 |  |
              *
839 |  |
              * @param type
840 |  |
              *            型
841 |  |
              * @return 型を表現する{@link ParameterizedClassDesc}
842 |  |
              */
843 |  |
             protected static ParameterizedClassDesc createParameterizedClassDesc(
844 |  |
                             final Type type) {
845 | 591 |
                     final Class<?> rowClass = getRawClass(type);
846 | 591 |
                     if (rowClass == null) {
847 | 37 |
                             return null;
848 |  |
                     }
849 | 554 |
                     final Type[] parameterTypes = getGenericParameter(type);
850 | 554 |
                     if (parameterTypes == null) {
851 | 495 |
                             final ParameterizedClassDescImpl desc = new ParameterizedClassDescImpl(
852 |  |
                                             rowClass);
853 | 495 |
                             return desc;
854 |  |
                     } else {
855 | 59 |
                             final ParameterizedClassDesc[] parameterDescs = new ParameterizedClassDesc[parameterTypes.length];
856 | 124 |
                             for (int i = 0; i < parameterTypes.length; ++i) {
857 | 65 |
                                     parameterDescs[i] = createParameterizedClassDesc(parameterTypes[i]);
858 |  |
                             }
859 | 59 |
                             final ParameterizedClassDescImpl desc = new ParameterizedClassDescImpl(
860 |  |
                                             rowClass, parameterDescs);
861 | 59 |
                             return desc;
862 |  |
                     }
863 |  |
             }
864 |  |
865 |  |
             /**
866 |  |
              * <code>type</code>の原型を返します。
867 |  |
              * <p>
868 |  |
              * <code>type</code>が原型でもパラメータ化された型でもない場合は<code>null</code>を返します。
869 |  |
              * </p>
870 |  |
              *
871 |  |
              * @param type
872 |  |
              *            タイプ
873 |  |
              * @return <code>type</code>の原型
874 |  |
              */
875 |  |
             protected static Class<?> getRawClass(final Type type) {
876 | 652 |
                     if (Class.class.isInstance(type)) {
877 | 554 |
                             return Class.class.cast(type);
878 |  |
                     }
879 | 98 |
                     if (ParameterizedType.class.isInstance(type)) {
880 | 59 |
                             final ParameterizedType parameterizedType = ParameterizedType.class
881 |  |
                                             .cast(type);
882 | 59 |
                             return getRawClass(parameterizedType.getRawType());
883 |  |
                     }
884 | 39 |
                     if (GenericArrayType.class.isInstance(type)) {
885 | 2 |
                             final GenericArrayType genericArrayType = GenericArrayType.class
886 |  |
                                             .cast(type);
887 | 2 |
                             final Class<?> rawClass = getRawClass(genericArrayType
888 |  |
                                             .getGenericComponentType());
889 | 2 |
                             return Array.newInstance(rawClass, 0).getClass();
890 |  |
                     }
891 | 37 |
                     return null;
892 |  |
             }
893 |  |
894 |  |
             /**
895 |  |
              * <code>type</code>の型引数の配列を返します。
896 |  |
              * <p>
897 |  |
              * <code>type</code>がパラメータ化された型でない場合は<code>null</code>を返します。
898 |  |
              * </p>
899 |  |
              *
900 |  |
              * @param type
901 |  |
              *            タイプ
902 |  |
              * @return <code>type</code>の型引数の配列
903 |  |
              */
904 |  |
             protected static Type[] getGenericParameter(final Type type) {
905 | 556 |
                     if (ParameterizedType.class.isInstance(type)) {
906 | 59 |
                             return ParameterizedType.class.cast(type).getActualTypeArguments();
907 |  |
                     }
908 | 497 |
                     if (GenericArrayType.class.isInstance(type)) {
909 | 2 |
                             return getGenericParameter(GenericArrayType.class.cast(type)
910 |  |
                                             .getGenericComponentType());
911 |  |
                     }
912 | 495 |
                     return null;
913 |  |
             }
914 |  |
915 |  |
     }