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 if (type.isPrimitive()) {
41 body.append("(").append(getPrimitiveWrapper(type));
42 body.append(")").append(field);
43 } else {
44 body.append("(").append(getJavaClassCodeNameOf(type)).append(")")
45 .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 declareObject(Class<?> type, String var) {
84 if (type.isPrimitive()) {
85 code(getPrimitiveWrapper(type));
86 } else {
87 code(getJavaClassCodeNameOf(type));
88 }
89 return code(" ").assign(var);
90 }
91
92 public abstract CodeBuilder end();
93
94 public CodeBuilder insert(boolean b) {
95 body.append(b);
96 return this;
97 }
98
99 public CodeBuilder insert(char c) {
100 body.append("'").append(c).append("'");
101 return this;
102 }
103
104 public CodeBuilder insert(Class<?> javaClass) {
105 body.append(getJavaClassObjectCode(javaClass));
106 return this;
107 }
108
109 public CodeBuilder insert(double d) {
110 body.append(d);
111 return this;
112 }
113
114 public CodeBuilder insert(float f) {
115 body.append(f);
116 return this;
117 }
118
119 public CodeBuilder insert(int i) {
120 body.append(i);
121 return this;
122 }
123
124 public CodeBuilder insert(long lng) {
125 body.append(lng);
126 return this;
127 }
128
129 public CodeBuilder insert(Method method) {
130 Class<?> declaringClass = method.getDeclaringClass();
131 String name = method.getName();
132 Class<?>[] params = method.getParameterTypes();
133 CodeBuilder cb = klass.getCodeBuilder();
134 String var = cb.methodVars.get(method);
135 if (var == null) {
136 var = cb.getVarName("Method");
137 } else {
138 body.append(var);
139 return this;
140 }
141 String before = toString();
142 clear();
143 String parameterTypes = declareVar(params, cb);
144 klass.assignStaticField(Method.class, var);
145 cb.insert(declaringClass);
146 cb.code(".getDeclaredMethod(").insert(name);
147 cb.code(", ").code(parameterTypes).code(")").end();
148 methodVars.put(method, var);
149 code(before);
150 body.append(var);
151 return this;
152 }
153
154 public CodeBuilder insert(Object o) {
155 if (o == null) {
156 body.append("null");
157 } else {
158 visit(o, o.getClass());
159 }
160 return this;
161 }
162
163 public CodeBuilder insert(String str) {
164 body.append("\"").append(str).append("\"");
165 return this;
166 }
167
168 public CodeBuilder insertMethod(String name, Class<?>[] params) {
169 List<Class<?>> list = Arrays.asList(params);
170 CodeBuilder cb = klass.getCodeBuilder();
171 Map<List<Class<?>>, String> map = cb.methodTemplateVars.get(name);
172 if (map == null) {
173 cb.methodTemplateVars.put(name, map = new HashMap());
174 } else {
175 if (map.containsKey(list)) {
176 body.append(map.get(list));
177 return this;
178 }
179 }
180 String parameterTypes = declareVar(params, cb);
181 String var = cb.getVarName("Method");
182 klass.assignStaticField(Method.class, var);
183 cb.insert(klass.getCtClass());
184 cb.code(".getDeclaredMethod(").insert(name);
185 cb.code(", ").code(parameterTypes).code(")").end();
186 map.put(list, var);
187 body.append(var);
188 return this;
189 }
190
191 public int length() {
192 return body.length();
193 }
194
195 public CodeBuilder semi() {
196 body.append(";\n");
197 return this;
198 }
199
200 @Override
201 public String toString() {
202 return body.toString();
203 }
204
205 protected void clear() {
206 body.delete(0, length());
207 }
208
209 private String declareVar(Class<?>[] classes, CodeBuilder cb) {
210 String var = cb.getVarName("Classes");
211 cb.code("java.lang.Class[] ").code(var);
212 cb.code(" = ").code("new java.lang.Class[");
213 cb.insert(classes.length).code("]").code(";\n");
214 for (int i = 0; i < classes.length; i++) {
215 cb.code(var).code("[").insert(i).code("]");
216 cb.code(" = ");
217 cb.insert(classes[i]);
218 cb.code(";\n");
219 }
220 return var;
221 }
222
223 private String getJavaClassCodeNameOf(Class<?> type) {
224 return cp.get(type).getName();
225 }
226
227 private CharSequence getJavaClassObjectCode(Class<?> type) {
228 CtClass cc = cp.get(type);
229 return getJavaClassObjectCode(cc);
230 }
231
232 private CharSequence getJavaClassObjectCode(CtClass cc) {
233 StringBuilder body = new StringBuilder();
234 if (cc.isPrimitive()) {
235 return body.append(getPrimitiveJavaClassWrapper(cc).getName())
236 .append(".TYPE");
237 }
238 body.append(Class.class.getName());
239 body.append(".forName(\"");
240 String name = Descriptor.toJavaName(Descriptor.toJvmName(cc));
241 body.append(name);
242 body.append("\")");
243 return body;
244 }
245
246 private Class<?> getPrimitiveJavaClassWrapper(CtClass cc) {
247 if (cc.equals(CtClass.booleanType))
248 return Boolean.class;
249 if (cc.equals(CtClass.byteType))
250 return Byte.class;
251 if (cc.equals(CtClass.charType))
252 return Character.class;
253 if (cc.equals(CtClass.doubleType))
254 return Double.class;
255 if (cc.equals(CtClass.floatType))
256 return Float.class;
257 if (cc.equals(CtClass.intType))
258 return Integer.class;
259 if (cc.equals(CtClass.longType))
260 return Long.class;
261 if (cc.equals(CtClass.shortType))
262 return Short.class;
263 throw new AssertionError();
264 }
265
266 private String getPrimitiveWrapper(Class<?> type) {
267 String wrap;
268 if (boolean.class.equals(type)) {
269 wrap = Boolean.class.getName();
270 } else if (char.class.equals(type)) {
271 wrap = Character.class.getName();
272 } else if (int.class.equals(type)) {
273 wrap = Integer.class.getName();
274 } else {
275 String prim = type.getName();
276 wrap = Character.toUpperCase(prim.charAt(0)) + prim.substring(1);
277
278 }
279 return wrap;
280 }
281
282 private String getVarName(String type) {
283 return "_$" + type + varCounter++;
284 }
285
286 private CodeBuilder insert(CtClass ctClass) {
287 body.append(getJavaClassObjectCode(ctClass));
288 return this;
289 }
290
291 private boolean visit(Object o, Class oc) {
292 try {
293 Class c = getClass();
294 Class[] args = new Class[] { oc };
295 Method m = c.getMethod("insert", args);
296 m.invoke(this, o);
297 return true;
298 } catch (NoSuchMethodException e) {
299 Class sc = oc.getSuperclass();
300 if (sc != null && !Object.class.equals(sc)) {
301 if (visit(o, sc))
302 return true;
303 }
304 for (Class face : oc.getInterfaces()) {
305 if (visit(o, face))
306 return true;
307 }
308 return false;
309 } catch (IllegalArgumentException e) {
310 throw new ElmoCompositionException(e);
311 } catch (IllegalAccessException e) {
312 throw new ElmoCompositionException(e);
313 } catch (InvocationTargetException e) {
314 throw new ElmoCompositionException(e);
315 }
316 }
317 }