View Javadoc

1   /*
2    * $Id: AnnotationsScanner.java 19191 2010-08-25 21:05:23Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  package org.mule.util.scan.annotations;
11  
12  import org.mule.util.scan.ClassScanner;
13  
14  import java.util.ArrayList;
15  import java.util.BitSet;
16  import java.util.List;
17  
18  import org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  import org.objectweb.asm.AnnotationVisitor;
21  import org.objectweb.asm.FieldVisitor;
22  import org.objectweb.asm.MethodVisitor;
23  import org.objectweb.asm.commons.EmptyVisitor;
24  
25  /**
26   * Scans a single class and registers all annotations on the class in four collections; class annotations, field annotations
27   * method annotations and parameter annotations.
28   *
29   * This scanner can process interfaces, implementation classes and annotation classes.  The scanner uses ASM to read the class
30   * bytecode, removing the need to actally load the class which would be expensive.
31   */
32  public class AnnotationsScanner extends EmptyVisitor implements ClassScanner
33  {
34      protected final Log log = LogFactory.getLog(getClass());
35  
36      private List<AnnotationInfo> classAnnotations = new ArrayList<AnnotationInfo>();
37      private List<AnnotationInfo> fieldAnnotations = new ArrayList<AnnotationInfo>();
38      private List<AnnotationInfo> methodAnnotations = new ArrayList<AnnotationInfo>();
39      private List<AnnotationInfo> paramAnnotations = new ArrayList<AnnotationInfo>();
40  
41      protected AnnotationInfo currentAnnotation;
42  
43      protected static final int PROCESSING_FIELD = 1;
44      protected static final int PROCESSING_METHOD = 2;
45      protected static final int PROCESSING_CLASS = 3;
46      protected static final int PROCESSING_PARAM = 4;
47  
48      protected BitSet currentlyProcessing = new BitSet(4);
49  
50      private AnnotationFilter filter;
51  
52      private BitSet lastProcessing = null;
53  
54      private String className;
55  
56      private boolean match;
57  
58      public AnnotationsScanner()
59      {
60          super();
61      }
62  
63      public AnnotationsScanner(AnnotationFilter filter)
64      {
65          this.filter = filter;
66      }
67  
68      @Override
69      public AnnotationVisitor visitParameterAnnotation(final int parameter, final String desc, final boolean visible)
70      {
71          currentAnnotation = new AnnotationInfo();
72          currentAnnotation.setClassName(getAnnotationClassName(desc));
73          currentlyProcessing.set(PROCESSING_PARAM);
74          if (log.isDebugEnabled())
75          {
76              log.debug("Parameter Annotation: " + getAnnotationClassName(desc));
77          }
78          return this;
79      }
80  
81      @Override
82      public AnnotationVisitor visitAnnotation(final String desc, final boolean visible)
83      {
84          // are we processing anything currently? If not w'ere looking at another annotation on the same class element
85          if (currentlyProcessing.nextSetBit(0) < 0)
86          {
87              if(lastProcessing!=null)
88              {
89                  currentlyProcessing = lastProcessing;
90              }
91              else
92              {
93                  return this;
94              }
95          }
96  
97          currentAnnotation = new AnnotationInfo();
98          currentAnnotation.setClassName(getAnnotationClassName(desc));
99          if (log.isDebugEnabled())
100         {
101             log.debug("Annotation: " + getAnnotationClassName(desc));
102         }
103 
104         return this;
105     }
106 
107 
108     /**
109      * This is the class entry.
110      */
111     @Override
112     public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces)
113     {
114         currentlyProcessing.set(PROCESSING_CLASS);
115         className = name;
116     }
117 
118     /**
119      * We get annotation values in this method, but have to track the current context.
120      */
121     @Override
122     public void visit(String name, Object value)
123     {
124         if (currentAnnotation != null)
125         {
126             currentAnnotation.getParams().add(new AnnotationInfo.NameValue(name, value));
127         }
128         if (log.isDebugEnabled())
129         {
130             // won't really output nicely with multithreaded parsing
131             log.debug("          : " + name + "=" + value);
132         }
133     }
134 
135     @Override
136     public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value)
137     {
138         currentlyProcessing.set(PROCESSING_FIELD);
139         return this;
140     }
141 
142     @Override
143     public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions)
144     {
145         currentlyProcessing.set(PROCESSING_METHOD);
146         return this;
147     }
148 
149 
150     @Override
151     public void visitEnd()
152     {
153         if (currentAnnotation != null)
154         {
155 
156             if(filter!=null && !filter.accept(currentAnnotation))
157             {
158                 currentlyProcessing.clear();
159                 currentAnnotation = null;
160                 return;
161             }
162             if (currentlyProcessing.get(PROCESSING_CLASS))
163             {
164                 classAnnotations.add(currentAnnotation);
165                 match = true;
166             }
167             if (currentlyProcessing.get(PROCESSING_FIELD))
168             {
169                 fieldAnnotations.add(currentAnnotation);
170                 match = true;
171             }
172             else if (currentlyProcessing.get(PROCESSING_PARAM))
173             {
174                 paramAnnotations.add(currentAnnotation);
175                 match = true;
176             }
177             else if (currentlyProcessing.get(PROCESSING_METHOD))
178             {
179                 methodAnnotations.add(currentAnnotation);
180                 match = true;                
181             }
182             currentAnnotation = null;
183         }
184         lastProcessing = (BitSet)currentlyProcessing.clone();
185         currentlyProcessing.clear();
186 
187     }
188 
189     public String getAnnotationClassName(String rawName)
190     {
191         return rawName.substring(1, rawName.length() - 1).replace('/', '.');
192     }
193 
194     public List<AnnotationInfo> getClassAnnotations()
195     {
196         return classAnnotations;
197     }
198 
199     public List<AnnotationInfo> getFieldAnnotations()
200     {
201         return fieldAnnotations;
202     }
203 
204     public List<AnnotationInfo> getMethodAnnotations()
205     {
206         return methodAnnotations;
207     }
208 
209     public List<AnnotationInfo> getParamAnnotations()
210     {
211         return paramAnnotations;
212     }
213 
214     public List<AnnotationInfo> getAllAnnotations()
215     {
216         List<AnnotationInfo> list = new ArrayList<AnnotationInfo>();
217         list.addAll(classAnnotations);
218         list.addAll(fieldAnnotations);
219         list.addAll(methodAnnotations);
220         list.addAll(paramAnnotations);
221         return list;
222     }
223 
224     public boolean isMatch()
225     {
226         return match;
227     }
228 
229     public String getClassName()
230     {
231         return className;
232     }
233 }