KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > annotation > instrumentation > asm > AsmAttributeEnhancer


1 /**************************************************************************************
2  * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
3  * http://aspectwerkz.codehaus.org *
4  * ---------------------------------------------------------------------------------- *
5  * The software in this package is published under the terms of the LGPL license *
6  * a copy of which has been included with this distribution in the license.txt file. *
7  **************************************************************************************/

8 package org.codehaus.aspectwerkz.annotation.instrumentation.asm;
9
10 import com.thoughtworks.qdox.model.JavaField;
11 import com.thoughtworks.qdox.model.JavaMethod;
12
13 import org.codehaus.aspectwerkz.annotation.instrumentation.AttributeEnhancer;
14 import org.codehaus.aspectwerkz.definition.DescriptorUtil;
15 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
16 import org.codehaus.aspectwerkz.expression.QDoxParser;
17 import org.codehaus.aspectwerkz.reflect.TypeConverter;
18 import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
19 import org.objectweb.asm.Attribute;
20 import org.objectweb.asm.ClassAdapter;
21 import org.objectweb.asm.ClassReader;
22 import org.objectweb.asm.ClassVisitor;
23 import org.objectweb.asm.ClassWriter;
24 import org.objectweb.asm.CodeVisitor;
25 import org.objectweb.asm.attrs.RuntimeInvisibleAnnotations;
26 import org.objectweb.asm.attrs.Attributes;
27
28 import java.io.ByteArrayOutputStream JavaDoc;
29 import java.io.File JavaDoc;
30 import java.io.FileOutputStream JavaDoc;
31 import java.io.IOException JavaDoc;
32 import java.io.InputStream JavaDoc;
33 import java.io.ObjectOutputStream JavaDoc;
34 import java.net.URL JavaDoc;
35 import java.net.URLClassLoader JavaDoc;
36 import java.util.ArrayList JavaDoc;
37 import java.util.Arrays JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.List JavaDoc;
40
41 /**
42  * Enhances classes with custom attributes using the ASM library.
43  *
44  * @author <a HREF="mailto:jboner@codehaus.org">Jonas Bonér </a>
45  * @author <a HREF="mailto:alex@gnilux.com">Alexandre Vasseur </a>
46  */

