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 }