Coverage Report - org.seasar.cubby.plugins.s2.spi.S2BeanDescProvider
 
Classes in this File Line Coverage Branch Coverage Complexity
S2BeanDescProvider
100%
9/9
100%
2/2
3.154
S2BeanDescProvider$1
25%
1/4
N/A
3.154
S2BeanDescProvider$S2BeanDescImpl
96%
26/27
85%
12/14
3.154
S2BeanDescProvider$S2ParameterizedClassDesc
27%
3/11
0%
0/2
3.154
S2BeanDescProvider$S2PropertyAttribute
25%
23/89
2%
1/50
3.154
 
 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.plugins.s2.spi;
 17  
 
 18  
 import static org.seasar.cubby.internal.util.ReflectionUtils.findAllDeclaredField;
 19  
 
 20  
 import java.lang.annotation.Annotation;
 21  
 import java.lang.reflect.Field;
 22  
 import java.lang.reflect.Method;
 23  
 import java.util.ArrayList;
 24  
 import java.util.HashMap;
 25  
 import java.util.HashSet;
 26  
 import java.util.LinkedHashMap;
 27  
 import java.util.List;
 28  
 import java.util.Map;
 29  
 import java.util.Set;
 30  
 
 31  
 import org.seasar.cubby.spi.BeanDescProvider;
 32  
 import org.seasar.cubby.spi.beans.Attribute;
 33  
 import org.seasar.cubby.spi.beans.BeanDesc;
 34  
 import org.seasar.cubby.spi.beans.IllegalAttributeException;
 35  
 import org.seasar.cubby.spi.beans.ParameterizedClassDesc;
 36  
 import org.seasar.cubby.spi.beans.impl.DefaultBeanDescProvider;
 37  
 import org.seasar.framework.beans.IllegalPropertyRuntimeException;
 38  
 import org.seasar.framework.beans.PropertyDesc;
 39  
 import org.seasar.framework.util.Disposable;
 40  
 import org.seasar.framework.util.DisposableUtil;
 41  
 
 42  
 /**
 43  
  * S2Container 向けの {@link BeanDescProvider} の実装クラスです。
 44  
  * <p>
 45  
  * getter/setter メソッドだけではなく、public フィールドもプロパティとして認識します。
 46  
  * </p>
 47  
  * <p>
 48  
  * {@link org.seasar.framework.beans.factory.BeanDescFactory} によって生成されるメタ情報を元に
 49  
  * {@link BeanDesc} を構築します。
 50  
  * </p>
 51  
  * 
 52  
  * @author baba
 53  
  */
 54  9
 public class S2BeanDescProvider extends DefaultBeanDescProvider {
 55  
 
 56  
         private static volatile boolean initialized;
 57  
 
 58  
         private void initialize() {
 59  9
                 if (!initialized) {
 60  1
                         DisposableUtil.add(new Disposable() {
 61  
 
 62  
                                 public void dispose() {
 63  0
                                         beanDescCache.clear();
 64  0
                                         initialized = false;
 65  0
                                 }
 66  
 
 67  
                         });
 68  1
                         initialized = true;
 69  
                 }
 70  9
         }
 71  
 
 72  
         /**
 73  
          * {@inheritDoc}
 74  
          */
 75  
         @Override
 76  
         public BeanDesc getBeanDesc(final Class<?> clazz) {
 77  9
                 initialize();
 78  9
                 return super.getBeanDesc(clazz);
 79  
         }
 80  
 
 81  
         /**
 82  
          * {@inheritDoc}
 83  
          */
 84  
         @Override
 85  
         protected BeanDesc createBeanDesc(final Class<?> clazz) {
 86  9
                 return new S2BeanDescImpl(clazz);
 87  
         }
 88  
 
 89  
         /**
 90  
          * {@link BeanDesc} の実装です。
 91  
          * <p>
 92  
          * Seasar2 の {@link org.seasar.framework.beans.BeanDesc} に処理を委譲します。
 93  
          * </p>
 94  
          * 
 95  
          * @author baba
 96  
          */
 97  
         private static class S2BeanDescImpl extends BeanDescImpl {
 98  
 
 99  
                 /** プロパティとして認識されたフィールド。 */
 100  
                 private Set<Field> recognizedAsPropertyFields;
 101  
 
 102  
                 /**
 103  
                  * インスタンス化します。
 104  
                  * 
 105  
                  * @param clazz
 106  
                  *            操作対象のクラス
 107  
                  */
 108  
                 S2BeanDescImpl(final Class<?> clazz) {
 109  9
                         super(clazz);
 110  9
                 }
 111  
 
 112  
                 /**
 113  
                  * {@inheritDoc}
 114  
                  */
 115  
                 @Override
 116  
                 protected Map<String, Attribute> collectPropertyAttributeMap(
 117  
                                 final Class<?> clazz) {
 118  9
                         this.recognizedAsPropertyFields = new HashSet<Field>();
 119  9
                         final org.seasar.framework.beans.BeanDesc s2BeanDesc = org.seasar.framework.beans.factory.BeanDescFactory
 120  
                                         .getBeanDesc(clazz);
 121  9
                         final Map<String, Attribute> attributes = new LinkedHashMap<String, Attribute>();
 122  98
                         for (int i = 0; i < s2BeanDesc.getPropertyDescSize(); i++) {
 123  89
                                 final org.seasar.framework.beans.PropertyDesc propertyDesc = s2BeanDesc
 124  
                                                 .getPropertyDesc(i);
 125  89
                                 final Attribute attribute = new S2PropertyAttribute(
 126  
                                                 propertyDesc);
 127  89
                                 if (!propertyDesc.hasReadMethod()
 128  
                                                 && !propertyDesc.hasWriteMethod()) {
 129  8
                                         final Field field = propertyDesc.getField();
 130  8
                                         if (field != null) {
 131  8
                                                 recognizedAsPropertyFields.add(field);
 132  
                                         }
 133  
                                 }
 134  89
                                 attributes.put(propertyDesc.getPropertyName(), attribute);
 135  
                         }
 136  9
                         return attributes;
 137  
                 }
 138  
 
 139  
                 /**
 140  
                  * {@inheritDoc}
 141  
                  */
 142  
                 @Override
 143  
                 protected Map<String, List<Attribute>> collectFieldAttributesMap(
 144  
                                 final Class<?> clazz) {
 145  9
                         final Map<String, List<Attribute>> fieldAttributes = new LinkedHashMap<String, List<Attribute>>();
 146  9
                         for (final Field field : findAllDeclaredField(clazz)) {
 147  61
                                 if (recognizedAsPropertyFields.contains(field)) {
 148  8
                                         continue;
 149  
                                 }
 150  53
                                 final String fieldName = field.getName();
 151  
                                 List<Attribute> fieldDescs;
 152  53
                                 if (!fieldAttributes.containsKey(fieldName)) {
 153  53
                                         fieldDescs = new ArrayList<Attribute>();
 154  53
                                         fieldAttributes.put(fieldName, fieldDescs);
 155  
                                 } else {
 156  0
                                         fieldDescs = fieldAttributes.get(fieldName);
 157  
                                 }
 158  53
                                 final Attribute attributes = new FieldAttribute(clazz, field);
 159  53
                                 fieldDescs.add(attributes);
 160  53
                         }
 161  9
                         return fieldAttributes;
 162  
                 }
 163  
 
 164  
         }
 165  
 
 166  
         /**
 167  
          * {@link PropertyDesc} の実装です。
 168  
          * <p>
 169  
          * Seasar2 の {@link org.seasar.framework.beans.PropertyDesc} に処理を委譲します。
 170  
          * </p>
 171  
          * 
 172  
          * @author baba
 173  
          */
 174  
         private static class S2PropertyAttribute implements Attribute {
 175  
 
 176  
                 /** Seasar2 の {@link org.seasar.framework.beans.PropertyDesc} */
 177  
                 private final org.seasar.framework.beans.PropertyDesc s2PropertyDesc;
 178  
 
 179  
                 /** パラメタ化されたクラスの定義。 */
 180  
                 private final ParameterizedClassDesc parameterizedClassDesc;
 181  
 
 182  
                 /** アノテーションのキャッシュ。 */
 183  89
                 private final Map<Class<? extends Annotation>, Annotation> annotationCache = new HashMap<Class<? extends Annotation>, Annotation>();
 184  
 
 185  
                 /**
 186  
                  * インスタンス化します。
 187  
                  * 
 188  
                  * @param s2PropertyDesc
 189  
                  *            Seasar2 の {@link org.seasar.framework.beans.PropertyDesc}
 190  
                  */
 191  
                 S2PropertyAttribute(
 192  89
                                 final org.seasar.framework.beans.PropertyDesc s2PropertyDesc) {
 193  89
                         this.s2PropertyDesc = s2PropertyDesc;
 194  89
                         this.parameterizedClassDesc = new S2ParameterizedClassDesc(
 195  
                                         s2PropertyDesc.getParameterizedClassDesc());
 196  89
                 }
 197  
 
 198  
                 /**
 199  
                  * {@inheritDoc}
 200  
                  */
 201  
                 public String getName() {
 202  4
                         return s2PropertyDesc.getPropertyName();
 203  
                 }
 204  
 
 205  
                 /**
 206  
                  * {@inheritDoc}
 207  
                  */
 208  
                 public Class<?> getType() {
 209  4
                         return s2PropertyDesc.getPropertyType();
 210  
                 }
 211  
 
 212  
                 /**
 213  
                  * {@inheritDoc}
 214  
                  */
 215  
                 public boolean isReadable() {
 216  3
                         return s2PropertyDesc.isReadable();
 217  
                 }
 218  
 
 219  
                 /**
 220  
                  * {@inheritDoc}
 221  
                  */
 222  
                 public boolean isWritable() {
 223  3
                         return s2PropertyDesc.isWritable();
 224  
                 }
 225  
 
 226  
                 /**
 227  
                  * {@inheritDoc}
 228  
                  */
 229  
                 public Object getValue(final Object target)
 230  
                                 throws IllegalAttributeException {
 231  
                         try {
 232  2
                                 return s2PropertyDesc.getValue(target);
 233  2
                         } catch (final IllegalPropertyRuntimeException e) {
 234  2
                                 throw new IllegalAttributeException(e.getTargetClass(), e
 235  
                                                 .getPropertyName(), e);
 236  
                         }
 237  
                 }
 238  
 
 239  
                 /**
 240  
                  * {@inheritDoc}
 241  
                  */
 242  
                 public void setValue(final Object target, final Object value)
 243  
                                 throws IllegalAttributeException {
 244  
                         try {
 245  5
                                 s2PropertyDesc.setValue(target, value);
 246  3
                         } catch (final IllegalPropertyRuntimeException e) {
 247  3
                                 throw new IllegalAttributeException(e.getTargetClass(), e
 248  
                                                 .getPropertyName(), e);
 249  2
                         }
 250  2
                 }
 251  
 
 252  
                 /**
 253  
                  * {@inheritDoc}
 254  
                  */
 255  
                 public ParameterizedClassDesc getParameterizedClassDesc() {
 256  0
                         return parameterizedClassDesc;
 257  
                 }
 258  
 
 259  
                 /**
 260  
                  * {@inheritDoc}
 261  
                  */
 262  
                 public <T extends Annotation> T getAnnotation(
 263  
                                 final Class<T> annotationClass) {
 264  0
                         if (annotationCache.containsKey(annotationClass)) {
 265  0
                                 return annotationClass.cast(annotationCache
 266  
                                                 .get(annotationClass));
 267  
                         }
 268  
 
 269  0
                         if (s2PropertyDesc.hasReadMethod()) {
 270  0
                                 final Method method = s2PropertyDesc.getReadMethod();
 271  0
                                 final T annotation = findAnnotation(annotationClass, method);
 272  0
                                 if (annotation != null) {
 273  0
                                         annotationCache.put(annotationClass, annotation);
 274  0
                                         return annotation;
 275  
                                 }
 276  
                         }
 277  0
                         if (s2PropertyDesc.hasWriteMethod()) {
 278  0
                                 final Method method = s2PropertyDesc.getWriteMethod();
 279  0
                                 final T annotation = findAnnotation(annotationClass, method);
 280  0
                                 if (annotation != null) {
 281  0
                                         annotationCache.put(annotationClass, annotation);
 282  0
                                         return annotation;
 283  
                                 }
 284  
                         }
 285  0
                         final Field field = s2PropertyDesc.getField();
 286  0
                         if (field != null) {
 287  0
                                 final T annotation = field.getAnnotation(annotationClass);
 288  0
                                 if (annotation != null) {
 289  0
                                         annotationCache.put(annotationClass, annotation);
 290  0
                                         return field.getAnnotation(annotationClass);
 291  
                                 }
 292  
                         }
 293  0
                         return null;
 294  
                 }
 295  
 
 296  
                 /**
 297  
                  * 指定されたメソッドのアノテーションを検索します。
 298  
                  * <p>
 299  
                  * インターフェイスやスーパークラスに定義されたメソッドの定義からもアノテーションが見つかるまで検索します。
 300  
                  * アノテーションが見つからなかった場合は <code>null</code> を返します。
 301  
                  * </p>
 302  
                  * 
 303  
                  * @param <T>
 304  
                  *            アノテーションの型
 305  
                  * @param annotationClass
 306  
                  *            アノテーションの型
 307  
                  * @param method
 308  
                  *            メソッド
 309  
                  * @return アノテーションが見つかった場合はそのアノテーション、見つからなかった場合は <code>null</code>
 310  
                  */
 311  
                 private static <T extends Annotation> T findAnnotation(
 312  
                                 final Class<T> annotationClass, final Method method) {
 313  0
                         final String methodName = method.getName();
 314  0
                         final Class<?>[] parameterTypes = method.getParameterTypes();
 315  0
                         for (Class<?> target = method.getDeclaringClass(); !target
 316  0
                                         .equals(Object.class); target = target.getSuperclass()) {
 317  0
                                 final T annotation = getAnnotation(annotationClass, target,
 318  
                                                 methodName, parameterTypes);
 319  0
                                 if (annotation != null) {
 320  0
                                         return annotation;
 321  
                                 }
 322  0
                                 final T annotationOfInterfaces = getAnnotationOfInterfaces(
 323  
                                                 annotationClass, target, methodName, parameterTypes);
 324  0
                                 if (annotationOfInterfaces != null) {
 325  0
                                         return annotationOfInterfaces;
 326  
                                 }
 327  
                         }
 328  0
                         return null;
 329  
                 }
 330  
 
 331  
                 /**
 332  
                  * 指定されたクラスが実装するインターフェイスにメソッド名、パラメータ型でシグニチャを指定されたメソッドが定義されていれば、
 333  
                  * そのメソッドに定義されたアノテーションを返します。
 334  
                  * 
 335  
                  * @param <T>
 336  
                  *            アノテーションの型
 337  
                  * @param annotationClass
 338  
                  *            アノテーションの型
 339  
                  * @param clazz
 340  
                  *            クラス
 341  
                  * @param methodName
 342  
                  *            メソッド名
 343  
                  * @param parameterTypes
 344  
                  *            パラメータの型
 345  
                  * @return アノテーション
 346  
                  */
 347  
                 private static <T extends Annotation> T getAnnotationOfInterfaces(
 348  
                                 final Class<T> annotationClass, final Class<?> clazz,
 349  
                                 final String methodName, final Class<?>[] parameterTypes) {
 350  0
                         for (final Class<?> interfaceClass : clazz.getInterfaces()) {
 351  0
                                 final T annotation = getAnnotation(annotationClass,
 352  
                                                 interfaceClass, methodName, parameterTypes);
 353  0
                                 if (annotation != null) {
 354  0
                                         return annotation;
 355  
                                 }
 356  
                         }
 357  0
                         return null;
 358  
                 }
 359  
 
 360  
                 /**
 361  
                  * 指定されたクラスにメソッド名、パラメータ型でシグニチャを指定されたメソッドが定義されていれば、
 362  
                  * そのメソッドに定義されたアノテーションを返します。
 363  
                  * 
 364  
                  * @param <T>
 365  
                  *            アノテーションの型
 366  
                  * @param annotationClass
 367  
                  *            アノテーションの型
 368  
                  * @param clazz
 369  
                  *            クラス
 370  
                  * @param methodName
 371  
                  *            メソッド名
 372  
                  * @param parameterTypes
 373  
                  *            パラメータの型
 374  
                  * @return アノテーション
 375  
                  */
 376  
                 private static <T extends Annotation> T getAnnotation(
 377  
                                 final Class<T> annotationClass, final Class<?> clazz,
 378  
                                 final String methodName,
 379  
                                 @SuppressWarnings("unchecked") final Class[] parameterTypes) {
 380  
                         try {
 381  0
                                 final Method method = clazz.getDeclaredMethod(methodName,
 382  
                                                 parameterTypes);
 383  0
                                 if (method.isAnnotationPresent(annotationClass)) {
 384  0
                                         return method.getAnnotation(annotationClass);
 385  
                                 }
 386  0
                         } catch (final NoSuchMethodException e) {
 387  
                                 // do nothing
 388  0
                         }
 389  
 
 390  0
                         return null;
 391  
                 }
 392  
 
 393  
                 /**
 394  
                  * {@inheritDoc}
 395  
                  */
 396  
                 public boolean isAnnotationPresent(
 397  
                                 final Class<? extends Annotation> annotationClass) {
 398  0
                         return this.getAnnotation(annotationClass) != null;
 399  
                 }
 400  
 
 401  
                 /**
 402  
                  * {@inheritDoc}
 403  
                  */
 404  
                 @Override
 405  
                 public int hashCode() {
 406  5
                         final int prime = 31;
 407  5
                         int result = 1;
 408  5
                         result = prime
 409  
                                         * result
 410  
                                         + ((s2PropertyDesc == null) ? 0 : s2PropertyDesc.hashCode());
 411  5
                         result = prime * result
 412  
                                         + s2PropertyDesc.getBeanDesc().getBeanClass().hashCode();
 413  5
                         result = prime * result
 414  
                                         + s2PropertyDesc.getPropertyName().hashCode();
 415  5
                         return result;
 416  
                 }
 417  
 
 418  
                 /**
 419  
                  * {@inheritDoc}
 420  
                  */
 421  
                 @Override
 422  
                 public boolean equals(final Object obj) {
 423  0
                         if (this == obj) {
 424  0
                                 return true;
 425  
                         }
 426  0
                         if (obj == null) {
 427  0
                                 return false;
 428  
                         }
 429  0
                         if (getClass() != obj.getClass()) {
 430  0
                                 return false;
 431  
                         }
 432  
 
 433  0
                         if (this == obj) {
 434  0
                                 return true;
 435  
                         }
 436  0
                         if (obj == null) {
 437  0
                                 return false;
 438  
                         }
 439  0
                         if (getClass() != obj.getClass()) {
 440  0
                                 return false;
 441  
                         }
 442  0
                         final S2PropertyAttribute other = (S2PropertyAttribute) obj;
 443  0
                         if (s2PropertyDesc == null) {
 444  0
                                 if (other.s2PropertyDesc != null) {
 445  0
                                         return false;
 446  
                                 }
 447  
                         } else {
 448  0
                                 if (!s2PropertyDesc.getBeanDesc().getBeanClass().equals(
 449  
                                                 other.s2PropertyDesc.getBeanDesc().getBeanClass())) {
 450  0
                                         return false;
 451  
                                 }
 452  0
                                 if (!s2PropertyDesc.getPropertyName().equals(
 453  
                                                 other.s2PropertyDesc.getPropertyName())) {
 454  0
                                         return false;
 455  
                                 }
 456  
                         }
 457  0
                         return true;
 458  
                 }
 459  
 
 460  
         }
 461  
 
 462  
         /**
 463  
          * {@link ParameterizedClassDesc} の実装です。
 464  
          * <p>
 465  
          * Seasar2 の {@link org.seasar.framework.beans.ParameterizedClassDesc}
 466  
          * に処理を委譲します。
 467  
          * </p>
 468  
          * 
 469  
          * @author baba
 470  
          */
 471  9
         private static class S2ParameterizedClassDesc implements
 472  
                         ParameterizedClassDesc {
 473  
 
 474  
                 /** Seasar2 の {@link org.seasar.framework.beans.ParameterizedClassDesc} */
 475  
                 private final org.seasar.framework.beans.ParameterizedClassDesc s2ParameterizedClassDesc;
 476  
 
 477  
                 /**
 478  
                  * インスタンス化します。
 479  
                  * 
 480  
                  * @param s2ParameterizedClassDesc
 481  
                  *            Seasar2 の
 482  
                  *            {@link org.seasar.framework.beans.ParameterizedClassDesc}
 483  
                  */
 484  
                 S2ParameterizedClassDesc(
 485  89
                                 final org.seasar.framework.beans.ParameterizedClassDesc s2ParameterizedClassDesc) {
 486  89
                         this.s2ParameterizedClassDesc = s2ParameterizedClassDesc;
 487  89
                 }
 488  
 
 489  
                 /**
 490  
                  * {@inheritDoc}
 491  
                  */
 492  
                 public boolean isParameterizedClass() {
 493  0
                         return s2ParameterizedClassDesc.isParameterizedClass();
 494  
                 }
 495  
 
 496  
                 /**
 497  
                  * {@inheritDoc}
 498  
                  */
 499  
                 public Class<?> getRawClass() {
 500  0
                         return s2ParameterizedClassDesc.getRawClass();
 501  
                 }
 502  
 
 503  
                 /**
 504  
                  * {@inheritDoc}
 505  
                  */
 506  
                 public ParameterizedClassDesc[] getArguments() {
 507  0
                         final org.seasar.framework.beans.ParameterizedClassDesc[] s2Arguments = this.s2ParameterizedClassDesc
 508  
                                         .getArguments();
 509  0
                         final List<ParameterizedClassDesc> arguments = new ArrayList<ParameterizedClassDesc>(
 510  
                                         s2Arguments.length);
 511  0
                         for (final org.seasar.framework.beans.ParameterizedClassDesc s2Argument : s2Arguments) {
 512  0
                                 final S2ParameterizedClassDesc argument = new S2ParameterizedClassDesc(
 513  
                                                 s2Argument);
 514  0
                                 arguments.add(argument);
 515  
                         }
 516  0
                         return arguments.toArray(new ParameterizedClassDesc[0]);
 517  
                 }
 518  
 
 519  
         }
 520  
 
 521  
 }