1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 package org.openrdf.elmo.impl;
30
31 import java.lang.reflect.Method;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collection;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.TreeSet;
41 import java.util.concurrent.ConcurrentHashMap;
42 import java.util.concurrent.ConcurrentMap;
43
44 import org.openrdf.elmo.ElmoBehaviourFactory;
45 import org.openrdf.elmo.ElmoEntityResolver;
46 import org.openrdf.elmo.ElmoMapperResolver;
47 import org.openrdf.elmo.annotations.factory;
48 import org.openrdf.elmo.dynacode.ClassFactory;
49 import org.openrdf.elmo.dynacode.ClassTemplate;
50 import org.openrdf.elmo.dynacode.CodeBuilder;
51 import org.openrdf.elmo.exceptions.ElmoCompositionException;
52
53
54
55
56
57
58
59
60 public class ElmoEntityCompositor implements ElmoEntityResolver {
61 private static final String _$INTERCEPTED = "_$intercepted";
62 private static final String PKG_PREFIX = "elmobeans.proxies._$";
63 private static final String CLASS_PREFIX = "_$EntityProxy";
64
65 private ElmoMapperResolver loader;
66
67 private ClassFactory cp;
68
69 private Map<List<Class<?>>, Class<?>> constructors;
70
71 private Map<Class<?>, ElmoBehaviourFactory<?>> factories;
72
73 private ConcurrentMap<List<Class<?>>, Class<?>> classes;
74
75 public ElmoEntityCompositor() {
76 constructors = new HashMap<List<Class<?>>, Class<?>>();
77 factories = new HashMap<Class<?>, ElmoBehaviourFactory<?>>();
78 classes = new ConcurrentHashMap<List<Class<?>>, Class<?>>();
79 }
80
81 public void setBehaviourClassResolver(ElmoMapperResolver loader) {
82 this.loader = loader;
83 }
84
85 public void setClassDefiner(ClassFactory definer) {
86 this.cp = definer;
87 }
88
89 public void addFactoryClass(Class<?> factory, Class<?>... constructor) {
90 constructors.put(Arrays.asList(constructor), factory);
91 }
92
93 public void setFactoryClasses(Map<List<Class<?>>, Class<?>> factories) {
94 this.constructors.putAll(factories);
95 }
96
97 public void addBehaviourFactory(ElmoBehaviourFactory<?> factory) {
98 factories.put(factory.getBehaviourClass(), factory);
99 }
100
101 public Class<?> resolveRoles(Class<?>[] roles) {
102 List<Class<?>> list = Arrays.asList(roles);
103 Class<?> type = classes.get(list);
104 if (type == null) {
105 type = createClass(list);
106 Class<?> o = classes.putIfAbsent(list, type);
107 if (o != null) {
108 type = o;
109 }
110 }
111 return type;
112 }
113
114 private Class<?> createClass(List<Class<?>> roles) {
115 try {
116 List<Class<?>> types = new ArrayList<Class<?>>(roles);
117 types = removeSuperClasses(types);
118 Set<Class<?>> interfaces = new HashSet<Class<?>>(types.size());
119 Set<Class<?>> behaviours = new HashSet<Class<?>>(types.size());
120 Map<Class<?>, ElmoBehaviourFactory<?>> map = new HashMap();
121 map.putAll(factories);
122 for (Class<?> role : types) {
123 if (role.isAnnotationPresent(factory.class)) {
124 for (Method m : role.getMethods()) {
125 if (m.isAnnotationPresent(factory.class)) {
126 map.put(m.getReturnType(), new MethodFactory(m,
127 role));
128 }
129 }
130 }
131 }
132 for (Class<?> role : types) {
133 if (role.isInterface()) {
134 interfaces.add(role);
135 }
136 if (role.isAnnotationPresent(factory.class))
137 continue;
138 if (!role.isInterface() || map.containsKey(role)) {
139 behaviours.add(role);
140 }
141 }
142 behaviours.addAll(loader.findMappers(interfaces));
143 String className = getJavaClassName(roles);
144 return getComposedBehaviours(className, interfaces, behaviours, map);
145 } catch (Exception e) {
146 List<String> roleNames = new ArrayList<String>();
147 for (Class<?> f : roles) {
148 roleNames.add(f.getSimpleName());
149 }
150 throw new ElmoCompositionException(e.getMessage()
151 + " for entity with roles: " + roleNames, e);
152 }
153 }
154
155 @SuppressWarnings("unchecked")
156 private List<Class<?>> removeSuperClasses(List<Class<?>> classes) {
157 for (int i = classes.size() - 1; i >= 0; i--) {
158 Class<?> c = classes.get(i);
159 for (int j = classes.size() - 1; j >= 0; j--) {
160 Class<?> d = classes.get(j);
161 if (i != j && c.isAssignableFrom(d)) {
162 classes.remove(i);
163 break;
164 }
165 }
166 }
167 return classes;
168 }
169
170 private Class<?> getComposedBehaviours(String className, Set<Class<?>> interfaces,
171 Set<Class<?>> javaClasses, Map<Class<?>, ElmoBehaviourFactory<?>> factories) throws Exception {
172 try {
173 return Class.forName(className, true, cp);
174 } catch (ClassNotFoundException e) {
175 synchronized (cp) {
176 try {
177 return Class.forName(className, true, cp);
178 } catch (ClassNotFoundException e1) {
179 return composeBehaviours(className, interfaces, javaClasses, factories);
180 }
181 }
182 }
183 }
184
185 private Class<?> composeBehaviours(String className,
186 Set<Class<?>> interfaces, Set<Class<?>> javaClasses,
187 Map<Class<?>, ElmoBehaviourFactory<?>> factories) throws Exception {
188 List<Behaviour> behaviours = new ArrayList<Behaviour>();
189 ClassTemplate cc = cp.createClassTemplate(className);
190 for (Class<?> clazz : javaClasses) {
191 addInterfaces(clazz, interfaces);
192 }
193 for (Class<?> face : interfaces) {
194 cc.addInterface(face);
195 }
196 for (Class<?> clazz : javaClasses) {
197 Behaviour behaviour = new Behaviour();
198 behaviour.setClassDefiner(cp);
199 behaviour.setJavaClass(clazz);
200 behaviour.setDeclaring(cc);
201 behaviour.setConstructorFactories(constructors);
202 behaviour.setFactories(factories);
203 behaviour.init();
204 behaviours.add(behaviour);
205 }
206 for (Method method : getMethods(javaClasses)) {
207 if (!method.getName().startsWith("_$")) {
208 Class<?> face = findInterface(method, cc.getInterfaces());
209 if (face == null)
210 face = method.getDeclaringClass();
211 List<Behaviour> incepts = getInterceptors(behaviours, method,
212 face, cc);
213 if (incepts.size() > 0) {
214 String name = _$INTERCEPTED + method.getName();
215 if (implementMethod(behaviours, method, name, cc)) {
216 interceptMethod(incepts, method, name, face, cc);
217 }
218 } else {
219 implementMethod(behaviours, method, method.getName(), cc);
220 }
221 }
222 }
223 return cp.createClass(cc);
224 }
225
226 private Collection<Method> getMethods(Set<Class<?>> javaClasses) {
227 Map map = new HashMap();
228 for (Class<?> jc : javaClasses) {
229 for (Method m : jc.getMethods()) {
230 if (m.getDeclaringClass().equals(Object.class))
231 continue;
232 List list = new ArrayList(m.getParameterTypes().length + 1);
233 list.add(m.getName());
234 list.addAll(Arrays.asList(m.getParameterTypes()));
235 if (!map.containsKey(list)) {
236 map.put(list, m);
237 }
238 }
239 }
240 return map.values();
241 }
242
243 private Set<Class<?>> addInterfaces(Class<?> clazz, Set<Class<?>> interfaces) {
244 if (interfaces.contains(clazz))
245 return interfaces;
246 if (clazz.isInterface()) {
247 interfaces.add(clazz);
248 }
249 Class<?> superclass = clazz.getSuperclass();
250 if (superclass != null) {
251 addInterfaces(superclass, interfaces);
252 }
253 for (Class<?> face : clazz.getInterfaces()) {
254 addInterfaces(face, interfaces);
255 }
256 return interfaces;
257 }
258
259 private String getJavaClassName(Collection<Class<?>> javaClasses) {
260 String phex = packagesToHexString(javaClasses);
261 String chex = classesToHexString(javaClasses);
262 return PKG_PREFIX + phex + "." + CLASS_PREFIX + chex;
263 }
264
265 private String packagesToHexString(Collection<Class<?>> javaClasses) {
266 TreeSet<String> names = new TreeSet<String>();
267 for (Class<?> clazz : javaClasses) {
268 names.add(clazz.getPackage().getName());
269 }
270 return toHexString(names);
271 }
272
273 private String classesToHexString(Collection<Class<?>> javaClasses) {
274 TreeSet<String> names = new TreeSet<String>();
275 for (Class<?> clazz : javaClasses) {
276 names.add(clazz.getName());
277 }
278 return toHexString(names);
279 }
280
281 private String toHexString(TreeSet<String> names) {
282 long hashCode = 0;
283 for (String name : names) {
284 hashCode = 31 * hashCode + name.hashCode();
285 }
286 return Long.toHexString(hashCode);
287 }
288
289 private boolean implementMethod(List<Behaviour> behaviours,
290 Method method, String name, ClassTemplate cc) throws Exception {
291 Class<?> type = method.getReturnType();
292 boolean voidReturnType = type.equals(Void.TYPE);
293 boolean booleanReturnType = type.equals(Boolean.TYPE);
294 boolean primitiveReturnType = type.isPrimitive();
295 StringBuilder body = new StringBuilder();
296 if (!voidReturnType && primitiveReturnType) {
297 body.append(type.getName()).append(" result;");
298 } else if (!voidReturnType) {
299 body.append("Object result;");
300 }
301 int implemented = 0;
302 StringBuilder eval = null;
303 for (Behaviour behaviour : behaviours) {
304 if (behaviour.isMethodPresent(method)) {
305 implemented++;
306 if (!voidReturnType)
307 body.append("result = ");
308 eval = new StringBuilder();
309 eval.append(behaviour.getGetterName()).append("()");
310 eval.append(".").append(method.getName()).append("($$);");
311 body.append(eval);
312 if (booleanReturnType) {
313 body.append("if (result) return result;");
314 } else if (!voidReturnType && primitiveReturnType) {
315 body.append("if (result != 0) return ($r) result;");
316 } else if (!voidReturnType) {
317 body.append("if (result != null) return ($r) result;");
318 }
319 }
320 }
321 if (!voidReturnType)
322 body.append("return ($r) result;");
323 if (implemented == 1 && eval != null) {
324 cc.createMethod(method.getReturnType(), name, method.getParameterTypes()).code("return ($r) " + eval.toString()).end();
325 return true;
326 } else if (implemented > 1) {
327 cc.createMethod(method.getReturnType(), name, method.getParameterTypes()).code(body.toString()).end();
328 return true;
329 }
330 return false;
331 }
332
333 private List<Behaviour> getInterceptors(List<Behaviour> behaviours,
334 Method method, Class<?> face, ClassTemplate cc) throws Exception {
335 List<Behaviour> result = new ArrayList<Behaviour>(behaviours.size());
336 for (Behaviour behaviour : behaviours) {
337 if (behaviour.invokeCondition(method, face)) {
338 result.add(behaviour);
339 }
340 }
341 return result;
342 }
343
344 private void interceptMethod(List<Behaviour> interceptors, Method method, String name,
345 Class<?> face, ClassTemplate cc) throws Exception {
346 CodeBuilder body = cc.overrideMethod(method);
347 body.code("return ($r) new ").code(
348 InvocationContextImpl.class.getName());
349 body.code("($0, ");
350 Method declaredMethod = face.getDeclaredMethod(method.getName(), method.getParameterTypes());
351 body.insert(declaredMethod);
352 body.code(", $args, ");
353 body.insertMethod(name, method.getParameterTypes()).code(")");
354 for (Behaviour behaviour : interceptors) {
355 for (Method m : behaviour.getAroundInvoke(method, face, cc)) {
356 body.code(".appendInvocation(");
357 body.code(behaviour.getGetterName()).code("(), ");
358 body.insert(m);
359 body.code(")");
360 }
361 }
362 body.code(".proceed();");
363 body.end();
364 }
365
366 private Class<?> findInterface(Method method, Class<?>[] interfaces) throws Exception {
367 Class<?> declaring = null;
368 for (Class<?> face : interfaces) {
369 try {
370 face.getDeclaredMethod(method.getName(), method.getParameterTypes());
371 declaring = face;
372 } catch (NoSuchMethodException e) {
373 declaring = findInterface(method, face.getInterfaces());
374 }
375 if (declaring != null)
376 return declaring;
377 }
378 return declaring;
379 }
380 }