ContextConverterFactory.java

biz/hammurapi/convert/ContextConverterFactory.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 175:58
Java Inspector 086 Use equals() instead of == or != to compare objects. 1 180:58
Java Inspector 049 Use a Collection instead of arrays Object[] 2 48:17
Java Inspector 049 Use a Collection instead of arrays Object[] 2 127:39
Java Inspector 070-A Cyclomatic complexity is too high: 14, maximum allowed is 12 2 89:9
Java Inspector 081 Avoid static collections, they can grow in size over time. 2 37:9
Java Inspector 089 Undocumented method 2 28:17
Java Inspector 089 Undocumented constructor 2 52:17
Java Inspector 089 Undocumented method 2 58:17
Java Inspector 089 Undocumented method 2 62:33
Java Inspector 089 Method is not properly documented 2 89:9
Java Inspector 089 Parameter sourceClass is not documented 2 89:9
Java Inspector 089 Parameter targetInterface is not documented 2 89:9
Java Inspector 005 Classes, interfaces, methods, and variables should be named according to Sun's naming conventions. 3 26:9
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 68:73
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 69:131
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 73:117
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 110:66
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 119:126
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 137:79
Java Inspector 026 Avoid hardwired string literals. Allowed literals: [] 3 140:86
Java Inspector 051 It is good practice to call in any case super() in a constructor. 3 52:17

Source code

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