1 package net.sourceforge.jparam.conversion;
2
3 import java.lang.reflect.Method;
4 import java.util.HashMap;
5 import java.util.Iterator;
6 import java.util.LinkedList;
7 import java.util.List;
8 import java.util.Map;
9
10 import net.sourceforge.jparam.JParamException;
11 import net.sourceforge.jparam.conversion.converters.AmbigousConverter;
12 import net.sourceforge.jparam.conversion.converters.IConverter;
13 import net.sourceforge.jparam.conversion.converters.MethodConverter;
14 import net.sourceforge.jparam.conversion.converters.PathConverter;
15 import net.sourceforge.jparam.conversion.creators.ObjectCreator;
16 import net.sourceforge.jparam.conversion.weights.ScalarConversionWeight;
17 import net.sourceforge.jparam.typename.TypeNameRegistry;
18
19 /***
20 * This registry maintains a list of all possible conversions that are currently
21 * supported by JParam, if new conversions are added than the list is updated
22 * accordingly
23 *
24 * @author ron_sidi
25 *
26 */
27 public class ConverterRegistry {
28 private static ConverterRegistry registry = null;
29
30 private Map converters = new HashMap();
31
32 private ConverterRegistry() {
33 }
34
35 public static ConverterRegistry getInstance() {
36 if (registry == null) {
37 registry = new ConverterRegistry();
38 }
39 return registry;
40 }
41
42 /***
43 * Register all conversion methods in the given helper class, a method is
44 * considered a conversion if:
45 * <p>
46 * <li>The method name is:
47 * <ul>
48 * <li>convert/user_convert for performing user conversions</li>
49 * <li>promote_convert for promotion conversion</li>
50 * <li>std_convert for standard conversion</li>
51 * </ul>
52 * <li>The method is <code>public</code></li>
53 * <li>The method is <code>static</code></li>
54 * <li>The method has exactly one argument</li>
55 * <li>The method has a typed return value and not void</li>
56 *
57 * @param helperClass
58 */
59 public void registerHelperClass(Class helperClass) {
60 Method[] creatorClassMethods = helperClass.getMethods();
61 for (int i = 0; i < creatorClassMethods.length; i++) {
62 Method m = creatorClassMethods[i];
63
64 if (MethodConverter.isMethodValid(m)) {
65 MethodConverter mc = new MethodConverter(m);
66
67 registerConverter(mc);
68 }
69 }
70 }
71
72 /***
73 * This method is used to register a new converter, it shouldn't be used
74 * externally, but is defined public to support special cases that might
75 * arise
76 *
77 * It will perform all needed changes to the registry that are introduced by
78 * the new converter
79 *
80 * @param c
81 */
82 public void registerConverter(IConverter c) {
83 ConverterKey key = new ConverterKey(c.getSourceClass(), c
84 .getTargetClass());
85
86
87 if (key.sourceType.equals(key.targetType)) {
88 return;
89 }
90
91
92 IConverter curConverter = (IConverter) converters.get(key);
93 if (curConverter != null) {
94
95
96 if (curConverter.getWeight().compareTo(c.getWeight()) > 0) {
97 updateConverter(c);
98 } else
99
100 if (curConverter.getWeight().compareTo(c.getWeight()) == 0) {
101
102
103
104
105
106 if (!curConverter.equals(c)) {
107
108 updateConverter(AmbigousConverter.createConverter(
109 curConverter, c));
110 }
111 }
112
113
114 } else {
115 List convertersToRegister = new LinkedList();
116 boolean userConversion = c.getWeight().compareTo(
117 ScalarConversionWeight.CONV_USER) >= 0;
118
119
120 for (Iterator iter = converters.entrySet().iterator(); iter
121 .hasNext();) {
122 Map.Entry entry = (Map.Entry) iter.next();
123
124 IConverter existingConverter = (IConverter) entry.getValue();
125 if (!userConversion
126 || existingConverter.getWeight().compareTo(
127 ScalarConversionWeight.CONV_USER) < 0) {
128 if (existingConverter.getSourceClass().equals(
129 c.getTargetClass())) {
130
131
132 if (!existingConverter.getTargetClass().equals(
133 c.getSourceClass())) {
134 convertersToRegister
135 .add(PathConverter.createConversionPath(c,
136 existingConverter));
137 }
138 } else if (existingConverter.getTargetClass().equals(
139 c.getSourceClass())) {
140
141
142 if (!existingConverter.getSourceClass().equals(
143 c.getTargetClass())) {
144 convertersToRegister
145 .add(PathConverter.createConversionPath(
146 existingConverter, c));
147 }
148 }
149 }
150 }
151
152
153
154 converters.put(key, c);
155 for (Iterator iter = convertersToRegister.iterator(); iter
156 .hasNext();) {
157 IConverter converter = (IConverter) iter.next();
158 registerConverter(converter);
159 }
160 }
161 }
162
163 private void updateConverter(IConverter converter) {
164 List updatedConverters = new LinkedList();
165 ConverterKey key = new ConverterKey(converter.getSourceClass(),
166 converter.getTargetClass());
167
168 converters.remove(key);
169 for (Iterator iter = converters.values().iterator(); iter.hasNext();) {
170 IConverter curConv = (IConverter) iter.next();
171 IConverter newConv = curConv.update(converter);
172
173 if (newConv != curConv) {
174 updatedConverters.add(newConv);
175 }
176 }
177 converters.put(key, converter);
178
179 for (Iterator iter = updatedConverters.iterator(); iter.hasNext();) {
180 IConverter conv = (IConverter) iter.next();
181 updateConverter(conv);
182 }
183 }
184
185 /***
186 * Convert the source object to the given target class, an exception is
187 * thrown either when there is no exception/exception is ambigous or when
188 * the conversion process itself failed
189 *
190 * @param targetType
191 * @param sourceObject
192 * @return the converted object
193 * @throws JParamException
194 */
195 public Object convert(Class targetType, Object sourceObject)
196 throws JParamException {
197 if (sourceObject == null) {
198 if (targetType.isPrimitive()) {
199 throw new JParamException(
200 "The value \"null\" cannot be converted into a primitive: "
201 + targetType);
202 }
203 return null;
204 }
205
206 targetType = ObjectCreator.getInstance().getNonPrimitiveClass(
207 targetType);
208 if (sourceObject.getClass().equals(targetType)) {
209 return sourceObject;
210 }
211
212 IConverter converter = (IConverter) converters.get(new ConverterKey(
213 sourceObject.getClass(), targetType));
214 if (converter == null) {
215 throw new JParamException("no conversion path found from "
216 + TypeNameRegistry.getInstance().getJParamTypeName(
217 sourceObject.getClass())
218 + " to "
219 + TypeNameRegistry.getInstance().getJParamTypeName(
220 targetType));
221 }
222 return converter.convert(sourceObject);
223 }
224
225 public static void clearSingleton() {
226 registry = null;
227 }
228
229 /***
230 * Return the weight of the conversion between the given source and target
231 * type
232 *
233 * @param sourceType
234 * @param targetType
235 * @return
236 */
237 public ScalarConversionWeight getConversionWeight(Class sourceType,
238 Class targetType) {
239 if (sourceType.equals(targetType)) {
240 return ScalarConversionWeight.CONV_EXACT;
241 }
242
243 IConverter converter = (IConverter) converters.get(new ConverterKey(
244 sourceType, targetType));
245 if (converter != null) {
246 return converter.getWeight();
247 }
248
249 return ScalarConversionWeight.CONV_IMPOSSIBLE;
250 }
251 }