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 }