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  		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 }