View Javadoc

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 }