Added intellij support. Compiled as java6
This commit is contained in:
parent
6ffa6711bb
commit
d3a931ab36
@ -7,13 +7,13 @@
|
||||
<src_folder value="file://$MODULE_DIR$/src" expected_position="0" />
|
||||
</src_description>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="jdk" jdkName="1.6" jdkType="JavaSDK" />
|
||||
<orderEntry type="module" module-name="Dorkbox-Util" />
|
||||
<orderEntry type="library" name="logging slf4j-api (1.7.5)" level="application" />
|
||||
<orderEntry type="library" name="bouncyCastle bcpkix-jdk15on (1.51)" level="application" />
|
||||
|
@ -24,52 +24,61 @@ import java.lang.reflect.Method;
|
||||
/**
|
||||
* for specifying the default report methods, without constantly creating new objects
|
||||
*/
|
||||
public class AnnotationDefaults {
|
||||
public
|
||||
class AnnotationDefaults {
|
||||
public static final ReporterFunction<String> getTypeName = new ReporterFunction<String>() {
|
||||
@Override
|
||||
public String report(Cursor cursor) {
|
||||
public
|
||||
String report(Cursor cursor) {
|
||||
return cursor.getTypeName();
|
||||
}
|
||||
};
|
||||
public static final ReporterFunction<Class<? extends Annotation>> getAnnotationType = new ReporterFunction<Class<? extends Annotation>>() {
|
||||
@Override
|
||||
public Class<? extends Annotation> report(Cursor cursor) {
|
||||
public
|
||||
Class<? extends Annotation> report(Cursor cursor) {
|
||||
return cursor.getAnnotationType();
|
||||
}
|
||||
};
|
||||
public static final ReporterFunction<ElementType> getElementType = new ReporterFunction<ElementType>() {
|
||||
@Override
|
||||
public ElementType report(Cursor cursor) {
|
||||
public
|
||||
ElementType report(Cursor cursor) {
|
||||
return cursor.getElementType();
|
||||
}
|
||||
};
|
||||
public static final ReporterFunction<String> getMemberName = new ReporterFunction<String>() {
|
||||
@Override
|
||||
public String report(Cursor cursor) {
|
||||
public
|
||||
String report(Cursor cursor) {
|
||||
return cursor.getMemberName();
|
||||
}
|
||||
};
|
||||
public static final ReporterFunction<Class<?>> getType = new ReporterFunction<Class<?>>() {
|
||||
@Override
|
||||
public Class<?> report(Cursor cursor) {
|
||||
public
|
||||
Class<?> report(Cursor cursor) {
|
||||
return cursor.getType();
|
||||
}
|
||||
};
|
||||
public static final ReporterFunction<Constructor<?>> getConstructor = new ReporterFunction<Constructor<?>>() {
|
||||
@Override
|
||||
public Constructor<?> report(Cursor cursor) {
|
||||
public
|
||||
Constructor<?> report(Cursor cursor) {
|
||||
return cursor.getConstructor();
|
||||
}
|
||||
};
|
||||
public static final ReporterFunction<Field> getField = new ReporterFunction<Field>() {
|
||||
@Override
|
||||
public Field report(Cursor cursor) {
|
||||
public
|
||||
Field report(Cursor cursor) {
|
||||
return cursor.getField();
|
||||
}
|
||||
};
|
||||
public static final ReporterFunction<Method> getMethod = new ReporterFunction<Method>() {
|
||||
@Override
|
||||
public Method report(Cursor cursor) {
|
||||
public
|
||||
Method report(Cursor cursor) {
|
||||
return cursor.getMethod();
|
||||
}
|
||||
};
|
||||
|
@ -21,11 +21,9 @@
|
||||
*/
|
||||
package dorkbox.util.annotation;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
@ -35,29 +33,18 @@ import java.lang.reflect.Method;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* {@code AnnotationDetector} reads Java Class Files ("*.class") and reports the
|
||||
* found annotations via a simple, developer friendly API.
|
||||
* <p>
|
||||
* <p/>
|
||||
* A Java Class File consists of a stream of 8-bit bytes. All 16-bit, 32-bit, and 64-bit
|
||||
* quantities are constructed by reading in two, four, and eight consecutive 8-bit
|
||||
* bytes, respectively. Multi byte data items are always stored in big-endian order,
|
||||
* where the high bytes come first. In the Java platforms, this format is
|
||||
* supported by interfaces {@link java.io.DataInput} and {@link java.io.DataOutput}.
|
||||
* <p>
|
||||
* <p/>
|
||||
* A class file consists of a single ClassFile structure:
|
||||
* <pre>
|
||||
* ClassFile {
|
||||
@ -97,7 +84,7 @@ import org.slf4j.LoggerFactory;
|
||||
* <li><a href="http://stackoverflow.com/questions/259140">scanning java annotations at
|
||||
* runtime</a>.
|
||||
* </ul>
|
||||
* <p>
|
||||
* <p/>
|
||||
* Similar projects / libraries:
|
||||
* <ul>
|
||||
* <li><a href="http://community.jboss.org/wiki/MCScanninglib">JBoss MC Scanning lib</a>;
|
||||
@ -108,14 +95,15 @@ import org.slf4j.LoggerFactory;
|
||||
* Available from maven: {@code tv.cntt:annovention:1.2};
|
||||
* <li>If using the Spring Framework, use {@code ClassPathScanningCandidateComponentProvider}
|
||||
* </ul>
|
||||
* <p>
|
||||
* <p/>
|
||||
* All above mentioned projects make use of a byte code manipulation library (like BCEL,
|
||||
* ASM or Javassist).
|
||||
*
|
||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
||||
* @since annotation-detector 3.0.0
|
||||
*/
|
||||
public final class AnnotationDetector implements Builder, Cursor {
|
||||
public final
|
||||
class AnnotationDetector implements Builder, Cursor {
|
||||
|
||||
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AnnotationDetector.class);
|
||||
|
||||
@ -180,18 +168,20 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
// "(Ljava/lang/String;II)I" String, int, int as arguments, return type int
|
||||
private String methodDescriptor;
|
||||
|
||||
private AnnotationDetector(ClassLoader loader, final File[] filesOrDirectories, ClassIterator iterator, final String[] pkgNameFilter) {
|
||||
private
|
||||
AnnotationDetector(ClassLoader loader, final File[] filesOrDirectories, ClassIterator iterator, final String[] pkgNameFilter) {
|
||||
this.loader = loader;
|
||||
if (iterator == null) {
|
||||
this.cfIterator = new ClassFileIterator(filesOrDirectories, pkgNameFilter);
|
||||
|
||||
if (filesOrDirectories.length == 0) {
|
||||
LOG.warn("No files or directories to scan!");
|
||||
} else if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Files and root directories scanned:\n{}",
|
||||
Arrays.toString(filesOrDirectories).replace(", ", "\n"));
|
||||
}
|
||||
} else {
|
||||
else if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Files and root directories scanned:\n{}", Arrays.toString(filesOrDirectories).replace(", ", "\n"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.cfIterator = iterator;
|
||||
|
||||
if (LOG.isTraceEnabled()) {
|
||||
@ -206,8 +196,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* Only scan Class Files in the specified packages. If nothing is specified, all classes
|
||||
* on the class path are scanned.
|
||||
*/
|
||||
public static Builder scanClassPath(final String... packageNames)
|
||||
throws IOException {
|
||||
public static
|
||||
Builder scanClassPath(final String... packageNames) throws IOException {
|
||||
|
||||
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
return scanClassPath(loader, packageNames);
|
||||
@ -218,8 +208,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* Only scan Class Files in the specified packages. If nothing is specified, all classes
|
||||
* on the class path are scanned.
|
||||
*/
|
||||
public static Builder scanClassPath(ClassLoader loader, final String... packageNames)
|
||||
throws IOException {
|
||||
public static
|
||||
Builder scanClassPath(ClassLoader loader, final String... packageNames) throws IOException {
|
||||
|
||||
final String[] pkgNameFilter;
|
||||
|
||||
@ -259,7 +249,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
}
|
||||
|
||||
return new AnnotationDetector(loader, null, new CustomClassloaderIterator(fileNames, packageNames), pkgNameFilter);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
final Set<File> files = new HashSet<File>();
|
||||
|
||||
if (packageNames.length == 0) {
|
||||
@ -268,7 +259,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
for (int i = 0; i < fileNames.length; ++i) {
|
||||
files.add(new File(fileNames[i]));
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
pkgNameFilter = new String[packageNames.length];
|
||||
for (int i = 0; i < pkgNameFilter.length; ++i) {
|
||||
pkgNameFilter[i] = packageNames[i].replace('.', '/');
|
||||
@ -288,7 +280,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* Factory method, starting point for the fluent interface.
|
||||
* Scan all files specified by the classFileIterator.
|
||||
*/
|
||||
public static Builder scan(ClassLoader loader, final ClassIterator iterator) {
|
||||
public static
|
||||
Builder scan(ClassLoader loader, final ClassIterator iterator) {
|
||||
return new AnnotationDetector(loader, null, iterator, null);
|
||||
}
|
||||
|
||||
@ -296,7 +289,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* Factory method, starting point for the fluent interface.
|
||||
* Scan all files in the specified jar files and directories.
|
||||
*/
|
||||
public static Builder scanFiles(ClassLoader loader, final File... filesOrDirectories) {
|
||||
public static
|
||||
Builder scanFiles(ClassLoader loader, final File... filesOrDirectories) {
|
||||
return new AnnotationDetector(loader, filesOrDirectories, null, null);
|
||||
}
|
||||
|
||||
@ -304,7 +298,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* Factory method, starting point for the fluent interface.
|
||||
* Scan all files in the specified jar files and directories.
|
||||
*/
|
||||
public static Builder scanFiles(final File... filesOrDirectories) {
|
||||
public static
|
||||
Builder scanFiles(final File... filesOrDirectories) {
|
||||
return new AnnotationDetector(Thread.currentThread().getContextClassLoader(), filesOrDirectories, null, null);
|
||||
}
|
||||
|
||||
@ -312,7 +307,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Builder#forAnnotations(java.lang.Class...) }.
|
||||
*/
|
||||
@Override
|
||||
public Builder forAnnotations(final Class<? extends Annotation> annotation) {
|
||||
public
|
||||
Builder forAnnotations(final Class<? extends Annotation> annotation) {
|
||||
this.annotations = new HashMap<String, Class<? extends Annotation>>(1);
|
||||
// map "raw" type names to Class object
|
||||
this.annotations.put("L" + annotation.getName().replace('.', '/') + ";", annotation);
|
||||
@ -324,7 +320,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Builder forAnnotations(final Class<? extends Annotation>... annotations) {
|
||||
public
|
||||
Builder forAnnotations(final Class<? extends Annotation>... annotations) {
|
||||
this.annotations = new HashMap<String, Class<? extends Annotation>>(annotations.length);
|
||||
// map "raw" type names to Class object
|
||||
for (int i = 0; i < annotations.length; ++i) {
|
||||
@ -337,7 +334,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Builder#on(java.lang.annotation.ElementType...) }.
|
||||
*/
|
||||
@Override
|
||||
public Builder on(final ElementType type) {
|
||||
public
|
||||
Builder on(final ElementType type) {
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("At least one Element Type must be specified");
|
||||
}
|
||||
@ -359,7 +357,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Builder#on(java.lang.annotation.ElementType...) }.
|
||||
*/
|
||||
@Override
|
||||
public Builder on(final ElementType... types) {
|
||||
public
|
||||
Builder on(final ElementType... types) {
|
||||
if (types.length == 0) {
|
||||
throw new IllegalArgumentException("At least one Element Type must be specified");
|
||||
}
|
||||
@ -383,7 +382,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Builder#filter(java.io.FilenameFilter) }.
|
||||
*/
|
||||
@Override
|
||||
public Builder filter(final FilenameFilter filter) {
|
||||
public
|
||||
Builder filter(final FilenameFilter filter) {
|
||||
if (filter == null) {
|
||||
throw new NullPointerException("'filter' may not be null");
|
||||
}
|
||||
@ -395,7 +395,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Builder#report(dorkbox.util.annotation.AnnotationDetector.Reporter) }.
|
||||
*/
|
||||
@Override
|
||||
public void report(final Reporter reporter) throws IOException {
|
||||
public
|
||||
void report(final Reporter reporter) throws IOException {
|
||||
this.reporter = reporter;
|
||||
detect(this.cfIterator);
|
||||
}
|
||||
@ -404,12 +405,14 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Builder#collect(dorkbox.util.annotation.AnnotationDetector.ReporterFunction) }.
|
||||
*/
|
||||
@Override
|
||||
public <T> List<T> collect(final ReporterFunction<T> reporter) throws IOException {
|
||||
public
|
||||
<T> List<T> collect(final ReporterFunction<T> reporter) throws IOException {
|
||||
final List<T> list = new ArrayList<T>();
|
||||
this.reporter = new Reporter() {
|
||||
|
||||
@Override
|
||||
public void report(Cursor cursor) {
|
||||
public
|
||||
void report(Cursor cursor) {
|
||||
list.add(reporter.report(cursor));
|
||||
}
|
||||
|
||||
@ -422,7 +425,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Cursor#getTypeName() }.
|
||||
*/
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
public
|
||||
String getTypeName() {
|
||||
return this.typeName.replace('/', '.');
|
||||
}
|
||||
|
||||
@ -430,7 +434,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Cursor#getAnnotationType() }.
|
||||
*/
|
||||
@Override
|
||||
public Class<? extends Annotation> getAnnotationType() {
|
||||
public
|
||||
Class<? extends Annotation> getAnnotationType() {
|
||||
return this.annotationType;
|
||||
}
|
||||
|
||||
@ -438,7 +443,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Cursor#getElementType() }.
|
||||
*/
|
||||
@Override
|
||||
public ElementType getElementType() {
|
||||
public
|
||||
ElementType getElementType() {
|
||||
return this.elementType;
|
||||
}
|
||||
|
||||
@ -446,7 +452,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Cursor#getMemberName() }.
|
||||
*/
|
||||
@Override
|
||||
public String getMemberName() {
|
||||
public
|
||||
String getMemberName() {
|
||||
return this.memberName;
|
||||
}
|
||||
|
||||
@ -454,7 +461,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Cursor#getType() }.
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
public
|
||||
Class<?> getType() {
|
||||
return loadClass(this.loader, getTypeName());
|
||||
}
|
||||
|
||||
@ -462,16 +470,15 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Cursor#getField() }.
|
||||
*/
|
||||
@Override
|
||||
public Field getField() {
|
||||
public
|
||||
Field getField() {
|
||||
if (this.elementType != ElementType.FIELD) {
|
||||
throw new IllegalStateException(
|
||||
"Illegal to call getField() when " + this.elementType + " is reported");
|
||||
throw new IllegalStateException("Illegal to call getField() when " + this.elementType + " is reported");
|
||||
}
|
||||
try {
|
||||
return getType().getDeclaredField(this.memberName);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
throw assertionError(
|
||||
"Cannot find Field '%s' for type %s", this.memberName, getTypeName());
|
||||
throw assertionError("Cannot find Field '%s' for type %s", this.memberName, getTypeName());
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,17 +486,16 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Cursor#getConstructor() }.
|
||||
*/
|
||||
@Override
|
||||
public Constructor<?> getConstructor() {
|
||||
public
|
||||
Constructor<?> getConstructor() {
|
||||
if (this.elementType != ElementType.CONSTRUCTOR) {
|
||||
throw new IllegalStateException(
|
||||
"Illegal to call getMethod() when " + this.elementType + " is reported");
|
||||
throw new IllegalStateException("Illegal to call getMethod() when " + this.elementType + " is reported");
|
||||
}
|
||||
try {
|
||||
final Class<?>[] parameterTypes = parseArguments(this.methodDescriptor);
|
||||
return getType().getConstructor(parameterTypes);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
throw assertionError(
|
||||
"Cannot find Contructor '%s(...)' for type %s", this.memberName, getTypeName());
|
||||
throw assertionError("Cannot find Contructor '%s(...)' for type %s", this.memberName, getTypeName());
|
||||
}
|
||||
}
|
||||
|
||||
@ -497,17 +503,16 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Cursor#getMethod() }.
|
||||
*/
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
public
|
||||
Method getMethod() {
|
||||
if (this.elementType != ElementType.METHOD) {
|
||||
throw new IllegalStateException(
|
||||
"Illegal to call getMethod() when " + this.elementType + " is reported");
|
||||
throw new IllegalStateException("Illegal to call getMethod() when " + this.elementType + " is reported");
|
||||
}
|
||||
try {
|
||||
final Class<?>[] parameterTypes = parseArguments(this.methodDescriptor);
|
||||
return getType().getDeclaredMethod(this.memberName, parameterTypes);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
throw assertionError(
|
||||
"Cannot find Method '%s(...)' for type %s", this.memberName, getTypeName());
|
||||
throw assertionError("Cannot find Method '%s(...)' for type %s", this.memberName, getTypeName());
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,7 +520,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* See {@link Cursor#getAnnotation(java.lang.Class) }.
|
||||
*/
|
||||
@Override
|
||||
public <T extends Annotation> T getAnnotation(final Class<T> annotationClass) {
|
||||
public
|
||||
<T extends Annotation> T getAnnotation(final Class<T> annotationClass) {
|
||||
if (!annotationClass.equals(this.annotationType)) {
|
||||
throw new IllegalStateException("Illegal to call getAnnotation() when " +
|
||||
this.annotationType.getName() + " is reported");
|
||||
@ -539,8 +545,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
|
||||
// private
|
||||
|
||||
private static void addFiles(ClassLoader loader, String resourceName, Set<File> files)
|
||||
throws IOException {
|
||||
private static
|
||||
void addFiles(ClassLoader loader, String resourceName, Set<File> files) throws IOException {
|
||||
|
||||
final Enumeration<URL> resourceEnum = loader.getResources(resourceName);
|
||||
while (resourceEnum.hasMoreElements()) {
|
||||
@ -557,7 +563,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
final File dir = toFile(url);
|
||||
if (dir.isDirectory()) {
|
||||
files.add(dir);
|
||||
} else if (isVfs) {
|
||||
}
|
||||
else if (isVfs) {
|
||||
//Jar file via JBoss VFS protocol - strip package name
|
||||
String jarPath = dir.getPath();
|
||||
final int idx = jarPath.indexOf(".jar");
|
||||
@ -568,23 +575,26 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
files.add(jarFile);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
throw assertionError("Not a recognized file URL: %s", url);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Resource in Jar File
|
||||
final File jarFile =
|
||||
toFile(((JarURLConnection)url.openConnection()).getJarFileURL());
|
||||
final File jarFile = toFile(((JarURLConnection) url.openConnection()).getJarFileURL());
|
||||
if (jarFile.isFile()) {
|
||||
files.add(jarFile);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
throw assertionError("Not a File: %s", jarFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static File toFile(final URL url) throws IOException {
|
||||
private static
|
||||
File toFile(final URL url) throws IOException {
|
||||
// only correct way to convert the URL to a File object, also see issue #16
|
||||
// Do not use URLDecoder
|
||||
try {
|
||||
@ -594,7 +604,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
}
|
||||
}
|
||||
|
||||
private void detect(final ClassIterator iterator) throws IOException {
|
||||
private
|
||||
void detect(final ClassIterator iterator) throws IOException {
|
||||
InputStream stream;
|
||||
boolean mustEndInClass = iterator instanceof ClassFileIterator;
|
||||
while ((stream = iterator.next(this.filter)) != null) {
|
||||
@ -620,14 +631,16 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasCafebabe(final ClassFileBuffer buffer) throws IOException {
|
||||
private
|
||||
boolean hasCafebabe(final ClassFileBuffer buffer) throws IOException {
|
||||
return buffer.size() > 4 && buffer.readInt() == 0xCAFEBABE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspect the given (Java) class file in streaming mode.
|
||||
*/
|
||||
private void read(final DataInput di) throws IOException {
|
||||
private
|
||||
void read(final DataInput di) throws IOException {
|
||||
readVersion(di);
|
||||
readConstantPoolEntries(di);
|
||||
readAccessFlags(di);
|
||||
@ -639,18 +652,21 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
readAttributes(di, ElementType.TYPE);
|
||||
}
|
||||
|
||||
private void readVersion(final DataInput di) throws IOException {
|
||||
private
|
||||
void readVersion(final DataInput di) throws IOException {
|
||||
// sequence: minor version, major version (argument_index is 1-based)
|
||||
if (LOG.isTraceEnabled()) {
|
||||
int minor = di.readUnsignedShort();
|
||||
int maj = di.readUnsignedShort();
|
||||
LOG.trace("Java Class version {}.{}", maj, minor);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
di.skipBytes(4);
|
||||
}
|
||||
}
|
||||
|
||||
private void readConstantPoolEntries(final DataInput di) throws IOException {
|
||||
private
|
||||
void readConstantPoolEntries(final DataInput di) throws IOException {
|
||||
final int count = di.readUnsignedShort();
|
||||
this.constantPool = new Object[count];
|
||||
for (int i = 1; i < count; ++i) {
|
||||
@ -664,8 +680,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
/**
|
||||
* Return {@code true} if a double slot is read (in case of Double or Long constant).
|
||||
*/
|
||||
private boolean readConstantPoolEntry(final DataInput di, final int index)
|
||||
throws IOException {
|
||||
private
|
||||
boolean readConstantPoolEntry(final DataInput di, final int index) throws IOException {
|
||||
|
||||
final int tag = di.readUnsignedByte();
|
||||
switch (tag) {
|
||||
@ -697,29 +713,33 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
this.constantPool[index] = di.readUnsignedShort();
|
||||
return false;
|
||||
default:
|
||||
throw new ClassFormatError(
|
||||
"Unkown tag value for constant pool entry: " + tag);
|
||||
throw new ClassFormatError("Unkown tag value for constant pool entry: " + tag);
|
||||
}
|
||||
}
|
||||
|
||||
private void readAccessFlags(final DataInput di) throws IOException {
|
||||
private
|
||||
void readAccessFlags(final DataInput di) throws IOException {
|
||||
di.skipBytes(2); // u2
|
||||
}
|
||||
|
||||
private void readThisClass(final DataInput di) throws IOException {
|
||||
private
|
||||
void readThisClass(final DataInput di) throws IOException {
|
||||
this.typeName = resolveUtf8(di);
|
||||
}
|
||||
|
||||
private void readSuperClass(final DataInput di) throws IOException {
|
||||
private
|
||||
void readSuperClass(final DataInput di) throws IOException {
|
||||
di.skipBytes(2); // u2
|
||||
}
|
||||
|
||||
private void readInterfaces(final DataInput di) throws IOException {
|
||||
private
|
||||
void readInterfaces(final DataInput di) throws IOException {
|
||||
final int count = di.readUnsignedShort();
|
||||
di.skipBytes(count * 2); // count * u2
|
||||
}
|
||||
|
||||
private void readFields(final DataInput di) throws IOException {
|
||||
private
|
||||
void readFields(final DataInput di) throws IOException {
|
||||
final int count = di.readUnsignedShort();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
readAccessFlags(di);
|
||||
@ -732,7 +752,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
}
|
||||
}
|
||||
|
||||
private void readMethods(final DataInput di) throws IOException {
|
||||
private
|
||||
void readMethods(final DataInput di) throws IOException {
|
||||
final int count = di.readUnsignedShort();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
readAccessFlags(di);
|
||||
@ -743,28 +764,28 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
}
|
||||
}
|
||||
|
||||
private void readAttributes(final DataInput di, final ElementType reporterType)
|
||||
throws IOException {
|
||||
private
|
||||
void readAttributes(final DataInput di, final ElementType reporterType) throws IOException {
|
||||
|
||||
final int count = di.readUnsignedShort();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
final String name = resolveUtf8(di);
|
||||
// in bytes, use this to skip the attribute info block
|
||||
final int length = di.readInt();
|
||||
if (this.elementTypes.contains(reporterType) &&
|
||||
("RuntimeVisibleAnnotations".equals(name) ||
|
||||
if (this.elementTypes.contains(reporterType) && ("RuntimeVisibleAnnotations".equals(name) ||
|
||||
"RuntimeInvisibleAnnotations".equals(name))) {
|
||||
LOG.trace("Attribute: {}", name);
|
||||
readAnnotations(di, reporterType);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
LOG.trace("Attribute: {} (ignored)", name);
|
||||
di.skipBytes(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readAnnotations(final DataInput di, final ElementType elementType)
|
||||
throws IOException {
|
||||
private
|
||||
void readAnnotations(final DataInput di, final ElementType elementType) throws IOException {
|
||||
|
||||
// the number of Runtime(In)VisibleAnnotations
|
||||
final int count = di.readUnsignedShort();
|
||||
@ -775,21 +796,23 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
LOG.trace("Annotation: {} (ignored)", rawTypeName);
|
||||
continue;
|
||||
}
|
||||
LOG.trace("Annotation: ''{}'' on type ''{}'', member ''{}'' (reported)",
|
||||
this.annotationType.getName(), getTypeName(), getMemberName());
|
||||
LOG.trace("Annotation: ''{}'' on type ''{}'', member ''{}'' (reported)", this.annotationType.getName(), getTypeName(),
|
||||
getMemberName());
|
||||
this.elementType = elementType;
|
||||
this.reporter.report(this);
|
||||
}
|
||||
}
|
||||
|
||||
private String readAnnotation(final DataInput di) throws IOException {
|
||||
private
|
||||
String readAnnotation(final DataInput di) throws IOException {
|
||||
final String rawTypeName = resolveUtf8(di);
|
||||
// num_element_value_pairs
|
||||
final int count = di.readUnsignedShort();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Anntotation Element: {}", resolveUtf8(di));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
di.skipBytes(2);
|
||||
}
|
||||
readAnnotationElementValue(di);
|
||||
@ -797,7 +820,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
return rawTypeName;
|
||||
}
|
||||
|
||||
private void readAnnotationElementValue(final DataInput di) throws IOException {
|
||||
private
|
||||
void readAnnotationElementValue(final DataInput di) throws IOException {
|
||||
final int tag = di.readUnsignedByte();
|
||||
switch (tag) {
|
||||
case BYTE:
|
||||
@ -827,8 +851,7 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ClassFormatError("Not a valid annotation element type tag: 0x" +
|
||||
Integer.toHexString(tag));
|
||||
throw new ClassFormatError("Not a valid annotation element type tag: 0x" + Integer.toHexString(tag));
|
||||
}
|
||||
}
|
||||
|
||||
@ -836,13 +859,15 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
* Look up the String value, identified by the u2 index value from constant pool
|
||||
* (direct or indirect).
|
||||
*/
|
||||
private String resolveUtf8(final DataInput di) throws IOException {
|
||||
private
|
||||
String resolveUtf8(final DataInput di) throws IOException {
|
||||
final int index = di.readUnsignedShort();
|
||||
final Object value = this.constantPool[index];
|
||||
final String s;
|
||||
if (value instanceof Integer) {
|
||||
s = (String) this.constantPool[(Integer) value];
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
s = (String) value;
|
||||
}
|
||||
return s;
|
||||
@ -854,7 +879,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
*/
|
||||
// incorrect detection of dereferencing possible null pointer
|
||||
// TODO: https://github.com/checkstyle/checkstyle/issues/14 fixed in 5.8?
|
||||
private Class<?>[] parseArguments(final String descriptor) {
|
||||
private
|
||||
Class<?>[] parseArguments(final String descriptor) {
|
||||
final int n = descriptor.length();
|
||||
// "minimal" descriptor: no arguments: "()V", first character is always '('
|
||||
if (n < 3 || descriptor.charAt(0) != '(') {
|
||||
@ -866,7 +892,8 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
if (i == 1) {
|
||||
if (c == ')') {
|
||||
return new Class<?>[0];
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
args = new LinkedList<Class<?>>();
|
||||
}
|
||||
}
|
||||
@ -929,25 +956,26 @@ public final class AnnotationDetector implements Builder, Cursor {
|
||||
/**
|
||||
* Load the class, but do not initialize it.
|
||||
*/
|
||||
private static Class<?> loadClass(ClassLoader loader, final String rawClassName) {
|
||||
private static
|
||||
Class<?> loadClass(ClassLoader loader, final String rawClassName) {
|
||||
final String typeName = rawClassName.replace('/', '.');
|
||||
try {
|
||||
return Class.forName(typeName, false, loader);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw assertionError(
|
||||
"Cannot load type '%s', scanned file not on class path? (%s)", typeName, ex);
|
||||
throw assertionError("Cannot load type '%s', scanned file not on class path? (%s)", typeName, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method descriptor must always be parseable, so if not an AssertionError is thrown.
|
||||
*/
|
||||
private static AssertionError unparseable(final String descriptor, final String cause) {
|
||||
return assertionError(
|
||||
"Unparseble method descriptor: '%s' (cause: %s)", descriptor, cause);
|
||||
private static
|
||||
AssertionError unparseable(final String descriptor, final String cause) {
|
||||
return assertionError("Unparseble method descriptor: '%s' (cause: %s)", descriptor, cause);
|
||||
}
|
||||
|
||||
private static AssertionError assertionError(String message, Object... args) {
|
||||
private static
|
||||
AssertionError assertionError(String message, Object... args) {
|
||||
return new AssertionError(String.format(message, args));
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,8 @@ import java.util.List;
|
||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
||||
* @since annotation-detector 3.1.0
|
||||
*/
|
||||
public interface Builder {
|
||||
public
|
||||
interface Builder {
|
||||
|
||||
/**
|
||||
* Specify the annotation types to report.
|
||||
@ -51,7 +52,7 @@ public interface Builder {
|
||||
/**
|
||||
* Specify the Element Types to scan. If this method is not called,
|
||||
* {@link ElementType#TYPE} is used as default.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Valid types are:
|
||||
* <ul>
|
||||
* <li>{@link ElementType#TYPE}
|
||||
@ -66,7 +67,7 @@ public interface Builder {
|
||||
/**
|
||||
* Specify the Element Types to scan. If this method is not called,
|
||||
* {@link ElementType#TYPE} is used as default.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Valid types are:
|
||||
* <ul>
|
||||
* <li>{@link ElementType#TYPE}
|
||||
@ -81,18 +82,18 @@ public interface Builder {
|
||||
/**
|
||||
* Filter the scanned Class Files based on its name and the directory or jar file it is
|
||||
* stored.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If the Class File is stored as a single file in the file system the {@code File}
|
||||
* argument in {@link FilenameFilter#accept(java.io.File, java.lang.String) } is the
|
||||
* absolute path to the root directory scanned.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If the Class File is stored in a jar file the {@code File} argument in
|
||||
* {@link FilenameFilter#accept(java.io.File, java.lang.String)} is the absolute path of
|
||||
* the jar file.
|
||||
* <p>
|
||||
* <p/>
|
||||
* The {@code String} argument is the full name of the ClassFile in native format,
|
||||
* including package name, like {@code eu/infomas/annotation/AnnotationDetector$1.class}.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Note that all non-Class Files are already filtered and not seen by the filter.
|
||||
*
|
||||
* @param filter The filter, never {@code null}
|
||||
|
@ -21,24 +21,21 @@
|
||||
*/
|
||||
package dorkbox.util.annotation;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* {@code ClassFileBuffer} is used by {@link AnnotationDetector} to efficiently read Java
|
||||
* ClassFile files from an {@link InputStream} and parse the content via the {@link DataInput}
|
||||
* interface.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Note that Java ClassFile files can grow really big,
|
||||
* {@code com.sun.corba.se.impl.logging.ORBUtilSystemException} is 128.2 kb!
|
||||
*
|
||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
||||
* @since annotation-detector 3.0.0
|
||||
*/
|
||||
final class ClassFileBuffer implements DataInput {
|
||||
final
|
||||
class ClassFileBuffer implements DataInput {
|
||||
|
||||
private byte[] buffer;
|
||||
private int size; // the number of significant bytes read
|
||||
@ -70,7 +67,8 @@ final class ClassFileBuffer implements DataInput {
|
||||
* supplied byte stream.
|
||||
* The read pointer is reset to the start of the byte array.
|
||||
*/
|
||||
public void readFrom(final InputStream in) throws IOException {
|
||||
public
|
||||
void readFrom(final InputStream in) throws IOException {
|
||||
this.pointer = 0;
|
||||
this.size = 0;
|
||||
int n;
|
||||
@ -87,7 +85,8 @@ final class ClassFileBuffer implements DataInput {
|
||||
* Sets the file-pointer offset, measured from the beginning of this file,
|
||||
* at which the next read or write occurs.
|
||||
*/
|
||||
public void seek(final int position) throws IOException {
|
||||
public
|
||||
void seek(final int position) throws IOException {
|
||||
if (position < 0) {
|
||||
throw new IllegalArgumentException("position < 0: " + position);
|
||||
}
|
||||
@ -100,20 +99,22 @@ final class ClassFileBuffer implements DataInput {
|
||||
/**
|
||||
* Return the size (in bytes) of this Java ClassFile file.
|
||||
*/
|
||||
public int size() {
|
||||
public
|
||||
int size() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
// DataInput
|
||||
|
||||
@Override
|
||||
public void readFully(final byte[] bytes) throws IOException {
|
||||
public
|
||||
void readFully(final byte[] bytes) throws IOException {
|
||||
readFully(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFully(final byte[] bytes, final int offset, final int length)
|
||||
throws IOException {
|
||||
public
|
||||
void readFully(final byte[] bytes, final int offset, final int length) throws IOException {
|
||||
|
||||
if (length < 0 || offset < 0 || offset + length > bytes.length) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
@ -126,13 +127,15 @@ final class ClassFileBuffer implements DataInput {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int skipBytes(final int n) throws IOException {
|
||||
public
|
||||
int skipBytes(final int n) throws IOException {
|
||||
seek(this.pointer + n);
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte() throws IOException {
|
||||
public
|
||||
byte readByte() throws IOException {
|
||||
if (this.pointer >= this.size) {
|
||||
throw new EOFException();
|
||||
}
|
||||
@ -140,12 +143,14 @@ final class ClassFileBuffer implements DataInput {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBoolean() throws IOException {
|
||||
public
|
||||
boolean readBoolean() throws IOException {
|
||||
return readByte() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedByte() throws IOException {
|
||||
public
|
||||
int readUnsignedByte() throws IOException {
|
||||
if (this.pointer >= this.size) {
|
||||
throw new EOFException();
|
||||
}
|
||||
@ -153,7 +158,8 @@ final class ClassFileBuffer implements DataInput {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedShort() throws IOException {
|
||||
public
|
||||
int readUnsignedShort() throws IOException {
|
||||
if (this.pointer + 2 > this.size) {
|
||||
throw new EOFException();
|
||||
}
|
||||
@ -161,17 +167,20 @@ final class ClassFileBuffer implements DataInput {
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort() throws IOException {
|
||||
public
|
||||
short readShort() throws IOException {
|
||||
return (short) readUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char readChar() throws IOException {
|
||||
public
|
||||
char readChar() throws IOException {
|
||||
return (char) readUnsignedShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt() throws IOException {
|
||||
public
|
||||
int readInt() throws IOException {
|
||||
if (this.pointer + 4 > this.size) {
|
||||
throw new EOFException();
|
||||
}
|
||||
@ -182,7 +191,8 @@ final class ClassFileBuffer implements DataInput {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong() throws IOException {
|
||||
public
|
||||
long readLong() throws IOException {
|
||||
if (this.pointer + 8 > this.size) {
|
||||
throw new EOFException();
|
||||
}
|
||||
@ -197,12 +207,14 @@ final class ClassFileBuffer implements DataInput {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat() throws IOException {
|
||||
public
|
||||
float readFloat() throws IOException {
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble() throws IOException {
|
||||
public
|
||||
double readDouble() throws IOException {
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
@ -214,22 +226,26 @@ final class ClassFileBuffer implements DataInput {
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public String readLine() throws IOException {
|
||||
public
|
||||
String readLine() throws IOException {
|
||||
throw new UnsupportedOperationException("readLine() is deprecated and not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readUTF() throws IOException {
|
||||
public
|
||||
String readUTF() throws IOException {
|
||||
return DataInputStream.readUTF(this);
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
private int read() {
|
||||
private
|
||||
int read() {
|
||||
return this.buffer[this.pointer++] & 0xff;
|
||||
}
|
||||
|
||||
private void resizeIfNeeded() {
|
||||
private
|
||||
void resizeIfNeeded() {
|
||||
if (this.size >= this.buffer.length) {
|
||||
final byte[] newBuffer = new byte[this.buffer.length * 2];
|
||||
System.arraycopy(this.buffer, 0, newBuffer, 0, this.buffer.length);
|
||||
|
@ -21,22 +21,19 @@
|
||||
*/
|
||||
package dorkbox.util.annotation;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* {@code ClassFileIterator} is used to iterate over all Java ClassFile files available within
|
||||
* a specific context.
|
||||
* <p>
|
||||
* <p/>
|
||||
* For every Java ClassFile ({@code .class}) an {@link InputStream} is returned.
|
||||
*
|
||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
||||
* @since annotation-detector 3.0.0
|
||||
*/
|
||||
public class ClassFileIterator implements ClassIterator {
|
||||
public
|
||||
class ClassFileIterator implements ClassIterator {
|
||||
|
||||
private FileIterator fileIter;
|
||||
protected final String[] pkgNameFilter;
|
||||
@ -47,24 +44,26 @@ public class ClassFileIterator implements ClassIterator {
|
||||
/**
|
||||
* Create a new {@code ClassFileIterator} returning all Java ClassFile files available
|
||||
* from the specified files and/or directories, including sub directories.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If the (optional) package filter is defined, only class files staring with one of the
|
||||
* defined package names are returned.
|
||||
* NOTE: package names must be defined in the native format (using '/' instead of '.').
|
||||
*/
|
||||
protected ClassFileIterator(final String[] pkgNameFilter) {
|
||||
protected
|
||||
ClassFileIterator(final String[] pkgNameFilter) {
|
||||
this.pkgNameFilter = pkgNameFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code ClassFileIterator} returning all Java ClassFile files available
|
||||
* from the specified files and/or directories, including sub directories.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If the (optional) package filter is defined, only class files staring with one of the
|
||||
* defined package names are returned.
|
||||
* NOTE: package names must be defined in the native format (using '/' instead of '.').
|
||||
*/
|
||||
protected ClassFileIterator(final File[] filesOrDirectories, final String[] pkgNameFilter) {
|
||||
protected
|
||||
ClassFileIterator(final File[] filesOrDirectories, final String[] pkgNameFilter) {
|
||||
this.fileIter = new FileIterator(filesOrDirectories);
|
||||
this.pkgNameFilter = pkgNameFilter;
|
||||
}
|
||||
@ -74,11 +73,10 @@ public class ClassFileIterator implements ClassIterator {
|
||||
* The name is either the path name of a file or the name of an ZIP/JAR file entry.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
public
|
||||
String getName() {
|
||||
// Both getPath() and getName() are very light weight method calls
|
||||
return this.zipIter == null ?
|
||||
this.fileIter.getFile().getPath() :
|
||||
this.zipIter.getEntry().getName();
|
||||
return this.zipIter == null ? this.fileIter.getFile().getPath() : this.zipIter.getEntry().getName();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -88,37 +86,43 @@ public class ClassFileIterator implements ClassIterator {
|
||||
* ZIP File Entry.
|
||||
*/
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
public
|
||||
boolean isFile() {
|
||||
return this.isFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next Java ClassFile as an {@code InputStream}.
|
||||
* <p>
|
||||
* <p/>
|
||||
* NOTICE: Client code MUST close the returned {@code InputStream}!
|
||||
*/
|
||||
@Override
|
||||
public InputStream next(final FilenameFilter filter) throws IOException {
|
||||
public
|
||||
InputStream next(final FilenameFilter filter) throws IOException {
|
||||
while (true) {
|
||||
if (this.zipIter == null) {
|
||||
final File file = this.fileIter.next();
|
||||
if (file == null) {
|
||||
return null;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
final String path = file.getPath();
|
||||
if (path.endsWith(".class") && (filter == null ||
|
||||
filter.accept(this.fileIter.getRootFile(), this.fileIter.relativize(path)))) {
|
||||
if (path.endsWith(".class") && (filter == null || filter.accept(this.fileIter.getRootFile(), this.fileIter.relativize(
|
||||
path)))) {
|
||||
this.isFile = true;
|
||||
return new FileInputStream(file);
|
||||
} else if (this.fileIter.isRootFile() && endsWithIgnoreCase(path, ".jar")) {
|
||||
}
|
||||
else if (this.fileIter.isRootFile() && endsWithIgnoreCase(path, ".jar")) {
|
||||
this.zipIter = new ZipFileIterator(file, this.pkgNameFilter);
|
||||
} // else just ignore
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
final InputStream is = this.zipIter.next(filter);
|
||||
if (is == null) {
|
||||
this.zipIter = null;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.isFile = false;
|
||||
return is;
|
||||
}
|
||||
@ -128,7 +132,8 @@ public class ClassFileIterator implements ClassIterator {
|
||||
|
||||
// private
|
||||
|
||||
private static boolean endsWithIgnoreCase(final String value, final String suffix) {
|
||||
private static
|
||||
boolean endsWithIgnoreCase(final String value, final String suffix) {
|
||||
final int n = suffix.length();
|
||||
return value.regionMatches(true, value.length() - n, suffix, 0, n);
|
||||
}
|
||||
|
@ -31,13 +31,14 @@ import java.io.InputStream;
|
||||
/**
|
||||
* {@code ClassFileIterator} is used to iterate over all Java ClassFile files available within
|
||||
* a specific context.
|
||||
* <p>
|
||||
* <p/>
|
||||
* For every Java ClassFile ({@code .class}) an {@link InputStream} is returned.
|
||||
*
|
||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
||||
* @since annotation-detector 3.0.0
|
||||
*/
|
||||
public interface ClassIterator {
|
||||
public
|
||||
interface ClassIterator {
|
||||
|
||||
/**
|
||||
* Return the name of the Java ClassFile returned from the last call to {@link #next()}.
|
||||
@ -55,7 +56,7 @@ public interface ClassIterator {
|
||||
|
||||
/**
|
||||
* Return the next Java ClassFile as an {@code InputStream}.
|
||||
* <p>
|
||||
* <p/>
|
||||
* NOTICE: Client code MUST close the returned {@code InputStream}!
|
||||
*/
|
||||
InputStream next(final FilenameFilter filter) throws IOException;
|
||||
|
@ -33,7 +33,8 @@ import java.lang.reflect.Method;
|
||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
||||
* @since annotation-detector 3.1.0
|
||||
*/
|
||||
public interface Cursor {
|
||||
public
|
||||
interface Cursor {
|
||||
|
||||
/**
|
||||
* Return the type name of the currently reported Java Class File.
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package dorkbox.util.annotation;
|
||||
|
||||
import dorkbox.util.FileUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
@ -26,17 +28,17 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import dorkbox.util.FileUtil;
|
||||
public
|
||||
class CustomClassloaderIterator implements ClassIterator {
|
||||
|
||||
public class CustomClassloaderIterator implements ClassIterator {
|
||||
|
||||
private volatile Iterator<URL> loaderFilesIterator;
|
||||
private final Iterator<URL> loaderFilesIterator;
|
||||
private ClassFileIterator classFileIterator;
|
||||
|
||||
// have to support
|
||||
// 1 - scanning the classpath
|
||||
// 2 - scanning a specific package
|
||||
public CustomClassloaderIterator(List<URL> fileNames, String[] packageNames) throws IOException {
|
||||
public
|
||||
CustomClassloaderIterator(List<URL> fileNames, String[] packageNames) throws IOException {
|
||||
// if ANY of our filenames DO NOT start with "box", we have to add it as a file, so our iterator picks it up (and if dir, it's childred)
|
||||
|
||||
Set<File> files = new HashSet<File>();
|
||||
@ -56,7 +58,8 @@ public class CustomClassloaderIterator implements ClassIterator {
|
||||
|
||||
if (files.isEmpty()) {
|
||||
this.classFileIterator = null;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.classFileIterator = new ClassFileIterator(files.toArray(new File[0]), packageNames);
|
||||
}
|
||||
|
||||
@ -65,13 +68,15 @@ public class CustomClassloaderIterator implements ClassIterator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
public
|
||||
String getName() {
|
||||
// not needed
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
public
|
||||
boolean isFile() {
|
||||
if (this.classFileIterator != null) {
|
||||
return this.classFileIterator.isFile();
|
||||
}
|
||||
@ -80,13 +85,15 @@ public class CustomClassloaderIterator implements ClassIterator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream next(FilenameFilter filter) throws IOException {
|
||||
public
|
||||
InputStream next(FilenameFilter filter) throws IOException {
|
||||
if (this.classFileIterator != null) {
|
||||
while (true) {
|
||||
InputStream next = this.classFileIterator.next(filter);
|
||||
if (next == null) {
|
||||
this.classFileIterator = null;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
String name = this.classFileIterator.getName();
|
||||
if (name.endsWith(".class")) {
|
||||
return next;
|
||||
|
@ -29,7 +29,7 @@ import java.util.NoSuchElementException;
|
||||
/**
|
||||
* {@code FileIterator} enables iteration over all files in a directory and all its sub
|
||||
* directories.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Usage:
|
||||
* <pre>
|
||||
* FileIterator iter = new FileIterator(new File("./src"));
|
||||
@ -43,7 +43,8 @@ import java.util.NoSuchElementException;
|
||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
||||
* @since annotation-detector 3.0.0
|
||||
*/
|
||||
final class FileIterator {
|
||||
final
|
||||
class FileIterator {
|
||||
|
||||
private final Deque<File> stack = new LinkedList<File>();
|
||||
private int rootCount;
|
||||
@ -52,7 +53,7 @@ final class FileIterator {
|
||||
|
||||
/**
|
||||
* Create a new {@code FileIterator} using the specified 'filesOrDirectories' as root.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If 'filesOrDirectories' contains a file, the iterator just returns that single file.
|
||||
* If 'filesOrDirectories' contains a directory, all files in that directory
|
||||
* and its sub directories are returned (depth first).
|
||||
@ -80,12 +81,12 @@ final class FileIterator {
|
||||
|
||||
/**
|
||||
* Relativize the absolute full (file) 'path' against the current root file.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Example:<br/>
|
||||
* Let current root be "/path/to/dir".
|
||||
* Then {@code relativize("/path/to/dir/with/file.ext")} equals "with/file.ext" (without
|
||||
* leading '/').
|
||||
* <p>
|
||||
* <p/>
|
||||
* Note: the paths are not canonicalized!
|
||||
*/
|
||||
String relativize(final String path) {
|
||||
@ -115,7 +116,8 @@ final class FileIterator {
|
||||
if (this.stack.isEmpty()) {
|
||||
this.current = null;
|
||||
return null;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.current = this.stack.removeLast();
|
||||
if (this.current.isDirectory()) {
|
||||
if (this.stack.size() < this.rootCount) {
|
||||
@ -124,7 +126,8 @@ final class FileIterator {
|
||||
}
|
||||
addReverse(this.current.listFiles());
|
||||
return next();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return this.current;
|
||||
}
|
||||
}
|
||||
@ -135,7 +138,8 @@ final class FileIterator {
|
||||
/**
|
||||
* Add the specified files in reverse order.
|
||||
*/
|
||||
private void addReverse(final File[] files) {
|
||||
private
|
||||
void addReverse(final File[] files) {
|
||||
for (int i = files.length - 1; i >= 0; --i) {
|
||||
this.stack.add(files[i]);
|
||||
}
|
||||
|
@ -24,22 +24,21 @@ package dorkbox.util.annotation;
|
||||
|
||||
/**
|
||||
* {@code Reporter} is used to report the detected annotations.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This interface is a so called "Single Abstract Method" (SAM) or "Functional Interface", so
|
||||
* can be used as a Lambda in Java 8 (see examples).
|
||||
*
|
||||
* @see Builder#report(dorkbox.util.annotation.Reporter)
|
||||
*
|
||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
||||
* @see Builder#report(dorkbox.util.annotation.Reporter)
|
||||
* @since annotation-detector 3.1.0
|
||||
*/
|
||||
public interface Reporter {
|
||||
public
|
||||
interface Reporter {
|
||||
|
||||
/**
|
||||
* This method is called when an {@code Annotation} is detected. Invoke methods on the
|
||||
* provided {@code Cursor} reference to get more specific information about the
|
||||
* {@code Annotation}.
|
||||
*
|
||||
*/
|
||||
void report(Cursor cursor);
|
||||
|
||||
|
@ -24,12 +24,12 @@ package dorkbox.util.annotation;
|
||||
/**
|
||||
* {@code ReporterFunction} is used to report the detected annotations.
|
||||
*
|
||||
* @see Builder#collect(dorkbox.util.annotation.ReporterFunction)
|
||||
*
|
||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
||||
* @see Builder#collect(dorkbox.util.annotation.ReporterFunction)
|
||||
* @since annotation-detector 3.1.0
|
||||
*/
|
||||
public interface ReporterFunction<T> {
|
||||
public
|
||||
interface ReporterFunction<T> {
|
||||
|
||||
/**
|
||||
* This method is called when an {@code Annotation} is detected.
|
||||
|
@ -32,15 +32,16 @@ import java.util.zip.ZipFile;
|
||||
/**
|
||||
* {@code ZipFileIterator} is used to iterate over all entries in a given {@code zip} or
|
||||
* {@code jar} file and returning the {@link InputStream} of these entries.
|
||||
* <p>
|
||||
* <p/>
|
||||
* It is possible to specify an (optional) entry name filter.
|
||||
* <p>
|
||||
* <p/>
|
||||
* The most efficient way of iterating is used, see benchmark in test classes.
|
||||
*
|
||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
||||
* @since annotation-detector 3.0.0
|
||||
*/
|
||||
final class ZipFileIterator {
|
||||
final
|
||||
class ZipFileIterator {
|
||||
|
||||
private final File file;
|
||||
private final ZipFile zipFile;
|
||||
@ -64,11 +65,13 @@ final class ZipFileIterator {
|
||||
this.entries = this.zipFile.entries();
|
||||
}
|
||||
|
||||
public ZipEntry getEntry() {
|
||||
public
|
||||
ZipEntry getEntry() {
|
||||
return this.current;
|
||||
}
|
||||
|
||||
public InputStream next(final FilenameFilter filter) throws IOException {
|
||||
public
|
||||
InputStream next(final FilenameFilter filter) throws IOException {
|
||||
while (this.entries.hasMoreElements()) {
|
||||
this.current = this.entries.nextElement();
|
||||
if (filter == null || accept(this.current, filter)) {
|
||||
@ -85,7 +88,8 @@ final class ZipFileIterator {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean accept(final ZipEntry entry, final FilenameFilter filter) {
|
||||
private
|
||||
boolean accept(final ZipEntry entry, final FilenameFilter filter) {
|
||||
if (entry.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user