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 static java.lang.reflect.Modifier.isTransient;
32
33 import java.lang.reflect.Constructor;
34 import java.lang.reflect.Method;
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.regex.Pattern;
39
40 import org.openrdf.elmo.annotations.intercepts;
41 import org.openrdf.elmo.dynacode.ClassTemplate;
42 import org.openrdf.elmo.dynacode.CodeBuilder;
43
44 class Behaviour {
45 private static final String FACTORY_SUFFIX = "Factory";
46
47 private Class<?> javaClass;
48
49 private ClassTemplate declaring;
50
51 private String getterName;
52
53 private Map<Class<?>, MethodFactory> factories;
54
55 public void setDeclaring(ClassTemplate declaring) {
56 this.declaring = declaring;
57 }
58
59 public void setFactories(Map<Class<?>, MethodFactory> factories) {
60 this.factories = factories;
61 }
62
63 public void setJavaClass(Class<?> javaClass) {
64 this.javaClass = javaClass;
65 }
66
67 public List<Method> getAroundInvoke(Method method, Class<?> face,
68 ClassTemplate cc) throws Exception {
69 List<Method> list = new ArrayList<Method>();
70 Method jm = findInterfaceMethod(method, face);
71 for (Method im : javaClass.getMethods()) {
72 if (im.isAnnotationPresent(intercepts.class)) {
73 intercepts it = im.getAnnotation(intercepts.class);
74 if (!nameMatches(method.getName(), it))
75 continue;
76 if (!argcMatch(it.argc(), jm.getParameterTypes().length))
77 continue;
78 if (!argsMatch(it.parameters(), jm.getParameterTypes()))
79 continue;
80 if (!returnTypeMatches(jm, it))
81 continue;
82 if (!declaredInMatches(jm, it))
83 continue;
84 if (isEnabled(jm, im.getDeclaringClass(), it))
85 list.add(im);
86 }
87 }
88 return list;
89 }
90
91 public String getGetterName() {
92 return getterName;
93 }
94
95 public Class<?> getJavaClass() {
96 return javaClass;
97 }
98
99 public boolean isMethodPresent(Method method) throws Exception {
100 try {
101 Class<?>[] types = method.getParameterTypes();
102 Method m = javaClass.getMethod(method.getName(), types);
103 if (isTransient(m.getModifiers()))
104 return false;
105 return !isObjectMethod(m);
106 } catch (NoSuchMethodException e) {
107 return false;
108 }
109 }
110
111 public boolean invokeCondition(Method jm, Class<?> face)
112 throws Exception {
113 for (Method im : javaClass.getMethods()) {
114 if (im.isAnnotationPresent(intercepts.class)) {
115 intercepts it = im.getAnnotation(intercepts.class);
116 if (!nameMatches(jm.getName(), it))
117 continue;
118 if (!argcMatch(it.argc(), jm.getParameterTypes().length))
119 continue;
120 if (!argsMatch(it.parameters(), jm.getParameterTypes()))
121 continue;
122 if (!returnTypeMatches(jm, it))
123 continue;
124 if (!declaredInMatches(jm, it))
125 continue;
126 if (isEnabled(jm, im.getDeclaringClass(), it))
127 return true;
128 }
129 }
130 return false;
131 }
132
133 public void init() throws Exception {
134 getterName = "_$get" + javaClass.getSimpleName()
135 + Integer.toHexString(javaClass.hashCode());
136 String fieldName = "_$" + getterName.substring(5);
137 declaring.createField(javaClass, fieldName);
138 CodeBuilder code = declaring.createMethod(javaClass, getterName);
139 code.code("if (").code(fieldName).code(" != null){\n");
140 code.code("return ").code(fieldName).code(";\n} else {\n");
141 code.code("return ").code(fieldName).code(" = ($r) ");
142 appendNewInstance(code);
143 code.code(";\n}").end();
144 }
145
146 private void appendNewInstance(CodeBuilder code) throws Exception {
147 MethodFactory mf = factories.get(javaClass);
148 if (mf == null) {
149 code.code("new ").code(javaClass.getName());
150 if (getConstructorParameterType() == null) {
151 javaClass.getDeclaredConstructor();
152 code.code("()");
153 } else {
154 code.code("($0)");
155 }
156 } else {
157 Class<?> factory = mf.getFactoryClass();
158 String name = "_$" + factory.getSimpleName()
159 + Integer.toHexString(factory.getName().hashCode())
160 + FACTORY_SUFFIX;
161 CodeBuilder field = declaring.assignStaticField(factory, name);
162 Method instanceMethod = mf.getInstanceMethod();
163 if (instanceMethod == null) {
164 field.construct(factory).end();
165 } else {
166 field.staticInvoke(instanceMethod).end();
167 }
168 code.code(name).code(".").code(mf.getMethod().getName());
169 if (mf.getMethod().getParameterTypes().length == 0) {
170 code.code("()");
171 } else {
172 code.code("($0)");
173 }
174 }
175 }
176
177 private String getConstructorParameterType() throws Exception {
178 for (Constructor<?> c : javaClass.getConstructors()) {
179 Class<?>[] param = c.getParameterTypes();
180 if (param.length == 1 && param[0].isInterface()) {
181 for (Class<?> f : declaring.getInterfaces()) {
182 if (param[0].isAssignableFrom(f)) {
183 return param[0].getName();
184 }
185 }
186 }
187 }
188 return null;
189 }
190
191 private boolean nameMatches(String method, intercepts it) {
192 if (it.method().length() == 0)
193 return true;
194 return Pattern.matches(it.method(), method);
195 }
196
197 private boolean argcMatch(int argc1, int argc2) {
198 return argc1 < 0 || argc2 < 0 || argc1 == argc2;
199 }
200
201 private boolean argsMatch(Class<?>[] pattern, Class<?>[] type) {
202 if (pattern.length == 1 && pattern[0] == intercepts.class)
203 return true;
204 if (pattern.length != type.length)
205 return false;
206 for (int i = 0; i < pattern.length; i++) {
207 if (!isAssignableFrom(pattern[i], type[i]))
208 return false;
209 if (!pattern[i].equals(type[i]))
210 return false;
211 }
212 return true;
213 }
214
215 private boolean returnTypeMatches(Method jm, intercepts it) {
216 if (it.returns() == intercepts.class)
217 return true;
218 return isAssignableFrom(jm.getReturnType(), it.returns());
219 }
220
221 private boolean declaredInMatches(Method jm, intercepts it) {
222 if (it.declaring() == intercepts.class)
223 return true;
224 try {
225 it.declaring().getMethod(jm.getName(), jm.getParameterTypes());
226 return true;
227 } catch (NoSuchMethodException e) {
228 return false;
229 }
230 }
231
232 private boolean isEnabled(Method jm, Class<?> interceptor, intercepts it)
233 throws Exception {
234 if (it.conditional().length() == 0)
235 return true;
236 Method cm = interceptor.getDeclaredMethod(it.conditional(),
237 new Class[] { Method.class });
238 assert cm != null : it.conditional();
239 return Boolean.TRUE.equals(cm.invoke(null, new Object[] { jm }));
240 }
241
242 private boolean isAssignableFrom(Class<?> t1, Class<?> t2) {
243 if (t1.isAssignableFrom(t2))
244 return true;
245 if (t2.isPrimitive())
246 return getWrapperClass(t2).equals(t1);
247 if (t1.isPrimitive())
248 return getWrapperClass(t1).equals(t2);
249 return false;
250 }
251
252 private Class<?> getWrapperClass(Class<?> primitiveClass) {
253 if (primitiveClass.equals(Boolean.TYPE))
254 return Boolean.class;
255 if (primitiveClass.equals(Byte.TYPE))
256 return Byte.class;
257 if (primitiveClass.equals(Character.TYPE))
258 return Character.class;
259 if (primitiveClass.equals(Short.TYPE))
260 return Short.class;
261 if (primitiveClass.equals(Integer.TYPE))
262 return Integer.class;
263 if (primitiveClass.equals(Long.TYPE))
264 return Long.class;
265 if (primitiveClass.equals(Float.TYPE))
266 return Float.class;
267 if (primitiveClass.equals(Double.TYPE))
268 return Double.class;
269 if (primitiveClass.equals(Void.TYPE))
270 return Void.class;
271 return primitiveClass;
272 }
273
274 private boolean isObjectMethod(Method m) {
275 return m.getDeclaringClass().getName().equals(Object.class.getName());
276 }
277
278 private Method findInterfaceMethod(Method method, Class<?> face)
279 throws Exception {
280 String name = method.getName();
281 Class<?>[] types = method.getParameterTypes();
282 Class<?> clazz = face;
283 return clazz.getDeclaredMethod(name, types);
284 }
285 }