1 package org.openrdf.elmo.impl;
2
3 import static java.lang.reflect.Modifier.isAbstract;
4 import static java.lang.reflect.Modifier.isFinal;
5 import static java.lang.reflect.Modifier.isProtected;
6
7 import java.lang.reflect.Constructor;
8 import java.lang.reflect.Method;
9 import java.util.ArrayList;
10 import java.util.Arrays;
11 import java.util.Collection;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15
16 import org.openrdf.elmo.Entity;
17 import org.openrdf.elmo.ImplementationResolver;
18 import org.openrdf.elmo.dynacode.ClassFactory;
19 import org.openrdf.elmo.dynacode.ClassTemplate;
20 import org.openrdf.elmo.dynacode.CodeBuilder;
21 import org.openrdf.elmo.exceptions.ElmoCompositionException;
22
23 public class AbstractBehaviourClassFactory implements ImplementationResolver {
24 public static final String CLASS_PREFIX = "elmobeans.behaviours.";
25 private static final String BEAN_FIELD_NAME = "_$elmoBean";
26 private ClassFactory cp;
27
28 public void setClassDefiner(ClassFactory definer) {
29 this.cp = definer;
30 }
31
32 public Collection<Class<?>> findImplementations(Collection<Class<?>> classes) {
33 try {
34 List<Class<?>> result = new ArrayList<Class<?>>();
35 for (Class<?> c : classes) {
36 result.add(findClass(c));
37 }
38 return result;
39 } catch (ElmoCompositionException e) {
40 throw e;
41 } catch (Exception e) {
42 throw new ElmoCompositionException(e);
43 }
44 }
45
46 private Class<?> findClass(Class<?> c) throws Exception {
47 String name = getClassName(c);
48 try {
49 return Class.forName(name, true, cp);
50 } catch (ClassNotFoundException e1) {
51 synchronized (cp) {
52 try {
53 return Class.forName(name, true, cp);
54 } catch (ClassNotFoundException e2) {
55 return createClass(name, c);
56 }
57 }
58 }
59
60 }
61
62 private Class<?> createClass(String name, Class<?> c) throws Exception {
63 ClassTemplate cc = cp.createClassTemplate(name, c);
64 cc.createField(Entity.class, BEAN_FIELD_NAME);
65 addConstructor(c, cc);
66 for (Method m : getMethods(c)) {
67 if (isFinal(m.getModifiers()))
68 continue;
69 if (!isAbstract(m.getModifiers()))
70 continue;
71 Class<?> r = m.getReturnType();
72 Class<?>[] types = m.getParameterTypes();
73 CodeBuilder code = cc.createTransientMethod(r, m.getName(), types);
74 if (!Void.TYPE.equals(r)) {
75 code.code("return ($r) ");
76 }
77 if (m.getDeclaringClass().isInterface()) {
78 code.code("(").castObject(BEAN_FIELD_NAME, m.getDeclaringClass());
79 code.code(").").code(m.getName()).code("($$);").end();
80 } else {
81 code.code(BEAN_FIELD_NAME).code(".getClass().getMethod(");
82 code.insert(m.getName()).code(", $sig)").code(".invoke(");
83 code.code(BEAN_FIELD_NAME).code(", $args);").end();
84 }
85 }
86 return cp.createClass(cc);
87 }
88
89 private Collection<Method> getMethods(Class<?> c) {
90 List<Method> methods = new ArrayList<Method>();
91 methods.addAll(Arrays.asList(c.getMethods()));
92 HashMap<Object, Method> map = new HashMap<Object, Method>();
93 Map<Object, Method> pms = getProtectedMethods(c, map);
94 methods.addAll(pms.values());
95 return methods;
96 }
97
98 private Map<Object, Method> getProtectedMethods(Class<?> c,
99 Map<Object, Method> methods) {
100 if (c == null)
101 return methods;
102 for (Method m : c.getDeclaredMethods()) {
103 if (isProtected(m.getModifiers())) {
104 Object types = Arrays.asList(m.getParameterTypes());
105 Object key = Arrays.asList(m.getName(), types);
106 if (!methods.containsKey(key)) {
107 methods.put(key, m);
108 }
109 }
110 }
111 return getProtectedMethods(c.getSuperclass(), methods);
112 }
113
114 private void addConstructor(Class<?> c, ClassTemplate cc) throws Exception {
115 String type = getConstructorParameterType(c);
116 StringBuilder body = new StringBuilder();
117 if (type != null) {
118 body.append("$proceed((");
119 body.append(type).append(")");
120 body.append("$1)");
121 }
122 body.append(BEAN_FIELD_NAME).append(" = $1;");
123 cc.addConstructor(new Class<?>[] { Entity.class }, body.toString());
124 }
125
126 private String getClassName(Class<?> klass) {
127 return CLASS_PREFIX + klass.getName() + "Behaviour";
128 }
129
130 private String getConstructorParameterType(Class<?> javaClass) throws Exception {
131 for (Constructor<?> c : javaClass.getConstructors()) {
132 Class<?>[] param = c.getParameterTypes();
133 if (param.length == 1 && param[0].isInterface()) {
134 return param[0].getName();
135 }
136 }
137 return null;
138 }
139
140 }