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
58%
28/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-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  
 }