EMMA Coverage Report (generated Thu Jan 20 11:39:44 EST 2011)
[all classes][com.hammurapi.convert]

COVERAGE SUMMARY FOR SOURCE FILE [ConvertingService.java]

nameclass, %method, %block, %line, %
ConvertingService.java69%  (9/13)67%  (29/43)57%  (712/1242)61%  (126.4/207)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ConvertingService$ClassLoaderConvertersEntry$40%   (0/1)0%   (0/2)0%   (0/38)0%   (0/7)
ConvertingService$ClassLoaderConvertersEntry$4 (ConvertingService$ClassLoader... 0%   (0/1)0%   (0/14)0%   (0/2)
convert (Object, Converter, Context, ClassLoader): Object 0%   (0/1)0%   (0/24)0%   (0/5)
     
class ConvertingService$ClassLoaderConvertersEntry$50%   (0/1)0%   (0/2)0%   (0/42)0%   (0/7)
ConvertingService$ClassLoaderConvertersEntry$5 (ConvertingService$ClassLoader... 0%   (0/1)0%   (0/14)0%   (0/2)
convert (Object, Converter, Context, ClassLoader): Object 0%   (0/1)0%   (0/28)0%   (0/5)
     
class ConvertingService$ClassLoaderConvertersEntry$60%   (0/1)0%   (0/2)0%   (0/42)0%   (0/7)
ConvertingService$ClassLoaderConvertersEntry$6 (ConvertingService$ClassLoader... 0%   (0/1)0%   (0/14)0%   (0/2)
convert (Object, Converter, Context, ClassLoader): Object 0%   (0/1)0%   (0/28)0%   (0/5)
     
class ConvertingService$ClassLoaderConvertersEntry$70%   (0/1)0%   (0/2)0%   (0/46)0%   (0/7)
ConvertingService$ClassLoaderConvertersEntry$7 (ConvertingService$ClassLoader... 0%   (0/1)0%   (0/14)0%   (0/2)
convert (Object, Converter, Context, ClassLoader): Object 0%   (0/1)0%   (0/32)0%   (0/5)
     
class ConvertingService$ClassLoaderConvertersEntry$1100% (1/1)100% (2/2)39%  (32/83)50%  (10/20)
convert (Object, Class, Context): Object 100% (1/1)34%  (26/77)44%  (8/18)
ConvertingService$ClassLoaderConvertersEntry$1 (ConvertingService$ClassLoader... 100% (1/1)100% (6/6)100% (2/2)
     
class ConvertingService$ClassLoaderConvertersEntry100% (1/1)86%  (6/7)59%  (272/458)65%  (47.6/73)
findChainingConverter (Class, Class): ConvertingService$ConvertersBucket 0%   (0/1)0%   (0/11)0%   (0/4)
addConverters (Object): void 100% (1/1)20%  (38/194)35%  (9/26)
findConverter (Class, Class): ConvertingService$ConvertersBucket 100% (1/1)86%  (113/131)85%  (18.6/22)
addAnnotationConverters (Class): void 100% (1/1)99%  (73/74)90%  (9/10)
ConvertingService$ClassLoaderConvertersEntry (ClassLoader): void 100% (1/1)100% (42/42)100% (10/10)
access$0 (ConvertingService$ClassLoaderConvertersEntry): Converter 100% (1/1)100% (3/3)100% (1/1)
access$1 (ConvertingService$ClassLoaderConvertersEntry): ClassLoader 100% (1/1)100% (3/3)100% (1/1)
     
class ConvertingService100% (1/1)62%  (8/13)67%  (179/266)66%  (31.7/48)
ConvertingService (): void 0%   (0/1)0%   (0/3)0%   (0/1)
getConverter (Class, Class): ConverterClosure 0%   (0/1)0%   (0/7)0%   (0/1)
getConverter (Class, Class, ClassLoader): ConverterClosure 0%   (0/1)0%   (0/7)0%   (0/1)
getConverter (Class, Class, Context): ConverterClosure 0%   (0/1)0%   (0/7)0%   (0/1)
getConverter (Class, Class, Context, ClassLoader): ConverterClosure 0%   (0/1)0%   (0/7)0%   (0/1)
convert (Object, Class, Context, boolean, ClassLoader): Object 100% (1/1)37%  (23/63)38%  (6/16)
getConverter (Class, Class, Context, boolean, ClassLoader): ConverterClosure 100% (1/1)87%  (109/125)89%  (16.9/19)
<static initializer> 100% (1/1)100% (9/9)100% (3/3)
convert (Object, Class): Object 100% (1/1)100% (7/7)100% (1/1)
convert (Object, Class, ClassLoader): Object 100% (1/1)100% (7/7)100% (1/1)
convert (Object, Class, Context): Object 100% (1/1)100% (7/7)100% (1/1)
convert (Object, Class, Context, ClassLoader): Object 100% (1/1)100% (7/7)100% (1/1)
promote (ClassLoader, ClassLoader): ClassLoader 100% (1/1)100% (10/10)100% (2/2)
     
class ConvertingService$ClassLoaderConvertersEntry$2100% (1/1)100% (2/2)73%  (69/95)69%  (11/16)
convert (Object, Context): Object 100% (1/1)69%  (57/83)64%  (9/14)
ConvertingService$ClassLoaderConvertersEntry$2 (ConvertingService$ClassLoader... 100% (1/1)100% (12/12)100% (2/2)
     
class ConvertingService$ConverterKey100% (1/1)100% (3/3)89%  (51/57)88%  (10.6/12)
equals (Object): boolean 100% (1/1)88%  (14/16)60%  (1.8/3)
hashCode (): int 100% (1/1)88%  (28/32)96%  (4.8/5)
ConvertingService$ConverterKey (Class, Class): void 100% (1/1)100% (9/9)100% (4/4)
     
class ConvertingService$ClassLoaderConvertersEntry$3100% (1/1)100% (2/2)90%  (54/60)83%  (10/12)
convert (Object, Converter, Context, ClassLoader): Object 100% (1/1)88%  (43/49)80%  (8/10)
ConvertingService$ClassLoaderConvertersEntry$3 (ConvertingService$ClassLoader... 100% (1/1)100% (11/11)100% (2/2)
     
class ConvertingService$1100% (1/1)100% (2/2)100% (8/8)100% (3/3)
ConvertingService$1 (): void 100% (1/1)100% (3/3)100% (2/2)
convert (Object, Class, Context): Object 100% (1/1)100% (5/5)100% (1/1)
     
class ConvertingService$2100% (1/1)100% (2/2)100% (16/16)100% (3/3)
ConvertingService$2 (ConvertingService$ConvertersBucket, Context): void 100% (1/1)100% (9/9)100% (2/2)
convert (Object): Object 100% (1/1)100% (7/7)100% (1/1)
     
class ConvertingService$ClassLoaderConvertersEntry$1ConverterEntry100% (1/1)100% (2/2)100% (31/31)100% (9/9)
ConvertingService$ClassLoaderConvertersEntry$1ConverterEntry (ConvertingServi... 100% (1/1)100% (15/15)100% (5/5)
compareTo (ConvertingService$ClassLoaderConvertersEntry$1ConverterEntry): int 100% (1/1)100% (16/16)100% (4/4)

1package com.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.List;
11import java.util.Map;
12import java.util.ServiceLoader;
13import java.util.Set;
14 
15import com.hammurapi.common.Adaptable;
16import com.hammurapi.common.Context;
17 
18public class ConvertingService {
19        
20        private interface ConvertersBucket<S, T> {
21                
22                T convert(S source, Context context);
23        }
24        
25        /**
26         * Converter which delegates to convert() method.
27         */
28        public static final Converter CONVERTER = new Converter() {
29 
30                @SuppressWarnings("unchecked")
31                public Object convert(Object source, Class targetType, Context context) throws ConversionException {
32                        return ConvertingService.convert(source, targetType, context);
33                }
34                
35        };                        
36        
37        /**
38         * Converts source to target type using Java 6 service loading for AtomicConverterBundle, AtomicConverter, and Converter services
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 <S, T> T convert(S source, Class<T> targetType) throws ConversionException {
51                return convert(source, targetType, null, false, null);
52        }
53        
54        /**
55         * Converts source to target type using Java 6 service loading for AtomicConverterBundle, AtomicConverter, and Converter services
56         * If no converter is found, this method performs converter discovery through
57         * constructors if target type is a class and through duck conversion if target
58         * type is an interface. 
59         * Invocation of this method is equivalent to invocation of  
60         * <code>convert(source, targetType, false)</code>
61         * @param source Source object.
62         * @param targetType Target type.
63         * @param context Conversion context.
64         * @return Source object converted to target type or null if no converter
65         * is available for source/targetType combination
66         * @throws ConversionException
67         */
68        public static <S, T> T convert(S source, Class<T> targetType, Context context) throws ConversionException {
69                return convert(source, targetType, context, false, null);
70        }
71        
72        /**
73         * Converts source to target type using Java 6 service loading for AtomicConverterBundle, AtomicConverter, and Converter services
74         * If no converter is found, this method performs converter discovery through
75         * constructors if target type is a class and through duck conversion if target
76         * type is an interface. 
77         * Invocation of this method is equivalent to invocation of  
78         * <code>convert(source, targetType, false)</code>
79         * @param source Source object.
80         * @param targetType Target type.
81         * @param classLoader Class loader, source of converters.
82         * @return Source object converted to target type or null if no converter
83         * is available for source/targetType combination
84         * @throws ConversionException
85         */
86        public static <S, T> T convert(S source, Class<T> targetType, ClassLoader classLoader) throws ConversionException {
87                return convert(source, targetType, null, false, classLoader);
88        }
89        
90        private static class ConverterKey<S,T> {
91                Class<S> sourceType;
92                Class<T> targetType;
93                
94                public ConverterKey(Class<S> sourceType, Class<T> targetType) {
95                        super();
96                        this.sourceType = sourceType;
97                        this.targetType = targetType;
98                }
99 
100                public int hashCode() {
101                        final int prime = 31;
102                        int result = 1;
103                        result = prime * result        + ((sourceType == null) ? 0 : sourceType.hashCode());
104                        result = prime * result        + ((targetType == null) ? 0 : targetType.hashCode());
105                        return result;
106                }
107 
108                @SuppressWarnings("unchecked")
109                public boolean equals(Object obj) {
110                        return 
111                                sourceType == ((ConverterKey<S, T>) obj).sourceType 
112                                && targetType == ((ConverterKey<S, T>) obj).targetType; 
113                }                
114                
115        }
116        
117        private static class ClassLoaderConvertersEntry {
118                private static final String CONVERT = "convert";
119                
120                private ClassLoader classLoader;
121                
122                public ClassLoaderConvertersEntry(ClassLoader classLoader) {
123                        this.classLoader = classLoader;
124                }
125                
126                private Converter master = new Converter() {
127 
128                        @SuppressWarnings("unchecked")
129                        public Object convert(Object source, Class targetType, Context context) throws ConversionException {
130                                if (source==null) {
131                                        return null;
132                                }
133                                
134                                if (targetType.isInstance(source)) {
135                                        return source;
136                                }
137                                
138                                final ConvertersBucket<Object, Object> bucket = findConverter(source.getClass(), targetType);
139                                if (bucket!=null) {
140                                        Object ret = bucket.convert(source, context);
141                                        if (ret!=null) {
142                                                return ret;
143                                        }
144                                }
145                                
146                                for (ConverterPart part: parts) {
147                                        Object ret = part.convert(source, targetType, context, this);
148                                        if (ret!=null) {
149                                                return ret;
150                                        }
151                                }
152 
153                                for (Converter subConverter: subConverters) {
154                                        Object ret = subConverter.convert(source, targetType, context);
155                                        if (ret!=null) {
156                                                return ret;
157                                        }
158                                }
159                                
160                                return null;
161                        }
162                        
163                };
164 
165//                private Converter<?,?> chainingMaster = new Converter<?,?>() {
166//
167//                        public Object convert(Object source, Class targetType, Context context) throws ConversionException {
168//                                if (source==null) {
169//                                        return null;
170//                                }
171//                                
172//                                if (targetType.isInstance(source)) {
173//                                        return source;
174//                                }
175//                                
176//                                ConvertersBucket bucket = findChainingConverter(source.getClass(), targetType);
177//                                return bucket==null ? null : bucket.convert(source, context);
178//                        }
179//                        
180//                };
181 
182                Collection<AtomicConverter<? extends Object,? extends Object>> atomicConverters = new ArrayList<AtomicConverter<? extends Object,? extends Object>>();
183                
184                /**
185                 * Map of resolved converters: Collection[source, target] -&gt; ConverterClosure. 
186                 */
187                Map<ConverterKey<?,?>, Object> converters = new HashMap<ConverterKey<?,?>, Object>(); 
188                
189                Set<Class<?>> introspectedTargetTypes = new HashSet<Class<?>>();
190 
191                Set<Class<?>> introspectedSourceTypes = new HashSet<Class<?>>();
192                
193                Collection<Converter> subConverters = new ArrayList<Converter>();
194                Collection<ConverterPart> parts = new ArrayList<ConverterPart>();
195                
196                /**
197                 * Map of resolved chaining converters: Collection[source, target] -&gt; ConverterClosure. 
198                 */
199//                Map chainingConverters = new HashMap();
200                
201                @SuppressWarnings("unchecked")
202                synchronized <S, T> ConvertersBucket<S, T> findConverter(Class<S> sourceType, final Class<T> targetType) {
203                        ConverterKey<S, T> key = new ConverterKey(sourceType, targetType);
204                        Object ret = converters.get(key);
205                        if (Boolean.FALSE.equals(ret)) { // Indicates that previous converter resolution didn't yield results.
206                                return null;
207                        }
208                        
209                        if (ret == null) {
210                                if (!targetType.isInterface() && introspectedTargetTypes.add(targetType)) {
211                                        atomicConverters.addAll(ReflectionConverter.discoverConstructorConverters(targetType));
212                                }
213                                
214                                class ConverterEntry implements Comparable<ConverterEntry> {
215                                        public ConverterEntry(AtomicConverter<? extends Object,? extends Object> converter, int affinity, int position) {
216                                                super();
217                                                this.converter = converter;
218                                                this.affinity = affinity;
219                                                this.position = position;
220                                        }
221                                        AtomicConverter<? extends Object, ? extends Object> converter;
222                                        int affinity;
223                                        int position;
224                                        public int compareTo(ConverterEntry obj) {
225                                                int cret = affinity - ((ConverterEntry) obj).affinity;
226                                                if (cret==0) {
227                                                        return position - ((ConverterEntry) obj).position;
228                                                }
229                                                return cret;
230                                        }
231                                }
232                                
233                                final List<ConverterEntry> theConverters  = new ArrayList<ConverterEntry>();
234                                
235                                for (AtomicConverter<? extends Object,? extends Object> ac: atomicConverters) {
236                                        int position = 0;
237                                        if (ac.getSourceType().isAssignableFrom(sourceType) && targetType.isAssignableFrom(ac.getTargetType()) ) {
238                                                Integer newSourceAffinity = DuckConverterFactory.classAffinity(sourceType, ac.getSourceType());
239                                                Integer newTargetAffinity = DuckConverterFactory.classAffinity(ac.getTargetType(), targetType);
240                                                if (newSourceAffinity==null || newTargetAffinity==null) {
241                                                        throw new IllegalArgumentException("Something is wrong - source and target affinities are null");
242                                                }
243                                                theConverters.add(new ConverterEntry(ac, newSourceAffinity.intValue()+newTargetAffinity.intValue(), ++position));
244                                        }
245                                }
246                                if (theConverters.isEmpty() && parts.isEmpty() && subConverters.isEmpty()) {
247                                        converters.put(key, Boolean.FALSE);
248                                } else {
249                                        Collections.sort(theConverters);
250                                        ret = new ConvertersBucket<S,T>() {
251 
252                                                public T convert(S source, Context context) {
253                                                        for (ConverterEntry ce: theConverters) {
254                                                                AtomicConverter<S, T> theConverter = (AtomicConverter<S, T>) ce.converter;
255                                                                T ret = (T) theConverter.convert(source, master, context, classLoader);
256                                                                if (ret!=null) {
257                                                                        return ret;
258                                                                }                                                                                                                                
259                                                        }
260                                                        
261                                                        for (ConverterPart part: parts) {
262                                                                Object ret = part.convert(source, targetType, context, master);
263                                                                if (ret!=null) {
264                                                                        return (T) ret;
265                                                                }
266                                                        }
267 
268                                                        for (Converter subConverter: subConverters) {
269                                                                Object ret = subConverter.convert(source, targetType, context);
270                                                                if (ret!=null) {
271                                                                        return (T) ret;
272                                                                }
273                                                        }
274                                                                                                                
275                                                        return null;
276                                                }
277                                                
278                                        };
279                                        converters.put(key, ret);
280                                }
281                        }
282                        
283                        return (ConvertersBucket<S, T>) ret;
284                }
285 
286                synchronized <S, T> ConvertersBucket<S,T> findChainingConverter(Class<S> sourceType, Class<T> targetType) {
287                        ConvertersBucket<S,T> ret = findConverter(sourceType, targetType);
288                        if (ret!=null) {
289                                return ret;
290                        }
291                        
292                        // TODO - Resolve chain.
293                        return ret;
294                }
295                
296                <S,T> void addAnnotationConverters(Class<S> source) {
297                        if (introspectedSourceTypes.add(source)) {
298                                Z: for (Method m: source.getMethods()) {                                                
299                                        if (m.getAnnotation(ConverterMethod.class)!=null && !void.class.equals(m.getReturnType())) {
300                                                for (Class<?> pt: m.getParameterTypes()) {
301                                                        if (!(Context.class.equals(pt) || Converter.class.equals(pt))) {
302                                                                continue Z;
303                                                        }
304                                                }
305                                                final Method converterMethod = m;
306                                                @SuppressWarnings("unchecked")
307                                                AtomicConverterBase<S, T> converter = new AtomicConverterBase<S, T>(source, (Class<T>) m.getReturnType()) {
308 
309                                                        @Override
310                                                        public T convert(S source, Converter master, Context context, ClassLoader classLoader) {
311                                                                try {
312                                                                        Class<?>[] pt = converterMethod.getParameterTypes();
313                                                                        Object[] args = new Object[pt.length];
314                                                                        for (int i=0; i<pt.length; ++i) {
315                                                                                if (Context.class.equals(pt[i])) {
316                                                                                        args[i] = context;
317                                                                                } else if (Converter.class.equals(pt[i])) {
318                                                                                        args[i] = master;
319                                                                                }                                                                                        
320                                                                        }
321                                                                        return (T) converterMethod.invoke(source, args);
322                                                                } catch (Exception e) {
323                                                                        throw new ConversionException(e);
324                                                                }
325                                                        }
326                                                };
327                                                atomicConverters.add(converter);
328                                        }
329                                }
330                        }
331                }
332                
333                @SuppressWarnings("unchecked")
334                void addConverters(final Object provider) {
335                        if (provider instanceof AtomicConverter) {
336                                atomicConverters.add((AtomicConverter<Object, Object>) provider);
337                        } else if (provider instanceof AtomicConvertersBundle) {
338                                atomicConverters.addAll(((AtomicConvertersBundle) provider).getConverters());
339                        } else if (provider instanceof Collection<?>) {
340                                for (Object obj: (Collection<?>) provider) {
341                                        addConverters(obj);
342                                }
343                        } else if (provider instanceof Converter) {
344                                subConverters.add((Converter) provider);
345                        } else if (provider instanceof ConverterPart) {
346                                parts.add((ConverterPart) provider);
347                        } else { // Introspection
348                                Class pClass = provider.getClass();
349                                Method[] methods = pClass.getMethods();
350                                for (int i=0; i<methods.length; ++i) {
351                                        final Method method = methods[i];
352                                        if (CONVERT.equals(method.getName()) && !void.class.equals(method.getReturnType())) {
353                                                Class[] pTypes = method.getParameterTypes();
354                                                if (pTypes.length == 1) {
355                                                        atomicConverters.add(new AtomicConverterBase(pTypes[0], method.getReturnType()) {
356 
357                                                                public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
358                                                                        try {
359                                                                                return method.invoke(provider, new Object[] {source});
360                                                                        } catch (IllegalAccessException e) {
361                                                                                throw new ConversionException(e);
362                                                                        } catch (InvocationTargetException e) {
363                                                                                throw new ConversionException(e);
364                                                                        }
365                                                                }
366                                                                
367                                                        });
368                                                } else if (pTypes.length == 2 && Converter.class.equals(pTypes[1])) {
369                                                        atomicConverters.add(new AtomicConverterBase(pTypes[0], method.getReturnType()) {
370 
371                                                                public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
372                                                                        try {
373                                                                                return method.invoke(provider, new Object[] {source, master});
374                                                                        } catch (IllegalAccessException e) {
375                                                                                throw new ConversionException(e);
376                                                                        } catch (InvocationTargetException e) {
377                                                                                throw new ConversionException(e);
378                                                                        }
379                                                                }
380                                                                
381                                                        });
382                                                        
383                                                } else if (pTypes.length == 2 && Context.class.equals(pTypes[1])) {
384                                                        atomicConverters.add(new AtomicConverterBase(pTypes[0], method.getReturnType()) {
385 
386                                                                public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
387                                                                        try {
388                                                                                return method.invoke(provider, new Object[] {source, context});
389                                                                        } catch (IllegalAccessException e) {
390                                                                                throw new ConversionException(e);
391                                                                        } catch (InvocationTargetException e) {
392                                                                                throw new ConversionException(e);
393                                                                        }
394                                                                }
395                                                                
396                                                        });
397                                                        
398                                                } else if (pTypes.length == 3 && Converter.class.equals(pTypes[1]) && Context.class.equals(pTypes[2])) {
399                                                        atomicConverters.add(new AtomicConverterBase(pTypes[0], method.getReturnType()) {
400 
401                                                                public Object convert(Object source, Converter master, Context context, ClassLoader classLoader) {
402                                                                        try {
403                                                                                return method.invoke(provider, new Object[] {source, master, context});
404                                                                        } catch (IllegalAccessException e) {
405                                                                                throw new ConversionException(e);
406                                                                        } catch (InvocationTargetException e) {
407                                                                                throw new ConversionException(e);
408                                                                        }
409                                                                }
410                                                                
411                                                        });
412                                                        
413                                                }
414                                                
415                                        }
416                                }
417                        }
418                }
419                
420        }
421        
422        /**
423         * Map of resolved converters: ClassLoader -&gt; ClassLoaderConvertersEntry. 
424         */
425        private static Map<ClassLoader, ClassLoaderConvertersEntry> classLoaderEntries = new HashMap<ClassLoader, ClassLoaderConvertersEntry>();                 
426 
427        /**
428         * Converts source to target type using Java 6 service loading for AtomicConverterBundle, AtomicConverter, and Converter services
429         * If no converter is found, this method performs converter discovery through
430         * constructors if target type is a class and through duck conversion if target
431         * type is an interface. 
432         * @param source Source object.
433         * @param targetType Target type.
434         * @param context Conversion context.
435         * @param classLoader Class loader.
436         * @return Source object converted to target type or null if no converter
437         * is available for source/targetType combination
438         * @throws ConversionException
439         */
440        public static <S, T> T convert(S source, Class<T> targetType, Context context, ClassLoader classLoader) throws ConversionException {
441                return convert(source, targetType, context, false, classLoader);
442        }
443        
444        /**
445         * Converts source to target type using Java 6 service loading for AtomicConverterBundle, AtomicConverter, and Converter services
446         * If no converter is found, this method performs converter discovery through
447         * constructors if target type is a class and through duck conversion if target
448         * type is an interface. 
449         * @param source Source object.
450         * @param targetType Target type.
451         * @param chain If true, the converter will attempt to build a chain of 
452         * conversions from source type to target type. 
453         * For example, StringBuffer doesn't have a constructor from Integer. If chain
454         * is <code>false</code> then this method will return <code>null</code>. If 
455         * <code>chain</code> is true, then the converter will convert Integer to String
456         * and then will create StringBuffer from it. If multiple conversion chains 
457         * are available, the shortest is chosen.
458         * @return Source object converted to target type or null if no converter
459         * is available for source/targetType combination
460         * @throws ConversionException
461         */
462        @SuppressWarnings("unchecked")
463        private static <S, T> T convert(S source, Class<T> targetType, Context context, boolean chain, ClassLoader classLoader) throws ConversionException {
464                if (source==null) {
465                        return null;
466                }
467                
468                if (targetType.isInstance(source)) {
469                        return (T) source;
470                }
471                
472                if (source instanceof Adaptable) {
473                        T ret = ((Adaptable) source).getAdapter(targetType, context);
474                        if (ret!=null) {
475                                return ret;
476                        }
477                }
478                
479                ConverterClosure<S, T> converter = (ConverterClosure<S, T>) getConverter(source.getClass(), targetType, context, chain, classLoader);
480                
481                if (converter!=null) {
482                        return converter.convert(source);
483                }
484                
485                ConverterClosure<S, Adaptable> adaptableConverter = (ConverterClosure<S, Adaptable>) getConverter(source.getClass(), Adaptable.class, context, chain, classLoader);
486                if (adaptableConverter==null) {
487                        return null;
488                }
489                
490                Adaptable adaptable = adaptableConverter.convert(source);                
491                return adaptable==null ? null : adaptable.getAdapter(targetType, context);                
492        }
493        
494        /**
495         * Converts source to target type using Java 6 service loading for AtomicConverterBundle, AtomicConverter, and Converter services
496         * If no converter is found, this method performs converter discovery through
497         * constructors if target type is a class and through duck conversion if target
498         * type is an interface. 
499         * @param sourceType Source type.
500         * @param targetType Target type.
501         * @return Converter from source type to target type, or null if such converter
502         * is not available.
503         * @throws ConversionException
504         */        
505        public static <S, T> ConverterClosure<S, T> getConverter(Class<S> sourceType, Class<T> targetType) { 
506                return getConverter(sourceType, targetType, null, false, null);
507        }
508        
509        /**
510         * Converts source to target type using Java 6 service loading for AtomicConverterBundle, AtomicConverter, and Converter services
511         * If no converter is found, this method performs converter discovery through
512         * constructors if target type is a class and through duck conversion if target
513         * type is an interface. 
514         * @param sourceType Source type.
515         * @param targetType Target type.
516         * @param classLoader Class loader to retrieve converters from.
517         * @return Converter from source type to target type, or null if such converter
518         * is not available.
519         * @throws ConversionException
520         */        
521        public static <S, T> ConverterClosure<S, T> getConverter(Class<S> sourceType, Class<T> targetType, ClassLoader classLoader) { 
522                return getConverter(sourceType, targetType, null, false, classLoader);
523        }
524        
525        /**
526         * Converts source to target type using Java 6 service loading for AtomicConverterBundle, AtomicConverter, and Converter services
527         * If no converter is found, this method performs converter discovery through
528         * constructors if target type is a class and through duck conversion if target
529         * type is an interface. 
530         * @param sourceType Source type.
531         * @param targetType Target type.
532         * @param context Conversion context.
533         * @return Converter from source type to target type, or null if such converter
534         * is not available.
535         * @throws ConversionException
536         */        
537        public static <S, T> ConverterClosure<S,T> getConverter(Class<S> sourceType, Class<T> targetType, Context context) { 
538                return getConverter(sourceType, targetType, context, false, null);
539        }
540        
541        /**
542         * Converts source to target type using Java 6 service loading for AtomicConverterBundle, AtomicConverter, and Converter services
543         * If no converter is found, this method performs converter discovery through
544         * constructors if target type is a class and through duck conversion if target
545         * type is an interface. 
546         * @param sourceType Source type.
547         * @param targetType Target type.
548         * @param context Conversion context.
549         * @param classLoader Class loader to retrieve converters from.
550         * @return Converter from source type to target type, or null if such converter
551         * is not available.
552         * @throws ConversionException
553         */        
554        public static <S, T> ConverterClosure<S,T> getConverter(Class<S> sourceType, Class<T> targetType, Context context, ClassLoader classLoader) { 
555                return getConverter(sourceType, targetType, context, false, classLoader);
556        }
557        
558        private static ClassLoader promote(ClassLoader present, ClassLoader candidate) {
559                ClassLoader ret = DuckConverterFactory.getChildClassLoader(present, candidate);
560                return ret==null ? present : ret;
561        }
562        
563        /**
564         * 
565         * @param sourceType
566         * @param targetType
567         * @param context
568         * @param chain If true, the converter will attempt to build a chain of 
569         * conversions from source type to target type. 
570         * For example, StringBuffer doesn't have a constructor from Integer. If chain
571         * is <code>false</code> then this method will return <code>null</code>. If 
572         * <code>chain</code> is true, then the converter will convert Integer to String
573         * and then will create StringBuffer from it. If multiple conversion chains 
574         * are available, the shortest is chosen.
575         * @param classLoader
576         * @return
577         */
578        @SuppressWarnings("unchecked")
579        private static <S, T> ConverterClosure<S, T> getConverter(Class<S> sourceType, Class<T> targetType, final Context context, boolean chain, ClassLoader pClassLoader) {
580                if (pClassLoader==null) {
581                        pClassLoader = ConvertingService.class.getClassLoader();
582                }
583                ClassLoader classLoader = promote(pClassLoader, promote(targetType.getClassLoader(), sourceType.getClassLoader()));
584                                        
585                ClassLoaderConvertersEntry clce;
586                synchronized (classLoaderEntries) {
587                        clce = classLoaderEntries.get(classLoader);
588                        if (clce == null) {
589                                clce = new ClassLoaderConvertersEntry(classLoader);
590                                for (Object provider: ServiceLoader.load(AtomicConvertersBundle.class, classLoader)) {
591                                        clce.addConverters(provider);
592                                }
593                                for (Object provider: ServiceLoader.load(AtomicConverter.class, classLoader)) {
594                                        clce.addConverters(provider);
595                                }
596                                for (Object provider: ServiceLoader.load(Converter.class, classLoader)) {
597                                        clce.addConverters(provider);
598                                }
599                                
600                                for (Object provider: ServiceLoader.load(ConverterPart.class, classLoader)) {
601                                        clce.addConverters(provider);
602                                }
603                                // TODO - Converter factories which may create converter (closure) or return null depending on context.
604                                classLoaderEntries.put(classLoader, clce);
605                        }        
606                        clce.addAnnotationConverters(sourceType);
607                }
608                final ConvertersBucket bucket = chain ? clce.findChainingConverter(sourceType, targetType) : clce.findConverter(sourceType, targetType);
609                return bucket==null ? null : new ConverterClosure() {
610 
611                        public Object convert(Object source) {
612                                return bucket.convert(source, context);
613                        }
614                        
615                };
616        }
617}

[all classes][com.hammurapi.convert]
EMMA 2.0.5312 EclEmma Fix 2 (C) Vladimir Roubtsov