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