ConvertingService.java

biz/hammurapi/convert/ConvertingService.java

Violations

Inspector Message Severity Location
Java Inspector 048 Copyrights information should be present in each file. 1
Java Inspector 086 Use equals() instead of == or != to compare objects. 1 112:44
Java Inspector 086 Use equals() instead of == or != to compare objects. 1 113:47
Java Inspector 049 Use a Collection instead of arrays Object[] 2 262:39
Java Inspector 049 Use a Collection instead of arrays Object[] 2 266:54
Java Inspector 070-A Cyclomatic complexity is too high: 17, maximum allowed is 12 2 250:17
Java Inspector 081 Avoid static collections, they can grow in size over time. 2 338:9
Java Inspector 082 Parenthesis are redundant. 2 105:52
Java Inspector 082 Parenthesis are redundant. 2 106:52
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 30:17
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 50:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 69:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 88:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 123:25
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 140:25
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 354:9
Java Inspector 085 Do not declare runtime exceptions in the throws clause. 2 377:9
Java Inspector 089 Undocumented top level type 2 18:1
Java Inspector 089 Undocumented method 2 30:17
Java Inspector 089 Undocumented constructor 2 96:17
Java Inspector 089 Undocumented method 2 102:17
Java Inspector 089 Undocumented method 2 110:17
Java Inspector 089 Undocumented method 2 123:25
Java Inspector 089 Undocumented method 2 140:25
Java Inspector 089 Undocumented constructor 2 183:41
Java Inspector 089 Undocumented method 2 192:41
Java Inspector 089 Undocumented method 2 220:49
Java Inspector 089 Undocumented method 2 270:65
Java Inspector 089 Undocumented method 2 284:65
Java Inspector 089 Undocumented method 2 299:65
Java Inspector 089 Undocumented method 2 314:65
Java Inspector 089 Javadoc contains tag for exception which method doesn't throw ConversionException 2 402:9
Java Inspector 089 Javadoc contains tag for exception which method doesn't throw ConversionException 2 419:9
Java Inspector 089 Javadoc contains tag for exception which method doesn't throw ConversionException 2 436:9
Java Inspector 089 Javadoc contains tag for exception which method doesn't throw ConversionException 2 454:9
Java Inspector 089 Undocumented method 2 496:25
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 103:43
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 281:77
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 296:77
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 311:77
Java Inspector 025 Avoid hardwired numeric literals. Allowed literals: [1, -1, 0] 3 311:147
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 209:92

Source code

