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

COVERAGE SUMMARY FOR SOURCE FILE [ContextConverterFactory.java]

nameclass, %method, %block, %line, %
ContextConverterFactory.java0%   (0/4)0%   (0/11)0%   (0/390)0%   (0/76)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ContextConverterFactory0%   (0/1)0%   (0/4)0%   (0/276)0%   (0/59)
<static initializer> 0%   (0/1)0%   (0/9)0%   (0/3)
ContextConverterFactory (): void 0%   (0/1)0%   (0/3)0%   (0/1)
getChildClassLoader (ClassLoader, ClassLoader): ClassLoader 0%   (0/1)0%   (0/46)0%   (0/11)
getConverter (Class, Class): ConverterClosure 0%   (0/1)0%   (0/218)0%   (0/45)
     
class ContextConverterFactory$10%   (0/1)0%   (0/2)0%   (0/5)0%   (0/3)
ContextConverterFactory$1 (): void 0%   (0/1)0%   (0/3)0%   (0/2)
convert (Object): Object 0%   (0/1)0%   (0/2)0%   (0/1)
     
class ContextConverterFactory$ProxyConverter0%   (0/1)0%   (0/3)0%   (0/33)0%   (0/8)
ContextConverterFactory$ProxyConverter (Class, Map, ClassLoader): void 0%   (0/1)0%   (0/17)0%   (0/5)
access$0 (ContextConverterFactory$ProxyConverter): Map 0%   (0/1)0%   (0/3)0%   (0/1)
convert (Object): Object 0%   (0/1)0%   (0/13)0%   (0/2)
     
