View Javadoc

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.reflect.impl.javassist;
9   
10  import gnu.trove.TIntObjectHashMap;
11  import org.codehaus.aspectwerkz.annotation.AnnotationInfo;
12  import org.codehaus.aspectwerkz.annotation.instrumentation.AttributeExtractor;
13  import org.codehaus.aspectwerkz.annotation.instrumentation.Attributes;
14  import org.codehaus.aspectwerkz.reflect.ClassInfo;
15  import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
16  import org.codehaus.aspectwerkz.reflect.FieldInfo;
17  import org.codehaus.aspectwerkz.reflect.MethodInfo;
18  
19  import java.lang.ref.WeakReference;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import javassist.CtClass;
24  import javassist.CtConstructor;
25  import javassist.CtField;
26  import javassist.CtMethod;
27  import javassist.NotFoundException;
28  
29  /***
30   * Implementation of the ClassInfo interface for Javassist.
31   * 
32   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
33   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
34   */
35  public class JavassistClassInfo implements ClassInfo {
36      /***
37       * The class.
38       */
39      private final CtClass m_class;
40  
41      /***
42       * The name of the class.
43       */
44      private String m_name;
45  
46      /***
47       * Is the class a primitive type.
48       */
49      private boolean m_isPrimitive = false;
50  
51      /***
52       * Is the class an interface.
53       */
54      private boolean m_isInterface = false;
55  
56      /***
57       * Is the class of type array.
58       */
59      private boolean m_isArray = false;
60  
61      /***
62       * A list with the <code>ConstructorMetaData</code> instances.
63       */
64      private final TIntObjectHashMap m_constructors = new TIntObjectHashMap();
65  
66      /***
67       * A list with the <code>MethodInfo</code> instances.
68       */
69      private final TIntObjectHashMap m_methods = new TIntObjectHashMap();
70  
71      /***
72       * A list with the <code>FieldMetaData</code> instances.
73       */
74      private final TIntObjectHashMap m_fields = new TIntObjectHashMap();
75  
76      /***
77       * A list with the interfaces.
78       */
79      private ClassInfo[] m_interfaces = null;
80  
81      /***
82       * The super class.
83       */
84      private ClassInfo m_superClass = null;
85  
86      /***
87       * The attributes.
88       */
89      private List m_annotations = null;
90  
91      /***
92       * The component type if array type.
93       */
94      private ClassInfo m_componentType = null;
95  
96      /***
97       * The class info repository.
98       */
99      private JavassistClassInfoRepository m_classInfoRepository;
100 
101     /***
102      * The class loader that loaded the class.
103      */
104     private transient final WeakReference m_loaderRef;
105 
106     /***
107      * The attribute extractor.
108      */
109     private AttributeExtractor m_attributeExtractor;
110 
111     /***
112      * Creates a new class meta data instance.
113      * 
114      * @param klass
115      * @param loader
116      */
117     JavassistClassInfo(final CtClass klass, final ClassLoader loader) {
118         if (klass == null) {
119             throw new IllegalArgumentException("class can not be null");
120         }
121         if (loader == null) {
122             throw new IllegalArgumentException("class loader can not be null");
123         }
124         m_class = klass;
125         m_loaderRef = new WeakReference(loader);
126         m_isInterface = klass.isInterface();
127         m_attributeExtractor = Attributes.getAttributeExtractor(m_class, loader);
128         m_classInfoRepository = JavassistClassInfoRepository.getRepository(loader);
129         if (klass.isPrimitive()) {
130             m_name = klass.getName();
131             m_isPrimitive = true;
132         } else if (klass.isArray()) {
133             m_name = klass.getName();
134             m_isArray = true;
135             m_interfaces = new ClassInfo[0];
136         } else {
137             m_name = klass.getName();
138             CtMethod[] methods = m_class.getDeclaredMethods();
139             for (int i = 0; i < methods.length; i++) {
140                 m_methods.put(JavassistMethodInfo.calculateHash(methods[i]), new JavassistMethodInfo(
141                     methods[i],
142                     this,
143                     loader,
144                     m_attributeExtractor));
145             }
146             CtConstructor[] constructors = m_class.getDeclaredConstructors();
147             for (int i = 0; i < constructors.length; i++) {
148                 CtConstructor constructor = constructors[i];
149                 m_constructors.put(JavassistConstructorInfo.calculateHash(constructor), new JavassistConstructorInfo(
150                     constructor,
151                     this,
152                     loader,
153                     m_attributeExtractor));
154             }
155             if (m_class.getClassInitializer() != null) {
156                 CtConstructor constructor = m_class.getClassInitializer();
157                 m_constructors.put(JavassistConstructorInfo.calculateHash(constructor), new JavassistConstructorInfo(
158                     constructor,
159                     this,
160                     loader,
161                     m_attributeExtractor));
162             }
163             CtField[] fields = m_class.getDeclaredFields();
164             for (int i = 0; i < fields.length; i++) {
165                 CtField field = fields[i];
166                 m_fields.put(JavassistFieldInfo.calculateHash(field), new JavassistFieldInfo(
167                     field,
168                     this,
169                     loader,
170                     m_attributeExtractor));
171             }
172         }
173         addAnnotations();
174         m_classInfoRepository.addClassInfo(this);
175     }
176 
177     /***
178      * Returns the class info for a specific ctClass.
179      * 
180      * @param clazz
181      * @param loader
182      * @return the class info
183      */
184     public static ClassInfo getClassInfo(final CtClass clazz, final ClassLoader loader) {
185         JavassistClassInfoRepository repository = JavassistClassInfoRepository.getRepository(loader);
186         ClassInfo classInfo = repository.getClassInfo(clazz.getName());
187         if (classInfo == null) {
188             classInfo = new JavassistClassInfo(clazz, loader);
189         }
190         return classInfo;
191     }
192     
193     /***
194      * Marks the class as dirty (since it has been modified and needs to be rebuild).
195      *  
196      * @param clazz
197      * @param loader
198      */
199     public static void markDirty(final CtClass clazz, final ClassLoader loader) {
200         JavassistClassInfoRepository.getRepository(loader).removeClassInfo(clazz);
201     }
202 
203     /***
204      * Returns the annotations.
205      * 
206      * @return the annotations
207      */
208     public List getAnnotations() {
209         return m_annotations;
210     }
211 
212     /***
213      * Returns the name of the class.
214      * 
215      * @return the name of the class
216      */
217     public String getName() {
218         return m_name;
219     }
220 
221     /***
222      * Returns the class modifiers.
223      * 
224      * @return the class modifiers
225      */
226     public int getModifiers() {
227         return m_class.getModifiers();
228     }
229 
230     /***
231      * Returns a constructor info by its hash.
232      * 
233      * @param hash
234      * @return
235      */
236     public ConstructorInfo getConstructor(final int hash) {
237         return (ConstructorInfo) m_constructors.get(hash);
238     }
239 
240     /***
241      * Returns a list with all the constructors info.
242      * 
243      * @return the constructors info
244      */
245     public ConstructorInfo[] getConstructors() {
246         Object[] values = m_constructors.getValues();
247         ConstructorInfo[] constructorInfos = new ConstructorInfo[values.length];
248         for (int i = 0; i < values.length; i++) {
249             constructorInfos[i] = (ConstructorInfo) values[i];
250         }
251         return constructorInfos;
252     }
253 
254     /***
255      * Returns a method info by its hash.
256      * 
257      * @param hash
258      * @return
259      */
260     public MethodInfo getMethod(final int hash) {
261         return (MethodInfo) m_methods.get(hash);
262     }
263 
264     /***
265      * Returns a list with all the methods info.
266      * 
267      * @return the methods info
268      */
269     public MethodInfo[] getMethods() {
270         Object[] values = m_methods.getValues();
271         MethodInfo[] methodInfos = new MethodInfo[values.length];
272         for (int i = 0; i < values.length; i++) {
273             methodInfos[i] = (MethodInfo) values[i];
274         }
275         return methodInfos;
276     }
277 
278     /***
279      * Returns a field info by its hash.
280      * 
281      * @param hash
282      * @return
283      */
284     public FieldInfo getField(final int hash) {
285         return (FieldInfo) m_fields.get(hash);
286     }
287 
288     /***
289      * Returns a list with all the field info.
290      * 
291      * @return the field info
292      */
293     public FieldInfo[] getFields() {
294         Object[] values = m_fields.getValues();
295         FieldInfo[] fieldInfos = new FieldInfo[values.length];
296         for (int i = 0; i < values.length; i++) {
297             fieldInfos[i] = (FieldInfo) values[i];
298         }
299         return fieldInfos;
300     }
301 
302     /***
303      * Returns the interfaces.
304      * 
305      * @return the interfaces
306      */
307     public ClassInfo[] getInterfaces() {
308         if (m_interfaces == null) {
309             try {
310                 CtClass[] interfaces = m_class.getInterfaces();
311                 m_interfaces = new ClassInfo[interfaces.length];
312                 for (int i = 0; i < interfaces.length; i++) {
313                     CtClass anInterface = interfaces[i];
314                     ClassInfo classInfo = JavassistClassInfo.getClassInfo(anInterface, (ClassLoader) m_loaderRef.get());
315                     m_interfaces[i] = classInfo;
316                     if (!m_classInfoRepository.hasClassInfo(anInterface.getName())) {
317                         m_classInfoRepository.addClassInfo(classInfo);
318                     }
319                 }
320             } catch (NotFoundException e) {
321                 // swallow, since ok
322             }
323         }
324         return m_interfaces;
325     }
326 
327     /***
328      * Returns the super class.
329      * 
330      * @return the super class
331      */
332     public ClassInfo getSuperClass() {
333         if (m_superClass == null) {
334             try {
335                 CtClass superclass = m_class.getSuperclass();
336                 if (superclass != null) {
337                     if (m_classInfoRepository.hasClassInfo(superclass.getName())) {
338                         m_superClass = m_classInfoRepository.getClassInfo(superclass.getName());
339                     } else {
340                         m_superClass = JavassistClassInfo.getClassInfo(superclass, (ClassLoader) m_loaderRef.get());
341                         m_classInfoRepository.addClassInfo(m_superClass);
342                     }
343                 }
344             } catch (NotFoundException e) {
345                 // swallow, since ok
346             }
347         }
348         return m_superClass;
349     }
350 
351     /***
352      * Returns the component type if array type else null.
353      * 
354      * @return the component type
355      */
356     public ClassInfo getComponentType() {
357         if (isArray() && (m_componentType == null)) {
358             // TODO: how to impl. array component types? Is it needed?
359             //            Class componentType = m_class.getComponentType();
360             //            if (m_classInfoRepository.hasClassInfo(componentType.getName())) {
361             //                m_componentType = m_classInfoRepository.getClassInfo(componentType.getName());
362             //            }
363             //            else {
364             //                m_componentType = new JavassistClassInfo(componentType);
365             //                m_classInfoRepository.addClassInfo(m_componentType);
366             //            }
367         }
368         return m_componentType;
369     }
370 
371     /***
372      * Is the class an interface.
373      * 
374      * @return
375      */
376     public boolean isInterface() {
377         return m_isInterface;
378     }
379 
380     /***
381      * Is the class a primitive type.
382      * 
383      * @return
384      */
385     public boolean isPrimitive() {
386         return m_isPrimitive;
387     }
388 
389     /***
390      * Is the class an array type.
391      * 
392      * @return
393      */
394     public boolean isArray() {
395         return m_isArray;
396     }
397 
398     public boolean equals(Object o) {
399         if (this == o) {
400             return true;
401         }
402         if (!(o instanceof ClassInfo)) {
403             return false;
404         }
405         ClassInfo classInfo = (ClassInfo) o;
406         return m_class.getName().toString().equals(classInfo.getName().toString());
407     }
408 
409     public int hashCode() {
410         return m_class.getName().toString().hashCode();
411     }
412 
413     /***
414      * Adds annotations to the class info.
415      */
416     private void addAnnotations() {
417         if (m_attributeExtractor == null) {
418             return;
419         }
420         m_annotations = new ArrayList();
421         Object[] attributes = m_attributeExtractor.getClassAttributes();
422         for (int i = 0; i < attributes.length; i++) {
423             Object attribute = attributes[i];
424             if (attribute instanceof AnnotationInfo) {
425                 m_annotations.add(attribute);
426             }
427         }
428     }
429 
430     public String toString() {
431         return getName();
432     }
433 }