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 }