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