View Javadoc

    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  public class S2BeanDescProvider extends DefaultBeanDescProvider {
    55
    56  	private static volatile boolean initialized;
    57
    58  	private void initialize() {
    59  		if (!initialized) {
    60  			DisposableUtil.add(new Disposable() {
    61
    62  				public void dispose() {
    63  					beanDescCache.clear();
    64  					initialized = false;
    65  				}
    66
    67  			});
    68  			initialized = true;
    69  		}
    70  	}
    71
    72  	/**
    73  	 * {@inheritDoc}
    74  	 */
    75  	@Override
    76  	public BeanDesc getBeanDesc(final Class<?> clazz) {
    77  		initialize();
    78  		return super.getBeanDesc(clazz);
    79  	}
    80
    81  	/**
    82  	 * {@inheritDoc}
    83  	 */
    84  	@Override
    85  	protected BeanDesc createBeanDesc(final Class<?> clazz) {
    86  		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 			super(clazz);
    110 		}
    111
    112 		/**
    113 		 * {@inheritDoc}
    114 		 */
    115 		@Override
    116 		protected Map<String, Attribute> collectPropertyAttributeMap(
    117 				final Class<?> clazz) {
    118 			this.recognizedAsPropertyFields = new HashSet<Field>();
    119 			final org.seasar.framework.beans.BeanDesc s2BeanDesc = org.seasar.framework.beans.factory.BeanDescFactory
    120 					.getBeanDesc(clazz);
    121 			final Map<String, Attribute> attributes = new LinkedHashMap<String, Attribute>();
    122 			for (int i = 0; i < s2BeanDesc.getPropertyDescSize(); i++) {
    123 				final org.seasar.framework.beans.PropertyDesc propertyDesc = s2BeanDesc
    124 						.getPropertyDesc(i);
    125 				final Attribute attribute = new S2PropertyAttribute(
    126 						propertyDesc);
    127 				if (!propertyDesc.hasReadMethod()
    128 						&& !propertyDesc.hasWriteMethod()) {
    129 					final Field field = propertyDesc.getField();
    130 					if (field != null) {
    131 						recognizedAsPropertyFields.add(field);
    132 					}
    133 				}
    134 				attributes.put(propertyDesc.getPropertyName(), attribute);
    135 			}
    136 			return attributes;
    137 		}
    138
    139 		/**
    140 		 * {@inheritDoc}
    141 		 */
    142 		@Override
    143 		protected Map<String, List<Attribute>> collectFieldAttributesMap(
    144 				final Class<?> clazz) {
    145 			final Map<String, List<Attribute>> fieldAttributes = new LinkedHashMap<String, List<Attribute>>();
    146 			for (final Field field : findAllDeclaredField(clazz)) {
    147 				if (recognizedAsPropertyFields.contains(field)) {
    148 					continue;
    149 				}
    150 				final String fieldName = field.getName();
    151 				List<Attribute> fieldDescs;
    152 				if (!fieldAttributes.containsKey(fieldName)) {
    153 					fieldDescs = new ArrayList<Attribute>();
    154 					fieldAttributes.put(fieldName, fieldDescs);
    155 				} else {
    156 					fieldDescs = fieldAttributes.get(fieldName);
    157 				}
    158 				final Attribute attributes = new FieldAttribute(clazz, field);
    159 				fieldDescs.add(attributes);
    160 			}
    161 			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 		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 				final org.seasar.framework.beans.PropertyDesc s2PropertyDesc) {
    193 			this.s2PropertyDesc = s2PropertyDesc;
    194 			this.parameterizedClassDesc = new S2ParameterizedClassDesc(
    195 					s2PropertyDesc.getParameterizedClassDesc());
    196 		}
    197
    198 		/**
    199 		 * {@inheritDoc}
    200 		 */
    201 		public String getName() {
    202 			return s2PropertyDesc.getPropertyName();
    203 		}
    204
    205 		/**
    206 		 * {@inheritDoc}
    207 		 */
    208 		public Class<?> getType() {
    209 			return s2PropertyDesc.getPropertyType();
    210 		}
    211
    212 		/**
    213 		 * {@inheritDoc}
    214 		 */
    215 		public boolean isReadable() {
    216 			return s2PropertyDesc.isReadable();
    217 		}
    218
    219 		/**
    220 		 * {@inheritDoc}
    221 		 */
    222 		public boolean isWritable() {
    223 			return s2PropertyDesc.isWritable();
    224 		}
    225
    226 		/**
    227 		 * {@inheritDoc}
    228 		 */
    229 		public Object getValue(final Object target)
    230 				throws IllegalAttributeException {
    231 			try {
    232 				return s2PropertyDesc.getValue(target);
    233 			} catch (final IllegalPropertyRuntimeException e) {
    234 				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 				s2PropertyDesc.setValue(target, value);
    246 			} catch (final IllegalPropertyRuntimeException e) {
    247 				throw new IllegalAttributeException(e.getTargetClass(), e
    248 						.getPropertyName(), e);
    249 			}
    250 		}
    251
    252 		/**
    253 		 * {@inheritDoc}
    254 		 */
    255 		public ParameterizedClassDesc getParameterizedClassDesc() {
    256 			return parameterizedClassDesc;
    257 		}
    258
    259 		/**
    260 		 * {@inheritDoc}
    261 		 */
    262 		public <T extends Annotation> T getAnnotation(
    263 				final Class<T> annotationClass) {
    264 			if (annotationCache.containsKey(annotationClass)) {
    265 				return annotationClass.cast(annotationCache
    266 						.get(annotationClass));
    267 			}
    268
    269 			if (s2PropertyDesc.hasReadMethod()) {
    270 				final Method method = s2PropertyDesc.getReadMethod();
    271 				final T annotation = findAnnotation(annotationClass, method);
    272 				if (annotation != null) {
    273 					annotationCache.put(annotationClass, annotation);
    274 					return annotation;
    275 				}
    276 			}
    277 			if (s2PropertyDesc.hasWriteMethod()) {
    278 				final Method method = s2PropertyDesc.getWriteMethod();
    279 				final T annotation = findAnnotation(annotationClass, method);
    280 				if (annotation != null) {
    281 					annotationCache.put(annotationClass, annotation);
    282 					return annotation;
    283 				}
    284 			}
    285 			final Field field = s2PropertyDesc.getField();
    286 			if (field != null) {
    287 				final T annotation = field.getAnnotation(annotationClass);
    288 				if (annotation != null) {
    289 					annotationCache.put(annotationClass, annotation);
    290 					return field.getAnnotation(annotationClass);
    291 				}
    292 			}
    293 			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 			final String methodName = method.getName();
    314 			final Class<?>[] parameterTypes = method.getParameterTypes();
    315 			for (Class<?> target = method.getDeclaringClass(); !target
    316 					.equals(Object.class); target = target.getSuperclass()) {
    317 				final T annotation = getAnnotation(annotationClass, target,
    318 						methodName, parameterTypes);
    319 				if (annotation != null) {
    320 					return annotation;
    321 				}
    322 				final T annotationOfInterfaces = getAnnotationOfInterfaces(
    323 						annotationClass, target, methodName, parameterTypes);
    324 				if (annotationOfInterfaces != null) {
    325 					return annotationOfInterfaces;
    326 				}
    327 			}
    328 			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 			for (final Class<?> interfaceClass : clazz.getInterfaces()) {
    351 				final T annotation = getAnnotation(annotationClass,
    352 						interfaceClass, methodName, parameterTypes);
    353 				if (annotation != null) {
    354 					return annotation;
    355 				}
    356 			}
    357 			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 				final Method method = clazz.getDeclaredMethod(methodName,
    382 						parameterTypes);
    383 				if (method.isAnnotationPresent(annotationClass)) {
    384 					return method.getAnnotation(annotationClass);
    385 				}
    386 			} catch (final NoSuchMethodException e) {
    387 				// do nothing
    388 			}
    389
    390 			return null;
    391 		}
    392
    393 		/**
    394 		 * {@inheritDoc}
    395 		 */
    396 		public boolean isAnnotationPresent(
    397 				final Class<? extends Annotation> annotationClass) {
    398 			return this.getAnnotation(annotationClass) != null;
    399 		}
    400
    401 		/**
    402 		 * {@inheritDoc}
    403 		 */
    404 		@Override
    405 		public int hashCode() {
    406 			final int prime = 31;
    407 			int result = 1;
    408 			result = prime
    409 					* result
    410 					+ ((s2PropertyDesc == null) ? 0 : s2PropertyDesc.hashCode());
    411 			result = prime * result
    412 					+ s2PropertyDesc.getBeanDesc().getBeanClass().hashCode();
    413 			result = prime * result
    414 					+ s2PropertyDesc.getPropertyName().hashCode();
    415 			return result;
    416 		}
    417
    418 		/**
    419 		 * {@inheritDoc}
    420 		 */
    421 		@Override
    422 		public boolean equals(final Object obj) {
    423 			if (this == obj) {
    424 				return true;
    425 			}
    426 			if (obj == null) {
    427 				return false;
    428 			}
    429 			if (getClass() != obj.getClass()) {
    430 				return false;
    431 			}
    432
    433 			if (this == obj) {
    434 				return true;
    435 			}
    436 			if (obj == null) {
    437 				return false;
    438 			}
    439 			if (getClass() != obj.getClass()) {
    440 				return false;
    441 			}
    442 			final S2PropertyAttribute other = (S2PropertyAttribute) obj;
    443 			if (s2PropertyDesc == null) {
    444 				if (other.s2PropertyDesc != null) {
    445 					return false;
    446 				}
    447 			} else {
    448 				if (!s2PropertyDesc.getBeanDesc().getBeanClass().equals(
    449 						other.s2PropertyDesc.getBeanDesc().getBeanClass())) {
    450 					return false;
    451 				}
    452 				if (!s2PropertyDesc.getPropertyName().equals(
    453 						other.s2PropertyDesc.getPropertyName())) {
    454 					return false;
    455 				}
    456 			}
    457 			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 	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 				final org.seasar.framework.beans.ParameterizedClassDesc s2ParameterizedClassDesc) {
    486 			this.s2ParameterizedClassDesc = s2ParameterizedClassDesc;
    487 		}
    488
    489 		/**
    490 		 * {@inheritDoc}
    491 		 */
    492 		public boolean isParameterizedClass() {
    493 			return s2ParameterizedClassDesc.isParameterizedClass();
    494 		}
    495
    496 		/**
    497 		 * {@inheritDoc}
    498 		 */
    499 		public Class<?> getRawClass() {
    500 			return s2ParameterizedClassDesc.getRawClass();
    501 		}
    502
    503 		/**
    504 		 * {@inheritDoc}
    505 		 */
    506 		public ParameterizedClassDesc[] getArguments() {
    507 			final org.seasar.framework.beans.ParameterizedClassDesc[] s2Arguments = this.s2ParameterizedClassDesc
    508 					.getArguments();
    509 			final List<ParameterizedClassDesc> arguments = new ArrayList<ParameterizedClassDesc>(
    510 					s2Arguments.length);
    511 			for (final org.seasar.framework.beans.ParameterizedClassDesc s2Argument : s2Arguments) {
    512 				final S2ParameterizedClassDesc argument = new S2ParameterizedClassDesc(
    513 						s2Argument);
    514 				arguments.add(argument);
    515 			}
    516 			return arguments.toArray(new ParameterizedClassDesc[0]);
    517 		}
    518
    519 	}
    520
    521 }