View Javadoc

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