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.Constructor;
32 import java.lang.reflect.Field;
33 import java.lang.reflect.Method;
34 import java.util.ArrayList;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.regex.Pattern;
39
40 import javassist.CannotCompileException;
41
42 import org.openrdf.elmo.ElmoBehaviourFactory;
43 import org.openrdf.elmo.Entity;
44 import org.openrdf.elmo.annotations.intercepts;
45 import org.openrdf.elmo.dynacode.ClassFactory;
46 import org.openrdf.elmo.dynacode.ClassTemplate;
47 import org.openrdf.elmo.dynacode.CodeBuilder;
48 import org.openrdf.elmo.exceptions.ElmoCompositionException;
49
50 class Behaviour {
51 private static final String FACTORY_SUFFIX = "Factory";
52
53 private static final String BEHAVIOUR_DELEGATE_FIELD = "_$staticDelegate";
54
55 private static final String NEW_INSTANCE = "newInstance";
56
57 private static final String CLASS_PREFIX = "elmobeans.factories.";
58
59 private static final String CREATE_BEHAVIOUR = "createBehaviour";
60
61 private ClassFactory cp;
62
63 private Class<?> javaClass;
64
65 private ClassTemplate declaring;
66
67 private String getterName;
68
69 private Map<List<Class<?>>, Class<?>> constructors;
70
71 private Map<Class<?>, ElmoBehaviourFactory<?>> factories;
72
73 public void setDeclaring(ClassTemplate declaring) {
74 this.declaring = declaring;
75 }
76
77 public void setClassDefiner(ClassFactory definer) {
78 this.cp = definer;
79 }
80
81 public void setConstructorFactories(Map<List<Class<?>>, Class<?>> factories) {
82 this.constructors = factories;
83 }
84
85 public void setFactories(Map<Class<?>, ElmoBehaviourFactory<?>> factories) {
86 this.factories = factories;
87 }
88
89 public void setJavaClass(Class<?> javaClass) {
90 this.javaClass = javaClass;
91 }
92
93 public List<Method> getAroundInvoke(Method method, Class<?> face,
94 ClassTemplate cc) throws Exception {
95 List<Method> list = new ArrayList<Method>();
96 Method jm = findInterfaceMethod(method, face);
97 for (Method im : javaClass.getMethods()) {
98 if (im.isAnnotationPresent(intercepts.class)) {
99 intercepts it = im.getAnnotation(intercepts.class);
100 if (!nameMatches(method.getName(), it))
101 continue;
102 if (!argcMatch(it.argc(), jm.getParameterTypes().length))
103 continue;
104 if (!argsMatch(it.parameters(), jm.getParameterTypes()))
105 continue;
106 if (!returnTypeMatches(jm, it))
107 continue;
108 if (!declaredInMatches(jm, it))
109 continue;
110 if (isEnabled(jm, im.getDeclaringClass(), it))
111 list.add(im);
112 }
113 }
114 return list;
115 }
116
117 public String getGetterName() {
118 return getterName;
119 }
120
121 public Class<?> getJavaClass() {
122 return javaClass;
123 }
124
125 public boolean isMethodPresent(Method method) throws Exception {
126 try {
127 Class<?>[] types = method.getParameterTypes();
128 Method m = javaClass.getMethod(method.getName(), types);
129 return !isObjectMethod(m);
130 } catch (NoSuchMethodException e) {
131 return false;
132 }
133 }
134
135 public boolean invokeCondition(Method jm, Class<?> face)
136 throws Exception {
137 for (Method im : javaClass.getMethods()) {
138 if (im.isAnnotationPresent(intercepts.class)) {
139 intercepts it = im.getAnnotation(intercepts.class);
140 if (!nameMatches(jm.getName(), it))
141 continue;
142 if (!argcMatch(it.argc(), jm.getParameterTypes().length))
143 continue;
144 if (!argsMatch(it.parameters(), jm.getParameterTypes()))
145 continue;
146 if (!returnTypeMatches(jm, it))
147 continue;
148 if (!declaredInMatches(jm, it))
149 continue;
150 if (isEnabled(jm, im.getDeclaringClass(), it))
151 return true;
152 }
153 }
154 return false;
155 }
156
157 public void init() throws Exception {
158 getterName = "_$get" + javaClass.getSimpleName()
159 + Integer.toHexString(javaClass.hashCode());
160 String fieldName = "_$" + getterName.substring(5);
161 declaring.createField(javaClass, fieldName);
162 CodeBuilder code = declaring.createMethod(javaClass, getterName);
163 code.code("if (").code(fieldName).code(" != null){");
164 code.code("return ").code(fieldName).code(";} else {");
165 code.code("return ").code(fieldName).code(" = ($r) ");
166 code.code(createFactoryField()).code(".");
167 code.code(CREATE_BEHAVIOUR).code("($0);");
168 code.code("}");
169 code.end();
170 }
171
172 private List<Class<?>> getConstructorLevel(Set<List<Class<?>>> constructors)
173 throws NoSuchMethodException {
174 for (List<Class<?>> constructor : constructors) {
175 try {
176 javaClass.getConstructor(constructor
177 .toArray(new Class[constructor.size()]));
178 return constructor;
179 } catch (NoSuchMethodException e) {
180 }
181 }
182 List<Class<?>> result = new ArrayList<Class<?>>();
183 result.add(Entity.class);
184 assert constructors.contains(result);
185 return result;
186 }
187
188 private String createFactoryField() throws Exception {
189 Class<?> fc = getFactoryClass();
190 String name = "_$" + javaClass.getSimpleName()
191 + Integer.toHexString(javaClass.getName().hashCode())
192 + FACTORY_SUFFIX;
193 Class<?> class1 = ElmoBehaviourFactory.class;
194 CodeBuilder code = declaring.assignStaticField(class1, name);
195 if (factories.get(javaClass) instanceof MethodFactory) {
196 MethodFactory mf = (MethodFactory) factories.get(javaClass);
197 Class<?> factory = mf.getFactoryClass();
198 code.construct(MethodFactory.class, mf.getMethod(), factory).end();
199 } else {
200 code.construct(fc, javaClass).end();
201 }
202 return name;
203 }
204
205 private Class<?> getFactoryClass() throws Exception {
206 Class<?> factory = getFactorySuperClass();
207 String cn = javaClass.getName();
208 String name = CLASS_PREFIX + cn + FACTORY_SUFFIX;
209 try {
210 return Class.forName(name, true, cp);
211 } catch (ClassNotFoundException e) {
212 if (factories.get(javaClass) instanceof MethodFactory) {
213 MethodFactory mf = (MethodFactory) factories.get(javaClass);
214 return mf.getFactoryClass();
215 }
216 ClassTemplate fc = cp.createClassTemplate(name, factory);
217 if (factories.containsKey(javaClass)) {
218 createDelegateFactoryField(fc, BEHAVIOUR_DELEGATE_FIELD);
219 } else {
220 overrideFactoryClassBody(fc);
221 }
222 Class<?> definedClass = cp.createClass(fc);
223 if (factories.containsKey(javaClass)) {
224 Field field = definedClass.getField(BEHAVIOUR_DELEGATE_FIELD);
225 field.set(null, factories.get(javaClass));
226 }
227 return definedClass;
228 }
229 }
230
231 private void createDelegateFactoryField(ClassTemplate fc, String fieldName)
232 throws Exception {
233 Class<?> type = ElmoBehaviourFactory.class;
234 fc.assignStaticField(type, fieldName).code(fieldName).end();
235 Class<?>[] param = new Class<?>[] { Class.class };
236 String body = "super(" + fieldName + ");";
237 fc.addConstructor(param, body);
238 }
239
240 private void overrideFactoryClassBody(ClassTemplate fc) throws Exception {
241 Class<?>[] param = new Class<?>[] { Class.class };
242 fc.addConstructor(param, "super($1);");
243 List<Class<?>> constructor = getConstructorLevel(constructors.keySet());
244 try {
245 overrideNewInstanceMethod(constructor, fc);
246 } catch (CannotCompileException e) {
247 throw new ElmoCompositionException(e.getMessage() + ' '
248 + javaClass.getSimpleName() + constructor, e);
249 }
250 }
251
252 private Class<?> getFactorySuperClass() throws Exception {
253 if (factories.containsKey(javaClass)) {
254 return ElmoBehaviourFactoryWrapper.class;
255 }
256 List<Class<?>> constructor = getConstructorLevel(constructors.keySet());
257 return constructors.get(constructor);
258 }
259
260 private void overrideNewInstanceMethod(List<Class<?>> constructor,
261 ClassTemplate fc) throws Exception {
262 Class<?>[] param = new Class<?>[constructor.size()];
263 for (int i = 0, n = constructor.size(); i < n; i++) {
264 param[i] = constructor.get(i);
265 }
266 CodeBuilder sb = fc.createMethod(Object.class, NEW_INSTANCE, param);
267 sb.code("return new ").code(javaClass.getName()).code("(");
268 for (int i = 1, n = constructor.size(); i <= n; i++) {
269 if (n == 1 && constructor.get(0).equals(Entity.class)) {
270 String face = getConstructorParameterType();
271 if (face != null) {
272 sb.code("(").code(face).code(")");
273 }
274 }
275 sb.code("$").code(Integer.toString(i));
276 if (i < n) {
277 sb.code(",");
278 }
279 }
280 sb.code(");").end();
281 }
282
283 private String getConstructorParameterType() throws Exception {
284 for (Constructor<?> c : javaClass.getConstructors()) {
285 Class<?>[] param = c.getParameterTypes();
286 if (param.length == 1 && param[0].isInterface()) {
287 for (Class<?> f : declaring.getInterfaces()) {
288 if (param[0].isAssignableFrom(f)) {
289 return param[0].getName();
290 }
291 }
292 }
293 }
294 return null;
295 }
296
297 private boolean nameMatches(String method, intercepts it) {
298 if (it.method().length() == 0)
299 return true;
300 return Pattern.matches(it.method(), method);
301 }
302
303 private boolean argcMatch(int argc1, int argc2) {
304 return argc1 < 0 || argc2 < 0 || argc1 == argc2;
305 }
306
307 private boolean argsMatch(Class<?>[] pattern, Class<?>[] type) {
308 if (pattern.length == 1 && pattern[0] == intercepts.class)
309 return true;
310 if (pattern.length != type.length)
311 return false;
312 for (int i = 0; i < pattern.length; i++) {
313 if (!isAssignableFrom(pattern[i], type[i]))
314 return false;
315 if (!pattern[i].equals(type[i]))
316 return false;
317 }
318 return true;
319 }
320
321 private boolean returnTypeMatches(Method jm, intercepts it) {
322 if (it.returns() == intercepts.class)
323 return true;
324 return isAssignableFrom(jm.getReturnType(), it.returns());
325 }
326
327 private boolean declaredInMatches(Method jm, intercepts it) {
328 if (it.declaring() == intercepts.class)
329 return true;
330 try {
331 it.declaring().getMethod(jm.getName(), jm.getParameterTypes());
332 return true;
333 } catch (NoSuchMethodException e) {
334 return false;
335 }
336 }
337
338 private boolean isEnabled(Method jm, Class<?> interceptor, intercepts it)
339 throws Exception {
340 if (it.conditional().length() == 0)
341 return true;
342 Method cm = interceptor.getDeclaredMethod(it.conditional(),
343 new Class[] { Method.class });
344 assert cm != null : it.conditional();
345 return Boolean.TRUE.equals(cm.invoke(null, new Object[] { jm }));
346 }
347
348 private boolean isAssignableFrom(Class<?> t1, Class<?> t2) {
349 if (t1.isAssignableFrom(t2))
350 return true;
351 if (t2.isPrimitive())
352 return getWrapperClass(t2).equals(t1);
353 if (t1.isPrimitive())
354 return getWrapperClass(t1).equals(t2);
355 return false;
356 }
357
358 private Class<?> getWrapperClass(Class<?> primitiveClass) {
359 if (primitiveClass.equals(Boolean.TYPE))
360 return Boolean.class;
361 if (primitiveClass.equals(Byte.TYPE))
362 return Byte.class;
363 if (primitiveClass.equals(Character.TYPE))
364 return Character.class;
365 if (primitiveClass.equals(Short.TYPE))
366 return Short.class;
367 if (primitiveClass.equals(Integer.TYPE))
368 return Integer.class;
369 if (primitiveClass.equals(Long.TYPE))
370 return Long.class;
371 if (primitiveClass.equals(Float.TYPE))
372 return Float.class;
373 if (primitiveClass.equals(Double.TYPE))
374 return Double.class;
375 if (primitiveClass.equals(Void.TYPE))
376 return Void.class;
377 return primitiveClass;
378 }
379
380 private boolean isObjectMethod(Method m) {
381 return m.getDeclaringClass().getName().equals(Object.class.getName());
382 }
383
384 private Method findInterfaceMethod(Method method, Class<?> face)
385 throws Exception {
386 String name = method.getName();
387 Class<?>[] types = method.getParameterTypes();
388 Class<?> clazz = face;
389 return clazz.getDeclaredMethod(name, types);
390 }
391 }