001 // Copyright 2007 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 package org.apache.tapestry.enhance;
015
016 import java.lang.reflect.Modifier;
017 import java.util.ArrayList;
018 import java.util.HashMap;
019 import java.util.Iterator;
020 import java.util.List;
021 import java.util.Map;
022
023 import javassist.CannotCompileException;
024 import javassist.CtClass;
025 import javassist.CtConstructor;
026 import javassist.CtField;
027 import javassist.CtMethod;
028 import javassist.NotFoundException;
029
030 import org.apache.hivemind.ApplicationRuntimeException;
031 import org.apache.hivemind.service.ClassFab;
032 import org.apache.hivemind.service.MethodFab;
033 import org.apache.hivemind.service.MethodSignature;
034
035 /**
036 * Implementation replacement for hivemind {@link ClassFab} utiltity to get around some javassist
037 * incompatibilties found with the latest 3.4 version of javassist.
038 *
039 * @author jkuhnert
040 */
041 public class ClassFabImpl extends AbstractFab implements ClassFab
042 {
043 /**
044 * Stores information about a constructor; used by toString().
045 *
046 * @since 1.1
047 */
048
049 private class AddedConstructor
050 {
051 private Class[] _parameterTypes;
052
053 private Class[] _exceptionTypes;
054
055 private String _body;
056
057 AddedConstructor(Class[] parameterTypes, Class[] exceptionTypes, String body)
058 {
059 _parameterTypes = parameterTypes;
060 _exceptionTypes = exceptionTypes;
061 _body = body;
062 }
063
064 public String toString()
065 {
066 StringBuffer buffer = new StringBuffer();
067
068 buffer.append("public ");
069 buffer.append(getCtClass().getName());
070
071 buffer.append("(");
072
073 int count = size(_parameterTypes);
074 for(int i = 0; i < count; i++) {
075 if (i > 0) buffer.append(", ");
076
077 buffer.append(_parameterTypes[i].getName());
078
079 buffer.append(" $");
080 buffer.append(i + 1);
081 }
082
083 buffer.append(")");
084
085 count = size(_exceptionTypes);
086 for(int i = 0; i < count; i++) {
087 if (i == 0)
088 buffer.append("\n throws ");
089 else buffer.append(", ");
090
091 buffer.append(_exceptionTypes[i].getName());
092 }
093
094 buffer.append("\n");
095 buffer.append(_body);
096
097 buffer.append("\n");
098
099 return buffer.toString();
100 }
101
102 private int size(Object[] array)
103 {
104 return array == null ? 0 : array.length;
105 }
106 }
107
108 /**
109 * Map of {@link MethodFab}keyed on {@link MethodSignature}.
110 */
111 private Map _methods = new HashMap();
112
113 /**
114 * List of {@link AddedConstructor}.
115 *
116 * @since 1.1
117 */
118
119 private List _constructors = new ArrayList();
120
121 public ClassFabImpl(CtClassSource source, CtClass ctClass)
122 {
123 super(source, ctClass);
124 }
125
126 /**
127 * Returns a representation of the fabricated class, including inheritance, fields,
128 * constructors, methods and method bodies.
129 *
130 * @since 1.1
131 */
132 public String toString()
133 {
134 StringBuffer buffer = new StringBuffer("ClassFab[\n");
135
136 try {
137 buildClassAndInheritance(buffer);
138
139 buildFields(buffer);
140
141 buildConstructors(buffer);
142
143 buildMethods(buffer);
144
145 } catch (Exception ex) {
146 buffer.append(" *** ");
147 buffer.append(ex);
148 }
149
150 buffer.append("\n]");
151
152 return buffer.toString();
153 }
154
155 /** @since 1.1 */
156 private void buildMethods(StringBuffer buffer)
157 {
158 Iterator i = _methods.values().iterator();
159 while(i.hasNext()) {
160
161 MethodFab mf = (MethodFab) i.next();
162
163 buffer.append("\n");
164 buffer.append(mf);
165 buffer.append("\n");
166 }
167 }
168
169 /** @since 1.1 */
170 private void buildConstructors(StringBuffer buffer)
171 {
172 Iterator i = _constructors.iterator();
173
174 while(i.hasNext()) {
175 buffer.append("\n");
176 buffer.append(i.next());
177 }
178 }
179
180 /** @since 1.1 */
181 private void buildFields(StringBuffer buffer)
182 throws NotFoundException
183 {
184 CtField[] fields = getCtClass().getDeclaredFields();
185
186 for(int i = 0; i < fields.length; i++) {
187 buffer.append("\n");
188 buffer.append(modifiers(fields[i].getModifiers()));
189 buffer.append(" ");
190 buffer.append(fields[i].getType().getName());
191 buffer.append(" ");
192 buffer.append(fields[i].getName());
193 buffer.append(";\n");
194 }
195 }
196
197 /** @since 1.1 */
198 private void buildClassAndInheritance(StringBuffer buffer)
199 throws NotFoundException
200 {
201 buffer.append(modifiers(getCtClass().getModifiers()));
202 buffer.append(" class ");
203 buffer.append(getCtClass().getName());
204 buffer.append(" extends ");
205 buffer.append(getCtClass().getSuperclass().getName());
206 buffer.append("\n");
207
208 CtClass[] interfaces = getCtClass().getInterfaces();
209
210 if (interfaces.length > 0) {
211 buffer.append(" implements ");
212
213 for(int i = 0; i < interfaces.length; i++) {
214 if (i > 0) buffer.append(", ");
215
216 buffer.append(interfaces[i].getName());
217 }
218
219 buffer.append("\n");
220 }
221 }
222
223 private String modifiers(int modifiers)
224 {
225 return Modifier.toString(modifiers);
226 }
227
228 /**
229 * Returns the name of the class fabricated by this instance.
230 */
231 String getName()
232 {
233 return getCtClass().getName();
234 }
235
236 public void addField(String name, Class type)
237 {
238 CtClass ctType = convertClass(type);
239
240 try {
241 CtField field = new CtField(ctType, name, getCtClass());
242 field.setModifiers(Modifier.PRIVATE);
243
244 getCtClass().addField(field);
245 } catch (CannotCompileException ex) {
246 throw new ApplicationRuntimeException(EnhanceMessages.unableToAddField(name, getCtClass(), ex), ex);
247 }
248 }
249
250 public boolean containsMethod(MethodSignature ms)
251 {
252 return _methods.get(ms) != null;
253 }
254
255 public MethodFab addMethod(int modifiers, MethodSignature ms, String body)
256 {
257 if (_methods.get(ms) != null)
258 throw new ApplicationRuntimeException(EnhanceMessages.duplicateMethodInClass(ms, this));
259
260 if (body.indexOf("isWrapperFor") > 0 || body.indexOf("unwrap") > 0)
261 return new MethodFabImpl(null, ms, null, "{ throw new UnsupportedOperationException(\"Method not implemented\"); }");
262
263 CtClass ctReturnType = convertClass(ms.getReturnType());
264
265 CtClass[] ctParameters = convertClasses(ms.getParameterTypes());
266 CtClass[] ctExceptions = convertClasses(ms.getExceptionTypes());
267
268 CtMethod method = new CtMethod(ctReturnType, ms.getName(), ctParameters, getCtClass());
269
270 try {
271 method.setModifiers(modifiers);
272 method.setBody(body);
273 method.setExceptionTypes(ctExceptions);
274
275 getCtClass().addMethod(method);
276 } catch (Exception ex) {
277
278 throw new ApplicationRuntimeException(EnhanceMessages.unableToAddMethod(ms, getCtClass(), ex), ex);
279 }
280
281 // Return a MethodFab so the caller can add catches.
282
283 MethodFab result = new MethodFabImpl(getSource(), ms, method, body);
284
285 _methods.put(ms, result);
286
287 return result;
288 }
289
290 public MethodFab getMethodFab(MethodSignature ms)
291 {
292 return (MethodFab) _methods.get(ms);
293 }
294
295 public void addConstructor(Class[] parameterTypes, Class[] exceptions, String body)
296 {
297 CtClass[] ctParameters = convertClasses(parameterTypes);
298 CtClass[] ctExceptions = convertClasses(exceptions);
299
300 try {
301 CtConstructor constructor = new CtConstructor(ctParameters, getCtClass());
302 constructor.setExceptionTypes(ctExceptions);
303 constructor.setBody(body);
304
305 getCtClass().addConstructor(constructor);
306
307 _constructors.add(new AddedConstructor(parameterTypes, exceptions, body));
308 } catch (Exception ex) {
309 throw new ApplicationRuntimeException(EnhanceMessages.unableToAddConstructor(getCtClass(), ex), ex);
310 }
311 }
312 }