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.java;
9   
10  import gnu.trove.TIntObjectHashMap;
11  import org.codehaus.aspectwerkz.annotation.Annotations;
12  import org.codehaus.aspectwerkz.reflect.ClassInfo;
13  import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
14  import org.codehaus.aspectwerkz.reflect.FieldInfo;
15  import org.codehaus.aspectwerkz.reflect.MethodInfo;
16  import org.codehaus.aspectwerkz.reflect.ReflectHelper;
17  import org.codehaus.aspectwerkz.reflect.ReflectHelper;
18  import org.codehaus.aspectwerkz.reflect.StaticInitializationInfo;
19  import org.codehaus.aspectwerkz.reflect.StaticInitializationInfoImpl;
20  import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
21  import org.codehaus.aspectwerkz.transform.TransformationConstants;
22  
23  import java.lang.reflect.Constructor;
24  import java.lang.reflect.Field;
25  import java.lang.reflect.Method;
26  import java.util.List;
27  
28  /***
29   * Implementation of the ClassInfo interface for java.lang.reflect.*.
30   *
31   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
32   */
33  public class JavaClassInfo implements ClassInfo {
34      /***
35       * The class.
36       */
37      // TODO might be safer to wrap this member in a weak ref
38      private final Class m_class;
39  
40      /***
41       * The name of the class.
42       */
43      private String m_name;
44  
45      /***
46       * The signature of the class.
47       */
48      private String m_signature;
49  
50      /***
51       * Is the class an interface.
52       */
53      private boolean m_isInterface = false;
54  
55      /***
56       * Is the class a primitive type.
57       */
58      private boolean m_isPrimitive = false;
59  
60      /***
61       * Is the class of type array.
62       */
63      private boolean m_isArray = false;
64  
65      /***
66       * A list with the <code>ConstructorInfo</code> instances.
67       */
68      private final TIntObjectHashMap m_constructors = new TIntObjectHashMap();
69  
70      /***
71       * A list with the <code>MethodInfo</code> instances.
72       */
73      private final TIntObjectHashMap m_methods = new TIntObjectHashMap();
74  
75      /***
76       * A list with the <code>FieldInfo</code> instances.
77       */
78      private final TIntObjectHashMap m_fields = new TIntObjectHashMap();
79  
80      /***
81       * A list with the interfaces.
82       */
83      private ClassInfo[] m_interfaces = null;
84  
85      /***
86       * The super class.
87       */
88      private ClassInfo m_superClass = null;
89  
90      /***
91       * The annotations.
92       */
93      private List m_annotations = null;
94  
95      /***
96       * The component type if array type.
97       */
98      private ClassInfo m_componentType = null;
99  
100     /***
101      * The class info repository.
102      */
103     private final JavaClassInfoRepository m_classInfoRepository;
104 
105     /***
106      * Lazy, the static initializer info or null if not present
107      */
108     private StaticInitializationInfo m_staticInitializer = null;
109 
110     /***
111      * Creates a new class meta data instance.
112      *
113      * @param klass
114      */
115     JavaClassInfo(final Class klass) {
116         if (klass == null) {
117             throw new IllegalArgumentException("class can not be null");
118         }
119         m_class = klass;
120 
121         m_signature = ReflectHelper.getClassSignature(klass);
122 
123         m_classInfoRepository = JavaClassInfoRepository.getRepository(klass.getClassLoader());
124         m_isInterface = klass.isInterface();
125         if (klass.isPrimitive()) {
126             m_name = klass.getName();
127             m_isPrimitive = true;
128         } else if (klass.getComponentType() != null) {
129             m_name = convertJavaArrayTypeNameToHumanTypeName(klass.getName());
130             m_isArray = true;
131             m_interfaces = new ClassInfo[0];
132         } else {
133             m_name = klass.getName();
134             Method[] methods = m_class.getDeclaredMethods();
135             for (int i = 0; i < methods.length; i++) {
136                 Method method = methods[i];
137                 m_methods.put(ReflectHelper.calculateHash(method), new JavaMethodInfo(method, this));
138             }
139             Constructor[] constructors = m_class.getDeclaredConstructors();
140             for (int i = 0; i < constructors.length; i++) {
141                 Constructor constructor = constructors[i];
142                 m_constructors.put(
143                         ReflectHelper.calculateHash(constructor), new JavaConstructorInfo(
144                                 constructor,
145                                 this
146                         )
147                 );
148             }
149             Field[] fields = m_class.getDeclaredFields();
150             for (int i = 0; i < fields.length; i++) {
151                 if (fields[i].getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX)) {
152                     continue;
153                 }
154                 Field field = fields[i];
155                 m_fields.put(ReflectHelper.calculateHash(field), new JavaFieldInfo(field, this));
156             }
157         }
158         m_classInfoRepository.addClassInfo(this);
159     }
160 
161     /***
162      * Returns the class info for a specific class.
163      *
164      * @return the class info
165      */
166     public static ClassInfo getClassInfo(final Class clazz) {
167         JavaClassInfoRepository repository = JavaClassInfoRepository.getRepository(clazz.getClassLoader());
168         ClassInfo classInfo = repository.getClassInfo(clazz.getName());
169         if (classInfo == null) {
170             classInfo = new JavaClassInfo(clazz);
171         }
172         return classInfo;
173     }
174 
175     /***
176      * Returns the annotations infos.
177      *
178      * @return the annotations infos
179      */
180     public List getAnnotations() {
181         if (m_annotations == null) {
182             // TODO this means that JavaClassInfo is always using AsmClassInfo to get that annotations
183             // TODO should optimize for Java5
184             m_annotations = Annotations.getAnnotationInfos(m_class);
185         }
186         return m_annotations;
187     }
188 
189     /***
190      * Returns the name of the class.
191      *
192      * @return the name of the class
193      */
194     public String getName() {
195         return m_name.replace('/', '.');
196     }
197 
198     /***
199      * Checks if the class has a static initalizer.
200      *
201      * @return
202      */
203     public boolean hasStaticInitializer() {
204         ClassInfo classInfo = AsmClassInfo.getClassInfo(getName(), getClassLoader());
205         return classInfo.hasStaticInitializer();
206     }
207 
208 	/***
209 	 * Returns the static initializer info of the current underlying class if any.
210 	 * 
211 	 * @see org.codehaus.aspectwerkz.reflect.ClassInfo#staticInitializer()
212 	 */
213 	public StaticInitializationInfo staticInitializer() {
214 		if(hasStaticInitializer() && m_staticInitializer == null) {
215 			m_staticInitializer = new StaticInitializationInfoImpl(this);
216 		}
217 		return m_staticInitializer;
218 	}
219 	
220     /***
221      * Returns the signature for the element.
222      *
223      * @return the signature for the element
224      */
225     public String getSignature() {
226         return m_signature;
227     }
228 
229     /***
230      * Returns the class modifiers.
231      *
232      * @return the class modifiers
233      */
234     public int getModifiers() {
235         return m_class.getModifiers();
236     }
237 
238     /***
239      * Returns the class loader that loaded this class.
240      *
241      * @return the class loader
242      */
243     public ClassLoader getClassLoader() {
244         return m_class.getClassLoader();
245     }
246 
247     /***
248      * Returns a constructor info by its hash.
249      *
250      * @param hash
251      * @return
252      */
253     public ConstructorInfo getConstructor(final int hash) {
254         ConstructorInfo constructor = (ConstructorInfo) m_constructors.get(hash);
255         if (constructor == null && getSuperclass() != null) {
256             constructor = getSuperclass().getConstructor(hash);
257         }
258         return constructor;
259     }
260 
261     /***
262      * Returns a list with all the constructors info.
263      *
264      * @return the constructors info
265      */
266     public ConstructorInfo[] getConstructors() {
267         Object[] values = m_constructors.getValues();
268         ConstructorInfo[] methodInfos = new ConstructorInfo[values.length];
269         for (int i = 0; i < values.length; i++) {
270             methodInfos[i] = (ConstructorInfo) values[i];
271         }
272         return methodInfos;
273     }
274 
275     /***
276      * Returns a method info by its hash.
277      *
278      * @param hash
279      * @return
280      */
281     public MethodInfo getMethod(final int hash) {
282         MethodInfo method = (MethodInfo) m_methods.get(hash);
283         if (method == null) {
284             for (int i = 0; i < getInterfaces().length; i++) {
285                 method = getInterfaces()[i].getMethod(hash);
286                 if (method != null) {
287                     break;
288                 }
289             }
290         }
291         if (method == null && getSuperclass() != null) {
292             method = getSuperclass().getMethod(hash);
293         }
294         return method;
295     }
296 
297     /***
298      * Returns a list with all the methods info.
299      *
300      * @return the methods info
301      */
302     public MethodInfo[] getMethods() {
303         Object[] values = m_methods.getValues();
304         MethodInfo[] methodInfos = new MethodInfo[values.length];
305         for (int i = 0; i < values.length; i++) {
306             methodInfos[i] = (MethodInfo) values[i];
307         }
308         return methodInfos;
309     }
310 
311     /***
312      * Returns a field info by its hash.
313      *
314      * @param hash
315      * @return
316      */
317     public FieldInfo getField(final int hash) {
318         FieldInfo field = (FieldInfo) m_fields.get(hash);
319         if (field == null && getSuperclass() != null) {
320             field = getSuperclass().getField(hash);
321         }
322         return field;
323     }
324 
325     /***
326      * Returns a list with all the field info.
327      *
328      * @return the field info
329      */
330     public FieldInfo[] getFields() {
331         Object[] values = m_fields.getValues();
332         FieldInfo[] fieldInfos = new FieldInfo[values.length];
333         for (int i = 0; i < values.length; i++) {
334             fieldInfos[i] = (FieldInfo) values[i];
335         }
336         return fieldInfos;
337     }
338 
339     /***
340      * Returns the interfaces.
341      *
342      * @return the interfaces
343      */
344     public ClassInfo[] getInterfaces() {
345         if (m_interfaces == null) {
346             Class[] interfaces = m_class.getInterfaces();
347             m_interfaces = new ClassInfo[interfaces.length];
348             for (int i = 0; i < interfaces.length; i++) {
349                 Class anInterface = interfaces[i];
350                 ClassInfo classInfo = JavaClassInfo.getClassInfo(anInterface);
351                 m_interfaces[i] = classInfo;
352                 if (!m_classInfoRepository.hasClassInfo(anInterface.getName())) {
353                     m_classInfoRepository.addClassInfo(classInfo);
354                 }
355             }
356         }
357         return m_interfaces;
358     }
359 
360     /***
361      * Returns the super class.
362      *
363      * @return the super class
364      */
365     public ClassInfo getSuperclass() {
366         if (m_superClass == null) {
367             Class superclass = m_class.getSuperclass();
368             if (superclass != null) {
369                 if (m_classInfoRepository.hasClassInfo(superclass.getName())) {
370                     m_superClass = m_classInfoRepository.getClassInfo(superclass.getName());
371                 } else {
372                     m_superClass = JavaClassInfo.getClassInfo(superclass);
373                     m_classInfoRepository.addClassInfo(m_superClass);
374                 }
375             }
376         }
377         return m_superClass;
378     }
379 
380     /***
381      * Returns the component type if array type else null.
382      *
383      * @return the component type
384      */
385     public ClassInfo getComponentType() {
386         if (isArray() && (m_componentType == null)) {
387             Class componentType = m_class.getComponentType();
388             if (m_classInfoRepository.hasClassInfo(componentType.getName())) {
389                 m_componentType = m_classInfoRepository.getClassInfo(componentType.getName());
390             } else {
391                 m_componentType = JavaClassInfo.getClassInfo(componentType);
392                 m_classInfoRepository.addClassInfo(m_componentType);
393             }
394         }
395         return m_componentType;
396     }
397 
398     /***
399      * Is the class an interface.
400      *
401      * @return
402      */
403     public boolean isInterface() {
404         return m_isInterface;
405     }
406 
407     /***
408      * Is the class a primitive type.
409      *
410      * @return
411      */
412     public boolean isPrimitive() {
413         return m_isPrimitive;
414     }
415 
416     /***
417      * Is the class an array type.
418      *
419      * @return
420      */
421     public boolean isArray() {
422         return m_isArray;
423     }
424 
425     /***
426      * Converts an internal Java array type name ([Lblabla) to the a the format used by the expression matcher
427      * (blabla[])
428      *
429      * @param typeName is type name
430      * @return
431      */
432     public static String convertJavaArrayTypeNameToHumanTypeName(final String typeName) {
433         int index = typeName.lastIndexOf('[');
434         if (index != -1) {
435             StringBuffer arrayType = new StringBuffer();
436             if (typeName.endsWith("I")) {
437                 arrayType.append("int");
438             } else if (typeName.endsWith("J")) {
439                 arrayType.append("long");
440             } else if (typeName.endsWith("S")) {
441                 arrayType.append("short");
442             } else if (typeName.endsWith("F")) {
443                 arrayType.append("float");
444             } else if (typeName.endsWith("D")) {
445                 arrayType.append("double");
446             } else if (typeName.endsWith("Z")) {
447                 arrayType.append("boolean");
448             } else if (typeName.endsWith("C")) {
449                 arrayType.append("char");
450             } else if (typeName.endsWith("B")) {
451                 arrayType.append("byte");
452             } else {
453                 arrayType.append(typeName.substring(index + 2, typeName.length() - 1));
454             }
455             for (int i = 0; i < (index + 1); i++) {
456                 arrayType.append("[]");
457             }
458             return arrayType.toString();
459         } else {
460             return typeName;
461         }
462     }
463 
464     public boolean equals(Object o) {
465         if (this == o) {
466             return true;
467         }
468         if (!(o instanceof ClassInfo)) {
469             return false;
470         }
471         ClassInfo classInfo = (ClassInfo) o;
472         return m_class.getName().toString().equals(classInfo.getName().toString());
473     }
474 
475     public int hashCode() {
476         return m_class.getName().toString().hashCode();
477     }
478 
479     public String toString() {
480         return getName();
481     }
482 }