X-Git-Url: https://juplo.de/gitweb/?p=scannotation;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Forg%2Fscannotation%2FAnnotationDB.java;h=230bd6e0410675130d6ebf1ca2fcecbddda21757;hp=da19ea277def4cb9bf9f71c35fd48042a26d4428;hb=f42e6e6e1662efe9a2fb23a325854c8327829c17;hpb=58b6663aae5313b41167d92851981ca549cbb461 diff --git a/src/main/java/org/scannotation/AnnotationDB.java b/src/main/java/org/scannotation/AnnotationDB.java index da19ea2..230bd6e 100644 --- a/src/main/java/org/scannotation/AnnotationDB.java +++ b/src/main/java/org/scannotation/AnnotationDB.java @@ -6,9 +6,9 @@ import javassist.bytecode.FieldInfo; import javassist.bytecode.MethodInfo; import javassist.bytecode.ParameterAnnotationsAttribute; import javassist.bytecode.annotation.Annotation; -import org.scannotation.classpath.Filter; -import org.scannotation.classpath.IteratorFactory; -import org.scannotation.classpath.StreamIterator; +import org.scannotation.archiveiterator.Filter; +import org.scannotation.archiveiterator.IteratorFactory; +import org.scannotation.archiveiterator.StreamIterator; import java.io.BufferedInputStream; import java.io.DataInputStream; @@ -25,6 +25,16 @@ import java.util.Map; import java.util.Set; /** + * The class allows you to scan an arbitrary set of "archives" for .class files. These class files + * are parsed to see what annotations they use. Two indexes are created. The javax, java, sun, com.sun, and javassist + * packages will not be scanned by default. + *

+ * One is a map of annotations and what classes + * use those annotations. This could be used, for example, by an EJB deployer to find all the EJBs contained + * in the archive + *

