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 code.code("()");
152 } else {
153 code.code("($0)");
154 }
155 } else {
156 Class<?> factory = mf.getFactoryClass();
157 String name = "_$" + factory.getSimpleName()
158 + Integer.toHexString(factory.getName().hashCode())
159 + FACTORY_SUFFIX;
160 CodeBuilder field = declaring.assignStaticField(factory, name);
161 Method instanceMethod = mf.getInstanceMethod();
162 if (instanceMethod == null) {
163 field.construct(factory).end();
164 } else {
165 field.staticInvoke(instanceMethod).end();
166 }
167 code.code(name).code(".").code(mf.getMethod().getName());
168 if (mf.getMethod().getParameterTypes().length == 0) {
169 code.code("()");
170 } else {
171 code.code("($0)");
172 }
173 }
174 }
175
176 private String getConstructorParameterType() throws Exception {
177 for (Constructor<?> c : javaClass.getConstructors()) {
178 Class<?>[] param = c.getParameterTypes();
179 if (param.length == 1 && param[0].isInterface()) {
180 for (Class<?> f : declaring.getInterfaces()) {
181 if (param[0].isAssignableFrom(f)) {
182 return param[0].getName();
183 }
184 }
185 }
186 }
187 return null;
188 }
189
190 private boolean nameMatches(String method, intercepts it) {
191 if (it.method().length() == 0)
192 return true;
193 return Pattern.matches(it.method(), method);
194 }
195
196 private boolean argcMatch(int argc1, int argc2) {
197 return argc1 < 0 || argc2 < 0 || argc1 == argc2;
198 }
199
200 private boolean argsMatch(Class<?>[] pattern, Class<?>[] type) {
201 if (pattern.length == 1 && pattern[0] == intercepts.class)
202 return true;
203 if (pattern.length != type.length)
204 return false;
205 for (int i = 0; i < pattern.length; i++) {
206 if (!isAssignableFrom(pattern[i], type[i]))
207 return false;
208 if (!pattern[i].equals(type[i]))
209 return false;
210 }
211 return true;
212 }
213
214 private boolean returnTypeMatches(Method jm, intercepts it) {
215 if (it.returns() == intercepts.class)
216 return true;
217 return isAssignableFrom(jm.getReturnType(), it.returns());
218 }
219
220 private boolean declaredInMatches(Method jm, intercepts it) {
221 if (it.declaring() == intercepts.class)
222 return true;
223 try {
224 it.declaring().getMethod(jm.getName(), jm.getParameterTypes());
225 return true;
226 } catch (NoSuchMethodException e) {
227 return false;
228 }
229 }
230
231 private boolean isEnabled(Method jm, Class<?> interceptor, intercepts it)
232 throws Exception {
233 if (it.conditional().length() == 0)
234 return true;
235 Method cm = interceptor.getDeclaredMethod(it.conditional(),
236 new Class[] { Method.class });
237 assert cm != null : it.conditional();
238 return Boolean.TRUE.equals(cm.invoke(null, new Object[] { jm }));
239 }
240
241 private boolean isAssignableFrom(Class<?> t1, Class<?> t2) {
242 if (t1.isAssignableFrom(t2))
243 return true;
244 if (t2.isPrimitive())
245 return getWrapperClass(t2).equals(t1);
246 if (t1.isPrimitive())
247 return getWrapperClass(t1).equals(t2);
248 return false;
249 }
250
251 private Class<?> getWrapperClass(Class<?> primitiveClass) {
252 if (primitiveClass.equals(Boolean.TYPE))
253 return Boolean.class;
254 if (primitiveClass.equals(Byte.TYPE))
255 return Byte.class;
256 if (primitiveClass.equals(Character.TYPE))
257 return Character.class;
258 if (primitiveClass.equals(Short.TYPE))
259 return Short.class;
260 if (primitiveClass.equals(Integer.TYPE))
261 return Integer.class;
262 if (primitiveClass.equals(Long.TYPE))
263 return Long.class;
264 if (primitiveClass.equals(Float.TYPE))
265 return Float.class;
266 if (primitiveClass.equals(Double.TYPE))
267 return Double.class;
268 if (primitiveClass.equals(Void.TYPE))
269 return Void.class;
270 return primitiveClass;
271 }
272
273 private boolean isObjectMethod(Method m) {
274 return m.getDeclaringClass().getName().equals(Object.class.getName());
275 }
276
277 private Method findInterfaceMethod(Method method, Class<?> face)
278 throws Exception {
279 String name = method.getName();
280 Class<?>[] types = method.getParameterTypes();
281 Class<?> clazz = face;
282 return clazz.getDeclaredMethod(name, types);
283 }
284 }