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.annotation;
9   
10  import com.thoughtworks.qdox.JavaDocBuilder;
11  import com.thoughtworks.qdox.model.DocletTag;
12  import com.thoughtworks.qdox.model.JavaClass;
13  import com.thoughtworks.qdox.model.JavaField;
14  import com.thoughtworks.qdox.model.JavaMethod;
15  
16  import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
17  import org.codehaus.aspectwerkz.util.Strings;
18  
19  import java.io.File;
20  import java.io.FileNotFoundException;
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  
29  /***
30   * Parses and retrieves annotations.
31   *
32   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
33   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
34   */
35  public class AnnotationManager {
36  
37      private static final String JAVA_LANG_OBJECT_CLASS_NAME = "java.lang.Object";
38  
39      /***
40       * The JavaDoc parser.
41       */
42      private final JavaDocBuilder m_parser = new JavaDocBuilder();
43  
44      /***
45       * Map with the registered annotations mapped to their proxy classes.
46       */
47      private final Map m_registeredAnnotations = new HashMap();
48  
49      /***
50       * Constructs a new annotation manager and had the given ClassLoader to the
51       * search path
52       * @param loader
53       */
54      public AnnotationManager(ClassLoader loader) {
55          m_parser.getClassLibrary().addClassLoader(loader);
56      }
57  
58      /***
59       * Adds a source tree to the builder.
60       *
61       * @param srcDirs the source trees
62       */
63      public void addSourceTrees(final String[] srcDirs) {
64          for (int i = 0; i < srcDirs.length; i++) {
65              m_parser.addSourceTree(new File(srcDirs[i]));
66          }
67      }
68  
69      /***
70       * Adds a source file.
71       *
72       * @param srcFile the source file
73       */
74      public void addSource(final String srcFile) {
75          try {
76              m_parser.addSource(new File(srcFile));
77          } catch (Exception e) {
78              throw new WrappedRuntimeException(e);
79          }
80      }
81  
82      /***
83       * Register an annotation together with its proxy implementation.
84       *
85       * @param proxyClass     the proxy class
86       * @param annotationName the name of the annotation
87       */
88      public void registerAnnotationProxy(final Class proxyClass, final String annotationName) {
89          m_registeredAnnotations.put(annotationName, proxyClass);
90      }
91  
92      /***
93       * Returns all classes.
94       *
95       * @return an array with all classes
96       */
97      public JavaClass[] getAllClasses() {
98          Collection classes = m_parser.getClassLibrary().all();
99          Collection javaClasses = new ArrayList();
100         String className;
101         for (Iterator it = classes.iterator(); it.hasNext();) {
102             className = (String) it.next();
103             if (JAVA_LANG_OBJECT_CLASS_NAME.equals(className)) {
104                 continue;
105             }
106             JavaClass clazz = m_parser.getClassByName(className);
107             javaClasses.add(clazz);
108         }
109         return (JavaClass[]) javaClasses.toArray(new JavaClass[]{});
110     }
111 
112     /***
113      * Returns the annotations with a specific name for a specific class.
114      *
115      * @param name
116      * @param clazz
117      * @return an array with the annotations
118      */
119     public Annotation[] getAnnotations(final String name, final JavaClass clazz) {
120         DocletTag[] tags = clazz.getTags();
121         List annotations = new ArrayList();
122         for (int i = 0; i < tags.length; i++) {
123             DocletTag tag = tags[i];
124             RawAnnotation rawAnnotation = getRawAnnotation(name, tag);
125             if (rawAnnotation != null) {
126                 annotations.add(instantiateAnnotation(rawAnnotation));
127             }
128         }
129         return (Annotation[]) annotations.toArray(new Annotation[]{});
130     }
131 
132     /***
133      * Returns the annotations with a specific name for a specific method.
134      *
135      * @param name
136      * @param method
137      * @return an array with the annotations
138      */
139     public Annotation[] getAnnotations(final String name, final JavaMethod method) {
140         DocletTag[] tags = method.getTags();
141         List annotations = new ArrayList();
142         for (int i = 0; i < tags.length; i++) {
143             DocletTag tag = tags[i];
144             RawAnnotation rawAnnotation = getRawAnnotation(name, tag);
145             if (rawAnnotation != null) {
146                 annotations.add(instantiateAnnotation(rawAnnotation));
147             }
148         }
149         return (Annotation[]) annotations.toArray(new Annotation[]{});
150     }
151 
152     /***
153      * Returns the annotations with a specific name for a specific field.
154      *
155      * @param name
156      * @param field
157      * @return an array with the annotations
158      */
159     public Annotation[] getAnnotations(final String name, final JavaField field) {
160         DocletTag[] tags = field.getTags();
161         List annotations = new ArrayList();
162         for (int i = 0; i < tags.length; i++) {
163             DocletTag tag = tags[i];
164             RawAnnotation rawAnnotation = getRawAnnotation(name, tag);
165             if (rawAnnotation != null) {
166                 annotations.add(instantiateAnnotation(rawAnnotation));
167             }
168         }
169         return (Annotation[]) annotations.toArray(new Annotation[]{});
170     }
171 
172     /***
173      * Instantiate the given annotation based on its name, and initialize it by passing the given value (may be parsed
174      * or not, depends on type/untyped)
175      *
176      * @param rawAnnotation
177      * @return
178      */
179     private Annotation instantiateAnnotation(RawAnnotation rawAnnotation) {
180         Class proxyClass = (Class) m_registeredAnnotations.get(rawAnnotation.name);
181         Annotation annotation;
182         try {
183             annotation = (Annotation) proxyClass.newInstance();
184         } catch (Exception e) {
185             throw new WrappedRuntimeException(e);
186         }
187         annotation.setName(rawAnnotation.name);
188         annotation.initialize(rawAnnotation.name, (rawAnnotation.value == null) ? "" : rawAnnotation.value);
189         return annotation;
190     }
191 
192     /***
193      * Extract the raw information (name + unparsed value without optional parenthesis) from a Qdox doclet Note:
194      * StringBuffer.append(null<string>) sucks and produce "null" string..
195      * Note: when using untyped annotation, then the first space character(s) in the value part will be
196      * resumed to only one space (untyped     type -> untyped type), due to QDox doclet handling.
197      *
198      * @param annotationName
199      * @param tag
200      * @return RawAnnotation or null if not found
201      */
202     private RawAnnotation getRawAnnotation(String annotationName, DocletTag tag) {
203         String tagName = tag.getName().trim();
204 
205         // early filtering
206         if (!tagName.startsWith(annotationName)) {
207             return null;
208         }
209 
210         // check first for untyped annotations
211         if (m_registeredAnnotations.containsKey(annotationName)) {
212             Class proxyClass = (Class) m_registeredAnnotations.get(annotationName);
213             if (UntypedAnnotationProxy.class.isAssignableFrom(proxyClass)) {
214                 // we do have an untyped annotation
215                 // does it match
216                 if (tagName.equals(annotationName)) {
217                     RawAnnotation rawAnnotation = new RawAnnotation();
218                     rawAnnotation.name = annotationName;
219                     rawAnnotation.value = Strings.removeFormattingCharacters(tag.getValue().trim());
220                     return rawAnnotation;
221                 } else {
222                     // go on, we will need to parse the doclet
223                     ;
224                 }
225             }
226         }
227 
228         // handles case where there is no space between the @Annotation and the annotation values (x=1)
229         // In such a case Qdox, see one single annotation whose name is the first part up to the first space
230         // character
231         String rawValue = null;
232         if (tagName.indexOf('(') > 0) {//@Void(), @Do(x = 3), @Do(x=3)
233             rawValue = tagName.substring(tagName.indexOf('(') + 1).trim();//), x, x=3)
234             tagName = tagName.substring(0, tagName.indexOf('(')).trim();//Void, Do
235             if (rawValue.endsWith(")")) {
236                 if (rawValue.length() > 1) {
237                     rawValue = rawValue.substring(0, rawValue.length() - 1);
238                 } else {
239                     rawValue = null;
240                 }
241             }
242         }
243         String rawEndValue = Strings.removeFormattingCharacters(tag.getValue().trim());
244         if (rawEndValue.endsWith(")")) {
245             if (rawEndValue.length() > 1) {
246                 rawEndValue = rawEndValue.substring(0, rawEndValue.length() - 1);
247             } else {
248                 rawEndValue = null;
249             }
250         }
251         StringBuffer raw = new StringBuffer();
252         if (rawValue != null) {
253             raw.append(rawValue);
254         }
255         if (rawEndValue != null) {
256             raw.append(' ').append(rawEndValue);
257         }
258 
259         // exact filtering
260         if (tagName.equals(annotationName) && m_registeredAnnotations.containsKey(tagName)) {
261             RawAnnotation rawAnnotation = new RawAnnotation();
262             rawAnnotation.name = annotationName;
263             rawAnnotation.value = raw.toString();
264             return rawAnnotation;
265         }
266 
267         // no match
268         return null;
269     }
270 
271     /***
272      * Raw info about an annotation: Do(foo) ==> Do + foo [unless untyped then ==> Do(foo) + null Do foo  ==> Do + foo
273      * etc
274      */
275     private static class RawAnnotation {
276         String name;
277         String value;
278     }
279 }