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.asm;
9   
10  import gnu.trove.TIntObjectHashMap;
11  import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAnnotationHelper;
12  import org.codehaus.aspectwerkz.annotation.AnnotationInfo;
13  import org.codehaus.aspectwerkz.annotation.Annotations;
14  import org.codehaus.aspectwerkz.annotation.TypedAnnotationProxy;
15  import org.codehaus.aspectwerkz.annotation.UntypedAnnotationProxy;
16  import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
17  import org.codehaus.aspectwerkz.reflect.ClassInfo;
18  import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
19  import org.codehaus.aspectwerkz.reflect.FieldInfo;
20  import org.codehaus.aspectwerkz.reflect.MethodInfo;
21  import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
22  import org.codehaus.aspectwerkz.transform.AsmHelper;
23  import org.codehaus.aspectwerkz.transform.AsmHelper;
24  import org.objectweb.asm.Attribute;
25  import org.objectweb.asm.ClassAdapter;
26  import org.objectweb.asm.ClassReader;
27  import org.objectweb.asm.ClassVisitor;
28  import org.objectweb.asm.CodeVisitor;
29  import org.objectweb.asm.attrs.Annotation;
30  
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.lang.ref.WeakReference;
34  import java.lang.reflect.Array;
35  import java.lang.reflect.Modifier;
36  import java.util.ArrayList;
37  import java.util.List;
38  import java.util.Iterator;
39  
40  /***
41   * Implementation of the ClassInfo interface utilizing the ASM bytecode library for the info retriaval.
42   *
43   * Annotations are lazily gathered, unless required to visit them at the same time as we visit methods and fields.
44   *
45   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
46   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
47   */
48  public class AsmClassInfo implements ClassInfo {
49  
50      protected final static List EMPTY_LIST = new ArrayList();
51  
52      private final static Attribute[] NO_ATTRIBUTES = new Attribute[0];
53  
54      /***
55       * The class loader wrapped in a weak ref.
56       */
57      private final WeakReference m_loaderRef;
58  
59      /***
60       * The name of the class.
61       */
62      private String m_name;
63  
64      /***
65       * The modifiers.
66       */
67      private int m_modifiers;
68  
69      /***
70       * Is the class an interface.
71       */
72      private boolean m_isInterface = false;
73  
74      /***
75       * Is the class a primitive type.
76       */
77      private boolean m_isPrimitive = false;
78  
79      /***
80       * Is the class of type array.
81       */
82      private boolean m_isArray = false;
83  
84      /***
85       * A list with the <code>ConstructorInfo</code> instances.
86       */
87      private final TIntObjectHashMap m_constructors = new TIntObjectHashMap();
88  
89      /***
90       * A list with the <code>MethodInfo</code> instances.
91       */
92      private final TIntObjectHashMap m_methods = new TIntObjectHashMap();
93  
94      /***
95       * A list with the <code>FieldInfo</code> instances.
96       */
97      private final TIntObjectHashMap m_fields = new TIntObjectHashMap();
98  
99      /***
100      * A list with the interfaces class names.
101      */
102     private String[] m_interfaceClassNames = null;
103 
104     /***
105      * A list with the interfaces.
106      */
107     private ClassInfo[] m_interfaces = null;
108 
109     /***
110      * The super class name.
111      */
112     private String m_superClassName = null;
113 
114     /***
115      * The super class.
116      */
117     private ClassInfo m_superClass = null;
118 
119     /***
120      * The annotations.
121      * Lasily populated.
122      */
123     private List m_annotations = null;
124 
125     /***
126      * The component type name if array type.
127      */
128     private String m_componentTypeName = null;
129 
130     /***
131      * The component type if array type.
132      */
133     private ClassInfo m_componentType = null;
134 
135     /***
136      * The class info repository.
137      */
138     private final AsmClassInfoRepository m_classInfoRepository;
139 
140     /***
141      * Creates a new ClassInfo instance.
142      *
143      * @param bytecode
144      * @param loader
145      */
146     AsmClassInfo(final byte[] bytecode, final ClassLoader loader, boolean lazyAttributes) {
147         if (bytecode == null) {
148             throw new IllegalArgumentException("bytecode can not be null");
149         }
150         m_loaderRef = new WeakReference(loader);
151         m_classInfoRepository = AsmClassInfoRepository.getRepository(loader);
152         try {
153             ClassReader cr = new ClassReader(bytecode);
154             ClassInfoClassAdapter visitor = new ClassInfoClassAdapter(
155                     AsmAnnotationHelper.NULL_CLASS_VISITOR,
156                     lazyAttributes
157             );
158             cr.accept(visitor, lazyAttributes?NO_ATTRIBUTES:AsmAnnotationHelper.ANNOTATIONS_ATTRIBUTES, true);
159         } catch (Throwable t) {
160             t.printStackTrace();
161         }
162         m_classInfoRepository.addClassInfo(this);
163     }
164 
165     /***
166      * Creates a new ClassInfo instance.
167      *
168      * @param resourceStream
169      * @param loader
170      */
171     AsmClassInfo(final InputStream resourceStream, final ClassLoader loader) {
172         if (resourceStream == null) {
173             throw new IllegalArgumentException("resource stream can not be null");
174         }
175         m_loaderRef = new WeakReference(loader);
176         m_classInfoRepository = AsmClassInfoRepository.getRepository(loader);
177         try {
178             ClassReader cr = new ClassReader(resourceStream);
179             ClassInfoClassAdapter visitor = new ClassInfoClassAdapter(
180                     AsmAnnotationHelper.NULL_CLASS_VISITOR,
181                     true);
182             cr.accept(visitor, NO_ATTRIBUTES, true);
183         } catch (Throwable t) {
184             t.printStackTrace();
185         }
186         m_classInfoRepository.addClassInfo(this);
187     }
188 
189     /***
190      * Create a ClassInfo based on a component type and a given dimension Due to java.lang.reflect. behavior, the
191      * ClassInfo is almost empty. It is not an interface, only subclass of java.lang.Object, no methods, fields, or
192      * constructor, no annotation.
193      *
194      * @param className
195      * @param loader
196      * @param componentInfo
197      * @param dimension
198      * @TODO: not sure it has to be abstract final but it looks like all reflect based are.
199      * @TODO: dimension param is not used
200      */
201     AsmClassInfo(final String className, final ClassLoader loader, final ClassInfo componentInfo, final int dimension) {
202         m_loaderRef = new WeakReference(loader);
203         m_name = className.replace('/', '.');
204         m_classInfoRepository = AsmClassInfoRepository.getRepository(loader);
205         m_isArray = true;
206         m_componentType = componentInfo;
207         m_componentTypeName = componentInfo.getName();
208         m_modifiers = componentInfo.getModifiers() | Modifier.ABSTRACT | Modifier.FINAL;
209         m_isInterface = false;//as in java.reflect
210         m_superClass = JavaClassInfo.getClassInfo(Object.class);
211         m_superClassName = m_superClass.getName();
212         m_interfaceClassNames = new String[0];
213         m_interfaces = new ClassInfo[0];
214         m_classInfoRepository.addClassInfo(this);
215     }
216 
217     /***
218      * Returns the class info for a specific class.
219      *
220      * @param className
221      * @param loader
222      * @return the class info
223      */
224     public static ClassInfo getClassInfo(final String className, final ClassLoader loader) {
225         return getClassInfo(className, loader, true);
226     }
227 
228     /***
229      * Returns the class info for a specific class.
230      *
231      * @param className
232      * @param loader
233      * @return the class info
234      */
235     public static ClassInfo getClassInfo(final String className, final ClassLoader loader, final boolean lazyAttributes) {
236         AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
237         ClassInfo classInfo = repository.getClassInfo(className);
238         if (classInfo == null) {
239             classInfo = createClassInfoFromStream(className, loader, lazyAttributes);
240         }
241         return classInfo;
242     }
243 
244     /***
245      * Returns the class info for a specific class.
246      *
247      * @param bytecode
248      * @param loader
249      * @return the class info
250      */
251     public static ClassInfo getClassInfo(final byte[] bytecode, final ClassLoader loader) {
252         String className = AsmClassInfo.retrieveClassNameFromBytecode(bytecode);
253         AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
254         ClassInfo classInfo = repository.getClassInfo(className);
255         if (classInfo == null) {
256             classInfo = new AsmClassInfo(bytecode, loader, true);
257         }
258         return classInfo;
259     }
260 
261     /***
262      * Returns the class info for a specific class.
263      *
264      * @param stream
265      * @param loader
266      * @return the class info
267      */
268     public static ClassInfo getClassInfo(final InputStream stream, final ClassLoader loader) {
269         try {
270             ClassReader cr = new ClassReader(stream);
271             // keep a copy of the bytecode, since me way want to "reuse the stream"
272             byte[] bytes = cr.b;
273             ClassNameRetrievalClassAdapter visitor = new ClassNameRetrievalClassAdapter(
274                     AsmAnnotationHelper.NULL_CLASS_VISITOR
275             );
276             cr.accept(visitor, NO_ATTRIBUTES, true);
277             String className = visitor.getClassName();
278             AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
279             ClassInfo classInfo = repository.getClassInfo(className);
280             if (classInfo == null) {
281                 classInfo = new AsmClassInfo(bytes, loader, true);
282             }
283             return classInfo;
284         } catch (IOException e) {
285             throw new WrappedRuntimeException(e);
286         }
287     }
288 
289     /***
290      * Returns the class info for a specific class.
291      *
292      * @param stream
293      * @param loader
294      * @param lazyAttributes
295      * @return the class info
296      */
297     public static ClassInfo getClassInfo(final InputStream stream, final ClassLoader loader, boolean lazyAttributes) {
298         if (lazyAttributes) {
299             return getClassInfo(stream, loader);
300         }
301         try {
302             ClassReader cr = new ClassReader(stream);
303             // keep a copy of the bytecode, since me way want to "reuse the stream"
304             byte[] bytes = cr.b;
305             ClassNameRetrievalClassAdapter visitor = new ClassNameRetrievalClassAdapter(
306                     AsmAnnotationHelper.NULL_CLASS_VISITOR
307             );
308             cr.accept(visitor, NO_ATTRIBUTES, true);
309             String className = visitor.getClassName();
310             AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
311             ClassInfo classInfo = repository.getClassInfo(className);
312             if (classInfo == null) {
313                 classInfo = new AsmClassInfo(bytes, loader, lazyAttributes);
314             }
315             return classInfo;
316         } catch (IOException e) {
317             throw new WrappedRuntimeException(e);
318         }
319     }
320 
321     /***
322      * Marks the class as dirty (since it has been modified and needs to be rebuild).
323      *
324      * @param className
325      */
326     public static void markDirty(final String className, final ClassLoader loader) {
327         AsmClassInfoRepository.getRepository(loader).removeClassInfo(className);
328     }
329 
330     /***
331      * Retrieves the class name from the bytecode of a class.
332      *
333      * @param bytecode
334      * @return the class name
335      */
336     public static String retrieveClassNameFromBytecode(final byte[] bytecode) {
337         ClassReader cr = new ClassReader(bytecode);
338         ClassNameRetrievalClassAdapter visitor = new ClassNameRetrievalClassAdapter(
339                 AsmAnnotationHelper.NULL_CLASS_VISITOR
340         );
341         cr.accept(visitor, NO_ATTRIBUTES, true);
342         return visitor.getClassName();
343     }
344 
345     /***
346      * Checks if the class is a of a primitive type, if so create and return the class for the type else return null.
347      *
348      * @param className
349      * @return the class for the primitive type or null
350      */
351     public static Class getPrimitiveClass(final String className) {
352         if (className.equals("void")) {
353             return void.class;
354         } else if (className.equals("long")) {
355             return long.class;
356         } else if (className.equals("int")) {
357             return int.class;
358         } else if (className.equals("short")) {
359             return short.class;
360         } else if (className.equals("double")) {
361             return double.class;
362         } else if (className.equals("float")) {
363             return float.class;
364         } else if (className.equals("byte")) {
365             return byte.class;
366         } else if (className.equals("boolean")) {
367             return boolean.class;
368         } else if (className.equals("char")) {
369             return char.class;
370         } else {
371             return null;
372         }
373     }
374 
375     /***
376      * Returns the annotations infos.
377      *
378      * @return the annotations infos
379      */
380     public List getAnnotations() {
381         if (m_annotations == null) {
382             if (isPrimitive() || isArray()) {
383                 m_annotations = EMPTY_LIST;
384             } else {
385                 try {
386                     ClassReader cr = new ClassReader(((ClassLoader)m_loaderRef.get()).getResourceAsStream(m_name.replace('.','/')+".class"));
387                     List annotations = new ArrayList();
388                     cr.accept(
389                             new AsmAnnotationHelper.ClassAnnotationExtractor(annotations, (ClassLoader)m_loaderRef.get()),
390                             AsmAnnotationHelper.ANNOTATIONS_ATTRIBUTES,
391                             true
392                     );
393                     m_annotations = annotations;
394                 } catch (IOException e) {
395                     // unlikely to occur since ClassInfo relies on getResourceAsStream
396                     System.err.println("WARN - could not load " + m_name + " as a resource to retrieve annotations");
397                     m_annotations = EMPTY_LIST;
398                 }
399             }
400         }
401         return m_annotations;
402     }
403 
404     /***
405      * Returns the name of the class.
406      *
407      * @return the name of the class
408      */
409     public String getName() {
410         return m_name;
411     }
412 
413     /***
414      * Returns the class modifiers.
415      *
416      * @return the class modifiers
417      */
418     public int getModifiers() {
419         return m_modifiers;
420     }
421 
422     /***
423      * Returns a constructor info by its hash.
424      *
425      * @param hash
426      * @return
427      */
428     public ConstructorInfo getConstructor(final int hash) {
429         return (ConstructorInfo)m_constructors.get(hash);
430     }
431 
432     /***
433      * Returns a list with all the constructors info.
434      *
435      * @return the constructors info
436      */
437     public ConstructorInfo[] getConstructors() {
438         Object[] values = m_constructors.getValues();
439         ConstructorInfo[] methodInfos = new ConstructorInfo[values.length];
440         for (int i = 0; i < values.length; i++) {
441             methodInfos[i] = (ConstructorInfo)values[i];
442         }
443         return methodInfos;
444     }
445 
446     /***
447      * Returns a method info by its hash.
448      *
449      * @param hash
450      * @return
451      */
452     public MethodInfo getMethod(final int hash) {
453         return (MethodInfo)m_methods.get(hash);
454     }
455 
456     /***
457      * Returns a list with all the methods info.
458      *
459      * @return the methods info
460      */
461     public MethodInfo[] getMethods() {
462         Object[] values = m_methods.getValues();
463         MethodInfo[] methodInfos = new MethodInfo[values.length];
464         for (int i = 0; i < values.length; i++) {
465             methodInfos[i] = (MethodInfo)values[i];
466         }
467         return methodInfos;
468     }
469 
470     /***
471      * Returns a field info by its hash.
472      *
473      * @param hash
474      * @return
475      */
476     public FieldInfo getField(final int hash) {
477         return (FieldInfo)m_fields.get(hash);
478     }
479 
480     /***
481      * Returns a list with all the field info.
482      *
483      * @return the field info
484      */
485     public FieldInfo[] getFields() {
486         Object[] values = m_fields.getValues();
487         FieldInfo[] fieldInfos = new FieldInfo[values.length];
488         for (int i = 0; i < values.length; i++) {
489             fieldInfos[i] = (FieldInfo)values[i];
490         }
491         return fieldInfos;
492     }
493 
494     /***
495      * Returns the interfaces.
496      *
497      * @return the interfaces
498      */
499     public ClassInfo[] getInterfaces() {
500         if (m_interfaces == null) {
501             m_interfaces = new ClassInfo[m_interfaceClassNames.length];
502             for (int i = 0; i < m_interfaceClassNames.length; i++) {
503                 m_interfaces[i] = AsmClassInfo.getClassInfo(m_interfaceClassNames[i], (ClassLoader)m_loaderRef.get());
504             }
505         }
506         return m_interfaces;
507     }
508 
509     /***
510      * Returns the super class.
511      *
512      * @return the super class
513      */
514     public ClassInfo getSuperClass() {
515         if (m_superClass == null && m_superClassName != null) {
516             m_superClass = AsmClassInfo.getClassInfo(m_superClassName, (ClassLoader)m_loaderRef.get());
517         }
518         return m_superClass;
519     }
520 
521     /***
522      * Returns the component type if array type else null.
523      *
524      * @return the component type
525      */
526     public ClassInfo getComponentType() {
527         if (isArray() && (m_componentTypeName == null)) {
528             m_componentType = AsmClassInfo.getClassInfo(m_componentTypeName, (ClassLoader)m_loaderRef.get());
529         }
530         return m_componentType;
531     }
532 
533     /***
534      * Is the class an interface.
535      *
536      * @return
537      */
538     public boolean isInterface() {
539         return m_isInterface;
540     }
541 
542     /***
543      * Is the class a primitive type.
544      *
545      * @return
546      */
547     public boolean isPrimitive() {
548         return m_isPrimitive;
549     }
550 
551     /***
552      * Is the class an array type.
553      *
554      * @return
555      */
556     public boolean isArray() {
557         return m_isArray;
558     }
559 
560     /***
561      * @see java.lang.Object#equals(java.lang.Object)
562      */
563     public boolean equals(Object o) {
564         if (this == o) {
565             return true;
566         }
567         if (!(o instanceof ClassInfo)) {
568             return false;
569         }
570         ClassInfo classInfo = (ClassInfo)o;
571         return m_name.equals(classInfo.getName());
572     }
573 
574     /***
575      * @see java.lang.Object#hashCode()
576      */
577     public int hashCode() {
578         return m_name.hashCode();
579     }
580 
581     public String toString() {
582         return m_name;
583     }
584 
585     /***
586      * Create a ClassInfo based on a component type and a given dimension
587      *
588      * @param className
589      * @param loader
590      * @param componentClassInfo
591      * @param dimension
592      * @return
593      */
594     public static ClassInfo getArrayClassInfo(
595             final String className,
596             final ClassLoader loader,
597             final ClassInfo componentClassInfo,
598             final int dimension) {
599         if (dimension <= 1) {
600             return componentClassInfo;
601         }
602         return new AsmClassInfo(className, loader, componentClassInfo, dimension);
603     }
604 
605     /***
606      * Creates a ClassInfo based on the stream retrieved from the class loader through
607      * <code>getResourceAsStream</code>.
608      *
609      * @param className
610      * @param loader
611      * @param lazyAttributes
612      */
613     private static ClassInfo createClassInfoFromStream(String className, final ClassLoader loader, boolean lazyAttributes) {
614         className = className.replace('.', '/');
615 
616         // compute array type dimension if any
617         int componentTypeIndex = className.indexOf('[');
618         String componentName = className;
619         int dimension = 1;
620         if (componentTypeIndex > 0) {
621             componentName = className.substring(0, componentTypeIndex);
622             dimension = 1 + (className.length() - componentTypeIndex) / 2;
623         }
624 
625         // primitive type
626         if (componentName.indexOf('/') < 0) {
627             // it might be one
628             Class primitiveClass = AsmClassInfo.getPrimitiveClass(componentName);
629             if (primitiveClass != null) {
630                 if (dimension <= 1) {
631                     return JavaClassInfo.getClassInfo(primitiveClass);
632                 } else {
633                     Class arrayClass = Array.newInstance(primitiveClass, dimension).getClass();
634                     return JavaClassInfo.getClassInfo(arrayClass);
635                 }
636             }
637         }
638 
639         // non primitive type
640         InputStream componentClassAsStream = null;
641         if (loader != null) {
642             componentClassAsStream = loader.getResourceAsStream(componentName + ".class");
643         } else {
644             // boot class loader, fall back to system classloader that will see it anyway
645             componentClassAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream(componentName + ".class");
646         }
647         if (componentClassAsStream == null) {
648             new RuntimeException(
649                     "could not load class ["
650                     + componentName
651                     + "] as a resource in loader ["
652                     + loader
653                     + "]"
654             ).printStackTrace();
655             return new ClassInfo.NullClassInfo();
656         }
657         ClassInfo componentInfo = null;
658         try {
659             componentInfo = AsmClassInfo.getClassInfo(componentClassAsStream, loader, lazyAttributes);
660         } finally {
661             try {
662                 componentClassAsStream.close();//AW-296
663             } catch (Exception e) {
664                 ;// nothing to do
665             }
666         }
667                                          
668         if (dimension <= 1) {
669             return componentInfo;
670         } else {
671             return AsmClassInfo.getArrayClassInfo(className, loader, componentInfo, dimension);
672         }
673     }
674 
675     /***
676      * Creates and returns a new annotation info build up from the Java5 annotation.
677      *
678      * @param annotation the ASM annotation abstractiono
679      * @param loader     the class loader that has loaded the proxy class to use
680      * @return the annotation info
681      */
682     public static AnnotationInfo getAnnotationInfo(final Annotation annotation, final ClassLoader loader) {
683         String annotationName = annotation.type.substring(1, annotation.type.length() - 1).replace('/', '.');
684         String annotationValues = createAnnotationKeyValueString(annotation);
685 
686         Class proxyClass = Annotations.getProxyClass(annotationName, loader);
687         org.codehaus.aspectwerkz.annotation.Annotation proxy;
688         if (proxyClass == null) {
689             proxy = new UntypedAnnotationProxy(); // no proxy specified, wrap in an untyped proxy
690         } else {
691             try {
692                 proxy = (TypedAnnotationProxy)proxyClass.newInstance(); // proxy specified
693             } catch (Exception e) {
694                 throw new WrappedRuntimeException(e);
695             }
696         }
697         proxy.initialize(annotationName, annotationValues);
698         return new AnnotationInfo(annotationName, proxy);
699     }
700 
701     /***
702      * Creates a string with the annotation key value pairs.
703      *
704      * @param annotation
705      * @return the string
706      */
707     private static String createAnnotationKeyValueString(final Annotation annotation) {
708         List elementValues = annotation.elementValues;
709         StringBuffer annotationValues = new StringBuffer();
710         if (elementValues.size() != 0) {
711             int i = 0;
712             for (Iterator iterator = elementValues.iterator(); iterator.hasNext();) {
713                 Object[] keyValuePair = (Object[])iterator.next();
714                 annotationValues.append((String)keyValuePair[0]);
715                 annotationValues.append('=');
716                 annotationValues.append(keyValuePair[1].toString());
717                 if (i < elementValues.size() - 1) {
718                     annotationValues.append(',');
719                 }
720             }
721         }
722         return annotationValues.toString();
723     }
724 
725     /***
726      * ASM bytecode visitor that retrieves the class name from the bytecode.
727      *
728      * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
729      */
730     public static class ClassNameRetrievalClassAdapter extends ClassAdapter {
731 
732         private String m_className;
733 
734         public ClassNameRetrievalClassAdapter(final ClassVisitor visitor) {
735             super(visitor);
736         }
737 
738         public void visit(
739                 final int version,
740                 final int access,
741                 final String name,
742                 final String superName,
743                 final String[] interfaces,
744                 final String sourceFile) {
745             m_className = name.replace('/', '.');
746             super.visit(version, access, name, superName, interfaces, sourceFile);
747         }
748 
749         public String getClassName() {
750             return m_className;
751         }
752     }
753 
754     /***
755      * ASM bytecode visitor that gathers info about the class.
756      *
757      * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
758      */
759     private class ClassInfoClassAdapter extends ClassAdapter {
760         private static final String CLINIT_METHOD_NAME = "<clinit>";
761         private static final String INIT_METHOD_NAME = "<init>";
762 
763         public boolean m_lazyAttributes = true;
764 
765         public ClassInfoClassAdapter(final ClassVisitor visitor, boolean lazyAttributes) {
766             super(visitor);
767             m_lazyAttributes = lazyAttributes;
768         }
769 
770         public void visit(
771                 final int version,
772                 final int access,
773                 final String name,
774                 final String superName,
775                 final String[] interfaces,
776                 final String sourceFile) {
777             m_name = name.replace('/', '.');
778             m_modifiers = access;
779             // special case for java.lang.Object, which does not extend anything
780             m_superClassName = superName == null ? null : superName.replace('/', '.');
781             m_interfaceClassNames = new String[interfaces.length];
782             for (int i = 0; i < interfaces.length; i++) {
783                 m_interfaceClassNames[i] = interfaces[i].replace('/', '.');
784             }
785             // FIXME this algo for array types does most likely NOT WORK (since
786             // I assume that ASM is handling arrays
787             // using the internal desriptor format '[L' and the algo is using '[]')
788             if (m_name.endsWith("[]")) {
789                 m_isArray = true;
790                 int index = m_name.indexOf('[');
791                 m_componentTypeName = m_name.substring(0, index);
792             } else if (m_name.equals("long")
793                        || m_name.equals("int")
794                        || m_name.equals("short")
795                        || m_name.equals("double")
796                        || m_name.equals("float")
797                        || m_name.equals("byte")
798                        || m_name.equals("boolean")
799                        || m_name.equals("char")) {
800                 m_isPrimitive = true;
801             }
802             super.visit(version, access, name, superName, interfaces, sourceFile);
803         }
804 
805         public void visitAttribute(final Attribute attribute) {
806             // attributes
807             if (!m_lazyAttributes) {
808                 List annotations = new ArrayList();
809                 annotations = AsmAnnotationHelper.extractAnnotations(annotations, attribute, (ClassLoader)m_loaderRef.get());
810                 m_annotations = annotations;
811             }
812             super.visitAttribute(attribute);
813         }
814 
815         public void visitField(
816                 final int access,
817                 final String name,
818                 final String desc,
819                 final Object value,
820                 final Attribute attrs) {
821             final FieldStruct struct = new FieldStruct();
822             struct.modifiers = access;
823             struct.name = name;
824             struct.desc = desc;
825             struct.value = value;
826             AsmFieldInfo fieldInfo = new AsmFieldInfo(struct, m_name, (ClassLoader)m_loaderRef.get());
827             // attributes
828             if (!m_lazyAttributes) {
829                 List annotations = new ArrayList();
830                 annotations = AsmAnnotationHelper.extractAnnotations(annotations, attrs, (ClassLoader)m_loaderRef.get());
831                 fieldInfo.m_annotations = annotations;
832             }
833             m_fields.put(AsmHelper.calculateFieldHash(name, desc), fieldInfo);
834             super.visitField(access, name, desc, value, attrs);
835         }
836 
837         public CodeVisitor visitMethod(
838                 final int access,
839                 final String name,
840                 final String desc,
841                 final String[] exceptions,
842                 final Attribute attrs) {
843             final MethodStruct struct = new MethodStruct();
844             struct.modifiers = access;
845             struct.name = name;
846             struct.desc = desc;
847             struct.exceptions = exceptions;
848             int hash = AsmHelper.calculateMethodHash(name, desc);
849             if (name.equals(CLINIT_METHOD_NAME)) {
850                 // skip <clinit>
851             } else {
852                 AsmMemberInfo memberInfo = null;
853                 if (name.equals(INIT_METHOD_NAME)) {
854                     memberInfo = new AsmConstructorInfo(struct, m_name, (ClassLoader)m_loaderRef.get());
855                     m_constructors.put(hash, memberInfo);
856                 } else {
857                     memberInfo = new AsmMethodInfo(struct, m_name, (ClassLoader)m_loaderRef.get());
858                     m_methods.put(hash, memberInfo);
859                 }
860                 // attributes
861                 if (!m_lazyAttributes) {
862                     List annotations = new ArrayList();
863                     annotations = AsmAnnotationHelper.extractAnnotations(annotations, attrs, (ClassLoader)m_loaderRef.get());
864                     memberInfo.m_annotations = annotations;
865                 }
866             }
867 
868             return super.visitMethod(access, name, desc, exceptions, attrs);
869         }
870 
871     }
872 
873 }