initial commit
authorpatriot1burke <patriot1burke@sourceforge.net>
Thu, 10 Jan 2008 19:50:10 +0000 (19:50 +0000)
committerKai Moritz <kai@juplo.de>
Tue, 12 May 2015 19:20:32 +0000 (21:20 +0200)
18 files changed:
annotation-db.ipr [new file with mode: 0644]
pom.xml [new file with mode: 0644]
src/main/java/org/scannotation/AnnotationDB.java [new file with mode: 0644]
src/main/java/org/scannotation/classpath/ClasspathUrlFinder.java [new file with mode: 0644]
src/main/java/org/scannotation/classpath/DirectoryIteratorFactory.java [new file with mode: 0644]
src/main/java/org/scannotation/classpath/FileIterator.java [new file with mode: 0644]
src/main/java/org/scannotation/classpath/FileProtocolIteratorFactory.java [new file with mode: 0644]
src/main/java/org/scannotation/classpath/Filter.java [new file with mode: 0644]
src/main/java/org/scannotation/classpath/InputStreamWrapper.java [new file with mode: 0644]
src/main/java/org/scannotation/classpath/IteratorFactory.java [new file with mode: 0644]
src/main/java/org/scannotation/classpath/JarIterator.java [new file with mode: 0644]
src/main/java/org/scannotation/classpath/StreamIterator.java [new file with mode: 0644]
src/main/java/org/scannotation/classpath/WarUrlFinder.java [new file with mode: 0644]
src/test/java/org/scannotation/test/ClassWithFieldAnnotation.java [new file with mode: 0644]
src/test/java/org/scannotation/test/CrossRef.java [new file with mode: 0644]
src/test/java/org/scannotation/test/InterfaceWithParameterAnnotations.java [new file with mode: 0644]
src/test/java/org/scannotation/test/SimpleAnnotation.java [new file with mode: 0644]
src/test/java/org/scannotation/test/TestSmoke.java [new file with mode: 0644]

