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