1 package org.openrdf.elmo.dynacode;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.lang.reflect.Method;
5 import java.util.Arrays;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.Map;
9
10 import javassist.CtClass;
11 import javassist.bytecode.Descriptor;
12
13 import org.openrdf.elmo.exceptions.ElmoCompositionException;
14
15 public abstract class CodeBuilder {
16 private StringBuilder body = new StringBuilder();
17
18 private ClassFactory cp;
19
20 private ClassTemplate klass;
21
22 private Map<String, Map<List<Class<?>>, String>> methodTemplateVars = new HashMap();
23
24 private Map<Method, String> methodVars = new HashMap();
25
26 private int varCounter;
27
28 protected CodeBuilder(ClassTemplate klass, ClassFactory cp) {
29 super();
30 this.klass = klass;
31 this.cp = cp;
32 }
33
34 public CodeBuilder assign(String var) {
35 body.append(var).append(" = ");
36 return this;
37 }
38
39 public CodeBuilder castObject(String field, Class<?> type) {
40 body.append("(");
41 if (type.isPrimitive()) {
42 body.append(getPrimitiveWrapper(type));
43 body.append(")").append(field);
44 } else {
45 body.append(getJavaClassCodeNameOf(type)).append(")").append(field);
46 }
47 return this;
48 }
49
50 public CodeBuilder code(String str) {
51 body.append(str);
52 return this;
53 }
54
55 public CodeBuilder codeInstanceof(String field, Class<?> type) {
56 body.append(field).append(" instanceof ");
57 body.append(getJavaClassCodeNameOf(type));
58 return this;
59 }
60
61 public CodeBuilder codeObject(String field, Class<?> type) {
62 if (type.isPrimitive()) {
63 body.append(getPrimitiveWrapper(type));
64 body.append(".valueOf(").append(field).append(")");
65 } else {
66 body.append(field);
67 }
68 return this;
69 }
70
71 public CodeBuilder construct(Class<?> javaClass, Object... args) {
72 body.append("new ").append(javaClass.getName()).append("(");
73 for (int i = 0; i < args.length; i++) {
74 if (i > 0) {
75 code(",");
76 }
77 insert(args[i]);
78 }
79 body.append(")");
80 return this;
81 }
82
83 public CodeBuilder staticInvoke(Method method, Object... args) {
84 code(method.getDeclaringClass().getName());
85 code(".").code(method.getName()).code("(");
86 for (int i = 0; i < args.length; i++) {
87 if (i > 0) {
88 code(",");
89 }
90 insert(args[i]);
91 }
92 code(")");
93 return this;
94 }
95
96 public CodeBuilder declareObject(Class<?> type, String var) {
97 if (type.isPrimitive()) {
98 code(getPrimitiveWrapper(type));
99 } else {
100 code(getJavaClassCodeNameOf(type));
101 }
102 return code(" ").assign(var);
103 }
104
105 public abstract CodeBuilder end();
106
107 public CodeBuilder insert(boolean b) {
108 body.append(b);
109 return this;
110 }
111
112 public CodeBuilder insert(char c) {
113 body.append("'").append(c).append("'");
114 return this;
115 }
116
117 public CodeBuilder insert(Class<?> javaClass) {
118 body.append(getJavaClassObjectCode(javaClass));
119 return this;
120 }
121
122 public CodeBuilder insert(double d) {
123 body.append(d);
124 return this;
125 }
126
127 public CodeBuilder insert(float f) {
128 body.append(f);
129 return this;
130 }
131
132 public CodeBuilder insert(int i) {
133 body.append(i);
134 return this;
135 }
136
137 public CodeBuilder insert(long lng) {
138 body.append(lng);
139 return this;
140 }
141
142 public CodeBuilder insert(Method method) {
143 Class<?> declaringClass = method.getDeclaringClass();
144 String name = method.getName();
145 Class<?>[] params = method.getParameterTypes();
146 CodeBuilder cb = klass.getCodeBuilder();
147 String var = cb.methodVars.get(method);
148 if (var == null) {
149 var = cb.getVarName("Method");
150 } else {
151 body.append(var);
152 return this;
153 }
154 String before = toString();
155 clear();
156 String parameterTypes = declareVar(params, cb);
157 CodeBuilder field = klass.assignStaticField(Method.class, var);
158 field.insert(declaringClass);
159 field.code(".getDeclaredMethod(").insert(name);
160 field.code(", ").code(parameterTypes).code(")").end();
161 methodVars.put(method, var);
162 code(before);
163 body.append(var);
164 return this;
165 }
166
167 public CodeBuilder insert(Object o) {
168 if (o == null) {
169 body.append("null");
170 } else {
171 visit(o, o.getClass());
172 }
173 return this;
174 }
175
176 public CodeBuilder insert(String str) {
177 body.append("\"").append(str).append("\"");
178 return this;
179 }
180
181 public CodeBuilder insertMethod(String name, Class<?>[] params) {
182 List<Class<?>> list = Arrays.asList(params);
183 CodeBuilder cb = klass.getCodeBuilder();
184 Map<List<Class<?>>, String> map = cb.methodTemplateVars.get(name);
185 if (map == null) {
186 cb.methodTemplateVars.put(name, map = new HashMap());
187 } else {
188 if (map.containsKey(list)) {
189 body.append(map.get(list));
190 return this;
191 }
192 }
193 String parameterTypes = declareVar(params, cb);
194 String var = cb.getVarName("Method");
195 CodeBuilder field = klass.assignStaticField(Method.class, var);
196 field.insert(klass.getCtClass());
197 field.code(".getDeclaredMethod(").insert(name);
198 field.code(", ").code(parameterTypes).code(")").end();
199 map.put(list, var);
200 body.append(var);
201 return this;
202 }
203
204 public int length() {
205 return body.length();
206 }
207
208 public CodeBuilder semi() {
209 body.append(";\n");
210 return this;
211 }
212
213 @Override
214 public String toString() {
215 return body.toString();
216 }
217
218 protected void clear() {
219 body.delete(0, length());
220 }
221
222 private String declareVar(Class<?>[] classes, CodeBuilder cb) {
223 String var = cb.getVarName("Classes");
224 cb.code("java.lang.Class[] ").code(var);
225 cb.code(" = ").code("new java.lang.Class[");
226 cb.insert(classes.length).code("]").code(";\n");
227 for (int i = 0; i < classes.length; i++) {
228 cb.code(var).code("[").insert(i).code("]");
229 cb.code(" = ");
230 cb.insert(classes[i]);
231 cb.code(";\n");
232 }
233 return var;
234 }
235
236 private String getJavaClassCodeNameOf(Class<?> type) {
237 return cp.get(type).getName();
238 }
239
240 private CharSequence getJavaClassObjectCode(Class<?> type) {
241 CtClass cc = cp.get(type);
242 return getJavaClassObjectCode(cc);
243 }
244
245 private CharSequence getJavaClassObjectCode(CtClass cc) {
246 StringBuilder body = new StringBuilder();
247 if (cc.isPrimitive()) {
248 return body.append(getPrimitiveJavaClassWrapper(cc).getName())
249 .append(".TYPE");
250 }
251 body.append(Class.class.getName());
252 body.append(".forName(\"");
253 String name = Descriptor.toJavaName(Descriptor.toJvmName(cc));
254 body.append(name);
255 body.append("\")");
256 return body;
257 }
258
259 private Class<?> getPrimitiveJavaClassWrapper(CtClass cc) {
260 if (cc.equals(CtClass.booleanType))
261 return Boolean.class;
262 if (cc.equals(CtClass.byteType))
263 return Byte.class;
264 if (cc.equals(CtClass.charType))
265 return Character.class;
266 if (cc.equals(CtClass.doubleType))
267 return Double.class;
268 if (cc.equals(CtClass.floatType))
269 return Float.class;
270 if (cc.equals(CtClass.intType))
271 return Integer.class;
272 if (cc.equals(CtClass.longType))
273 return Long.class;
274 if (cc.equals(CtClass.shortType))
275 return Short.class;
276 throw new AssertionError();
277 }
278
279 private String getPrimitiveWrapper(Class<?> type) {
280 String wrap;
281 if (boolean.class.equals(type)) {
282 wrap = Boolean.class.getName();
283 } else if (char.class.equals(type)) {
284 wrap = Character.class.getName();
285 } else if (int.class.equals(type)) {
286 wrap = Integer.class.getName();
287 } else {
288 String prim = type.getName();
289 wrap = Character.toUpperCase(prim.charAt(0)) + prim.substring(1);
290
291 }
292 return wrap;
293 }
294
295 private String getVarName(String type) {
296 return "_$" + type + varCounter++;
297 }
298
299 private CodeBuilder insert(CtClass ctClass) {
300 body.append(getJavaClassObjectCode(ctClass));
301 return this;
302 }
303
304 private boolean visit(Object o, Class oc) {
305 try {
306 Class c = getClass();
307 Class[] args = new Class[] { oc };
308 Method m = c.getMethod("insert", args);
309 m.invoke(this, o);
310 return true;
311 } catch (NoSuchMethodException e) {
312 Class sc = oc.getSuperclass();
313 if (sc != null && !Object.class.equals(sc)) {
314 if (visit(o, sc))
315 return true;
316 }
317 for (Class face : oc.getInterfaces()) {
318 if (visit(o, face))
319 return true;
320 }
321 return false;
322 } catch (IllegalArgumentException e) {
323 throw new ElmoCompositionException(e);
324 } catch (IllegalAccessException e) {
325 throw new ElmoCompositionException(e);
326 } catch (InvocationTargetException e) {
327 throw new ElmoCompositionException(e);
328 }
329 }
330 }