diff --git a/annotation-db.ipr b/annotation-db.ipr
new file mode 100644 (file)
index 0000000..2c15589
--- /dev/null
@@ -0,0 +1,363 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project relativePaths="true" version="4">
+  <component name="AntConfiguration">
+    <defaultAnt bundledAnt="true" />
+  </component>
+  <component name="BuildJarProjectSettings">
+    <option name="BUILD_JARS_ON_MAKE" value="false" />
+  </component>
+  <component name="CCaseConfig">
+    <option name="checkoutReserved" value="false" />
+    <option name="markExternalChangeAsUpToDate" value="true" />
+    <option name="checkInUseHijack" value="true" />
+    <option name="useUcmModel" value="true" />
+    <option name="isOffline" value="false" />
+    <option name="synchOutside" value="false" />
+    <option name="isHistoryResticted" value="true" />
+    <option name="useIdenticalSwitch" value="true" />
+    <option name="synchActivitiesOnRefresh" value="true" />
+    <option name="lastScr" value="" />
+    <option name="scrTextFileName" value="" />
+    <option name="historyRevisionsNumber" value="4" />
+  </component>
+  <component name="CodeStyleProjectProfileManger">
+    <option name="PROJECT_PROFILE" />
+    <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+  </component>
+  <component name="CodeStyleSettingsManager">
+    <option name="PER_PROJECT_SETTINGS" />
+    <option name="USE_PER_PROJECT_SETTINGS" value="false" />
+  </component>
+  <component name="CompilerConfiguration">
+    <option name="DEFAULT_COMPILER" value="Javac" />
+    <option name="DEPLOY_AFTER_MAKE" value="0" />
+    <resourceExtensions>
+      <entry name=".+\.(properties|xml|html|dtd|tld)" />
+      <entry name=".+\.(gif|png|jpeg|jpg)" />
+    </resourceExtensions>
+    <wildcardResourcePatterns>
+      <entry name="?*.properties" />
+      <entry name="?*.xml" />
+      <entry name="?*.gif" />
+      <entry name="?*.png" />
+      <entry name="?*.jpeg" />
+      <entry name="?*.jpg" />
+      <entry name="?*.html" />
+      <entry name="?*.dtd" />
+      <entry name="?*.tld" />
+    </wildcardResourcePatterns>
+  </component>
+  <component name="DependenciesAnalyzeManager">
+    <option name="myForwardDirection" value="false" />
+  </component>
+  <component name="DependencyValidationManager">
+    <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+  </component>
+  <component name="EclipseCompilerSettings">
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="true" />
+    <option name="DEPRECATION" value="false" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+    <option name="MAXIMUM_HEAP_SIZE" value="128" />
+  </component>
+  <component name="EclipseEmbeddedCompilerSettings">
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="true" />
+    <option name="DEPRECATION" value="false" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+    <option name="MAXIMUM_HEAP_SIZE" value="128" />
+  </component>
+  <component name="EntryPointsManager">
+    <entry_points version="2.0" />
+  </component>
+  <component name="ErrorOptionsConfigurable.UI">
+    <option name="proportions">
+      <SplitterProportionsDataImpl />
+    </option>
+  </component>
+  <component name="ExportToHTMLSettings">
+    <option name="PRINT_LINE_NUMBERS" value="false" />
+    <option name="OPEN_IN_BROWSER" value="false" />
+    <option name="OUTPUT_DIRECTORY" />
+  </component>
+  <component name="IdProvider" IDEtalkID="010DDB55D24EE954FEBC2143B5D8470F" />
+  <component name="InspectionProjectProfileManager">
+    <option name="PROJECT_PROFILE" value="Project Default" />
+    <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+    <scopes />
+    <profiles>
+      <profile version="1.0" is_locked="false">
+        <option name="myName" value="Project Default" />
+        <option name="myLocal" value="false" />
+        <inspection_tool class="JavaDoc" level="WARNING" enabled="false">
+          <option name="TOP_LEVEL_CLASS_OPTIONS">
+            <value>
+              <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+              <option name="REQUIRED_TAGS" value="" />
+            </value>
+          </option>
+          <option name="INNER_CLASS_OPTIONS">
+            <value>
+              <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+              <option name="REQUIRED_TAGS" value="" />
+            </value>
+          </option>
+          <option name="METHOD_OPTIONS">
+            <value>
+              <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+              <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
+            </value>
+          </option>
+          <option name="FIELD_OPTIONS">
+            <value>
+              <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
+              <option name="REQUIRED_TAGS" value="" />
+            </value>
+          </option>
+          <option name="IGNORE_DEPRECATED" value="false" />
+          <option name="IGNORE_JAVADOC_PERIOD" value="true" />
+          <option name="myAdditionalJavadocTags" value="" />
+        </inspection_tool>
+        <inspection_tool class="JavadocReference" level="ERROR" enabled="false" />
+      </profile>
+    </profiles>
+    <list size="0" />
+  </component>
+  <component name="JavacSettings">
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="false" />
+    <option name="DEPRECATION" value="true" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+    <option name="MAXIMUM_HEAP_SIZE" value="128" />
+  </component>
+  <component name="JavadocGenerationManager">
+    <option name="OUTPUT_DIRECTORY" />
+    <option name="OPTION_SCOPE" value="protected" />
+    <option name="OPTION_HIERARCHY" value="true" />
+    <option name="OPTION_NAVIGATOR" value="true" />
+    <option name="OPTION_INDEX" value="true" />
+    <option name="OPTION_SEPARATE_INDEX" value="true" />
+    <option name="OPTION_DOCUMENT_TAG_USE" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
+    <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true" />
+    <option name="OPTION_DEPRECATED_LIST" value="true" />
+    <option name="OTHER_OPTIONS" value="" />
+    <option name="HEAP_SIZE" />
+    <option name="LOCALE" />
+    <option name="OPEN_IN_BROWSER" value="true" />
+  </component>
+  <component name="JikesSettings">
+    <option name="JIKES_PATH" value="" />
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="DEPRECATION" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="false" />
+    <option name="IS_EMACS_ERRORS_MODE" value="true" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+  </component>
+  <component name="MavenImporterState">
+    <option name="autoImport">
+      <set>
+        <option value="$PROJECT_DIR$/pom.xml" />
+      </set>
+    </option>
+  </component>
+  <component name="Palette2">
+    <group name="Swing">
+      <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+      </item>
+      <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+        <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+        <initial-values>
+          <property name="text" value="Button" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="RadioButton" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="CheckBox" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+        <initial-values>
+          <property name="text" value="Label" />
+        </initial-values>
+      </item>
+      <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+          <preferred-size width="150" height="-1" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+          <preferred-size width="150" height="50" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+          <preferred-size width="200" height="200" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+      </item>
+      <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+      </item>
+      <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+          <preferred-size width="-1" height="20" />
+        </default-constraints>
+      </item>
+      <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+        <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+      </item>
+      <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+        <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+      </item>
+    </group>
+  </component>
+  <component name="ProjectFileVersion" converted="true" />
+  <component name="ProjectModuleManager">
+    <modules />
+  </component>
+  <component name="ProjectRootConfigurable.UI">
+    <option name="proportions">
+      <SplitterProportionsDataImpl />
+    </option>
+  </component>
+  <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+  <component name="ResourceManagerContainer">
+    <option name="myResourceBundles">
+      <value>
+        <list size="0" />
+      </value>
+    </option>
+  </component>
+  <component name="RmicSettings">
+    <option name="IS_EANABLED" value="false" />
+    <option name="DEBUGGING_INFO" value="true" />
+    <option name="GENERATE_NO_WARNINGS" value="false" />
+    <option name="GENERATE_IIOP_STUBS" value="false" />
+    <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+  </component>
+  <component name="ScopeChooserConfigurable.UI">
+    <option name="proportions">
+      <SplitterProportionsDataImpl />
+    </option>
+  </component>
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="svn" />
+  </component>
+  <component name="VssConfiguration">
+    <option name="CLIENT_PATH" value="" />
+    <option name="SRCSAFEINI_PATH" value="" />
+    <option name="USER_NAME" value="" />
+    <option name="PWD" value="" />
+    <CheckoutOptions>
+      <option name="COMMENT" value="" />
+      <option name="DO_NOT_GET_LATEST_VERSION" value="false" />
+      <option name="REPLACE_WRITABLE" value="false" />
+      <option name="RECURSIVE" value="false" />
+    </CheckoutOptions>
+    <CheckinOptions>
+      <option name="COMMENT" value="" />
+      <option name="KEEP_CHECKED_OUT" value="false" />
+      <option name="RECURSIVE" value="false" />
+    </CheckinOptions>
+    <AddOptions>
+      <option name="STORE_ONLY_LATEST_VERSION" value="false" />
+      <option name="CHECK_OUT_IMMEDIATELY" value="false" />
+    </AddOptions>
+    <UndocheckoutOptions>
+      <option name="MAKE_WRITABLE" value="false" />
+      <option name="REPLACE_LOCAL_COPY" value="0" />
+      <option name="RECURSIVE" value="false" />
+    </UndocheckoutOptions>
+    <GetOptions>
+      <option name="REPLACE_WRITABLE" value="0" />
+      <option name="MAKE_WRITABLE" value="false" />
+      <option name="ANSWER_NEGATIVELY" value="false" />
+      <option name="ANSWER_POSITIVELY" value="false" />
+      <option name="RECURSIVE" value="false" />
+      <option name="VERSION" />
+    </GetOptions>
+  </component>
+  <component name="WebServicesPlugin" addRequiredLibraries="true" />
+  <component name="com.intellij.jsf.UserDefinedFacesConfigs">
+    <option name="USER_DEFINED_CONFIGS">
+      <value>
+        <list size="0" />
+      </value>
+    </option>
+  </component>
+</project>
+
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..353ce83
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,53 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.scannotation</groupId>
+    <artifactId>annotation-db</artifactId>
+    <packaging>jar</packaging>
+    <version>1.0</version>
+    <name>resteasy-jsr311</name>
+    <url>http://maven.apache.org</url>
+    <repositories>
+        <repository>
+            <id>jboss</id>
+            <url>http://repository.jboss.org/maven2</url>
+        </repository>
+    </repositories>
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.resteasy</groupId>
+            <artifactId>titan-cruise</artifactId>
+            <version>1.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>javassist</groupId>
+            <artifactId>javassist</artifactId>
+            <version>3.6.0.GA</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/main/java/org/scannotation/AnnotationDB.java b/src/main/java/org/scannotation/AnnotationDB.java
new file mode 100644 (file)
index 0000000..da19ea2
--- /dev/null
@@ -0,0 +1,301 @@
+package org.scannotation;
+
+import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.ClassFile;
+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 java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AnnotationDB implements Serializable
+{
+   protected Map<String, Set<String>> annotationIndex = new HashMap<String, Set<String>>();
+   protected Map<String, Set<String>> implementsIndex = new HashMap<String, Set<String>>();
+   protected Map<String, Set<String>> classIndex = new HashMap<String, Set<String>>();
+
+   protected transient boolean scanClassAnnotations = true;
+   protected transient boolean scanMethodAnnotations = true;
+   protected transient boolean scanParameterAnnotations = true;
+   protected transient boolean scanFieldAnnotations = true;
+
+   public class CrossReferenceException extends RuntimeException
+   {
+      private Map<String, Set<String>> unresolved;
+
+      public CrossReferenceException(Map<String, Set<String>> unresolved)
+      {
+         this.unresolved = unresolved;
+      }
+
+      public Map<String, Set<String>> getUnresolved()
+      {
+         return 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
+    * a class's implemented interfaces.  The cross references will be added to the annotationIndex and
+    * classIndex indexes
+    *
+    * @param ignoredPackages var arg list of packages to ignore
+    * @throws CrossReferenceException a RuntimeException thrown if referenced interfaces haven't been scanned
+    */
+   public void crossReferenceImplementedInterfaces(String... ignoredPackages) throws CrossReferenceException
+   {
+      Map<String, Set<String>> unresolved = new HashMap<String, Set<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;
+
+            Set<String> unresolvedInterfaces = new HashSet<String>();
+            Set<String> xrefAnnotations = classIndex.get(intf);
+            if (xrefAnnotations == null)
+            {
+               unresolvedInterfaces.add(intf);
+               unresolved.put(clazz, unresolvedInterfaces);
+            }
+            Set<String> classAnnotations = classIndex.get(clazz);
+            classAnnotations.addAll(xrefAnnotations);
+            for (String annotation : xrefAnnotations)
+            {
+               Set<String> classes = annotationIndex.get(annotation);
+               classes.add(clazz);
+            }
+         }
+      }
+
+   }
+
+   public Map<String, Set<String>> getAnnotationIndex()
+   {
+      return annotationIndex;
+   }
+
+   public Map<String, Set<String>> getClassIndex()
+   {
+      return classIndex;
+   }
+
+   public void setScanClassAnnotations(boolean scanClassAnnotations)
+   {
+      this.scanClassAnnotations = scanClassAnnotations;
+   }
+
+   public void setScanMethodAnnotations(boolean scanMethodAnnotations)
+   {
+      this.scanMethodAnnotations = scanMethodAnnotations;
+   }
+
+   public void setScanParameterAnnotations(boolean scanParameterAnnotations)
+   {
+      this.scanParameterAnnotations = scanParameterAnnotations;
+   }
+
+   public void setScanFieldAnnotations(boolean scanFieldAnnotations)
+   {
+      this.scanFieldAnnotations = scanFieldAnnotations;
+   }
+
+
+   /**
+    * Scan a url that represents an "archive"  this is a classpath directory or jar file
+    *
+    * @param url
+    * @throws IOException
+    */
+   public void scanArchives(URL... urls) throws IOException
+   {
+      for (URL url : urls)
+      {
+         Filter filter = new Filter()
+         {
+            public boolean accepts(String filename)
+            {
+               return filename.endsWith(".class");
+            }
+         };
+
+         StreamIterator it = IteratorFactory.create(url, filter);
+
+         InputStream stream;
+         while ((stream = it.next()) != null) scanClass(stream);
+      }
+
+   }
+
+   /**
+    * Can a .class file for annotations
+    *
+    * @param bits
+    * @throws IOException
+    */
+   public void scanClass(InputStream bits) throws IOException
+   {
+      DataInputStream dstream = new DataInputStream(new BufferedInputStream(bits));
+      ClassFile cf = null;
+      try
+      {
+         cf = new ClassFile(dstream);
+         classIndex.put(cf.getName(), new HashSet<String>());
+         if (scanClassAnnotations) ;
+         scanClass(cf);
+         if (scanMethodAnnotations || scanParameterAnnotations) scanMethods(cf);
+         if (scanFieldAnnotations) scanFields(cf);
+
+         // create an index of interfaces the class implements
+         if (cf.getInterfaces() != null)
+         {
+            Set<String> intfs = new HashSet<String>();
+            for (String intf : cf.getInterfaces()) intfs.add(intf);
+            implementsIndex.put(cf.getName(), intfs);
+         }
+
+      }
+      finally
+      {
+         dstream.close();
+         bits.close();
+      }
+   }
+
+   protected void scanClass(ClassFile cf)
+   {
+      String className = cf.getName();
+      AnnotationsAttribute visible = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.visibleTag);
+      AnnotationsAttribute invisible = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.invisibleTag);
+
+      if (visible != null) populate(visible.getAnnotations(), className);
+      if (invisible != null) populate(invisible.getAnnotations(), className);
+   }
+
+   /**
+    * Scanns both the method and its parameters for annotations.
+    *
+    * @param cf
+    */
+   protected void scanMethods(ClassFile cf)
+   {
+      List methods = cf.getMethods();
+      if (methods == null) return;
+      for (Object obj : methods)
+      {
+         MethodInfo method = (MethodInfo) obj;
+         if (scanMethodAnnotations)
+         {
+            AnnotationsAttribute visible = (AnnotationsAttribute) method.getAttribute(AnnotationsAttribute.visibleTag);
+            AnnotationsAttribute invisible = (AnnotationsAttribute) method.getAttribute(AnnotationsAttribute.invisibleTag);
+
+            if (visible != null) populate(visible.getAnnotations(), cf.getName());
+            if (invisible != null) populate(invisible.getAnnotations(), cf.getName());
+         }
+         if (scanParameterAnnotations)
+         {
+            ParameterAnnotationsAttribute paramsVisible = (ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.visibleTag);
+            ParameterAnnotationsAttribute paramsInvisible = (ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.invisibleTag);
+
+            if (paramsVisible != null && paramsVisible.getAnnotations() != null)
+            {
+               for (Annotation[] anns : paramsVisible.getAnnotations())
+               {
+                  populate(anns, cf.getName());
+               }
+            }
+            if (paramsInvisible != null && paramsInvisible.getAnnotations() != null)
+            {
+               for (Annotation[] anns : paramsInvisible.getAnnotations())
+               {
+                  populate(anns, cf.getName());
+               }
+            }
+         }
+      }
+   }
+
+   protected void scanFields(ClassFile cf)
+   {
+      List fields = cf.getFields();
+      if (fields == null) return;
+      for (Object obj : fields)
+      {
+         FieldInfo field = (FieldInfo) obj;
+         AnnotationsAttribute visible = (AnnotationsAttribute) field.getAttribute(AnnotationsAttribute.visibleTag);
+         AnnotationsAttribute invisible = (AnnotationsAttribute) field.getAttribute(AnnotationsAttribute.invisibleTag);
+
+         if (visible != null) populate(visible.getAnnotations(), cf.getName());
+         if (invisible != null) populate(invisible.getAnnotations(), cf.getName());
+
+      }
+
+   }
+
+   protected void populate(Annotation[] annotations, String className)
+   {
+      if (annotations == null) return;
+      Set<String> classAnnotations = classIndex.get(className);
+      for (Annotation ann : annotations)
+      {
+         Set<String> classes = annotationIndex.get(ann.getTypeName());
+         if (classes == null)
+         {
+            classes = new HashSet<String>();
+            annotationIndex.put(ann.getTypeName(), classes);
+         }
+         classes.add(className);
+         classAnnotations.add(ann.getTypeName());
+      }
+   }
+
+   public void outputAnnotationDB(PrintWriter writer)
+   {
+      for (String ann : annotationIndex.keySet())
+      {
+         writer.print(ann);
+         writer.print(": ");
+         Set<String> classes = annotationIndex.get(ann);
+         Iterator<String> it = classes.iterator();
+         while (it.hasNext())
+         {
+            writer.print(it.next());
+            if (it.hasNext()) writer.print(", ");
+         }
+         writer.println();
+      }
+   }
+
+}
diff --git a/src/main/java/org/scannotation/classpath/ClasspathUrlFinder.java b/src/main/java/org/scannotation/classpath/ClasspathUrlFinder.java
new file mode 100644 (file)
index 0000000..f56be22
--- /dev/null
@@ -0,0 +1,190 @@
+package org.scannotation.classpath;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * Various functions to locate URLs to scan
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ClasspathUrlFinder
+{
+
+   /**
+    * Find the classpath URLs for a specific classpath resource.  The classpath URL is extracted
+    * from loader.getResources() using the baseResource.
+    *
+    * @param baseResource
+    * @return
+    */
+   public static URL[] findResourceBases(String baseResource, ClassLoader loader)
+   {
+      ArrayList<URL> list = new ArrayList<URL>();
+      try
+      {
+         Enumeration<URL> urls = loader.getResources(baseResource);
+         while (urls.hasMoreElements())
+         {
+            URL url = urls.nextElement();
+            list.add(findResourceBase(url, baseResource));
+         }
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException(e);
+      }
+      return list.toArray(new URL[list.size()]);
+   }
+
+   /**
+    * Find the classpath URLs for a specific classpath resource.  The classpath URL is extracted
+    * from loader.getResources() using the baseResource.
+    *
+    * @param baseResource
+    * @return
+    */
+   public static URL[] findResourceBases(String baseResource)
+   {
+      return findResourceBases(baseResource, Thread.currentThread().getContextClassLoader());
+   }
+
+   private static URL findResourceBase(URL url, String baseResource)
+   {
+      String urlString = url.toString();
+      int idx = urlString.lastIndexOf(baseResource);
+      urlString = urlString.substring(0, idx);
+      URL deployUrl = null;
+      try
+      {
+         deployUrl = new URL(urlString);
+      }
+      catch (MalformedURLException e)
+      {
+         throw new RuntimeException(e);
+      }
+      return deployUrl;
+   }
+
+   /**
+    * Find the classpath URL for a specific classpath resource.  The classpath URL is extracted
+    * from Thread.currentThread().getContextClassLoader().getResource() using the baseResource.
+    *
+    * @param baseResource
+    * @return
+    */
+   public static URL findResourceBase(String baseResource)
+   {
+      return findResourceBase(baseResource, Thread.currentThread().getContextClassLoader());
+   }
+
+   /**
+    * Find the classpath URL for a specific classpath resource.  The classpath URL is extracted
+    * from loader.getResource() using the baseResource.
+    *
+    * @param baseResource
+    * @param loader
+    * @return
+    */
+   public static URL findResourceBase(String baseResource, ClassLoader loader)
+   {
+      URL url = loader.getResource(baseResource);
+      return findResourceBase(url, baseResource);
+   }
+
+   /**
+    * Find the classpath for the particular class
+    *
+    * @param clazz
+    * @return
+    */
+   public static URL findClassBase(Class clazz)
+   {
+      String resource = clazz.getName().replace('.', '/') + ".class";
+      return findResourceBase(resource, clazz.getClassLoader());
+   }
+
+   /**
+    * Uses the java.class.path system property to obtain a list of URLs that represent the CLASSPATH
+    *
+    * @return
+    */
+   public static URL[] findClassPaths()
+   {
+      List<URL> list = new ArrayList<URL>();
+      String classpath = System.getProperty("java.class.path");
+      StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
+
+      while (tokenizer.hasMoreTokens())
+      {
+         String path = tokenizer.nextToken();
+         File fp = new File(path);
+         if (!fp.exists()) throw new RuntimeException("File in java.class.path does not exist: " + fp);
+         try
+         {
+            list.add(fp.toURL());
+         }
+         catch (MalformedURLException e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      return list.toArray(new URL[list.size()]);
+   }
+
+   /**
+    * Uses the java.class.path system property to obtain a list of URLs that represent the CLASSPATH
+    * <p/>
+    * paths is used as a filter to only include paths that have the specific relative file within it
+    *
+    * @param paths comma list of files that should exist in a particular path
+    * @return
+    */
+   public static URL[] findClassPaths(String... paths)
+   {
+      ArrayList<URL> list = new ArrayList<URL>();
+
+      String classpath = System.getProperty("java.class.path");
+      StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
+      for (int i = 0; i < paths.length; i++)
+      {
+         paths[i] = paths[i].trim();
+      }
+
+      while (tokenizer.hasMoreTokens())
+      {
+         String path = tokenizer.nextToken().trim();
+         boolean found = false;
+         for (String wantedPath : paths)
+         {
+            if (path.endsWith(File.separator + wantedPath))
+            {
+               found = true;
+               break;
+            }
+         }
+         if (!found) continue;
+         File fp = new File(path);
+         if (!fp.exists()) throw new RuntimeException("File in java.class.path does not exists: " + fp);
+         try
+         {
+            list.add(fp.toURL());
+         }
+         catch (MalformedURLException e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      return list.toArray(new URL[list.size()]);
+   }
+
+
+}
+
diff --git a/src/main/java/org/scannotation/classpath/DirectoryIteratorFactory.java b/src/main/java/org/scannotation/classpath/DirectoryIteratorFactory.java
new file mode 100644 (file)
index 0000000..7239d1b
--- /dev/null
@@ -0,0 +1,13 @@
+package org.scannotation.classpath;
+
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface DirectoryIteratorFactory
+{
+   StreamIterator create(URL url, Filter filter) throws IOException;
+}
diff --git a/src/main/java/org/scannotation/classpath/FileIterator.java b/src/main/java/org/scannotation/classpath/FileIterator.java
new file mode 100644 (file)
index 0000000..b69709c
--- /dev/null
@@ -0,0 +1,68 @@
+package org.scannotation.classpath;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class FileIterator implements StreamIterator
+{
+   private ArrayList files;
+   private int index = 0;
+
+   public FileIterator(File file, Filter filter)
+   {
+      files = new ArrayList();
+      try
+      {
+         create(files, file, filter);
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException(e);
+      }
+   }
+
+   protected static void create(List list, File dir, Filter filter) throws Exception
+   {
+      File[] files = dir.listFiles();
+      for (int i = 0; i < files.length; i++)
+      {
+         if (files[i].isDirectory())
+         {
+            create(list, files[i], filter);
+         }
+         else
+         {
+            if (filter == null || filter.accepts(files[i].getAbsolutePath()))
+            {
+               list.add(files[i]);
+            }
+         }
+      }
+   }
+
+   public InputStream next()
+   {
+      if (index >= files.size()) return null;
+      File fp = (File) files.get(index++);
+      try
+      {
+         return new FileInputStream(fp);
+      }
+      catch (FileNotFoundException e)
+      {
+         throw new RuntimeException(e);
+      }
+   }
+
+   public void close()
+   {
+   }
+}
diff --git a/src/main/java/org/scannotation/classpath/FileProtocolIteratorFactory.java b/src/main/java/org/scannotation/classpath/FileProtocolIteratorFactory.java
new file mode 100644 (file)
index 0000000..5d3b44c
--- /dev/null
@@ -0,0 +1,26 @@
+package org.scannotation.classpath;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class FileProtocolIteratorFactory implements DirectoryIteratorFactory
+{
+
+   public StreamIterator create(URL url, Filter filter) throws IOException
+   {
+      File f = new File(url.getPath());
+      if (f.isDirectory())
+      {
+         return new FileIterator(f, filter);
+      }
+      else
+      {
+         return new JarIterator(url.openStream(), filter);
+      }
+   }
+}
diff --git a/src/main/java/org/scannotation/classpath/Filter.java b/src/main/java/org/scannotation/classpath/Filter.java
new file mode 100644 (file)
index 0000000..208b313
--- /dev/null
@@ -0,0 +1,10 @@
+package org.scannotation.classpath;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface Filter
+{
+   boolean accepts(String filename);
+}
diff --git a/src/main/java/org/scannotation/classpath/InputStreamWrapper.java b/src/main/java/org/scannotation/classpath/InputStreamWrapper.java
new file mode 100644 (file)
index 0000000..c19d0e7
--- /dev/null
@@ -0,0 +1,72 @@
+package org.scannotation.classpath;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Delegate to everything but close().  This object will not close the stream
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class InputStreamWrapper extends InputStream
+{
+   private InputStream delegate;
+
+   public InputStreamWrapper(InputStream delegate)
+   {
+      this.delegate = delegate;
+   }
+
+   public int read()
+           throws IOException
+   {
+      return delegate.read();
+   }
+
+   public int read(byte[] bytes)
+           throws IOException
+   {
+      return delegate.read(bytes);
+   }
+
+   public int read(byte[] bytes, int i, int i1)
+           throws IOException
+   {
+      return delegate.read(bytes, i, i1);
+   }
+
+   public long skip(long l)
+           throws IOException
+   {
+      return delegate.skip(l);
+   }
+
+   public int available()
+           throws IOException
+   {
+      return delegate.available();
+   }
+
+   public void close()
+           throws IOException
+   {
+      // ignored
+   }
+
+   public void mark(int i)
+   {
+      delegate.mark(i);
+   }
+
+   public void reset()
+           throws IOException
+   {
+      delegate.reset();
+   }
+
+   public boolean markSupported()
+   {
+      return delegate.markSupported();
+   }
+}
diff --git a/src/main/java/org/scannotation/classpath/IteratorFactory.java b/src/main/java/org/scannotation/classpath/IteratorFactory.java
new file mode 100644 (file)
index 0000000..adbc091
--- /dev/null
@@ -0,0 +1,43 @@
+package org.scannotation.classpath;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class IteratorFactory
+{
+   private static final ConcurrentHashMap<String, DirectoryIteratorFactory> registry = new ConcurrentHashMap<String, DirectoryIteratorFactory>();
+
+   static
+   {
+      registry.put("file", new FileProtocolIteratorFactory());
+   }
+
+
+   public static StreamIterator create(URL url, Filter filter) throws IOException
+   {
+      String urlString = url.toString();
+      if (urlString.endsWith("!/"))
+      {
+         urlString = urlString.substring(4);
+         urlString = urlString.substring(0, urlString.length() - 2);
+         url = new URL(urlString);
+      }
+
+
+      if (!urlString.endsWith("/"))
+      {
+         return new JarIterator(url.openStream(), filter);
+      }
+      else
+      {
+         DirectoryIteratorFactory factory = registry.get(url.getProtocol());
+         if (factory == null) throw new IOException("Unable to scan directory of protocol: " + url.getProtocol());
+         return factory.create(url, filter);
+      }
+   }
+}
diff --git a/src/main/java/org/scannotation/classpath/JarIterator.java b/src/main/java/org/scannotation/classpath/JarIterator.java
new file mode 100644 (file)
index 0000000..dd4cf35
--- /dev/null
@@ -0,0 +1,77 @@
+package org.scannotation.classpath;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JarIterator implements StreamIterator
+{
+   JarInputStream jar;
+   JarEntry next;
+   Filter filter;
+   boolean initial = true;
+   boolean closed = false;
+
+   public JarIterator(File file, Filter filter) throws IOException
+   {
+      this(new FileInputStream(file), filter);
+   }
+
+
+   public JarIterator(InputStream is, Filter filter) throws IOException
+   {
+      this.filter = filter;
+      jar = new JarInputStream(is);
+   }
+
+   private void setNext()
+   {
+      initial = true;
+      try
+      {
+         if (next != null) jar.closeEntry();
+         next = null;
+         do
+         {
+            next = jar.getNextJarEntry();
+         } while (next != null && (next.isDirectory() || (filter == null || !filter.accepts(next.getName()))));
+         if (next == null)
+         {
+            close();
+         }
+      }
+      catch (IOException e)
+      {
+         throw new RuntimeException("failed to browse jar", e);
+      }
+   }
+
+   public InputStream next()
+   {
+      if (closed || (next == null && !initial)) return null;
+      setNext();
+      if (next == null) return null;
+      return new InputStreamWrapper(jar);
+   }
+
+   public void close()
+   {
+      try
+      {
+         closed = true;
+         jar.close();
+      }
+      catch (IOException ignored)
+      {
+
+      }
+
+   }
+}
diff --git a/src/main/java/org/scannotation/classpath/StreamIterator.java b/src/main/java/org/scannotation/classpath/StreamIterator.java
new file mode 100644 (file)
index 0000000..410df8f
--- /dev/null
@@ -0,0 +1,25 @@
+package org.scannotation.classpath;
+
+import java.io.InputStream;
+
+/**
+ * Simpler iterator than java.util.iterator.  Things like JarInputStream does not allow you to implement hasNext()
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface StreamIterator
+{
+   /**
+    * User is resposible for closing the InputStream returned
+    *
+    * @return null if no more streams left to iterate on
+    */
+   InputStream next();
+
+   /**
+    * Cleanup any open resources of the iterator
+    *
+    */
+   void close();
+}
diff --git a/src/main/java/org/scannotation/classpath/WarUrlFinder.java b/src/main/java/org/scannotation/classpath/WarUrlFinder.java
new file mode 100644 (file)
index 0000000..73d3372
--- /dev/null
@@ -0,0 +1,74 @@
+package org.scannotation.classpath;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class WarUrlFinder
+{
+   public static URL[] findWebInfLibClasspaths(ServletContextEvent servletContextEvent)
+   {
+      ServletContext servletContext = servletContextEvent.getServletContext();
+      return findWebInfLibClasspaths(servletContext);
+   }
+
+   public static URL[] findWebInfLibClasspaths(ServletContext servletContext)
+   {
+      ArrayList<URL> list = new ArrayList<URL>();
+      Set libJars = servletContext.getResourcePaths("/WEB-INF/lib");
+      for (Object jar : libJars)
+      {
+         try
+         {
+            list.add(servletContext.getResource((String) jar));
+         }
+         catch (MalformedURLException e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      return list.toArray(new URL[list.size()]);
+   }
+
+   public static URL findWebInfClassesPath(ServletContextEvent servletContextEvent)
+   {
+      ServletContext servletContext = servletContextEvent.getServletContext();
+      return findWebInfClassesPath(servletContext);
+   }
+
+   /**
+    * Find the URL pointing to "/WEB-INF/classes"  This method may not work in conjunction with IteratorFactory
+    * if your servlet container does not extract the /WEB-INF/classes into a real file-based directory
+    *
+    * @param servletContext
+    * @return
+    */
+   public static URL findWebInfClassesPath(ServletContext servletContext)
+   {
+      Set libJars = servletContext.getResourcePaths("/WEB-INF/classes");
+      for (Object jar : libJars)
+      {
+         try
+         {
+            URL url = servletContext.getResource((String) jar);
+            String urlString = url.toString();
+            int index = urlString.lastIndexOf("/WEB-INF/classes/");
+            urlString = urlString.substring(0, index + "/WEB-INF/classes/".length());
+            return new URL(urlString);
+         }
+         catch (MalformedURLException e)
+         {
+            throw new RuntimeException(e);
+         }
+      }
+      return null;
+
+   }
+}
diff --git a/src/test/java/org/scannotation/test/ClassWithFieldAnnotation.java b/src/test/java/org/scannotation/test/ClassWithFieldAnnotation.java
new file mode 100644 (file)
index 0000000..ba97384
--- /dev/null
@@ -0,0 +1,10 @@
+package org.scannotation.test;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ClassWithFieldAnnotation
+{
+   private @SimpleAnnotation int field;
+}
diff --git a/src/test/java/org/scannotation/test/CrossRef.java b/src/test/java/org/scannotation/test/CrossRef.java
new file mode 100644 (file)
index 0000000..07e6a02
--- /dev/null
@@ -0,0 +1,12 @@
+package org.scannotation.test;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CrossRef implements InterfaceWithParameterAnnotations
+{
+   public void junk(int param)
+   {
+   }
+}
diff --git a/src/test/java/org/scannotation/test/InterfaceWithParameterAnnotations.java b/src/test/java/org/scannotation/test/InterfaceWithParameterAnnotations.java
new file mode 100644 (file)
index 0000000..ae77276
--- /dev/null
@@ -0,0 +1,10 @@
+package org.scannotation.test;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface InterfaceWithParameterAnnotations
+{
+   public void junk(@SimpleAnnotation int param);
+}
diff --git a/src/test/java/org/scannotation/test/SimpleAnnotation.java b/src/test/java/org/scannotation/test/SimpleAnnotation.java
new file mode 100644 (file)
index 0000000..6b2af2d
--- /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 $
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SimpleAnnotation
+{
+}
diff --git a/src/test/java/org/scannotation/test/TestSmoke.java b/src/test/java/org/scannotation/test/TestSmoke.java
new file mode 100644 (file)
index 0000000..8f914aa
--- /dev/null
@@ -0,0 +1,227 @@
+package org.scannotation.test;
+
+import com.titan.domain.Address;
+import org.junit.Assert;
+import org.junit.Test;
+import org.scannotation.AnnotationDB;
+import org.scannotation.classpath.ClasspathUrlFinder;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class TestSmoke
+{
+
+   @Test
+   public void testFindResourceBase() throws Exception
+   {
+      URL url = ClasspathUrlFinder.findResourceBase("com/titan/domain/Address.class");
+      Assert.assertNotNull(url);
+      verify(url);
+   }
+
+   @Test
+   public void testFindResourceBases() throws Exception
+   {
+      URL[] urls = ClasspathUrlFinder.findResourceBases("com/titan/domain/Address.class");
+      Assert.assertNotNull(urls);
+      verify(urls);
+   }
+
+   @Test
+   public void testFindClasspaths() throws Exception
+   {
+      Assert.assertNotNull(System.getProperty("java.class.path"));
+      if (System.getProperty("java.class.path").indexOf("titan-cruise-1.0.jar") == -1)
+      {
+         System.err.println("WARNING!!!!!!!! CANNOT TEST testFindClasspaths():  This is a Maven2 and Surefire problem in that it doesn't set java.class.path correctly.  I run this test within the IDE");
+      }
+
+      URL[] urls = ClasspathUrlFinder.findClassPaths("titan-cruise-1.0.jar");
+      Assert.assertNotNull(urls);
+      verify(urls);
+   }
+
+
+   @Test
+   public void testFindClasspaths2() throws Exception
+   {
+      Assert.assertNotNull(System.getProperty("java.class.path"));
+      if (System.getProperty("java.class.path").indexOf("titan-cruise-1.0.jar") == -1)
+      {
+         System.err.println("WARNING!!!!!!! CANNOT TEST testFindClasspaths2():  This is a Maven2 and Surefire problem in that it doesn't set java.class.path correctly.  I run this test within the IDE");
+      }
+
+      URL[] urls = ClasspathUrlFinder.findClassPaths();
+      Assert.assertNotNull(urls);
+      AnnotationDB db = verify(urls);
+
+      Map<String, Set<String>> annotationIndex = db.getAnnotationIndex();
+      Set<String> tests = annotationIndex.get("org.junit.Test");
+      Assert.assertTrue(tests.contains(TestSmoke.class.getName()));
+
+   }
+
+   @Test
+   public void testFieldParameter() throws Exception
+   {
+      URL url = ClasspathUrlFinder.findClassBase(TestSmoke.class);
+      AnnotationDB db = new AnnotationDB();
+      db.scanArchives(url);
+
+      Map<String, Set<String>> annotationIndex = db.getAnnotationIndex();
+      Set<String> simpleClasses = annotationIndex.get(SimpleAnnotation.class.getName());
+      Assert.assertTrue(simpleClasses.contains(ClassWithFieldAnnotation.class.getName()));
+      Assert.assertTrue(simpleClasses.contains(InterfaceWithParameterAnnotations.class.getName()));
+
+      Set<String> simpleAnnotations = db.getClassIndex().get(ClassWithFieldAnnotation.class.getName());
+      Assert.assertTrue(simpleAnnotations.contains(SimpleAnnotation.class.getName()));
+      simpleAnnotations = db.getClassIndex().get(InterfaceWithParameterAnnotations.class.getName());
+      Assert.assertTrue(simpleAnnotations.contains(SimpleAnnotation.class.getName()));
+
+
+
+   }
+
+   @Test
+   public void testCrossRef() throws Exception
+   {
+      URL url = ClasspathUrlFinder.findClassBase(TestSmoke.class);
+      AnnotationDB db = new AnnotationDB();
+      db.scanArchives(url);
+      db.crossReferenceImplementedInterfaces();
+
+      Map<String, Set<String>> annotationIndex = db.getAnnotationIndex();
+      Set<String> simpleClasses = annotationIndex.get(SimpleAnnotation.class.getName());
+      Assert.assertTrue(simpleClasses.contains(CrossRef.class.getName()));
+
+      Set<String> simpleAnnotations = db.getClassIndex().get(CrossRef.class.getName());
+      Assert.assertTrue(simpleAnnotations.contains(SimpleAnnotation.class.getName()));
+   }
+
+
+   @Test
+   public void testByClass() throws Exception
+   {
+      URL url = ClasspathUrlFinder.findClassBase(Address.class);
+      Assert.assertNotNull(url);
+      verify(url);
+   }
+
+
+   private AnnotationDB verify(URL... urls)
+           throws IOException
+   {
+      AnnotationDB db = new AnnotationDB();
+      db.scanArchives(urls);
+
+      Map<String, Set<String>> annotationIndex = db.getAnnotationIndex();
+      {
+         Set<String> entities = annotationIndex.get("javax.persistence.Entity");
+         Assert.assertNotNull(entities);
+
+         Assert.assertTrue(entities.contains("com.titan.domain.Address"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Cabin"));
+         Assert.assertTrue(entities.contains("com.titan.domain.CreditCard"));
+         Assert.assertTrue(entities.contains("com.titan.domain.CreditCompany"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Cruise"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Customer"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Phone"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Reservation"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Ship"));
+      }
+
+      {
+         Set<String> entities = annotationIndex.get("javax.persistence.GeneratedValue");
+         Assert.assertNotNull(entities);
+
+         Assert.assertTrue(entities.contains("com.titan.domain.Address"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Cabin"));
+         Assert.assertTrue(entities.contains("com.titan.domain.CreditCard"));
+         Assert.assertTrue(entities.contains("com.titan.domain.CreditCompany"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Cruise"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Customer"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Phone"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Reservation"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Ship"));
+      }
+
+      {
+         Set<String> entities = annotationIndex.get("javax.persistence.Id");
+         Assert.assertNotNull(entities);
+
+         Assert.assertTrue(entities.contains("com.titan.domain.Address"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Cabin"));
+         Assert.assertTrue(entities.contains("com.titan.domain.CreditCard"));
+         Assert.assertTrue(entities.contains("com.titan.domain.CreditCompany"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Cruise"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Customer"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Phone"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Reservation"));
+         Assert.assertTrue(entities.contains("com.titan.domain.Ship"));
+      }
+
+      Map<String, Set<String>> classIndex = db.getClassIndex();
+      Set<String> annotations = classIndex.get("com.titan.domain.Address");
+      Assert.assertNotNull(annotations);
+      Assert.assertTrue(annotations.contains("javax.persistence.Entity"));
+      Assert.assertTrue(annotations.contains("javax.persistence.Id"));
+      Assert.assertTrue(annotations.contains("javax.persistence.GeneratedValue"));
+
+      annotations = classIndex.get("com.titan.domain.Cabin");
+      Assert.assertNotNull(annotations);
+      Assert.assertTrue(annotations.contains("javax.persistence.Entity"));
+      Assert.assertTrue(annotations.contains("javax.persistence.Id"));
+      Assert.assertTrue(annotations.contains("javax.persistence.GeneratedValue"));
+
+      annotations = classIndex.get("com.titan.domain.CreditCard");
+      Assert.assertNotNull(annotations);
+      Assert.assertTrue(annotations.contains("javax.persistence.Entity"));
+      Assert.assertTrue(annotations.contains("javax.persistence.Id"));
+      Assert.assertTrue(annotations.contains("javax.persistence.GeneratedValue"));
+
+      annotations = classIndex.get("com.titan.domain.CreditCompany");
+      Assert.assertNotNull(annotations);
+      Assert.assertTrue(annotations.contains("javax.persistence.Entity"));
+      Assert.assertTrue(annotations.contains("javax.persistence.Id"));
+      Assert.assertTrue(annotations.contains("javax.persistence.GeneratedValue"));
+
+      annotations = classIndex.get("com.titan.domain.Cruise");
+      Assert.assertNotNull(annotations);
+      Assert.assertTrue(annotations.contains("javax.persistence.Entity"));
+      Assert.assertTrue(annotations.contains("javax.persistence.Id"));
+      Assert.assertTrue(annotations.contains("javax.persistence.GeneratedValue"));
+
+      annotations = classIndex.get("com.titan.domain.Customer");
+      Assert.assertNotNull(annotations);
+      Assert.assertTrue(annotations.contains("javax.persistence.Entity"));
+      Assert.assertTrue(annotations.contains("javax.persistence.Id"));
+      Assert.assertTrue(annotations.contains("javax.persistence.GeneratedValue"));
+
+      annotations = classIndex.get("com.titan.domain.Phone");
+      Assert.assertNotNull(annotations);
+      Assert.assertTrue(annotations.contains("javax.persistence.Entity"));
+      Assert.assertTrue(annotations.contains("javax.persistence.Id"));
+      Assert.assertTrue(annotations.contains("javax.persistence.GeneratedValue"));
+
+      annotations = classIndex.get("com.titan.domain.Reservation");
+      Assert.assertNotNull(annotations);
+      Assert.assertTrue(annotations.contains("javax.persistence.Entity"));
+      Assert.assertTrue(annotations.contains("javax.persistence.Id"));
+      Assert.assertTrue(annotations.contains("javax.persistence.GeneratedValue"));
+
+      annotations = classIndex.get("com.titan.domain.Ship");
+      Assert.assertNotNull(annotations);
+      Assert.assertTrue(annotations.contains("javax.persistence.Entity"));
+      Assert.assertTrue(annotations.contains("javax.persistence.Id"));
+      Assert.assertTrue(annotations.contains("javax.persistence.GeneratedValue"));
+            
+      return db;
+   }
+}