47 public class AsmAttributeEnhancer implements AttributeEnhancer {
48     /**
49      * The class reader.
50      */

51     private ClassReader m_reader = null;
52
53     /**
54      * The name of the class file.
55      */

56     private String JavaDoc m_classFileName = null;
57
58     /**
59      * The class name.
60      */

61     private String JavaDoc m_className = null;
62
63     /**
64      * Compiled class class loader
65      */

66     private URLClassLoader JavaDoc m_loader = null;
67
68     /**
69      * The class attributes.
70      */

71     private List JavaDoc m_classAttributes = new ArrayList JavaDoc();
72
73     /**
74      * The constructor attributes.
75      */

76     private List JavaDoc m_constructorAttributes = new ArrayList JavaDoc();
77
78     /**
79      * The method attributes.
80      */

81     private List JavaDoc m_methodAttributes = new ArrayList JavaDoc();
82
83     /**
84      * The field attributes.
85      */

86     private List JavaDoc m_fieldAttributes = new ArrayList JavaDoc();
87
88     /**
89      * Initializes the attribute enhancer. Must always be called before use.
90      *
91      * @param className the class name
92      * @param classPath the class path
93      * @return true if the class was succefully loaded, false otherwise
94      */

95     public boolean initialize(final String JavaDoc className, final URL JavaDoc[] classPath) {
96         try {
97             m_className = className;
98             m_loader = new URLClassLoader JavaDoc(classPath);
99             m_classFileName = className.replace('.', '/') + ".class";
100             InputStream JavaDoc classAsStream = m_loader.getResourceAsStream(m_classFileName);
101             if (classAsStream == null) {
102                 return false;
103             }
104             // setup the ASM stuff in init, but only parse at write time
105
try {
106                 m_reader = new ClassReader(classAsStream);
107             } catch (Exception JavaDoc e) {
108                 throw new ClassNotFoundException JavaDoc(m_className, e);
109             } finally {
110                 classAsStream.close();//AW-296
111
}
112         } catch (Exception JavaDoc e) {
113             throw new WrappedRuntimeException(e);
114         }
115         return true;
116     }
117
118     /**
119      * Inserts an attribute on class level.
120      *
121      * @param attribute the attribute
122      */

123     public void insertClassAttribute(final Object JavaDoc attribute) {
124         if (m_reader == null) {
125             throw new IllegalStateException JavaDoc("attribute enhancer is not initialized");
126         }
127         final byte[] serializedAttribute = serialize(attribute);
128         m_classAttributes.add(serializedAttribute);
129     }
130
131     /**
132      * Inserts an attribute on field level.
133      *
134      * @param field the QDox java field
135      * @param attribute the attribute
136      */

137     public void insertFieldAttribute(final JavaField field, final Object JavaDoc attribute) {
138         if (m_reader == null) {
139             throw new IllegalStateException JavaDoc("attribute enhancer is not initialized");
140         }
141         final byte[] serializedAttribute = serialize(attribute);
142         m_fieldAttributes.add(new FieldAttributeInfo(field, serializedAttribute));
143     }
144
145     /**
146      * Inserts an attribute on method level.
147      *
148      * @param method the QDox java method
149      * @param attribute the attribute
150      */

151     public void insertMethodAttribute(final JavaMethod method, final Object JavaDoc attribute) {
152         if (m_reader == null) {
153             throw new IllegalStateException JavaDoc("attribute enhancer is not initialized");
154         }
155         final String JavaDoc[] methodParamTypes = new String JavaDoc[method.getParameters().length];
156         for (int i = 0; i < methodParamTypes.length; i++) {
157             methodParamTypes[i] = TypeConverter.convertTypeToJava(method.getParameters()[i].getType());
158         }
159         final byte[] serializedAttribute = serialize(attribute);
160         m_methodAttributes.add(new MethodAttributeInfo(method, serializedAttribute));
161     }
162
163     /**
164      * Inserts an attribute on constructor level.
165      *
166      * @param constructor the QDox java method
167      * @param attribute the attribute
168      */

169     public void insertConstructorAttribute(final JavaMethod constructor, final Object JavaDoc attribute) {
170         if (m_reader == null) {
171             throw new IllegalStateException JavaDoc("attribute enhancer is not initialized");
172         }
173         final String JavaDoc[] methodParamTypes = new String JavaDoc[constructor.getParameters().length];
174         for (int i = 0; i < methodParamTypes.length; i++) {
175             methodParamTypes[i] = TypeConverter.convertTypeToJava(constructor.getParameters()[i].getType());
176         }
177         final byte[] serializedAttribute = serialize(attribute);
178         m_constructorAttributes.add(new MethodAttributeInfo(constructor, serializedAttribute));
179     }
180
181     /**
182      * Writes the enhanced class to file.
183      *
184      * @param destDir the destination directory
185      */

186     public void write(final String JavaDoc destDir) {
187         if (m_reader == null) {
188             throw new IllegalStateException JavaDoc("attribute enhancer is not initialized");
189         }
190         try {
191             // parse the bytecode
192
ClassWriter writer = AsmHelper.newClassWriter(true);
193             m_reader.accept(new AttributeClassAdapter(writer), Attributes.getDefaultAttributes(), false);
194
195             // write the bytecode to disk
196
String JavaDoc path = destDir + File.separator + m_classFileName;
197             File JavaDoc file = new File JavaDoc(path);
198             File JavaDoc parentFile = file.getParentFile();
199             if (!parentFile.exists()) {
200                 // directory does not exist create all directories in the path
201
if (!parentFile.mkdirs()) {
202                     throw new RuntimeException JavaDoc(
203                             "could not create dir structure needed to write file "
204                             + path
205                             + " to disk"
206                     );
207                 }
208             }
209             FileOutputStream JavaDoc os = new FileOutputStream JavaDoc(destDir + File.separator + m_classFileName);
210             os.write(writer.toByteArray());
211             os.close();
212         } catch (IOException JavaDoc e) {
213             throw new WrappedRuntimeException(e);
214         }
215     }
216
217     /**
218      * Serializes the attribute to byte array.
219      *
220      * @param attribute the attribute
221      * @return the attribute as a byte array
222      */

223     public static byte[] serialize(final Object JavaDoc attribute) {
224         try {
225             ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
226             ObjectOutputStream JavaDoc oos = new ObjectOutputStream JavaDoc(baos);
227             oos.writeObject(attribute);
228             return baos.toByteArray();
229         } catch (IOException JavaDoc e) {
230             throw new WrappedRuntimeException(e);
231         }
232     }
233
234     /**
235      * Return the first interfaces implemented by a level in the class hierarchy (bottom top)
236      *
237      * @return nearest superclass (including itself) implemented interfaces
238      */

239     public String JavaDoc[] getNearestInterfacesInHierarchy(final String JavaDoc innerClassName) {
240         if (m_loader == null) {
241             throw new IllegalStateException JavaDoc("attribute enhancer is not initialized");
242         }
243         try {
244             Class JavaDoc innerClass = Class.forName(innerClassName, false, m_loader);
245             return getNearestInterfacesInHierarchy(innerClass);
246         } catch (ClassNotFoundException JavaDoc e) {
247             throw new RuntimeException JavaDoc("could not load mixin for mixin implicit interface: " + e.toString());
248         } catch (NoClassDefFoundError JavaDoc er) {
249             // raised if extends / implements dependancies not found
250
throw new RuntimeException JavaDoc(
251                     "could not find dependency for mixin implicit interface: "
252                     + innerClassName
253                     + " due to: "
254                     + er.toString()
255             );
256         }
257     }
258
259     /**
260      * Return the first interfaces implemented by a level in the class hierarchy (bottom top)
261      *
262      * @return nearest superclass (including itself) implemented interfaces starting from root
263      */

264     private String JavaDoc[] getNearestInterfacesInHierarchy(final Class JavaDoc root) {
265         if (root == null) {
266             return new String JavaDoc[]{};
267         }
268         Class JavaDoc[] implementedClasses = root.getInterfaces();
269         String JavaDoc[] interfaces = null;
270         if (implementedClasses.length == 0) {
271             interfaces = getNearestInterfacesInHierarchy(root.getSuperclass());
272         } else {
273             interfaces = new String JavaDoc[implementedClasses.length];
274             for (int i = 0; i < implementedClasses.length; i++) {
275                 interfaces[i] = implementedClasses[i].getName();
276             }
277         }
278         return interfaces;
279     }
280
281     /**
282      * Base class for the attribute adapter visitors.
283      *
284      * @author <a HREF="mailto:jboner@codehaus.org">Jonas Bonér </a>
285      */

286     private class AttributeClassAdapter extends ClassAdapter {
287         private static final String JavaDoc INIT_METHOD_NAME = "<init>";
288
289         private boolean classLevelAnnotationDone = false;
290
291         public AttributeClassAdapter(final ClassVisitor cv) {
292             super(cv);
293         }
294
295         public void visitField(final int access,
296                                final String JavaDoc name,
297                                final String JavaDoc desc,
298                                final Object JavaDoc value,
299                                final Attribute attrs) {
300
301             RuntimeInvisibleAnnotations invisible = CustomAttributeHelper.linkRuntimeInvisibleAnnotations(attrs);
302             for (Iterator JavaDoc it = m_fieldAttributes.iterator(); it.hasNext();) {
303                 FieldAttributeInfo struct = (FieldAttributeInfo) it.next();
304                 if (name.equals(struct.field.getName())) {
305                     invisible.annotations.add(CustomAttributeHelper.createCustomAnnotation(struct.attribute));
306                 }
307             }
308             if (invisible.annotations.size() == 0) {
309                 invisible = null;
310             }
311             super.visitField(access, name, desc, value, (attrs != null) ? attrs : invisible);
312         }
313
314         public CodeVisitor visitMethod(final int access,
315                                        final String JavaDoc name,
316                                        final String JavaDoc desc,
317                                        final String JavaDoc[] exceptions,
318                                        final Attribute attrs) {
319
320             RuntimeInvisibleAnnotations invisible = CustomAttributeHelper.linkRuntimeInvisibleAnnotations(attrs);
321             if (!name.equals(INIT_METHOD_NAME)) {
322                 for (Iterator JavaDoc it = m_methodAttributes.iterator(); it.hasNext();) {
323                     MethodAttributeInfo struct = (MethodAttributeInfo) it.next();
324                     JavaMethod method = struct.method;
325                     String JavaDoc[] parameters = QDoxParser.getJavaMethodParametersAsStringArray(method);
326                     if (name.equals(method.getName()) && Arrays.equals(parameters, DescriptorUtil.getParameters(desc))) {
327                         invisible.annotations.add(CustomAttributeHelper.createCustomAnnotation(struct.attribute));
328                     }
329                 }
330             } else {
331                 for (Iterator JavaDoc it = m_constructorAttributes.iterator(); it.hasNext();) {
332                     MethodAttributeInfo struct = (MethodAttributeInfo) it.next();
333                     JavaMethod method = struct.method;
334                     String JavaDoc[] parameters = QDoxParser.getJavaMethodParametersAsStringArray(method);
335                     if (name.equals(INIT_METHOD_NAME) && Arrays.equals(parameters, DescriptorUtil.getParameters(desc))) {
336                         invisible.annotations.add(CustomAttributeHelper.createCustomAnnotation(struct.attribute));
337                     }
338                 }
339             }
340             if (invisible.annotations.size() == 0) {
341                 invisible = null;
342             }
343             return cv.visitMethod(access, name, desc, exceptions, (attrs != null) ? attrs : invisible);
344         }
345
346         public void visitAttribute(Attribute attrs) {
347             classLevelAnnotationDone = true;
348             RuntimeInvisibleAnnotations invisible = CustomAttributeHelper.linkRuntimeInvisibleAnnotations(attrs);
349             for (Iterator JavaDoc it = m_classAttributes.iterator(); it.hasNext();) {
350                 byte[] bytes = (byte[]) it.next();
351                 invisible.annotations.add(CustomAttributeHelper.createCustomAnnotation(bytes));
352             }
353             if (invisible.annotations.size() == 0) {
354                 invisible = null;
355             }
356             super.visitAttribute((attrs != null) ? attrs : invisible);
357         }
358
359         public void visitEnd() {
360             if (!classLevelAnnotationDone) {
361                 classLevelAnnotationDone = true;
362                 RuntimeInvisibleAnnotations invisible = CustomAttributeHelper.linkRuntimeInvisibleAnnotations(null);
363                 for (Iterator JavaDoc it = m_classAttributes.iterator(); it.hasNext();) {
364                     byte[] bytes = (byte[]) it.next();
365                     invisible.annotations.add(CustomAttributeHelper.createCustomAnnotation(bytes));
366                 }
367                 if (invisible.annotations.size() > 0) {
368                     super.visitAttribute(invisible);
369                 }
370                 super.visitEnd();
371             }
372         }
373     }
374
375     /**
376      * @author <a HREF="mailto:jboner@codehaus.org">Jonas Bonér </a>
377      */

378     private static class FieldAttributeInfo {
379         public final byte[] attribute;
380         public final JavaField field;
381
382         public FieldAttributeInfo(final JavaField field, final byte[] attribute) {
383             this.field = field;
384             this.attribute = attribute;
385         }
386     }
387
388     /**
389      * @author <a HREF="mailto:jboner@codehaus.org">Jonas Bonér </a>
390      */

391     private static class MethodAttributeInfo {
392         public final byte[] attribute;
393         public final JavaMethod method;
394
395         public MethodAttributeInfo(final JavaMethod method, final byte[] attribute) {
396             this.method = method;
397             this.attribute = attribute;
398         }
399     }
400 }
Popular Tags