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 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  public class S2BeanDescProvider implements BeanDescProvider {
52  
53  	private static volatile boolean initialized;
54  
55  	/** <code>BeanDesc</code> のキャッシュ。 */
56  	private final Map<Class<?>, BeanDesc> beanDescCache = new ConcurrentHashMap<Class<?>, BeanDesc>(
57  			1024);
58  
59  	private void initialize() {
60  		if (!initialized) {
61  			DisposableUtil.add(new Disposable() {
62  
63  				public void dispose() {
64  					beanDescCache.clear();
65  					initialized = false;
66  				}
67  
68  			});
69  			initialized = true;
70  		}
71  	}
72  
73  	/**
74  	 * {@inheritDoc}
75  	 */
76  	public BeanDesc getBeanDesc(final Class<?> clazz) {
77  		initialize();
78  
79  		if (beanDescCache.containsKey(clazz)) {
80  			return beanDescCache.get(clazz);
81  		}
82  
83  		synchronized (clazz) {
84  			if (beanDescCache.containsKey(clazz)) {
85  				return beanDescCache.get(clazz);
86  			}
87  
88  			final org.seasar.framework.beans.BeanDesc s2beanDesc = org.seasar.framework.beans.factory.BeanDescFactory
89  					.getBeanDesc(clazz);
90  			final BeanDesc beanDesc = new S2BeanDescImpl(s2beanDesc);
91  			beanDescCache.put(clazz, beanDesc);
92  			return beanDesc;
93  		}
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 		S2BeanDescImpl(final org.seasar.framework.beans.BeanDesc s2BeanDesc) {
120 			this.s2BeanDesc = s2BeanDesc;
121 			this.propertyDescMap = new LinkedHashMap<String, PropertyDesc>();
122 			for (int i = 0; i < s2BeanDesc.getPropertyDescSize(); i++) {
123 				final org.seasar.framework.beans.PropertyDesc propertyDesc = s2BeanDesc
124 						.getPropertyDesc(i);
125 				propertyDescMap.put(propertyDesc.getPropertyName(),
126 						new S2PropertyDescImpl(propertyDesc));
127 			}
128 		}
129 
130 		/**
131 		 * {@inheritDoc}
132 		 */
133 		public Class<?> getBeanClass() {
134 			return s2BeanDesc.getBeanClass();
135 		}
136 
137 		/**
138 		 * {@inheritDoc}
139 		 */
140 		public PropertyDesc getPropertyDesc(final String propertyName)
141 				throws PropertyNotFoundException {
142 			if (!propertyDescMap.containsKey(propertyName)) {
143 				throw new PropertyNotFoundException(s2BeanDesc.getBeanClass(),
144 						propertyName);
145 			}
146 			return propertyDescMap.get(propertyName);
147 		}
148 
149 		/**
150 		 * {@inheritDoc}
151 		 */
152 		public PropertyDesc[] getPropertyDescs() {
153 			return propertyDescMap.values().toArray(new PropertyDesc[0]);
154 		}
155 
156 		/**
157 		 * {@inheritDoc}
158 		 */
159 		public boolean hasPropertyDesc(final String propertyName) {
160 			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 		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 				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 getPropertyName() {
202 			return s2PropertyDesc.getPropertyName();
203 		}
204 
205 		/**
206 		 * {@inheritDoc}
207 		 */
208 		public Class<?> getPropertyType() {
209 			return s2PropertyDesc.getPropertyType();
210 		}
211 
212 		/**
213 		 * {@inheritDoc}
214 		 */
215 		public Method getReadMethod() {
216 			return s2PropertyDesc.getReadMethod();
217 		}
218 
219 		/**
220 		 * {@inheritDoc}
221 		 */
222 		public boolean hasReadMethod() {
223 			return s2PropertyDesc.hasReadMethod();
224 		}
225 
226 		/**
227 		 * {@inheritDoc}
228 		 */
229 		public Method getWriteMethod() {
230 			return s2PropertyDesc.getWriteMethod();
231 		}
232 
233 		/**
234 		 * {@inheritDoc}
235 		 */
236 		public boolean hasWriteMethod() {
237 			return s2PropertyDesc.hasWriteMethod();
238 		}
239 
240 		/**
241 		 * {@inheritDoc}
242 		 */
243 		public boolean isReadable() {
244 			return s2PropertyDesc.isReadable();
245 		}
246 
247 		/**
248 		 * {@inheritDoc}
249 		 */
250 		public boolean isWritable() {
251 			return s2PropertyDesc.isWritable();
252 		}
253 
254 		/**
255 		 * {@inheritDoc}
256 		 */
257 		public Object getValue(final Object target)
258 				throws IllegalPropertyException, IllegalStateException {
259 			try {
260 				return s2PropertyDesc.getValue(target);
261 			} catch (final IllegalPropertyRuntimeException e) {
262 				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 				s2PropertyDesc.setValue(target, value);
274 			} catch (final IllegalPropertyRuntimeException e) {
275 				throw new IllegalPropertyException(e.getTargetClass(), e
276 						.getPropertyName(), e);
277 			}
278 		}
279 
280 		/**
281 		 * {@inheritDoc}
282 		 */
283 		public boolean isParameterized() {
284 			return s2PropertyDesc.isParameterized();
285 		}
286 
287 		/**
288 		 * {@inheritDoc}
289 		 */
290 		public ParameterizedClassDesc getParameterizedClassDesc() {
291 			return parameterizedClassDesc;
292 		}
293 
294 		/**
295 		 * {@inheritDoc}
296 		 */
297 		public <T extends Annotation> T getAnnotation(
298 				final Class<T> annotationClass) {
299 			if (annotationCache.containsKey(annotationClass)) {
300 				return annotationClass.cast(annotationCache
301 						.get(annotationClass));
302 			}
303 
304 			if (s2PropertyDesc.hasReadMethod()) {
305 				final Method method = s2PropertyDesc.getReadMethod();
306 				final T annotation = findAnnotation(annotationClass, method);
307 				if (annotation != null) {
308 					annotationCache.put(annotationClass, annotation);
309 					return annotation;
310 				}
311 			}
312 			if (s2PropertyDesc.hasWriteMethod()) {
313 				final Method method = s2PropertyDesc.getWriteMethod();
314 				final T annotation = findAnnotation(annotationClass, method);
315 				if (annotation != null) {
316 					annotationCache.put(annotationClass, annotation);
317 					return annotation;
318 				}
319 			}
320 			final Field field = s2PropertyDesc.getField();
321 			if (field != null) {
322 				final T annotation = field.getAnnotation(annotationClass);
323 				if (annotation != null) {
324 					annotationCache.put(annotationClass, annotation);
325 					return field.getAnnotation(annotationClass);
326 				}
327 			}
328 			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 			final String methodName = method.getName();
349 			final Class<?>[] parameterTypes = method.getParameterTypes();
350 			for (Class<?> target = method.getDeclaringClass(); !target
351 					.equals(Object.class); target = target.getSuperclass()) {
352 				final T annotation = getAnnotation(annotationClass, target,
353 						methodName, parameterTypes);
354 				if (annotation != null) {
355 					return annotation;
356 				}
357 				final T annotationOfInterfaces = getAnnotationOfInterfaces(
358 						annotationClass, target, methodName, parameterTypes);
359 				if (annotationOfInterfaces != null) {
360 					return annotationOfInterfaces;
361 				}
362 			}
363 			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 			for (final Class<?> interfaceClass : clazz.getInterfaces()) {
386 				final T annotation = getAnnotation(annotationClass,
387 						interfaceClass, methodName, parameterTypes);
388 				if (annotation != null) {
389 					return annotation;
390 				}
391 			}
392 			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 				final Method method = clazz.getDeclaredMethod(methodName,
417 						parameterTypes);
418 				if (method.isAnnotationPresent(annotationClass)) {
419 					return method.getAnnotation(annotationClass);
420 				}
421 			} catch (final NoSuchMethodException e) {
422 				// do nothing
423 			}
424 
425 			return null;
426 		}
427 
428 		/**
429 		 * {@inheritDoc}
430 		 */
431 		public boolean isAnnotationPresent(
432 				final Class<? extends Annotation> annotationClass) {
433 			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 	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 				final org.seasar.framework.beans.ParameterizedClassDesc s2ParameterizedClassDesc) {
463 			this.s2ParameterizedClassDesc = s2ParameterizedClassDesc;
464 		}
465 
466 		/**
467 		 * {@inheritDoc}
468 		 */
469 		public boolean isParameterizedClass() {
470 			return s2ParameterizedClassDesc.isParameterizedClass();
471 		}
472 
473 		/**
474 		 * {@inheritDoc}
475 		 */
476 		public Class<?> getRawClass() {
477 			return s2ParameterizedClassDesc.getRawClass();
478 		}
479 
480 		/**
481 		 * {@inheritDoc}
482 		 */
483 		public ParameterizedClassDesc[] getArguments() {
484 			final org.seasar.framework.beans.ParameterizedClassDesc[] s2Arguments = this.s2ParameterizedClassDesc
485 					.getArguments();
486 			final List<ParameterizedClassDesc> arguments = new ArrayList<ParameterizedClassDesc>(
487 					s2Arguments.length);
488 			for (final org.seasar.framework.beans.ParameterizedClassDesc s2Argument : s2Arguments) {
489 				final S2ParameterizedClassDesc argument = new S2ParameterizedClassDesc(
490 						s2Argument);
491 				arguments.add(argument);
492 			}
493 			return arguments.toArray(new ParameterizedClassDesc[0]);
494 		}
495 
496 	}
497 
498 }