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