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.util.HashMap;
19  import java.util.Map;
20  import java.util.TreeMap;
21  
22  import org.seasar.cubby.converter.Converter;
23  import org.seasar.cubby.converter.ConverterFactory;
24  import org.seasar.framework.container.S2Container;
25  import org.seasar.framework.util.ClassUtil;
26  import org.seasar.framework.util.Disposable;
27  import org.seasar.framework.util.DisposableUtil;
28  
29  /**
30   * コンバータファクトリの実装クラスです。
31   * 
32   * @author baba
33   * @since 1.1.0
34   */
35  public class ConverterFactoryImpl implements ConverterFactory, Disposable {
36  
37  	/** インスタンスが初期化済みであることを示します。 */
38  	protected boolean initialized;
39  
40  	/** このファクトリを管理しているS2コンテナです。 */
41  	protected S2Container container;
42  
43  	/** S2コンテナに登録されているコンバータの配列です。 */
44  	protected Converter[] converters;
45  
46  	/** コンバータのキャッシュです。 */
47  	protected Map<String, Converter> converterCache = new HashMap<String, Converter>();
48  
49  	/**
50  	 * <code>ConverterFactoryImpl</code>のインスタンスを構築します。
51  	 * 
52  	 */
53  	public ConverterFactoryImpl() {
54  	}
55  
56  	/**
57  	 * コンテナを設定します。
58  	 * 
59  	 * @param container
60  	 *            コンテナ
61  	 */
62  	public void setContainer(final S2Container container) {
63  		this.container = container.getRoot();
64  	}
65  
66  	/**
67  	 * インスタンスを初期化します。
68  	 */
69  	public void initialize() {
70  		if (initialized) {
71  			return;
72  		}
73  		converters = (Converter[]) container.findAllComponents(Converter.class);
74  		DisposableUtil.add(this);
75  		initialized = true;
76  	}
77  
78  	/**
79  	 * キャッシュ情報等を破棄し、インスタンスを未初期化状態に戻します。
80  	 * 
81  	 */
82  	public void dispose() {
83  		converters = null;
84  		converterCache.clear();
85  		initialized = false;
86  	}
87  
88  	/**
89  	 * {@inheritDoc}
90  	 */
91  	public Converter getConverter(final Class<?> parameterType,
92  			final Class<?> objectType) {
93  		initialize();
94  		final Class<?> destType = ClassUtil
95  				.getWrapperClassIfPrimitive(objectType);
96  		final String cacheKey = cacheKey(parameterType, destType);
97  		final Converter converter = converterCache.get(cacheKey);
98  		if (converter != null) {
99  			return converter;
100 		}
101 		return detectConverter(parameterType, destType);
102 	}
103 
104 	private Converter detectConverter(final Class<?> parameterType,
105 			final Class<?> objectType) {
106 		final Converter converter = getDistanceTable(parameterType, objectType);
107 		final String cacheKey = cacheKey(parameterType, objectType);
108 		converterCache.put(cacheKey, converter);
109 		return converter;
110 	}
111 
112 	private static String cacheKey(final Class<?> parameterType,
113 			final Class<?> objectType) {
114 		if (parameterType == null) {
115 			return objectType.getName();
116 		}
117 		return parameterType.getName() + objectType.getName();
118 	}
119 
120 	private Converter getDistanceTable(final Class<?> parameterType,
121 			final Class<?> objectType) {
122 		final Map<Integer, Converter> distanceTable = new TreeMap<Integer, Converter>();
123 		for (final Converter converter : converters) {
124 			if (!converter.canConvert(parameterType, objectType)) {
125 				continue;
126 			}
127 			final int distance = getDistance(converter.getObjectType(),
128 					objectType);
129 			distanceTable.put(distance, converter);
130 		}
131 		if (distanceTable.isEmpty()) {
132 			return null;
133 		}
134 		return distanceTable.values().iterator().next();
135 	}
136 
137 	private int getDistance(final Class<?> assigner, final Class<?> assignee) {
138 		return getDistance(assigner, assignee, 0);
139 	}
140 
141 	private int getDistance(final Class<?> assigner, final Class<?> assignee,
142 			final int distance) {
143 		if (assignee.equals(assigner)) {
144 			return distance;
145 		}
146 		if (Enum.class.equals(assigner) && assignee.isEnum()) {
147 			return distance + 5;
148 		}
149 		if (isImplements(assigner, assignee)) {
150 			return distance + 5;
151 		}
152 
153 		final Class<?> superClass = assigner.getSuperclass();
154 		if (superClass == null) {
155 			return distance + 10;
156 		}
157 		return getDistance(superClass, assignee, distance + 10);
158 	}
159 
160 	private boolean isImplements(final Class<?> assigner,
161 			final Class<?> assignee) {
162 		return !assigner.isInterface() && assignee.isInterface()
163 				&& assignee.isAssignableFrom(assigner);
164 	}
165 
166 }