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