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
206 if (!tagName.startsWith(annotationName)) {
207 return null;
208 }
209
210
211 if (m_registeredAnnotations.containsKey(annotationName)) {
212 Class proxyClass = (Class) m_registeredAnnotations.get(annotationName);
213 if (UntypedAnnotationProxy.class.isAssignableFrom(proxyClass)) {
214
215
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
223 ;
224 }
225 }
226 }
227
228
229
230
231 String rawValue = null;
232 if (tagName.indexOf('(') > 0) {
233 rawValue = tagName.substring(tagName.indexOf('(') + 1).trim();
234 tagName = tagName.substring(0, tagName.indexOf('(')).trim();
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
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
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 }