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