1 package org.openrdf.elmo.dynacode;
2
3 import java.lang.reflect.Method;
4 import java.util.HashSet;
5 import java.util.Set;
6
7 import javassist.CannotCompileException;
8 import javassist.CtClass;
9 import javassist.CtConstructor;
10 import javassist.CtField;
11 import javassist.CtMethod;
12 import javassist.CtNewMethod;
13 import javassist.Modifier;
14 import javassist.NotFoundException;
15
16 import org.openrdf.elmo.exceptions.ElmoCompositionException;
17
18 public class ClassTemplate {
19
20 private CodeBuilder cb;
21
22 private CtClass cc;
23
24 private ClassFactory cp;
25
26 protected ClassTemplate(final CtClass cc, final ClassFactory cp) {
27 this.cc = cc;
28 this.cp = cp;
29 this.cb = new CodeBuilder(this, cp) {
30 @Override
31 public CodeBuilder end() {
32 try {
33 semi();
34 cc.makeClassInitializer().insertAfter(toString());
35 } catch (CannotCompileException e) {
36 throw new ElmoCompositionException(e.getMessage() + " for "
37 + toString(), e);
38 }
39 clear();
40 return this;
41 }
42 };
43 }
44
45 public void addConstructor(Class<?>[] types, String string)
46 throws ElmoCompositionException {
47 try {
48 CtConstructor con = new CtConstructor(asCtClassArray(types), cc);
49 con.setBody(string);
50 cc.addConstructor(con);
51 } catch (CannotCompileException e) {
52 throw new ElmoCompositionException(e);
53 } catch (NotFoundException e) {
54 throw new ElmoCompositionException(e);
55 }
56 }
57
58 public void addInterface(Class<?> face) throws ElmoCompositionException {
59 cc.addInterface(cp.get(face));
60 }
61
62 public CodeBuilder assignStaticField(Class<?> type, final String fieldName)
63 throws ElmoCompositionException {
64 try {
65 CtField field = new CtField(cp.get(type), fieldName, cc);
66 field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
67 cc.addField(field);
68 } catch (CannotCompileException e) {
69 throw new ElmoCompositionException(e);
70 }
71 CodeBuilder code = new CodeBuilder(this, cp) {
72 @Override
73 public CodeBuilder end() {
74 semi();
75 return cb.code(toString()).end();
76 }
77 };
78 return code.assign(fieldName);
79 }
80
81 public void createField(Class<?> type, String fieldName)
82 throws ElmoCompositionException {
83 try {
84 CtField field = new CtField(cp.get(type), fieldName, cc);
85 field.setModifiers(Modifier.PRIVATE);
86 cc.addField(field);
87 } catch (CannotCompileException e) {
88 throw new ElmoCompositionException(e);
89 }
90 }
91
92 public CodeBuilder createMethod(Class<?> type, String name,
93 Class<?>... parameters) throws ElmoCompositionException {
94 CtClass[] exces = new CtClass[] { cp.get(Throwable.class) };
95 try {
96 return begin(CtNewMethod.make(cp.get(type), name,
97 asCtClassArray(parameters), exces, null, cc));
98 } catch (CannotCompileException e) {
99 throw new ElmoCompositionException(e);
100 } catch (NotFoundException e) {
101 throw new ElmoCompositionException(e);
102 }
103 }
104
105 public CodeBuilder createTransientMethod(Class<?> type, String name,
106 Class<?>... parameters) throws ElmoCompositionException {
107 CtClass[] exces = new CtClass[] { cp.get(Throwable.class) };
108 try {
109 CtMethod cm = CtNewMethod.make(cp.get(type), name,
110 asCtClassArray(parameters), exces, null, cc);
111 cm.setModifiers(cm.getModifiers() | Modifier.TRANSIENT);
112 return begin(cm);
113 } catch (CannotCompileException e) {
114 throw new ElmoCompositionException(e);
115 } catch (NotFoundException e) {
116 throw new ElmoCompositionException(e);
117 }
118 }
119
120 public CodeBuilder getCodeBuilder() {
121 return cb;
122 }
123
124 public CtClass getCtClass() {
125 return cc;
126 }
127
128 public Set<String> getDeclaredFieldNames() {
129 CtField[] fields = cc.getDeclaredFields();
130 Set<String> result = new HashSet<String>(fields.length);
131 for (CtField field : fields) {
132 result.add(field.getName());
133 }
134 return result;
135 }
136
137 public Class<?>[] getInterfaces() throws ElmoCompositionException {
138 try {
139 CtClass[] cc1 = cc.getInterfaces();
140 Class<?>[] result = new Class<?>[cc1.length];
141 for (int i = 0; i < cc1.length; i++) {
142 result[i] = cp.getJavaClass(cc1[i]);
143 }
144 return result;
145 } catch (NotFoundException e) {
146 throw new ElmoCompositionException(e);
147 } catch (ClassNotFoundException e) {
148 throw new ElmoCompositionException(e);
149 }
150 }
151
152 public CodeBuilder overrideMethod(Method method)
153 throws ElmoCompositionException {
154 return createMethod(method.getReturnType(), method.getName(), method
155 .getParameterTypes());
156 }
157
158 @Override
159 public String toString() {
160 return cc.getName();
161 }
162
163 private CtClass[] asCtClassArray(Class<?>[] cc) throws NotFoundException {
164 CtClass[] result = new CtClass[cc.length];
165 for (int i = 0; i < cc.length; i++) {
166 result[i] = cp.get(cc[i]);
167 }
168 return result;
169 }
170
171 private CodeBuilder begin(final CtMethod cm) {
172 CodeBuilder cb = new CodeBuilder(this, cp) {
173 @Override
174 public CodeBuilder end() {
175 code("}");
176 CtClass cc = cm.getDeclaringClass();
177 try {
178 int mod = cm.getModifiers();
179 mod = Modifier.clear(mod, Modifier.ABSTRACT);
180 mod = Modifier.clear(mod, Modifier.NATIVE);
181 cm.setModifiers(mod);
182 cm.setBody(toString());
183 try {
184 cc.getDeclaredMethod(cm.getName(), cm
185 .getParameterTypes());
186 } catch (NotFoundException e) {
187 cc.addMethod(cm);
188 }
189 } catch (Exception e) {
190 StringBuilder sb = new StringBuilder();
191 try {
192 for (CtClass inter : cc.getInterfaces()) {
193 sb.append(inter.getSimpleName()).append(" ");
194 }
195 } catch (NotFoundException e2) {
196 }
197 String sn = cc.getSimpleName();
198 System.err.println(sn + " implements " + sb);
199 throw new ElmoCompositionException(e.getMessage() + " for "
200 + toString(), e);
201 }
202 clear();
203 return this;
204 }
205 };
206 return cb.code("{");
207 }
208
209 }