class ContextConverterFactory$ProxyConverter$10%   (0/1)0%   (0/2)0%   (0/76)0%   (0/9)
ContextConverterFactory$ProxyConverter$1 (ContextConverterFactory$ProxyConver... 0%   (0/1)0%   (0/9)0%   (0/2)
invoke (Object, Method, Object []): Object 0%   (0/1)0%   (0/67)0%   (0/7)

1package com.hammurapi.convert;
2 
3import java.lang.reflect.InvocationHandler;
4import java.lang.reflect.Method;
5import java.lang.reflect.Proxy;
6import java.util.ArrayList;
7import java.util.Collection;
8import java.util.HashMap;
9import java.util.Map;
10 
11import com.hammurapi.common.Context;
12import com.hammurapi.common.MutableContext;
13 
14 
15 
16/**
17 * Creates converters from Context and MutableContext to interfaces which have only setters and getters (beans)
18 * @author Pavel
19 *
20 */
21public class ContextConverterFactory {
22        
23        private static final String SET = "set";
24 
25        private static final String GET = "get";
26 
27        /**
28         * Returns source object unchanged
29         */
30        private static ConverterClosure<?,?> ZERO_CONVERTER = new ConverterClosure<Object,Object>() {
31 
32                public Object convert(Object source) {                        
33                        return source;
34                }
35                
36        };
37        
38        /**
39         * Contains [sourceClass, targetClass] -> ProxyConverter(targetMethod -> sourceMethod) mapping.
40         */
41        private static Map<Collection<Class<?>>, Object> converterMap = new HashMap<Collection<Class<?>>, Object>();
42        
43        /**
44         * Converts object to target interface using dynamic proxy.
45         * @author Pavel Vlasov
46         *
47         */
48        private static class ProxyConverter<S, T> implements ConverterClosure<S, T> {
49 
50                /**
51                 * Maps target methods to source methods. Unmapped methods
52                 * are invoked directly (e.g. equals() or if method in both source and target belongs
53                 * to the same interface (partial overlap)).
54                 */
55                private Map<Method,Method> methodMap;
56                
57                private Class<?>[] targetInterfaces;
58 
59                private ClassLoader classLoader;
60                
61                public ProxyConverter(Class<T> targetInterface, Map<Method,Method> methodMap, ClassLoader classLoader) {
62                        this.methodMap = methodMap;
63                        this.targetInterfaces = new Class[] {targetInterface};
64                        this.classLoader = classLoader;
65                }
66                
67                @SuppressWarnings("unchecked")
68                public T convert(final S source) {
69                        
70                        InvocationHandler ih = new InvocationHandler() {
71 
72                                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
73                                        Method sourceMethod = (Method) (methodMap==null ? null : methodMap.get(method));
74                                        if (sourceMethod==null) {
75                                                return method.invoke(source, args);
76                                        }
77 
78                                        if (method.getName().startsWith(GET)) {
79                                                Object ret = sourceMethod.invoke(source, new Object[] {method.getName().substring(GET.length())});                                                
80                                                return ConvertingService.convert(ret, method.getReturnType());                                                
81                                        }
82                                        
83                                        return sourceMethod.invoke(source, new Object[] {method.getName().substring(SET.length()), args[0]});
84                                }
85                                
86                        };
87                        
88                        return (T) Proxy.newProxyInstance(classLoader, targetInterfaces, ih);
89                }
90                
91        }
92 
93        /**
94         * @param sourceClass
95         * @param targetInterface
96         * @return Converter which can "duck-type" instance of source class to target interface or null if conversion is not possible.
97         * Methods are mapped as follows: return types shall be compatible, arguments shall be compatible, exception declarations are ignored.
98         */
99        @SuppressWarnings("unchecked")
100        public static <S, T> ConverterClosure<S, T> getConverter(Class<S> sourceClass, Class<T> targetInterface) {
101                if (targetInterface.isAssignableFrom(sourceClass)) {
102                        return (ConverterClosure<S, T>) ZERO_CONVERTER;
103                }
104                
105                Collection<Class<?>> key=new ArrayList<Class<?>>();
106                key.add(sourceClass);
107                key.add(targetInterface);
108                synchronized (converterMap) {
109                        Object value = converterMap.get(key);
110                        if (Boolean.FALSE.equals(value)) {
111                                return null;
112                        }
113                        
114                        if (!Context.class.isAssignableFrom(sourceClass)) {
115                                converterMap.put(key, Boolean.FALSE); // To indicate that we tried and failed
116                                return null;                                
117                        }
118                        
119                        Method getter;
120                        try {
121                                getter = Context.class.getMethod("lookup", new Class[] {String.class});
122                        } catch (SecurityException e) {
123                                throw new ConversionException(e);
124                        } catch (NoSuchMethodException e) {
125                                throw new ConversionException(e);
126                        }
127                        
128                        Method setter;
129                        try {
130                                setter = MutableContext.class.isAssignableFrom(sourceClass) ? MutableContext.class.getMethod("bind", new Class[] {String.class, Object.class}) : null;
131                        } catch (SecurityException e) {
132                                throw new ConversionException(e);
133                        } catch (NoSuchMethodException e) {
134                                throw new ConversionException(e);
135                        }                        
136                                                
137                        if (value==null) {
138                                Method[] targetMethods = targetInterface.getMethods();
139                                
140                                Map<Method, Method> methodMap = new HashMap<Method, Method>();
141                                
142                                for (int i=0, l=targetMethods.length; i<l; ++i) {
143                                        if (Object.class.equals(targetMethods[i].getDeclaringClass())) { 
144                                                continue;
145                                        }
146                                                                                
147                                        Method targetMethod = targetMethods[i];
148                                        if (targetMethod.getName().startsWith("get") 
149                                                        && targetMethod.getParameterTypes().length==0) {
150                                                methodMap.put(targetMethod, getter);
151                                        } else if (targetMethod.getName().startsWith("set") 
152                                                        && targetMethod.getParameterTypes().length==1
153                                                        && setter!=null) {
154                                                methodMap.put(targetMethod, setter);
155                                        } else {
156                                                converterMap.put(key, Boolean.FALSE); // To indicate that we tried and failed
157                                                return null;                                                                                
158                                        }
159                                }
160                                
161                                ClassLoader cl = getChildClassLoader(sourceClass.getClassLoader(), targetInterface.getClassLoader());
162                                if (cl==null) {
163                                        converterMap.put(key, Boolean.FALSE); // To indicate that we tried and failed
164                                        return null;                                        
165                                }
166                                
167                                value = new ProxyConverter<S, T>(targetInterface, methodMap.isEmpty() ? null : methodMap, cl);
168                                converterMap.put(key, value);
169                        }
170                        return (ConverterClosure<S, T>) value;
171                }
172        }
173 
174        /**
175         * @param cl1
176         * @param cl2
177         * @return Child classloader or null if classloaders are not related
178         */
179        private static ClassLoader getChildClassLoader(ClassLoader cl1, ClassLoader cl2) {
180                if (cl1==null) {
181                        return cl2;
182                }
183                if (cl2==null) {
184                        return cl1;
185                }
186                for (ClassLoader cl = cl1; cl!=null && cl!=cl.getParent(); cl=cl.getParent()) {
187                        if (cl2.equals(cl)) {
188                                return cl1;
189                        }
190                }
191                for (ClassLoader cl = cl2; cl!=null && cl!=cl.getParent(); cl=cl.getParent()) {
192                        if (cl1.equals(cl)) {
193                                return cl2;
194                        }
195                }
196                return null;
197        }                
198}

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