1 package org.scannotation;
3 import javassist.bytecode.AnnotationsAttribute;
4 import javassist.bytecode.ClassFile;
5 import javassist.bytecode.FieldInfo;
6 import javassist.bytecode.MethodInfo;
7 import javassist.bytecode.ParameterAnnotationsAttribute;
8 import javassist.bytecode.annotation.Annotation;
9 import org.scannotation.classpath.Filter;
10 import org.scannotation.classpath.IteratorFactory;
11 import org.scannotation.classpath.StreamIterator;
13 import java.io.BufferedInputStream;
14 import java.io.DataInputStream;
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.io.PrintWriter;
18 import java.io.Serializable;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.List;
28 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
29 * @version $Revision: 1 $
31 public class AnnotationDB implements Serializable
33 protected Map<String, Set<String>> annotationIndex = new HashMap<String, Set<String>>();
34 protected Map<String, Set<String>> implementsIndex = new HashMap<String, Set<String>>();
35 protected Map<String, Set<String>> classIndex = new HashMap<String, Set<String>>();
37 protected transient boolean scanClassAnnotations = true;
38 protected transient boolean scanMethodAnnotations = true;
39 protected transient boolean scanParameterAnnotations = true;
40 protected transient boolean scanFieldAnnotations = true;
42 public class CrossReferenceException extends RuntimeException
44 private Map<String, Set<String>> unresolved;
46 public CrossReferenceException(Map<String, Set<String>> unresolved)
48 this.unresolved = unresolved;
51 public Map<String, Set<String>> getUnresolved()
58 * Sometimes you want to see if a particular class implements an interface with certain annotations
59 * After you have loaded all your classpaths with the scanArchive() method, call this method to cross reference
60 * a class's implemented interfaces. The cross references will be added to the annotationIndex and
63 * @param ignoredPackages var arg list of packages to ignore
64 * @throws CrossReferenceException a RuntimeException thrown if referenced interfaces haven't been scanned
66 public void crossReferenceImplementedInterfaces(String... ignoredPackages) throws CrossReferenceException
68 Map<String, Set<String>> unresolved = new HashMap<String, Set<String>>();
69 for (String clazz : implementsIndex.keySet())
71 Set<String> intfs = implementsIndex.get(clazz);
72 for (String intf : intfs)
74 if (intf.startsWith("java.") || intf.startsWith("javax.")) continue;
75 boolean ignoreInterface = false;
76 for (String ignored : ignoredPackages)
78 if (intf.startsWith(ignored + "."))
80 ignoreInterface = true;
84 if (ignoreInterface) continue;
86 Set<String> unresolvedInterfaces = new HashSet<String>();
87 Set<String> xrefAnnotations = classIndex.get(intf);
88 if (xrefAnnotations == null)
90 unresolvedInterfaces.add(intf);
91 unresolved.put(clazz, unresolvedInterfaces);
93 Set<String> classAnnotations = classIndex.get(clazz);
94 classAnnotations.addAll(xrefAnnotations);
95 for (String annotation : xrefAnnotations)
97 Set<String> classes = annotationIndex.get(annotation);
105 public Map<String, Set<String>> getAnnotationIndex()
107 return annotationIndex;
110 public Map<String, Set<String>> getClassIndex()
115 public void setScanClassAnnotations(boolean scanClassAnnotations)
117 this.scanClassAnnotations = scanClassAnnotations;
120 public void setScanMethodAnnotations(boolean scanMethodAnnotations)
122 this.scanMethodAnnotations = scanMethodAnnotations;
125 public void setScanParameterAnnotations(boolean scanParameterAnnotations)
127 this.scanParameterAnnotations = scanParameterAnnotations;
130 public void setScanFieldAnnotations(boolean scanFieldAnnotations)
132 this.scanFieldAnnotations = scanFieldAnnotations;
137 * Scan a url that represents an "archive" this is a classpath directory or jar file
140 * @throws IOException
142 public void scanArchives(URL... urls) throws IOException
146 Filter filter = new Filter()
148 public boolean accepts(String filename)
150 return filename.endsWith(".class");
154 StreamIterator it = IteratorFactory.create(url, filter);
157 while ((stream = it.next()) != null) scanClass(stream);
163 * Can a .class file for annotations
166 * @throws IOException
168 public void scanClass(InputStream bits) throws IOException
170 DataInputStream dstream = new DataInputStream(new BufferedInputStream(bits));
174 cf = new ClassFile(dstream);
175 classIndex.put(cf.getName(), new HashSet<String>());
176 if (scanClassAnnotations) ;
178 if (scanMethodAnnotations || scanParameterAnnotations) scanMethods(cf);
179 if (scanFieldAnnotations) scanFields(cf);
181 // create an index of interfaces the class implements
182 if (cf.getInterfaces() != null)
184 Set<String> intfs = new HashSet<String>();
185 for (String intf : cf.getInterfaces()) intfs.add(intf);
186 implementsIndex.put(cf.getName(), intfs);
197 protected void scanClass(ClassFile cf)
199 String className = cf.getName();
200 AnnotationsAttribute visible = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.visibleTag);
201 AnnotationsAttribute invisible = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.invisibleTag);
203 if (visible != null) populate(visible.getAnnotations(), className);
204 if (invisible != null) populate(invisible.getAnnotations(), className);
208 * Scanns both the method and its parameters for annotations.
212 protected void scanMethods(ClassFile cf)
214 List methods = cf.getMethods();
215 if (methods == null) return;
216 for (Object obj : methods)
218 MethodInfo method = (MethodInfo) obj;
219 if (scanMethodAnnotations)
221 AnnotationsAttribute visible = (AnnotationsAttribute) method.getAttribute(AnnotationsAttribute.visibleTag);
222 AnnotationsAttribute invisible = (AnnotationsAttribute) method.getAttribute(AnnotationsAttribute.invisibleTag);
224 if (visible != null) populate(visible.getAnnotations(), cf.getName());
225 if (invisible != null) populate(invisible.getAnnotations(), cf.getName());
227 if (scanParameterAnnotations)
229 ParameterAnnotationsAttribute paramsVisible = (ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.visibleTag);
230 ParameterAnnotationsAttribute paramsInvisible = (ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.invisibleTag);
232 if (paramsVisible != null && paramsVisible.getAnnotations() != null)
234 for (Annotation[] anns : paramsVisible.getAnnotations())
236 populate(anns, cf.getName());
239 if (paramsInvisible != null && paramsInvisible.getAnnotations() != null)
241 for (Annotation[] anns : paramsInvisible.getAnnotations())
243 populate(anns, cf.getName());
250 protected void scanFields(ClassFile cf)
252 List fields = cf.getFields();
253 if (fields == null) return;
254 for (Object obj : fields)
256 FieldInfo field = (FieldInfo) obj;
257 AnnotationsAttribute visible = (AnnotationsAttribute) field.getAttribute(AnnotationsAttribute.visibleTag);
258 AnnotationsAttribute invisible = (AnnotationsAttribute) field.getAttribute(AnnotationsAttribute.invisibleTag);
260 if (visible != null) populate(visible.getAnnotations(), cf.getName());
261 if (invisible != null) populate(invisible.getAnnotations(), cf.getName());
267 protected void populate(Annotation[] annotations, String className)
269 if (annotations == null) return;
270 Set<String> classAnnotations = classIndex.get(className);
271 for (Annotation ann : annotations)
273 Set<String> classes = annotationIndex.get(ann.getTypeName());
276 classes = new HashSet<String>();
277 annotationIndex.put(ann.getTypeName(), classes);
279 classes.add(className);
280 classAnnotations.add(ann.getTypeName());
284 public void outputAnnotationDB(PrintWriter writer)
286 for (String ann : annotationIndex.keySet())
290 Set<String> classes = annotationIndex.get(ann);
291 Iterator<String> it = classes.iterator();
294 writer.print(it.next());
295 if (it.hasNext()) writer.print(", ");