* configurable ignored packages
authorpatriot1burke <patriot1burke@sourceforge.net>
Tue, 22 Jan 2008 16:02:55 +0000 (16:02 +0000)
committerKai Moritz <kai@juplo.de>
Tue, 12 May 2015 19:25:16 +0000 (21:25 +0200)
* ability to xref meta-annotations
* CrossRefException is now checked

pom.xml
src/main/java/org/scannotation/AnnotationDB.java
src/test/java/org/scannotation/test/AnnotationWithMetaAnnotation.java [new file with mode: 0644]
src/test/java/org/scannotation/test/CrossRefMetaAnnotaiton.java [new file with mode: 0644]
src/test/java/org/scannotation/test/MetaAnnotation.java [new file with mode: 0644]
src/test/java/org/scannotation/test/TestSmoke.java

diff --git a/pom.xml b/pom.xml
index 4b32de2..1c1141b 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
     <groupId>org.scannotation</groupId>
     <artifactId>scannotation</artifactId>
     <packaging>jar</packaging>
-    <version>1.0</version>
+    <version>1.0.1</version>
     <name>scannotation</name>
     <url>http://maven.apache.org</url>
     <repositories>
index 2c062f2..92e28c6 100644 (file)
@@ -26,7 +26,8 @@ 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.
+ * 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
@@ -47,22 +48,105 @@ 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"};
 
-   public class CrossReferenceException extends RuntimeException
+   public class CrossReferenceException extends Exception
    {
-      private Map<String, Set<String>> unresolved;
+      private Set<String> unresolved;
 
-      public CrossReferenceException(Map<String, Set<String>> unresolved)
+      public CrossReferenceException(Set<String> unresolved)
       {
          this.unresolved = unresolved;
       }
 
-      public Map<String, Set<String>> getUnresolved()
+      public Set<String> getUnresolved()
       {
          return unresolved;
       }
    }
 
+   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 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<String> unresolved = new HashSet<String>();
+
+      Set<String> index = new HashSet<String>();
+      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
@@ -70,34 +154,22 @@ 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 aException thrown if referenced interfaces haven't been scanned
     */
-   public void crossReferenceImplementedInterfaces(String... ignoredPackages) throws CrossReferenceException
+   public void crossReferenceImplementedInterfaces() throws CrossReferenceException
    {
-      Map<String, Set<String>> unresolved = new HashMap<String, Set<String>>();
+      Set<String> unresolved = new HashSet<String>();
       for (String clazz : implementsIndex.keySet())
       {
          Set<String> 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<String> unresolvedInterfaces = new HashSet<String>();
             Set<String> xrefAnnotations = classIndex.get(intf);
             if (xrefAnnotations == null)
             {
-               unresolvedInterfaces.add(intf);
-               unresolved.put(clazz, unresolvedInterfaces);
+               unresolved.add(intf);
             }
             Set<String> classAnnotations = classIndex.get(clazz);
             classAnnotations.addAll(xrefAnnotations);
@@ -108,9 +180,26 @@ public class AnnotationDB implements Serializable
             }
          }
       }
+      if (unresolved.size() > 0) throw new CrossReferenceException(unresolved);
 
    }
 
+   private boolean ignoreScan(String intf)
+   {
+      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.
@@ -173,6 +262,7 @@ public class AnnotationDB implements Serializable
    }
 
 
+
    /**
     * Scan a url that represents an "archive"  this is a classpath directory or jar file
     *
@@ -187,7 +277,13 @@ public class AnnotationDB implements Serializable
          {
             public boolean accepts(String filename)
             {
-               return filename.endsWith(".class");
+               if (filename.endsWith(".class"))
+               {
+                  if (filename.startsWith("/")) filename = filename.substring(1);
+                  if (!ignoreScan(filename.replace('/', '.'))) return true;
+                  //System.out.println("IGNORED: " + filename);
+               }
+               return false;
             }
          };
 
@@ -299,9 +395,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)
diff --git a/src/test/java/org/scannotation/test/AnnotationWithMetaAnnotation.java b/src/test/java/org/scannotation/test/AnnotationWithMetaAnnotation.java
new file mode 100644 (file)
index 0000000..3c5dbef
--- /dev/null
@@ -0,0 +1,15 @@
+package org.scannotation.test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@MetaAnnotation
+public @interface AnnotationWithMetaAnnotation
+{
+}
diff --git a/src/test/java/org/scannotation/test/CrossRefMetaAnnotaiton.java b/src/test/java/org/scannotation/test/CrossRefMetaAnnotaiton.java
new file mode 100644 (file)
index 0000000..8176e82
--- /dev/null
@@ -0,0 +1,10 @@
+package org.scannotation.test;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@AnnotationWithMetaAnnotation
+public class CrossRefMetaAnnotaiton
+{
+}
diff --git a/src/test/java/org/scannotation/test/MetaAnnotation.java b/src/test/java/org/scannotation/test/MetaAnnotation.java
new file mode 100644 (file)
index 0000000..95f04a3
--- /dev/null
@@ -0,0 +1,14 @@
+package org.scannotation.test;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public @interface MetaAnnotation
+{
+}
index 8a9331d..cb8b842 100644 (file)
@@ -7,6 +7,7 @@ import org.scannotation.AnnotationDB;
 import org.scannotation.ClasspathUrlFinder;
 
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.net.URL;
 import java.util.Map;
 import java.util.Set;
@@ -108,6 +109,18 @@ public class TestSmoke
    }
 
 
+   @Test
+   public void testCrossRefMetaAnnotations() throws Exception
+   {
+      URL url = ClasspathUrlFinder.findClassBase(TestSmoke.class);
+      AnnotationDB db = new AnnotationDB();
+      db.scanArchives(url);
+      db.crossReferenceMetaAnnotations();
+
+      Assert.assertTrue(db.getAnnotationIndex().get(MetaAnnotation.class.getName()).contains(CrossRefMetaAnnotaiton.class.getName()));
+   }
+
+
    @Test
    public void testByClass() throws Exception
    {