1package biz.hammurapi.convert;
2
3import java.lang.reflect.InvocationTargetException;
4import java.lang.reflect.Method;
5import java.util.ArrayList;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.HashMap;
9import java.util.HashSet;
10import java.util.Iterator;
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14
15import biz.hammurapi.config.Context;
16import biz.hammurapi.config.DomConfigFactory;
17
18public class ConvertingService {
19
20 private interface ConvertersBucket {
21
22 Object convert(Object source, Context context);
23 }
24
25 /**
26 * Converter which delegates to convert() method.
27 */
28 public static final Converter CONVERTER = new Converter() {
29
30 public Object convert(Object source, Class targetType, Context context) throws ConversionException {
31 return ConvertingService.convert(source, targetType, context);
32 }
33
34 };
35
36 /**
37 * Converts source to target type using converters loaded from
38 * META-INF/config/biz.hammurapi.convert.Converter resources.
39 * If no converter is found, this method performs converter discovery through
40 * constructors if target type is a class and through duck conversion if target
41 * type is an interface.
42 * Invocation of this method is equivalent to invocation of
43 * <code>convert(source, targetType, false)</code>
44 * @param source Source object.
45 * @param targetType Target type.
46 * @return Source object converted to target type or null if no converter
47 * is available for source/targetType combination
48 * @throws ConversionException
49 */
50 public static Object convert(Object source, Class targetType) throws ConversionException {
51 return convert(source, targetType, null, false, null);
52 }
53
54 /**
55 * Converts source to target type using converters loaded from
56 * META-INF/config/biz.hammurapi.convert.Converter resources.
57 * If no converter is found, this method performs converter discovery through
58 * constructors if target type is a class and through duck conversion if target
59 * type is an interface.
60 * Invocation of this method is equivalent to invocation of
61 * <code>convert(source, targetType, false)</code>
62 * @param source Source object.
63 * @param targetType Target type.
64 * @param context Conversion context.
65 * @return Source object converted to target type or null if no converter
66 * is available for source/targetType combination
67 * @throws ConversionException
68 */
69 public static Object convert(Object source, Class targetType, Context context) throws ConversionException {
70 return convert(source, targetType, context, false, null);
71 }
72
73 /**
74 * Converts source to target type using converters loaded from
75 * META-INF/config/biz.hammurapi.convert.Converter resources.
76 * If no converter is found, this method performs converter discovery through
77 * constructors if target type is a class and through duck conversion if target
78 * type is an interface.
79 * Invocation of this method is equivalent to invocation of
80 * <code>convert(source, targetType, false)</code>
81 * @param source Source object.
82 * @param targetType Target type.
83 * @param classLoader Class loader, source of converters.
84 * @return Source object converted to target type or null if no converter
85 * is available for source/targetType combination
86 * @throws ConversionException
87 */
88 public static Object convert(Object source, Class targetType, ClassLoader classLoader) throws ConversionException {
89 return convert(source, targetType, null, false, classLoader);
90 }
91
92 private static class ConverterKey {
93 Class sourceType;
94 Class targetType;
95
96 public ConverterKey(Class sourceType, Class targetType) {
97 super();
98 this.sourceType = sourceType;
99 this.targetType = targetType;
100 }
101
102 public int hashCode() {
103 final int prime = 31;
104 int result = 1;
105 result = prime * result + ((sourceType == null) ? 0 : sourceType.hashCode());
106 result = prime * result + ((targetType == null) ? 0 : targetType.hashCode());
107 return result;
108 }
109
110 public boolean equals(Object obj) {
111 return
112 sourceType == ((ConverterKey) obj).sourceType
113 && targetType == ((ConverterKey) obj).targetType;
114 }
115
116 }
117
118 private static class ClassLoaderConvertersEntry {
119 private static final String CONVERT = "convert";
120
121 private Converter master = new Converter() {
122
123 public Object convert(Object source, Class targetType, Context context) throws ConversionException {
124 if (source==null) {
125 return null;
126 }
127
128 if (targetType.isInstance(source)) {
129 return source;
130 }
131
132 final ConvertersBucket bucket = findConverter(source.getClass(), targetType);
133 return bucket==null ? null : bucket.convert(source, context);
134 }
135
136 };
137
138 private Converter chainingMaster = new Converter() {
139
140 public Object convert(Object source, Class targetType, Context context) throws ConversionException {
141 if (source==null) {
142 return null;
143 }
144
145 if (targetType.isInstance(source)) {
146 return source;
147 }
148
149 ConvertersBucket bucket = findChainingConverter(source.getClass(), targetType);
150 return bucket==null ? null : bucket.convert(source, context);
151 }
152
153 };
154
155 Collection atomicConverters = new ArrayList();
156
157 /**
158 * Map of resolved converters: Collection[source, target] -&gt; ConverterClosure.
159 */
160 Map converters = new HashMap();
161
162 Set introspectedTargetTypes = new HashSet();
163
164 /**
165 * Map of resolved chaining converters: Collection[source, target] -&gt; ConverterClosure.
166 */
167 Map chainingConverters = new HashMap();
168
169 synchronized ConvertersBucket findConverter(Class sourceType, Class targetType) {
170 ConverterKey key = new ConverterKey(sourceType, targetType);
171 Object ret = converters.get(key);
172 if (Boolean.FALSE.equals(ret)) { // Indicates that previous converter resolution didn't yield results.
173 return null;
174 }
175
176 if (ret == null) {
177 if (!targetType.isInterface() && introspectedTargetTypes.add(targetType)) {
178 atomicConverters.addAll(ReflectionConverter.discoverConstructorConverters(targetType));
179 }
180 final List theConverters = new ArrayList();
181
182 class ConverterEntry implements Comparable {
183 public ConverterEntry(AtomicConverter converter, int affinity, int position) {
184 super();
185 this.converter = converter;
186 this.affinity = affinity;
187 this.position = position;
188 }
189 AtomicConverter converter;
190 int affinity;
191 int position;
192 public int compareTo(Object obj) {
193 int cret = affinity - ((ConverterEntry) obj).affinity;
194 if (cret==0) {
195 return position - ((ConverterEntry) obj).position;
196 }
197 return cret;
198 }
199 }
200
201 Iterator it = atomicConverters.iterator();
202 while (it.hasNext()) {
203 AtomicConverter ac = (AtomicConverter) it.next();
204 int position = 0;
205 if (ac.getSourceType().isAssignableFrom(sourceType) && targetType.isAssignableFrom(ac.getTargetType()) ) {
206 Integer newSourceAffinity = DuckConverterFactory.classAffinity(sourceType, ac.getSourceType());
207 Integer newTargetAffinity = DuckConverterFactory.classAffinity(ac.getTargetType(), targetType);
208 if (newSourceAffinity==null || newTargetAffinity==null) {
209 throw new IllegalArgumentException("kosyak");
210 }
211 theConverters.add(new ConverterEntry(ac, newSourceAffinity.intValue()+newTargetAffinity.intValue(), ++position));
212 }
213 }
214 if (theConverters.isEmpty()) {
215 converters.put(key, Boolean.FALSE);
216 } else {
217 Collections.sort(theConverters);
218 ret = new ConvertersBucket() {
219
220 public Object convert(Object source, Context context) {
221 Iterator it = theConverters.iterator();
222 while (it.hasNext()) {
223 ConverterEntry ce = (ConverterEntry) it.next();
224 Object ret = ce.converter.convert(source, master, context);
225 if (ret!=null) {
226 return ret;
227 }
228 }
229 return null;
230 }
231
232 };
233 converters.put(key, ret);
234 }
235 }
236
237 return (ConvertersBucket) ret;
238 }
239
240 synchronized ConvertersBucket findChainingConverter(Class sourceType, Class targetType) {
241 ConvertersBucket ret = findConverter(sourceType, targetType);
242 if (ret!=null) {
243 return ret;
244 }
245
246 // TODO 777 - Resolve chain.
247 return ret;
248 }
249
250 void addConverters(final Object provider) {
251 if (provider instanceof AtomicConverter) {
252 atomicConverters.add(provider);
253 } else if (provider instanceof AtomicConvertersBundle) {
254 atomicConverters.addAll(((AtomicConvertersBundle) provider).getConverters());
255 } else if (provider instanceof Collection) {
256 Iterator it = ((Collection) provider).iterator();
257 while (it.hasNext()) {
258 addConverters(it.next());
259 }
260 } else { // Introspection
261 Class pClass = provider.getClass();
262 Method[] methods = pClass.getMethods();
263 for (int i=0; i<methods.length; ++i) {
264 final Method method = methods[i];
265 if (CONVERT.equals(method.getName()) && !void.class.equals(method.getReturnType())) {
266 Class[] pTypes = method.getParameterTypes();
267 if (pTypes.length == 1) {
268 atomicConverters.add(new AtomicConverterBase(pTypes[0], method.getReturnType()) {
269
270 public Object convert(Object source, Converter master, Context context) {
271 try {
272 return method.invoke(provider, new Object[] {source});
273 } catch (IllegalAccessException e) {
274 throw new ConversionException(e);
275 } catch (InvocationTargetException e) {
276 throw new ConversionException(e);
277 }
278 }
279
280 });
281 } else if (pTypes.length == 2 && Converter.class.equals(pTypes[1])) {
282 atomicConverters.add(new AtomicConverterBase(pTypes[0], method.getReturnType()) {
283
284 public Object convert(Object source, Converter master, Context context) {
285 try {
286 return method.invoke(provider, new Object[] {source, master});
287 } catch (IllegalAccessException e) {
288 throw new ConversionException(e);
289 } catch (InvocationTargetException e) {
290 throw new ConversionException(e);
291 }
292 }
293
294 });
295
296 } else if (pTypes.length == 2 && Context.class.equals(pTypes[1])) {
297 atomicConverters.add(new AtomicConverterBase(pTypes[0], method.getReturnType()) {
298
299 public Object convert(Object source, Converter master, Context context) {
300 try {
301 return method.invoke(provider, new Object[] {source, context});
302 } catch (IllegalAccessException e) {
303 throw new ConversionException(e);
304 } catch (InvocationTargetException e) {
305 throw new ConversionException(e);
306 }
307 }
308
309 });
310
311 } else if (pTypes.length == 3 && Converter.class.equals(pTypes[1]) && Context.class.equals(pTypes[2])) {
312 atomicConverters.add(new AtomicConverterBase(pTypes[0], method.getReturnType()) {
313
314 public Object convert(Object source, Converter master, Context context) {
315 try {
316 return method.invoke(provider, new Object[] {source, master, context});
317 } catch (IllegalAccessException e) {
318 throw new ConversionException(e);
319 } catch (InvocationTargetException e) {
320 throw new ConversionException(e);
321 }
322 }
323
324 });
325
326 }
327
328 }
329 }
330 }
331 }
332
333 }
334
335 /**
336 * Map of resolved converters: ClassLoader -&gt; ClassLoaderConvertersEntry.
337 */
338 private static Map classLoaderEntries = new HashMap();
339
340 /**
341 * Converts source to target type using converters loaded from
342 * <code>META-INF/config/biz.hammurapi.convert.Converter</code> resources.
343 * If no converter is found, this method performs converter discovery through
344 * constructors if target type is a class and through duck conversion if target
345 * type is an interface.
346 * @param source Source object.
347 * @param targetType Target type.
348 * @param context Conversion context.
349 * @param classLoader Class loader.
350 * @return Source object converted to target type or null if no converter
351 * is available for source/targetType combination
352 * @throws ConversionException
353 */
354 public static Object convert(Object source, Class targetType, Context context, ClassLoader classLoader) throws ConversionException {
355 return convert(source, targetType, context, false, classLoader);
356 }
357
358 /**
359 * Converts source to target type using converters loaded from
360 * <code>META-INF/config/biz.hammurapi.convert.Converter</code> resources.
361 * If no converter is found, this method performs converter discovery through
362 * constructors if target type is a class and through duck conversion if target
363 * type is an interface.
364 * @param source Source object.
365 * @param targetType Target type.
366 * @param chain If true, the converter will attempt to build a chain of
367 * conversions from source type to target type.
368 * For example, StringBuffer doesn't have a constructor from Integer. If chain
369 * is <code>false</code> then this method will return <code>null</code>. If
370 * <code>chain</code> is true, then the converter will convert Integer to String
371 * and then will create StringBuffer from it. If multiple conversion chains
372 * are available, the shortest is chosen.
373 * @return Source object converted to target type or null if no converter
374 * is available for source/targetType combination
375 * @throws ConversionException
376 */
377 private static Object convert(Object source, Class targetType, Context context, boolean chain, ClassLoader classLoader) throws ConversionException {
378 if (source==null) {
379 return null;
380 }
381
382 if (targetType.isInstance(source)) {
383 return source;
384 }
385
386 ConverterClosure converter = getConverter(source.getClass(), targetType, context, chain, classLoader);
387 return converter==null ? null : converter.convert(source);
388 }
389
390 /**
391 * Converts source to target type using converters loaded from
392 * <code>META-INF/config/biz.hammurapi.convert.Converter</code> resources.
393 * If no converter is found, this method performs converter discovery through
394 * constructors if target type is a class and through duck conversion if target
395 * type is an interface.
396 * @param sourceType Source type.
397 * @param targetType Target type.
398 * @return Converter from source type to target type, or null if such converter
399 * is not available.
400 * @throws ConversionException
401 */
402 public static ConverterClosure getConverter(Class sourceType, Class targetType) {
403 return getConverter(sourceType, targetType, null, false, null);
404 }
405
406 /**
407 * Converts source to target type using converters loaded from
408 * <code>META-INF/config/biz.hammurapi.convert.Converter</code> resources.
409 * If no converter is found, this method performs converter discovery through
410 * constructors if target type is a class and through duck conversion if target
411 * type is an interface.
412 * @param sourceType Source type.
413 * @param targetType Target type.
414 * @param classLoader Class loader to retrieve converters from.
415 * @return Converter from source type to target type, or null if such converter
416 * is not available.
417 * @throws ConversionException
418 */
419 public static ConverterClosure getConverter(Class sourceType, Class targetType, ClassLoader classLoader) {
420 return getConverter(sourceType, targetType, null, false, classLoader);
421 }
422
423 /**
424 * Converts source to target type using converters loaded from
425 * <code>META-INF/config/biz.hammurapi.convert.Converter</code> resources.
426 * If no converter is found, this method performs converter discovery through
427 * constructors if target type is a class and through duck conversion if target
428 * type is an interface.
429 * @param sourceType Source type.
430 * @param targetType Target type.
431 * @param context Conversion context.
432 * @return Converter from source type to target type, or null if such converter
433 * is not available.
434 * @throws ConversionException
435 */
436 public static ConverterClosure getConverter(Class sourceType, Class targetType, Context context) {
437 return getConverter(sourceType, targetType, context, false, null);
438 }
439
440 /**
441 * Converts source to target type using converters loaded from
442 * <code>META-INF/config/biz.hammurapi.convert.Converter</code> resources.
443 * If no converter is found, this method performs converter discovery through
444 * constructors if target type is a class and through duck conversion if target
445 * type is an interface.
446 * @param sourceType Source type.
447 * @param targetType Target type.
448 * @param context Conversion context.
449 * @param classLoader Class loader to retrieve converters from.
450 * @return Converter from source type to target type, or null if such converter
451 * is not available.
452 * @throws ConversionException
453 */
454 public static ConverterClosure getConverter(Class sourceType, Class targetType, Context context, ClassLoader classLoader) {
455 return getConverter(sourceType, targetType, context, false, classLoader);
456 }
457
458 private static ClassLoader promote(ClassLoader present, ClassLoader candidate) {
459 ClassLoader ret = DuckConverterFactory.getChildClassLoader(present, candidate);
460 return ret==null ? present : ret;
461 }
462
463 /**
464 *
465 * @param sourceType
466 * @param targetType
467 * @param context
468 * @param chain If true, the converter will attempt to build a chain of
469 * conversions from source type to target type.
470 * For example, StringBuffer doesn't have a constructor from Integer. If chain
471 * is <code>false</code> then this method will return <code>null</code>. If
472 * <code>chain</code> is true, then the converter will convert Integer to String
473 * and then will create StringBuffer from it. If multiple conversion chains
474 * are available, the shortest is chosen.
475 * @param classLoader
476 * @return
477 */
478 private static ConverterClosure getConverter(Class sourceType, Class targetType, final Context context, boolean chain, final ClassLoader pClassLoader) {
479 ClassLoader classLoader = promote(pClassLoader, promote(targetType.getClassLoader(), sourceType.getClassLoader()));
480
481 ClassLoaderConvertersEntry clce;
482 synchronized (classLoaderEntries) {
483 clce = (ClassLoaderConvertersEntry) classLoaderEntries.get(classLoader);
484 if (clce == null) {
485 clce = new ClassLoaderConvertersEntry();
486 Iterator pit = DomConfigFactory.loadProviders(Converter.class, classLoader);
487 while (pit.hasNext()) {
488 clce.addConverters(pit.next());
489 }
490 classLoaderEntries.put(classLoader, clce);
491 }
492 }
493 final ConvertersBucket bucket = chain ? clce.findChainingConverter(sourceType, targetType) : clce.findConverter(sourceType, targetType);
494 return bucket==null ? null : new ConverterClosure() {
495
496 public Object convert(Object source) {
497 return bucket.convert(source, context);
498 }
499
500 };
501 }
502}
503