+ * Another is a mpa of classes and what annotations those classes use. + * * @author Bill Burke * @version $Revision: 1 $ */ @@ -38,22 +48,120 @@ public class AnnotationDB implements Serializable protected transient boolean scanMethodAnnotations = true; protected transient boolean scanParameterAnnotations = true; protected transient boolean scanFieldAnnotations = true; + protected transient String[] ignoredPackages = {"javax", "java", "sun", "com.sun", "javassist"}; + protected transient String[] scanPackages = null; - public class CrossReferenceException extends RuntimeException + public class CrossReferenceException extends Exception { - private Map> unresolved; + private Set unresolved; - public CrossReferenceException(Map> unresolved) + public CrossReferenceException(Set unresolved) { this.unresolved = unresolved; } - public Map> getUnresolved() + public Set getUnresolved() { return unresolved; } } + public String[] getScanPackages() + { + return scanPackages; + } + + /** + * Set explicit packages to scan. + * Set to null to enable ignore list. + * + * @param scanPackages packages to scan or null + */ + public void setScanPackages(String[] scanPackages) + { + this.scanPackages = scanPackages; + } + + public String[] getIgnoredPackages() + { + return ignoredPackages; + } + + /** + * Override/overwrite any ignored packages + * + * @param ignoredPackages cannot be null + */ + public void setIgnoredPackages(String[] ignoredPackages) + { + this.ignoredPackages = ignoredPackages; + } + + public void addIgnoredPackages(String... ignored) + { + String[] tmp = new String[ignoredPackages.length + ignored.length]; + int i = 0; + for (String ign : ignoredPackages) tmp[i++] = ign; + for (String ign : ignored) tmp[i++] = ign; + this.ignoredPackages = tmp; + } + + /** + * This method will cross reference annotations in the annotation index with any meta-annotations that they have + * and create additional entries as needed. For example: + * + * @HttpMethod("GET") public @interface GET {} + *

+ * The HttpMethod index will have additional classes added to it for any classes annotated with annotations that + * have the HttpMethod meta-annotation. + *

+ * WARNING: If the annotation class has not already been scaned, this method will load all annotation classes indexed + * as a resource so they must be in your classpath + */ + public void crossReferenceMetaAnnotations() throws CrossReferenceException + { + Set unresolved = new HashSet(); + + Set index = new HashSet(); + index.addAll(annotationIndex.keySet()); + + for (String annotation : index) + { + if (ignoreScan(annotation)) + { + continue; + } + if (classIndex.containsKey(annotation)) + { + for (String xref : classIndex.get(annotation)) + { + annotationIndex.get(xref).addAll(annotationIndex.get(annotation)); + } + continue; + } + InputStream bits = Thread.currentThread().getContextClassLoader().getResourceAsStream(annotation.replace('.', '/') + ".class"); + if (bits == null) + { + unresolved.add(annotation); + continue; + } + try + { + scanClass(bits); + } + catch (IOException e) + { + unresolved.add(annotation); + } + for (String xref : classIndex.get(annotation)) + { + annotationIndex.get(xref).addAll(annotationIndex.get(annotation)); + } + + } + if (unresolved.size() > 0) throw new CrossReferenceException(unresolved); + } + /** * Sometimes you want to see if a particular class implements an interface with certain annotations * After you have loaded all your classpaths with the scanArchive() method, call this method to cross reference @@ -61,72 +169,125 @@ public class AnnotationDB implements Serializable * classIndex indexes * * @param ignoredPackages var arg list of packages to ignore - * @throws CrossReferenceException a RuntimeException thrown if referenced interfaces haven't been scanned + * @throws CrossReferenceException an Exception thrown if referenced interfaces haven't been scanned */ - public void crossReferenceImplementedInterfaces(String... ignoredPackages) throws CrossReferenceException + public void crossReferenceImplementedInterfaces() throws CrossReferenceException { - Map> unresolved = new HashMap>(); + Set unresolved = new HashSet(); for (String clazz : implementsIndex.keySet()) { Set intfs = implementsIndex.get(clazz); for (String intf : intfs) { - if (intf.startsWith("java.") || intf.startsWith("javax.")) continue; - boolean ignoreInterface = false; - for (String ignored : ignoredPackages) - { - if (intf.startsWith(ignored + ".")) - { - ignoreInterface = true; - break; - } - } - if (ignoreInterface) continue; + if (ignoreScan(intf)) continue; - Set unresolvedInterfaces = new HashSet(); Set xrefAnnotations = classIndex.get(intf); if (xrefAnnotations == null) { - unresolvedInterfaces.add(intf); - unresolved.put(clazz, unresolvedInterfaces); + unresolved.add(intf); } - Set classAnnotations = classIndex.get(clazz); - classAnnotations.addAll(xrefAnnotations); - for (String annotation : xrefAnnotations) + else { - Set classes = annotationIndex.get(annotation); - classes.add(clazz); + Set classAnnotations = classIndex.get(clazz); + if (classAnnotations == null) + { + classIndex.put(clazz, xrefAnnotations); + } + else classAnnotations.addAll(xrefAnnotations); + for (String annotation : xrefAnnotations) + { + Set classes = annotationIndex.get(annotation); + classes.add(clazz); + } } } } + if (unresolved.size() > 0) throw new CrossReferenceException(unresolved); + + } + private boolean ignoreScan(String intf) + { + if (scanPackages != null) + { + for (String scan : scanPackages) + { + // do not ignore if on packages to scan list + if (intf.startsWith(scan + ".")) + { + return false; + } + } + return true; // didn't match whitelist, ignore + } + for (String ignored : ignoredPackages) + { + if (intf.startsWith(ignored + ".")) + { + return true; + } + else + { + //System.out.println("NOT IGNORING: " + intf); + } + } + return false; } + /** + * returns a map keyed by the fully qualified string name of a annotation class. The Set returne is + * a list of classes that use that annotation somehow. + */ public Map> getAnnotationIndex() { return annotationIndex; } + /** + * returns a map keyed by the list of classes scanned. The value set returned is a list of annotations + * used by that class. + */ public Map> getClassIndex() { return classIndex; } + + /** + * Whether or not you want AnnotationDB to scan for class level annotations + * + * @param scanClassAnnotations + */ public void setScanClassAnnotations(boolean scanClassAnnotations) { this.scanClassAnnotations = scanClassAnnotations; } + /** + * Wheter or not you want AnnotationDB to scan for method level annotations + * + * @param scanMethodAnnotations + */ public void setScanMethodAnnotations(boolean scanMethodAnnotations) { this.scanMethodAnnotations = scanMethodAnnotations; } + /** + * Whether or not you want AnnotationDB to scan for parameter level annotations + * + * @param scanParameterAnnotations + */ public void setScanParameterAnnotations(boolean scanParameterAnnotations) { this.scanParameterAnnotations = scanParameterAnnotations; } + /** + * Whether or not you want AnnotationDB to scan for parameter level annotations + * + * @param scanFieldAnnotations + */ public void setScanFieldAnnotations(boolean scanFieldAnnotations) { this.scanFieldAnnotations = scanFieldAnnotations; @@ -136,7 +297,7 @@ public class AnnotationDB implements Serializable /** * Scan a url that represents an "archive" this is a classpath directory or jar file * - * @param url + * @param urls variable list of URLs to scan as archives * @throws IOException */ public void scanArchives(URL... urls) throws IOException @@ -147,7 +308,15 @@ public class AnnotationDB implements Serializable { public boolean accepts(String filename) { - return filename.endsWith(".class"); + if (filename.endsWith(".class")) + { + if (filename.startsWith("/") || filename.startsWith("\\")) + filename = filename.substring(1); + if (!ignoreScan(filename.replace('/', '.'))) + return true; + //System.out.println("IGNORED: " + filename); + } + return false; } }; @@ -160,9 +329,9 @@ public class AnnotationDB implements Serializable } /** - * Can a .class file for annotations + * Parse a .class file for annotations * - * @param bits + * @param bits input stream pointing to .class file bits * @throws IOException */ public void scanClass(InputStream bits) throws IOException @@ -173,8 +342,8 @@ public class AnnotationDB implements Serializable { cf = new ClassFile(dstream); classIndex.put(cf.getName(), new HashSet()); - if (scanClassAnnotations) ; - scanClass(cf); + if (scanClassAnnotations) + scanClass(cf); if (scanMethodAnnotations || scanParameterAnnotations) scanMethods(cf); if (scanFieldAnnotations) scanFields(cf); @@ -259,9 +428,9 @@ public class AnnotationDB implements Serializable if (visible != null) populate(visible.getAnnotations(), cf.getName()); if (invisible != null) populate(invisible.getAnnotations(), cf.getName()); - } + } protected void populate(Annotation[] annotations, String className) @@ -281,7 +450,12 @@ public class AnnotationDB implements Serializable } } - public void outputAnnotationDB(PrintWriter writer) + /** + * Prints out annotationIndex + * + * @param writer + */ + public void outputAnnotationIndex(PrintWriter writer) { for (String ann : annotationIndex.keySet()) {