001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.hivemind.service.impl;
016
017 import java.lang.reflect.Modifier;
018 import java.util.ArrayList;
019 import java.util.HashMap;
020 import java.util.Iterator;
021 import java.util.List;
022 import java.util.Map;
023
024 import javassist.CannotCompileException;
025 import javassist.CtClass;
026 import javassist.CtConstructor;
027 import javassist.CtField;
028 import javassist.CtMethod;
029 import javassist.NotFoundException;
030
031 import org.apache.hivemind.ApplicationRuntimeException;
032 import org.apache.hivemind.service.ClassFab;
033 import org.apache.hivemind.service.MethodFab;
034 import org.apache.hivemind.service.MethodSignature;
035
036 /**
037 * Implementation of {@link org.apache.hivemind.service.ClassFab}. Hides, as much as possible, the
038 * underlying library (Javassist).
039 *
040 * @author Howard Lewis Ship
041 */
042 public class ClassFabImpl extends AbstractFab implements ClassFab
043 {
044 /**
045 * Stores information about a constructor; used by toString().
046 *
047 * @since 1.1
048 */
049
050 private class AddedConstructor
051 {
052 private Class[] _parameterTypes;
053
054 private Class[] _exceptionTypes;
055
056 private String _body;
057
058 AddedConstructor(Class[] parameterTypes, Class[] exceptionTypes, String body)
059 {
060 _parameterTypes = parameterTypes;
061 _exceptionTypes = exceptionTypes;
062 _body = body;
063 }
064
065 public String toString()
066 {
067 StringBuffer buffer = new StringBuffer();
068
069 buffer.append("public ");
070 buffer.append(getCtClass().getName());
071
072 buffer.append("(");
073
074 int count = size(_parameterTypes);
075 for (int i = 0; i < count; i++)
076 {
077 if (i > 0)
078 buffer.append(", ");
079
080 buffer.append(_parameterTypes[i].getName());
081
082 buffer.append(" $");
083 buffer.append(i + 1);
084 }
085
086 buffer.append(")");
087
088 count = size(_exceptionTypes);
089 for (int i = 0; i < count; i++)
090 {
091 if (i == 0)
092 buffer.append("\n throws ");
093 else
094 buffer.append(", ");
095
096 buffer.append(_exceptionTypes[i].getName());
097 }
098
099 buffer.append("\n");
100 buffer.append(_body);
101
102 buffer.append("\n");
103
104 return buffer.toString();
105 }
106
107 private int size(Object[] array)
108 {
109 return array == null ? 0 : array.length;
110 }
111 }
112
113 /**
114 * Map of {@link MethodFab}keyed on {@link MethodSignature}.
115 */
116 private Map _methods = new HashMap();
117
118 /**
119 * List of {@link AddedConstructor}.
120 *
121 * @since 1.1
122 */
123
124 private List _constructors = new ArrayList();
125
126 public ClassFabImpl(CtClassSource source, CtClass ctClass)
127 {
128 super(source, ctClass);
129 }
130
131 /**
132 * Returns a representation of the fabricated class, including inheritance, fields,
133 * constructors, methods and method bodies.
134 *
135 * @since 1.1
136 */
137 public String toString()
138 {
139 StringBuffer buffer = new StringBuffer("ClassFab[\n");
140
141 try
142 {
143 buildClassAndInheritance(buffer);
144
145 buildFields(buffer);
146
147 buildConstructors(buffer);
148
149 buildMethods(buffer);
150
151 }
152 catch (Exception ex)
153 {
154 buffer.append(" *** ");
155 buffer.append(ex);
156 }
157
158 buffer.append("\n]");
159
160 return buffer.toString();
161 }
162
163 /** @since 1.1 */
164 private void buildMethods(StringBuffer buffer)
165 {
166 Iterator i = _methods.values().iterator();
167 while (i.hasNext())
168 {
169
170 MethodFab mf = (MethodFab) i.next();
171
172 buffer.append("\n");
173 buffer.append(mf);
174 buffer.append("\n");
175 }
176 }
177
178 /** @since 1.1 */
179 private void buildConstructors(StringBuffer buffer)
180 {
181 Iterator i = _constructors.iterator();
182
183 while (i.hasNext())
184 {
185 buffer.append("\n");
186 buffer.append(i.next());
187 }
188 }
189
190 /** @since 1.1 */
191 private void buildFields(StringBuffer buffer) throws NotFoundException
192 {
193 CtField fields[] = getCtClass().getDeclaredFields();
194
195 for (int i = 0; i < fields.length; i++)
196 {
197 buffer.append("\n");
198 buffer.append(modifiers(fields[i].getModifiers()));
199 buffer.append(" ");
200 buffer.append(fields[i].getType().getName());
201 buffer.append(" ");
202 buffer.append(fields[i].getName());
203 buffer.append(";\n");
204 }
205 }
206
207 /** @since 1.1 */
208 private void buildClassAndInheritance(StringBuffer buffer) throws NotFoundException
209 {
210 buffer.append(modifiers(getCtClass().getModifiers()));
211 buffer.append(" class ");
212 buffer.append(getCtClass().getName());
213 buffer.append(" extends ");
214 buffer.append(getCtClass().getSuperclass().getName());
215 buffer.append("\n");
216
217 CtClass[] interfaces = getCtClass().getInterfaces();
218
219 if (interfaces.length > 0)
220 {
221 buffer.append(" implements ");
222
223 for (int i = 0; i < interfaces.length; i++)
224 {
225 if (i > 0)
226 buffer.append(", ");
227
228 buffer.append(interfaces[i].getName());
229 }
230
231 buffer.append("\n");
232 }
233 }
234
235 private String modifiers(int modifiers)
236 {
237 return Modifier.toString(modifiers);
238 }
239
240 /**
241 * Returns the name of the class fabricated by this instance.
242 */
243 String getName()
244 {
245 return getCtClass().getName();
246 }
247
248 public void addField(String name, Class type)
249 {
250 CtClass ctType = convertClass(type);
251
252 try
253 {
254 CtField field = new CtField(ctType, name, getCtClass());
255 field.setModifiers(Modifier.PRIVATE);
256
257 getCtClass().addField(field);
258 }
259 catch (CannotCompileException ex)
260 {
261 throw new ApplicationRuntimeException(ServiceMessages.unableToAddField(
262 name,
263 getCtClass(),
264 ex), ex);
265 }
266 }
267
268 public boolean containsMethod( MethodSignature ms )
269 {
270 return _methods.get( ms ) != null;
271 }
272
273 public MethodFab addMethod(int modifiers, MethodSignature ms, String body)
274 {
275 if (_methods.get(ms) != null)
276 throw new ApplicationRuntimeException(ServiceMessages.duplicateMethodInClass(ms, this));
277
278 CtClass ctReturnType = convertClass(ms.getReturnType());
279
280 CtClass[] ctParameters = convertClasses(ms.getParameterTypes());
281 CtClass[] ctExceptions = convertClasses(ms.getExceptionTypes());
282
283 CtMethod method = new CtMethod(ctReturnType, ms.getName(), ctParameters, getCtClass());
284
285 try
286 {
287 method.setModifiers(modifiers);
288 method.setBody(body);
289 method.setExceptionTypes(ctExceptions);
290
291 getCtClass().addMethod(method);
292 }
293 catch (Exception ex)
294 {
295 throw new ApplicationRuntimeException(ServiceMessages.unableToAddMethod(
296 ms,
297 getCtClass(),
298 ex), ex);
299 }
300
301 // Return a MethodFab so the caller can add catches.
302
303 MethodFab result = new MethodFabImpl(getSource(), ms, method, body);
304
305 _methods.put(ms, result);
306
307 return result;
308 }
309
310 public MethodFab getMethodFab(MethodSignature ms)
311 {
312 return (MethodFab) _methods.get(ms);
313 }
314
315 public void addConstructor(Class[] parameterTypes, Class[] exceptions, String body)
316 {
317 CtClass[] ctParameters = convertClasses(parameterTypes);
318 CtClass[] ctExceptions = convertClasses(exceptions);
319
320 try
321 {
322 CtConstructor constructor = new CtConstructor(ctParameters, getCtClass());
323 constructor.setExceptionTypes(ctExceptions);
324 constructor.setBody(body);
325
326 getCtClass().addConstructor(constructor);
327
328 _constructors.add(new AddedConstructor(parameterTypes, exceptions, body));
329 }
330 catch (Exception ex)
331 {
332 throw new ApplicationRuntimeException(ServiceMessages.unableToAddConstructor(
333 getCtClass(),
334 ex), ex);
335 }
336 }
337
338 }