View Javadoc

1   /*
2    * Copyright 2004-2008 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.converter.impl;
17  
18  import java.lang.reflect.Modifier;
19  import java.util.HashMap;
20  import java.util.LinkedHashSet;
21  import java.util.Map;
22  import java.util.Set;
23  import java.util.TreeMap;
24  
25  import org.seasar.cubby.controller.ClassDetector;
26  import org.seasar.cubby.controller.DetectClassProcessor;
27  import org.seasar.cubby.converter.Converter;
28  import org.seasar.cubby.converter.ConverterFactory;
29  import org.seasar.framework.container.S2Container;
30  import org.seasar.framework.convention.NamingConvention;
31  import org.seasar.framework.util.ClassUtil;
32  import org.seasar.framework.util.Disposable;
33  import org.seasar.framework.util.DisposableUtil;
34  
35  /**
36   * コンバータファクトリの実装クラスです。
37   * 
38   * @author baba
39   * @since 1.1.0
40   */
41  public class ConverterFactoryImpl implements ConverterFactory, DetectClassProcessor,
42  		Disposable {
43  
44  	/** インスタンスが初期化済みであることを示します。 */
45  	private boolean initialized;
46  
47  	/** このファクトリを管理しているコンテナです。 */
48  	private S2Container container;
49  
50  	/** 命名規約。 */
51  	private NamingConvention namingConvention;
52  
53  	/** クラスパスを走査してクラスを検出するクラス。 */
54  	private ClassDetector classDetector;
55  
56  	/** コンバータのセットです。 */
57  	private Set<Converter> converters = new LinkedHashSet<Converter>();
58  
59  	/** コンバータのキャッシュです。 */
60  	private Map<String, Converter> converterCache = new HashMap<String, Converter>();
61  
62  	/**
63  	 * <code>ConverterFactoryImpl</code>のインスタンスを構築します。
64  	 */
65  	public ConverterFactoryImpl() {
66  	}
67  
68  	/**
69  	 * コンテナを設定します。
70  	 * 
71  	 * @param container
72  	 *            コンテナ
73  	 */
74  	public void setContainer(final S2Container container) {
75  		this.container = container.getRoot();
76  	}
77  
78  	/**
79  	 * 命名規約を設定します。
80  	 * 
81  	 * @param namingConvention
82  	 *            命名規約
83  	 */
84  	public void setNamingConvention(final NamingConvention namingConvention) {
85  		this.namingConvention = namingConvention;
86  	}
87  
88  	/**
89  	 * クラスパスを走査してクラスを検出するクラスを設定します。
90  	 * 
91  	 * @param classDetector
92  	 *            クラスパスを走査してクラスを設定します。
93  	 */
94  	public void setClassDetector(final ClassDetector classDetector) {
95  		this.classDetector = classDetector;
96  	}
97  
98  	/**
99  	 * インスタンスを初期化します。
100 	 */
101 	public void initialize() {
102 		if (initialized) {
103 			return;
104 		}
105 		classDetector.detect();
106 
107 		for (final Converter converter : Converter[].class.cast(container
108 				.findAllComponents(Converter.class))) {
109 			converters.add(converter);
110 		}
111 		DisposableUtil.add(this);
112 		initialized = true;
113 	}
114 
115 	/**
116 	 * キャッシュ情報等を破棄し、インスタンスを未初期化状態に戻します。
117 	 * 
118 	 */
119 	public void dispose() {
120 		converters.clear();
121 		converterCache.clear();
122 		initialized = false;
123 	}
124 
125 	/**
126 	 * {@inheritDoc}
127 	 */
128 	public Converter getConverter(final Class<?> parameterType,
129 			final Class<?> objectType) {
130 		initialize();
131 		final Class<?> destType = ClassUtil
132 				.getWrapperClassIfPrimitive(objectType);
133 		final String cacheKey = cacheKey(parameterType, destType);
134 		final Converter converter = converterCache.get(cacheKey);
135 		if (converter != null) {
136 			return converter;
137 		}
138 		return detectConverter(parameterType, destType);
139 	}
140 
141 	private Converter detectConverter(final Class<?> parameterType,
142 			final Class<?> objectType) {
143 		final Converter converter = getDistanceTable(parameterType, objectType);
144 		final String cacheKey = cacheKey(parameterType, objectType);
145 		converterCache.put(cacheKey, converter);
146 		return converter;
147 	}
148 
149 	private static String cacheKey(final Class<?> parameterType,
150 			final Class<?> objectType) {
151 		if (parameterType == null) {
152 			return objectType.getName();
153 		}
154 		return parameterType.getName() + objectType.getName();
155 	}
156 
157 	private Converter getDistanceTable(final Class<?> parameterType,
158 			final Class<?> objectType) {
159 		final Map<Integer, Converter> distanceTable = new TreeMap<Integer, Converter>();
160 		for (final Converter converter : converters) {
161 			if (!converter.canConvert(parameterType, objectType)) {
162 				continue;
163 			}
164 			final int distance = getDistance(converter.getObjectType(),
165 					objectType);
166 			distanceTable.put(distance, converter);
167 		}
168 		if (distanceTable.isEmpty()) {
169 			return null;
170 		}
171 		return distanceTable.values().iterator().next();
172 	}
173 
174 	private int getDistance(final Class<?> assigner, final Class<?> assignee) {
175 		return getDistance(assigner, assignee, 0);
176 	}
177 
178 	private int getDistance(final Class<?> assigner, final Class<?> assignee,
179 			final int distance) {
180 		if (assignee.equals(assigner)) {
181 			return distance;
182 		}
183 		if (Enum.class.equals(assigner) && assignee.isEnum()) {
184 			return distance + 5;
185 		}
186 		if (isImplements(assigner, assignee)) {
187 			return distance + 5;
188 		}
189 
190 		final Class<?> superClass = assigner.getSuperclass();
191 		if (superClass == null) {
192 			return distance + 10;
193 		}
194 		return getDistance(superClass, assignee, distance + 10);
195 	}
196 
197 	private boolean isImplements(final Class<?> assigner,
198 			final Class<?> assignee) {
199 		return !assigner.isInterface() && assignee.isInterface()
200 				&& assignee.isAssignableFrom(assigner);
201 	}
202 
203 	/**
204 	 * {@inheritDoc}
205 	 * <p>
206 	 * 指定されたパッケージ名、クラス名から導出されるクラスがコンバータだった場合はファクトリにコンバータを登録します。
207 	 * </p>
208 	 */
209 	public void processClass(final String packageName,
210 			final String shortClassName) {
211 		if (shortClassName.indexOf('$') != -1) {
212 			return;
213 		}
214 		final String className = ClassUtil.concatName(packageName,
215 				shortClassName);
216 		if (!namingConvention.isTargetClassName(className)) {
217 			return;
218 		}
219 		if (!className.endsWith(namingConvention.getConverterSuffix())) {
220 			return;
221 		}
222 		final Class<?> clazz = ClassUtil.forName(className);
223 		if (namingConvention.isSkipClass(clazz)) {
224 			return;
225 		}
226 		if ((clazz.getModifiers() & Modifier.ABSTRACT) != 0) {
227 			return;
228 		}
229 		if (!Converter.class.isAssignableFrom(clazz)) {
230 			return;
231 		}
232 		final Converter converter = Converter.class.cast(container
233 				.getComponent(clazz));
234 		converters.add(converter);
235 	}
236 
237 }