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())
{