Moved annotations to separate project
This commit is contained in:
parent
e3bad2a55a
commit
e000c346b4
|
@ -1,61 +0,0 @@
|
||||||
package dorkbox.util.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* for specifying the default report methods, without constantly creating new objects
|
|
||||||
*/
|
|
||||||
public class AnnotationDefaults {
|
|
||||||
public static final ReporterFunction<String> getTypeName = new ReporterFunction<String>() {
|
|
||||||
@Override
|
|
||||||
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) {
|
|
||||||
return cursor.getAnnotationType();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final ReporterFunction<ElementType> getElementType = new ReporterFunction<ElementType>() {
|
|
||||||
@Override
|
|
||||||
public ElementType report(Cursor cursor) {
|
|
||||||
return cursor.getElementType();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final ReporterFunction<String> getMemberName = new ReporterFunction<String>() {
|
|
||||||
@Override
|
|
||||||
public String report(Cursor cursor) {
|
|
||||||
return cursor.getMemberName();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final ReporterFunction<Class<?>> getType = new ReporterFunction<Class<?>>() {
|
|
||||||
@Override
|
|
||||||
public Class<?> report(Cursor cursor) {
|
|
||||||
return cursor.getType();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final ReporterFunction<Constructor<?>> getConstructor = new ReporterFunction<Constructor<?>>() {
|
|
||||||
@Override
|
|
||||||
public Constructor<?> report(Cursor cursor) {
|
|
||||||
return cursor.getConstructor();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final ReporterFunction<Field> getField = new ReporterFunction<Field>() {
|
|
||||||
@Override
|
|
||||||
public Field report(Cursor cursor) {
|
|
||||||
return cursor.getField();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public static final ReporterFunction<Method> getMethod = new ReporterFunction<Method>() {
|
|
||||||
@Override
|
|
||||||
public Method report(Cursor cursor) {
|
|
||||||
return cursor.getMethod();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,954 +0,0 @@
|
||||||
/* AnnotationDetector.java
|
|
||||||
*
|
|
||||||
* Created: 2011-10-10 (Year-Month-Day)
|
|
||||||
* Character encoding: UTF-8
|
|
||||||
*
|
|
||||||
****************************************** LICENSE *******************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011 - 2014 XIAM Solutions B.V. (http://www.xiam.nl)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
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 java.lang.annotation.Annotation;
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.reflect.AnnotatedElement;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code AnnotationDetector} reads Java Class Files ("*.class") and reports the
|
|
||||||
* found annotations via a simple, developer friendly API.
|
|
||||||
* <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>
|
|
||||||
* A class file consists of a single ClassFile structure:
|
|
||||||
* <pre>
|
|
||||||
* ClassFile {
|
|
||||||
* u4 magic;
|
|
||||||
* u2 minor_version;
|
|
||||||
* u2 major_version;
|
|
||||||
* u2 constant_pool_count;
|
|
||||||
* cp_info constant_pool[constant_pool_count-1];
|
|
||||||
* u2 access_flags;
|
|
||||||
* u2 this_class;
|
|
||||||
* u2 super_class;
|
|
||||||
* u2 interfaces_count;
|
|
||||||
* u2 interfaces[interfaces_count];
|
|
||||||
* u2 fields_count;
|
|
||||||
* field_info fields[fields_count];
|
|
||||||
* u2 methods_count;
|
|
||||||
* method_info methods[methods_count];
|
|
||||||
* u2 attributes_count;
|
|
||||||
* attribute_info attributes[attributes_count];
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* Where:
|
|
||||||
* u1 unsigned byte {@link java.io.DataInput#readUnsignedByte()}
|
|
||||||
* u2 unsigned short {@link java.io.DataInput#readUnsignedShort()}
|
|
||||||
* u4 unsigned int {@link java.io.DataInput#readInt()}
|
|
||||||
*
|
|
||||||
* Annotations are stored as Attributes, named "RuntimeVisibleAnnotations" for
|
|
||||||
* {@link java.lang.annotation.RetentionPolicy#RUNTIME} and "RuntimeInvisibleAnnotations" for
|
|
||||||
* {@link java.lang.annotation.RetentionPolicy#CLASS}.
|
|
||||||
* </pre>
|
|
||||||
* References:
|
|
||||||
* <ul>
|
|
||||||
* <li><a href="http://en.wikipedia.org/wiki/Java_class_file">Java class file (Wikipedia)</a>
|
|
||||||
* (Gentle Introduction);
|
|
||||||
* <li><a href="http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html">Java
|
|
||||||
* VM Specification, Java SE 8 Edition (Chapter 4)</a> for the real work.
|
|
||||||
* <li><a href="http://stackoverflow.com/questions/259140">scanning java annotations at
|
|
||||||
* runtime</a>.
|
|
||||||
* </ul>
|
|
||||||
* <p>
|
|
||||||
* Similar projects / libraries:
|
|
||||||
* <ul>
|
|
||||||
* <li><a href="http://community.jboss.org/wiki/MCScanninglib">JBoss MC Scanning lib</a>;
|
|
||||||
* <li><a href="http://code.google.com/p/reflections/">Google Reflections</a>, in fact an
|
|
||||||
* improved version of <a href="http://scannotation.sourceforge.net/">scannotation</a>;
|
|
||||||
* <li><a href="https://github.com/ngocdaothanh/annovention">annovention</a>, improved version
|
|
||||||
* of the <a href="http://code.google.com/p/annovention">original Annovention</a> project.
|
|
||||||
* Available from maven: {@code tv.cntt:annovention:1.2};
|
|
||||||
* <li>If using the Spring Framework, use {@code ClassPathScanningCandidateComponentProvider}
|
|
||||||
* </ul>
|
|
||||||
* <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 {
|
|
||||||
|
|
||||||
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AnnotationDetector.class);
|
|
||||||
|
|
||||||
// Constant Pool type tags
|
|
||||||
private static final int CP_UTF8 = 1;
|
|
||||||
private static final int CP_INTEGER = 3;
|
|
||||||
private static final int CP_FLOAT = 4;
|
|
||||||
private static final int CP_LONG = 5;
|
|
||||||
private static final int CP_DOUBLE = 6;
|
|
||||||
private static final int CP_CLASS = 7;
|
|
||||||
private static final int CP_STRING = 8;
|
|
||||||
private static final int CP_REF_FIELD = 9;
|
|
||||||
private static final int CP_REF_METHOD = 10;
|
|
||||||
private static final int CP_REF_INTERFACE = 11;
|
|
||||||
private static final int CP_NAME_AND_TYPE = 12;
|
|
||||||
private static final int CP_METHOD_HANDLE = 15; // Java VM SE 7
|
|
||||||
private static final int CP_METHOD_TYPE = 16; // Java VM SE 7
|
|
||||||
private static final int CP_INVOKE_DYNAMIC = 18; // Java VM SE 7
|
|
||||||
|
|
||||||
// AnnotationElementValue / Java raw types
|
|
||||||
private static final int BYTE = 'B';
|
|
||||||
private static final int CHAR = 'C';
|
|
||||||
private static final int DOUBLE = 'D';
|
|
||||||
private static final int FLOAT = 'F';
|
|
||||||
private static final int INT = 'I';
|
|
||||||
private static final int LONG = 'J';
|
|
||||||
private static final int SHORT = 'S';
|
|
||||||
private static final int BOOLEAN = 'Z';
|
|
||||||
private static final int ARRAY = '[';
|
|
||||||
// used for AnnotationElement only
|
|
||||||
private static final int STRING = 's';
|
|
||||||
private static final int ENUM = 'e';
|
|
||||||
private static final int CLASS = 'c';
|
|
||||||
private static final int ANNOTATION = '@';
|
|
||||||
|
|
||||||
private final ClassLoader loader;
|
|
||||||
// The buffer is reused during the life cycle of this AnnotationDetector instance
|
|
||||||
private final ClassFileBuffer cpBuffer = new ClassFileBuffer();
|
|
||||||
private final ClassIterator cfIterator;
|
|
||||||
// The Element Types to detect
|
|
||||||
private final Set<ElementType> elementTypes = EnumSet.of(ElementType.TYPE);
|
|
||||||
// Reusing the constantPool is not needed for better performance
|
|
||||||
private Object[] constantPool;
|
|
||||||
|
|
||||||
// The cached annotation types to report, maps raw Annotation type name to Class object
|
|
||||||
private Map<String, Class<? extends Annotation>> annotations;
|
|
||||||
private FilenameFilter filter;
|
|
||||||
private Reporter reporter;
|
|
||||||
|
|
||||||
// The current annotation reported
|
|
||||||
private Class<? extends Annotation> annotationType;
|
|
||||||
// The 'raw' name of the current interface or class being scanned and reported
|
|
||||||
// (using '/' instead of '.' in package name)
|
|
||||||
private String typeName;
|
|
||||||
// The current method or field (if any) being scanned
|
|
||||||
private String memberName;
|
|
||||||
// The current Element Type beinig reported
|
|
||||||
private ElementType elementType;
|
|
||||||
// The method descriptor of the currently reported annotated method in "raw"
|
|
||||||
// format (as it appears in the Java Class File). Example method descriptors:
|
|
||||||
// "()V" no arguments, return type void
|
|
||||||
// "(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) {
|
|
||||||
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 {
|
|
||||||
this.cfIterator = iterator;
|
|
||||||
|
|
||||||
if (LOG.isTraceEnabled()) {
|
|
||||||
LOG.trace("Class Files from the custom classfileiterator scanned.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory method, starting point for the fluent interface.
|
|
||||||
* 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 {
|
|
||||||
|
|
||||||
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
|
||||||
return scanClassPath(loader, packageNames);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory method, starting point for the fluent interface.
|
|
||||||
* 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 {
|
|
||||||
|
|
||||||
final String[] pkgNameFilter;
|
|
||||||
|
|
||||||
// DORKBOX added
|
|
||||||
boolean isCustomLoader = "dorkbox.classloader.ClassLoader" == loader.getClass().getName();
|
|
||||||
if (isCustomLoader) {
|
|
||||||
final List<URL> fileNames;
|
|
||||||
|
|
||||||
// scanning the classpath
|
|
||||||
if (packageNames.length == 0) {
|
|
||||||
pkgNameFilter = null;
|
|
||||||
List<String> asList = Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator));
|
|
||||||
fileNames = new ArrayList<URL>(asList.size());
|
|
||||||
for (String s : asList) {
|
|
||||||
File file = new File(s);
|
|
||||||
fileNames.add(file.toURI().toURL());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// scanning specific packages
|
|
||||||
else {
|
|
||||||
pkgNameFilter = new String[packageNames.length];
|
|
||||||
for (int i = 0; i < pkgNameFilter.length; ++i) {
|
|
||||||
pkgNameFilter[i] = packageNames[i].replace('.', '/');
|
|
||||||
if (!pkgNameFilter[i].endsWith("/")) {
|
|
||||||
pkgNameFilter[i] = pkgNameFilter[i].concat("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileNames = new ArrayList<URL>();
|
|
||||||
for (final String packageName : pkgNameFilter) {
|
|
||||||
final Enumeration<URL> resourceEnum = loader.getResources(packageName);
|
|
||||||
while (resourceEnum.hasMoreElements()) {
|
|
||||||
final URL url = resourceEnum.nextElement();
|
|
||||||
fileNames.add(url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AnnotationDetector(loader, null, new CustomClassloaderIterator(fileNames, packageNames), pkgNameFilter);
|
|
||||||
} else {
|
|
||||||
final Set<File> files = new HashSet<File>();
|
|
||||||
|
|
||||||
if (packageNames.length == 0) {
|
|
||||||
pkgNameFilter = null;
|
|
||||||
final String[] fileNames = System.getProperty("java.class.path").split(File.pathSeparator);
|
|
||||||
for (int i = 0; i < fileNames.length; ++i) {
|
|
||||||
files.add(new File(fileNames[i]));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pkgNameFilter = new String[packageNames.length];
|
|
||||||
for (int i = 0; i < pkgNameFilter.length; ++i) {
|
|
||||||
pkgNameFilter[i] = packageNames[i].replace('.', '/');
|
|
||||||
if (!pkgNameFilter[i].endsWith("/")) {
|
|
||||||
pkgNameFilter[i] = pkgNameFilter[i].concat("/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (final String packageName : pkgNameFilter) {
|
|
||||||
addFiles(loader, packageName, files);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new AnnotationDetector(loader, files.toArray(new File[files.size()]), null, pkgNameFilter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory method, starting point for the fluent interface.
|
|
||||||
* Scan all files specified by the classFileIterator.
|
|
||||||
*/
|
|
||||||
public static Builder scan(ClassLoader loader, final ClassIterator iterator) {
|
|
||||||
return new AnnotationDetector(loader, null, iterator, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
return new AnnotationDetector(loader, filesOrDirectories, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
return new AnnotationDetector(Thread.currentThread().getContextClassLoader(), filesOrDirectories, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Builder#forAnnotations(java.lang.Class...) }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
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);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Builder#forAnnotations(java.lang.Class...) }.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
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) {
|
|
||||||
this.annotations.put("L" + annotations[i].getName().replace('.', '/') + ";", annotations[i]);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Builder#on(java.lang.annotation.ElementType...) }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Builder on(final ElementType type) {
|
|
||||||
if (type == null) {
|
|
||||||
throw new IllegalArgumentException("At least one Element Type must be specified");
|
|
||||||
}
|
|
||||||
this.elementTypes.clear();
|
|
||||||
switch (type) {
|
|
||||||
case TYPE:
|
|
||||||
case CONSTRUCTOR:
|
|
||||||
case METHOD:
|
|
||||||
case FIELD:
|
|
||||||
this.elementTypes.add(type);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unsupported: " + type);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Builder#on(java.lang.annotation.ElementType...) }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Builder on(final ElementType... types) {
|
|
||||||
if (types.length == 0) {
|
|
||||||
throw new IllegalArgumentException("At least one Element Type must be specified");
|
|
||||||
}
|
|
||||||
this.elementTypes.clear();
|
|
||||||
for (ElementType t : types) {
|
|
||||||
switch (t) {
|
|
||||||
case TYPE:
|
|
||||||
case CONSTRUCTOR:
|
|
||||||
case METHOD:
|
|
||||||
case FIELD:
|
|
||||||
this.elementTypes.add(t);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unsupported: " + t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Builder#filter(java.io.FilenameFilter) }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Builder filter(final FilenameFilter filter) {
|
|
||||||
if (filter == null) {
|
|
||||||
throw new NullPointerException("'filter' may not be null");
|
|
||||||
}
|
|
||||||
this.filter = filter;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Builder#report(dorkbox.util.annotation.AnnotationDetector.Reporter) }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void report(final Reporter reporter) throws IOException {
|
|
||||||
this.reporter = reporter;
|
|
||||||
detect(this.cfIterator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Builder#collect(dorkbox.util.annotation.AnnotationDetector.ReporterFunction) }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
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) {
|
|
||||||
list.add(reporter.report(cursor));
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
detect(this.cfIterator);
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Cursor#getTypeName() }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String getTypeName() {
|
|
||||||
return this.typeName.replace('/', '.');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Cursor#getAnnotationType() }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Class<? extends Annotation> getAnnotationType() {
|
|
||||||
return this.annotationType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Cursor#getElementType() }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public ElementType getElementType() {
|
|
||||||
return this.elementType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Cursor#getMemberName() }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public String getMemberName() {
|
|
||||||
return this.memberName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Cursor#getType() }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Class<?> getType() {
|
|
||||||
return loadClass(this.loader, getTypeName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Cursor#getField() }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Field getField() {
|
|
||||||
if (this.elementType != ElementType.FIELD) {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Cursor#getConstructor() }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Constructor<?> getConstructor() {
|
|
||||||
if (this.elementType != ElementType.CONSTRUCTOR) {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Cursor#getMethod() }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Method getMethod() {
|
|
||||||
if (this.elementType != ElementType.METHOD) {
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See {@link Cursor#getAnnotation(java.lang.Class) }.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
final AnnotatedElement ae;
|
|
||||||
switch (this.elementType) {
|
|
||||||
case TYPE:
|
|
||||||
ae = getType();
|
|
||||||
break;
|
|
||||||
case FIELD:
|
|
||||||
ae = getField();
|
|
||||||
break;
|
|
||||||
case METHOD:
|
|
||||||
ae = getMethod();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new AssertionError(this.elementType);
|
|
||||||
}
|
|
||||||
return ae.getAnnotation(annotationClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
// private
|
|
||||||
|
|
||||||
private static void addFiles(ClassLoader loader, String resourceName, Set<File> files)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
final Enumeration<URL> resourceEnum = loader.getResources(resourceName);
|
|
||||||
while (resourceEnum.hasMoreElements()) {
|
|
||||||
final URL url = resourceEnum.nextElement();
|
|
||||||
if (LOG.isTraceEnabled()) {
|
|
||||||
LOG.trace("Resource URL: {}", url);
|
|
||||||
}
|
|
||||||
// Handle JBoss VFS URL's which look like (example package 'nl.dvelop'):
|
|
||||||
// vfs:/foo/bar/website.war/WEB-INF/classes/nl/dvelop/
|
|
||||||
// vfs:/foo/bar/website.war/WEB-INF/lib/dwebcore-0.0.1.jar/nl/dvelop/
|
|
||||||
String protocol = url.getProtocol();
|
|
||||||
final boolean isVfs = "vfs".equals(protocol);
|
|
||||||
if ("file".equals(protocol) || isVfs) {
|
|
||||||
final File dir = toFile(url);
|
|
||||||
if (dir.isDirectory()) {
|
|
||||||
files.add(dir);
|
|
||||||
} else if (isVfs) {
|
|
||||||
//Jar file via JBoss VFS protocol - strip package name
|
|
||||||
String jarPath = dir.getPath();
|
|
||||||
final int idx = jarPath.indexOf(".jar");
|
|
||||||
if (idx > -1) {
|
|
||||||
jarPath = jarPath.substring(0, idx + 4);
|
|
||||||
final File jarFile = new File(jarPath);
|
|
||||||
if (jarFile.isFile()) {
|
|
||||||
files.add(jarFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw assertionError("Not a recognized file URL: %s", url);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Resource in Jar File
|
|
||||||
final File jarFile =
|
|
||||||
toFile(((JarURLConnection)url.openConnection()).getJarFileURL());
|
|
||||||
if (jarFile.isFile()) {
|
|
||||||
files.add(jarFile);
|
|
||||||
} else {
|
|
||||||
throw assertionError("Not a File: %s", jarFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
return new File(url.toURI());
|
|
||||||
} catch (URISyntaxException ex) {
|
|
||||||
throw new IOException(ex.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void detect(final ClassIterator iterator) throws IOException {
|
|
||||||
InputStream stream;
|
|
||||||
boolean mustEndInClass = iterator instanceof ClassFileIterator;
|
|
||||||
while ((stream = iterator.next(this.filter)) != null) {
|
|
||||||
try {
|
|
||||||
this.cpBuffer.readFrom(stream);
|
|
||||||
String name = iterator.getName();
|
|
||||||
// SOME files can actually have CAFEBABE (binary files), but are NOT CLASSFILES! Explicitly define this!
|
|
||||||
if (hasCafebabe(this.cpBuffer)) {
|
|
||||||
if (mustEndInClass && !name.endsWith(".class")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (LOG.isTraceEnabled()) {
|
|
||||||
LOG.trace("Class File: {}", name);
|
|
||||||
}
|
|
||||||
read(this.cpBuffer);
|
|
||||||
} // else ignore
|
|
||||||
} finally {
|
|
||||||
// closing InputStream from ZIP Entry is handled by ZipFileIterator
|
|
||||||
if (iterator.isFile()) {
|
|
||||||
stream.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
readVersion(di);
|
|
||||||
readConstantPoolEntries(di);
|
|
||||||
readAccessFlags(di);
|
|
||||||
readThisClass(di);
|
|
||||||
readSuperClass(di);
|
|
||||||
readInterfaces(di);
|
|
||||||
readFields(di);
|
|
||||||
readMethods(di);
|
|
||||||
readAttributes(di, ElementType.TYPE);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
di.skipBytes(4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if (readConstantPoolEntry(di, i)) {
|
|
||||||
// double slot
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 {
|
|
||||||
|
|
||||||
final int tag = di.readUnsignedByte();
|
|
||||||
switch (tag) {
|
|
||||||
case CP_METHOD_TYPE:
|
|
||||||
di.skipBytes(2); // readUnsignedShort()
|
|
||||||
return false;
|
|
||||||
case CP_METHOD_HANDLE:
|
|
||||||
di.skipBytes(3);
|
|
||||||
return false;
|
|
||||||
case CP_INTEGER:
|
|
||||||
case CP_FLOAT:
|
|
||||||
case CP_REF_FIELD:
|
|
||||||
case CP_REF_METHOD:
|
|
||||||
case CP_REF_INTERFACE:
|
|
||||||
case CP_NAME_AND_TYPE:
|
|
||||||
case CP_INVOKE_DYNAMIC:
|
|
||||||
di.skipBytes(4); // readInt() / readFloat() / readUnsignedShort() * 2
|
|
||||||
return false;
|
|
||||||
case CP_LONG:
|
|
||||||
case CP_DOUBLE:
|
|
||||||
di.skipBytes(8); // readLong() / readDouble()
|
|
||||||
return true;
|
|
||||||
case CP_UTF8:
|
|
||||||
this.constantPool[index] = di.readUTF();
|
|
||||||
return false;
|
|
||||||
case CP_CLASS:
|
|
||||||
case CP_STRING:
|
|
||||||
// reference to CP_UTF8 entry. The referenced index can have a higher number!
|
|
||||||
this.constantPool[index] = di.readUnsignedShort();
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
throw new ClassFormatError(
|
|
||||||
"Unkown tag value for constant pool entry: " + tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readAccessFlags(final DataInput di) throws IOException {
|
|
||||||
di.skipBytes(2); // u2
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readThisClass(final DataInput di) throws IOException {
|
|
||||||
this.typeName = resolveUtf8(di);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readSuperClass(final DataInput di) throws IOException {
|
|
||||||
di.skipBytes(2); // u2
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
final int count = di.readUnsignedShort();
|
|
||||||
for (int i = 0; i < count; ++i) {
|
|
||||||
readAccessFlags(di);
|
|
||||||
this.memberName = resolveUtf8(di);
|
|
||||||
// decriptor is Field type in raw format, we do not need it, so skip
|
|
||||||
//final String descriptor = resolveUtf8(di);
|
|
||||||
di.skipBytes(2);
|
|
||||||
LOG.trace("Field: {}", this.memberName);
|
|
||||||
readAttributes(di, ElementType.FIELD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readMethods(final DataInput di) throws IOException {
|
|
||||||
final int count = di.readUnsignedShort();
|
|
||||||
for (int i = 0; i < count; ++i) {
|
|
||||||
readAccessFlags(di);
|
|
||||||
this.memberName = resolveUtf8(di);
|
|
||||||
this.methodDescriptor = resolveUtf8(di);
|
|
||||||
LOG.trace("Method: {}", this.memberName);
|
|
||||||
readAttributes(di, "<init>".equals(this.memberName) ? ElementType.CONSTRUCTOR : ElementType.METHOD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) ||
|
|
||||||
"RuntimeInvisibleAnnotations".equals(name))) {
|
|
||||||
LOG.trace("Attribute: {}", name);
|
|
||||||
readAnnotations(di, reporterType);
|
|
||||||
} else {
|
|
||||||
LOG.trace("Attribute: {} (ignored)", name);
|
|
||||||
di.skipBytes(length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readAnnotations(final DataInput di, final ElementType elementType)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
// the number of Runtime(In)VisibleAnnotations
|
|
||||||
final int count = di.readUnsignedShort();
|
|
||||||
for (int i = 0; i < count; ++i) {
|
|
||||||
final String rawTypeName = readAnnotation(di);
|
|
||||||
this.annotationType = this.annotations.get(rawTypeName);
|
|
||||||
if (this.annotationType == null) {
|
|
||||||
LOG.trace("Annotation: {} (ignored)", rawTypeName);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
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 {
|
|
||||||
di.skipBytes(2);
|
|
||||||
}
|
|
||||||
readAnnotationElementValue(di);
|
|
||||||
}
|
|
||||||
return rawTypeName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void readAnnotationElementValue(final DataInput di) throws IOException {
|
|
||||||
final int tag = di.readUnsignedByte();
|
|
||||||
switch (tag) {
|
|
||||||
case BYTE:
|
|
||||||
case CHAR:
|
|
||||||
case DOUBLE:
|
|
||||||
case FLOAT:
|
|
||||||
case INT:
|
|
||||||
case LONG:
|
|
||||||
case SHORT:
|
|
||||||
case BOOLEAN:
|
|
||||||
case STRING:
|
|
||||||
di.skipBytes(2);
|
|
||||||
break;
|
|
||||||
case ENUM:
|
|
||||||
di.skipBytes(4); // 2 * u2
|
|
||||||
break;
|
|
||||||
case CLASS:
|
|
||||||
di.skipBytes(2);
|
|
||||||
break;
|
|
||||||
case ANNOTATION:
|
|
||||||
readAnnotation(di);
|
|
||||||
break;
|
|
||||||
case ARRAY:
|
|
||||||
final int count = di.readUnsignedShort();
|
|
||||||
for (int i = 0; i < count; ++i) {
|
|
||||||
readAnnotationElementValue(di);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ClassFormatError("Not a valid annotation element type tag: 0x" +
|
|
||||||
Integer.toHexString(tag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 {
|
|
||||||
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 {
|
|
||||||
s = (String)value;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the method arguments of the currently reported annotated method as a
|
|
||||||
* {@code Class} array.
|
|
||||||
*/
|
|
||||||
// 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) {
|
|
||||||
final int n = descriptor.length();
|
|
||||||
// "minimal" descriptor: no arguments: "()V", first character is always '('
|
|
||||||
if (n < 3 || descriptor.charAt(0) != '(') {
|
|
||||||
throw unparseable(descriptor, "Wrong format");
|
|
||||||
}
|
|
||||||
List<Class<?>> args = null;
|
|
||||||
for (int i = 1; i < n; ++i) {
|
|
||||||
char c = descriptor.charAt(i);
|
|
||||||
if (i == 1) {
|
|
||||||
if (c == ')') {
|
|
||||||
return new Class<?>[0];
|
|
||||||
} else {
|
|
||||||
args = new LinkedList<Class<?>>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert args != null;
|
|
||||||
int j;
|
|
||||||
switch (c) {
|
|
||||||
case 'V':
|
|
||||||
args.add(void.class);
|
|
||||||
break;
|
|
||||||
case 'Z':
|
|
||||||
args.add(boolean.class);
|
|
||||||
break;
|
|
||||||
case 'C':
|
|
||||||
args.add(char.class);
|
|
||||||
break;
|
|
||||||
case 'B':
|
|
||||||
args.add(byte.class);
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
args.add(short.class);
|
|
||||||
break;
|
|
||||||
case 'I':
|
|
||||||
args.add(int.class);
|
|
||||||
break;
|
|
||||||
case 'F':
|
|
||||||
args.add(float.class);
|
|
||||||
break;
|
|
||||||
case 'J':
|
|
||||||
args.add(long.class);
|
|
||||||
break;
|
|
||||||
case 'D':
|
|
||||||
args.add(double.class);
|
|
||||||
break;
|
|
||||||
case '[':
|
|
||||||
j = i;
|
|
||||||
do {
|
|
||||||
c = descriptor.charAt(++j);
|
|
||||||
} while (c == '['); // multi dimensional array
|
|
||||||
if (c == 'L') {
|
|
||||||
j = descriptor.indexOf(';', i + 1);
|
|
||||||
}
|
|
||||||
args.add(loadClass(this.loader, descriptor.substring(i, j + 1)));
|
|
||||||
i = j;
|
|
||||||
break;
|
|
||||||
case 'L':
|
|
||||||
j = descriptor.indexOf(';', i + 1);
|
|
||||||
args.add(loadClass(this.loader, descriptor.substring(i + 1, j)));
|
|
||||||
i = j;
|
|
||||||
break;
|
|
||||||
case ')':
|
|
||||||
// end of argument type list, stop parsing
|
|
||||||
return args.toArray(new Class<?>[args.size()]);
|
|
||||||
default:
|
|
||||||
throw unparseable(descriptor, "Not a recognoized type: " + c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw unparseable(descriptor, "No closing parenthesis");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the class, but do not initialize it.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 assertionError(String message, Object... args) {
|
|
||||||
return new AssertionError(String.format(message, args));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
/* Builder.java
|
|
||||||
*
|
|
||||||
* Created: 2014-06-15 (Year-Month-Day)
|
|
||||||
* Character encoding: UTF-8
|
|
||||||
*
|
|
||||||
****************************************** LICENSE *******************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 XIAM Solutions B.V. (http://www.xiam.nl)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.annotation;
|
|
||||||
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code Builder} offers a fluent API for using {@link AnnotationDetector}.
|
|
||||||
* Its only role is to offer a more clean API.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
|
||||||
* @since annotation-detector 3.1.0
|
|
||||||
*/
|
|
||||||
public interface Builder {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the annotation types to report.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Builder forAnnotations(final Class<? extends Annotation>... annotations);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the annotation types to report.
|
|
||||||
*/
|
|
||||||
Builder forAnnotations(Class<? extends Annotation> annotation);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the Element Types to scan. If this method is not called,
|
|
||||||
* {@link ElementType#TYPE} is used as default.
|
|
||||||
* <p>
|
|
||||||
* Valid types are:
|
|
||||||
* <ul>
|
|
||||||
* <li>{@link ElementType#TYPE}
|
|
||||||
* <li>{@link ElementType#METHOD}
|
|
||||||
* <li>{@link ElementType#FIELD}
|
|
||||||
* </ul>
|
|
||||||
* An {@code IllegalArgumentException} is thrown if another Element Type is specified or
|
|
||||||
* no types are specified.
|
|
||||||
*/
|
|
||||||
Builder on(final ElementType type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the Element Types to scan. If this method is not called,
|
|
||||||
* {@link ElementType#TYPE} is used as default.
|
|
||||||
* <p>
|
|
||||||
* Valid types are:
|
|
||||||
* <ul>
|
|
||||||
* <li>{@link ElementType#TYPE}
|
|
||||||
* <li>{@link ElementType#METHOD}
|
|
||||||
* <li>{@link ElementType#FIELD}
|
|
||||||
* </ul>
|
|
||||||
* An {@code IllegalArgumentException} is thrown if another Element Type is specified or
|
|
||||||
* no types are specified.
|
|
||||||
*/
|
|
||||||
Builder on(final ElementType... types);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter the scanned Class Files based on its name and the directory or jar file it is
|
|
||||||
* stored.
|
|
||||||
* <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>
|
|
||||||
* 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>
|
|
||||||
* 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>
|
|
||||||
* Note that all non-Class Files are already filtered and not seen by the filter.
|
|
||||||
*
|
|
||||||
* @param filter The filter, never {@code null}
|
|
||||||
*/
|
|
||||||
Builder filter(final FilenameFilter filter);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Report the detected annotations to the specified {@code Reporter} instance.
|
|
||||||
*
|
|
||||||
* @see Reporter#report(dorkbox.util.annotations.Cursor)
|
|
||||||
* @see #collect(dorkbox.util.annotations.ReporterFunction)
|
|
||||||
*/
|
|
||||||
void report(final Reporter reporter) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Report the detected annotations to the specified {@code ReporterFunction} instance and
|
|
||||||
* collect the returned values of
|
|
||||||
* {@link ReporterFunction#report(dorkbox.util.annotations.Cursor) }.
|
|
||||||
* The collected values are returned as a {@code List}.
|
|
||||||
*
|
|
||||||
* @see #report(dorkbox.util.annotations.Reporter)
|
|
||||||
*/
|
|
||||||
<T> List<T> collect(final ReporterFunction<T> reporter) throws IOException;
|
|
||||||
}
|
|
|
@ -1,240 +0,0 @@
|
||||||
/* ClassFileBuffer.java
|
|
||||||
*
|
|
||||||
* Created: 2011-10-10 (Year-Month-Day)
|
|
||||||
* Character encoding: UTF-8
|
|
||||||
*
|
|
||||||
****************************************** LICENSE *******************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011 - 2013 XIAM Solutions B.V. (http://www.xiam.nl)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.annotation;
|
|
||||||
|
|
||||||
import java.io.DataInput;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@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>
|
|
||||||
* 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 {
|
|
||||||
|
|
||||||
private byte[] buffer;
|
|
||||||
private int size; // the number of significant bytes read
|
|
||||||
private int pointer; // the "read pointer"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new, empty {@code ClassFileBuffer} with the default initial capacity (8 kb).
|
|
||||||
*/
|
|
||||||
ClassFileBuffer() {
|
|
||||||
this(8 * 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new, empty {@code ClassFileBuffer} with the specified initial capacity.
|
|
||||||
* The initial capacity must be greater than zero. The internal buffer will grow
|
|
||||||
* automatically when a higher capacity is required. However, buffer resizing occurs
|
|
||||||
* extra overhead. So in good initial capacity is important in performance critical
|
|
||||||
* situations.
|
|
||||||
*/
|
|
||||||
ClassFileBuffer(final int initialCapacity) {
|
|
||||||
if (initialCapacity < 1) {
|
|
||||||
throw new IllegalArgumentException("initialCapacity < 1: " + initialCapacity);
|
|
||||||
}
|
|
||||||
this.buffer = new byte[initialCapacity];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear and fill the buffer of this {@code ClassFileBuffer} with the
|
|
||||||
* supplied byte stream.
|
|
||||||
* The read pointer is reset to the start of the byte array.
|
|
||||||
*/
|
|
||||||
public void readFrom(final InputStream in) throws IOException {
|
|
||||||
this.pointer = 0;
|
|
||||||
this.size = 0;
|
|
||||||
int n;
|
|
||||||
do {
|
|
||||||
n = in.read(this.buffer, this.size, this.buffer.length - this.size);
|
|
||||||
if (n > 0) {
|
|
||||||
this.size += n;
|
|
||||||
}
|
|
||||||
resizeIfNeeded();
|
|
||||||
} while (n >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 {
|
|
||||||
if (position < 0) {
|
|
||||||
throw new IllegalArgumentException("position < 0: " + position);
|
|
||||||
}
|
|
||||||
if (position > this.size) {
|
|
||||||
throw new EOFException();
|
|
||||||
}
|
|
||||||
this.pointer = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the size (in bytes) of this Java ClassFile file.
|
|
||||||
*/
|
|
||||||
public int size() {
|
|
||||||
return this.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DataInput
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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 {
|
|
||||||
|
|
||||||
if (length < 0 || offset < 0 || offset + length > bytes.length) {
|
|
||||||
throw new IndexOutOfBoundsException();
|
|
||||||
}
|
|
||||||
if (this.pointer + length > this.size) {
|
|
||||||
throw new EOFException();
|
|
||||||
}
|
|
||||||
System.arraycopy(this.buffer, this.pointer, bytes, offset, length);
|
|
||||||
this.pointer += length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int skipBytes(final int n) throws IOException {
|
|
||||||
seek(this.pointer + n);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte readByte() throws IOException {
|
|
||||||
if (this.pointer >= this.size) {
|
|
||||||
throw new EOFException();
|
|
||||||
}
|
|
||||||
return this.buffer[this.pointer++];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean readBoolean() throws IOException {
|
|
||||||
return readByte() != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readUnsignedByte() throws IOException {
|
|
||||||
if (this.pointer >= this.size) {
|
|
||||||
throw new EOFException();
|
|
||||||
}
|
|
||||||
return read();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readUnsignedShort() throws IOException {
|
|
||||||
if (this.pointer + 2 > this.size) {
|
|
||||||
throw new EOFException();
|
|
||||||
}
|
|
||||||
return (read() << 8) + read();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public short readShort() throws IOException {
|
|
||||||
return (short)readUnsignedShort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char readChar() throws IOException {
|
|
||||||
return (char)readUnsignedShort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int readInt() throws IOException {
|
|
||||||
if (this.pointer + 4 > this.size) {
|
|
||||||
throw new EOFException();
|
|
||||||
}
|
|
||||||
return (read() << 24) +
|
|
||||||
(read() << 16) +
|
|
||||||
(read() << 8) +
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long readLong() throws IOException {
|
|
||||||
if (this.pointer + 8 > this.size) {
|
|
||||||
throw new EOFException();
|
|
||||||
}
|
|
||||||
return ((long)read() << 56) +
|
|
||||||
((long)read() << 48) +
|
|
||||||
((long)read() << 40) +
|
|
||||||
((long)read() << 32) +
|
|
||||||
(read() << 24) +
|
|
||||||
(read() << 16) +
|
|
||||||
(read() << 8) +
|
|
||||||
read();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float readFloat() throws IOException {
|
|
||||||
return Float.intBitsToFloat(readInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double readDouble() throws IOException {
|
|
||||||
return Double.longBitsToDouble(readLong());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This methods throws an {@link UnsupportedOperationException} because the method
|
|
||||||
* is deprecated and not used in the context of this implementation.
|
|
||||||
*
|
|
||||||
* @deprecated Does not support UTF-8, use readUTF() instead
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
@Deprecated
|
|
||||||
public String readLine() throws IOException {
|
|
||||||
throw new UnsupportedOperationException("readLine() is deprecated and not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String readUTF() throws IOException {
|
|
||||||
return DataInputStream.readUTF(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// private
|
|
||||||
|
|
||||||
private int read() {
|
|
||||||
return this.buffer[this.pointer++] & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
this.buffer = newBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,135 +0,0 @@
|
||||||
/* ClassFileIterator.java
|
|
||||||
*
|
|
||||||
* Created: 2011-10-10 (Year-Month-Day)
|
|
||||||
* Character encoding: UTF-8
|
|
||||||
*
|
|
||||||
****************************************** LICENSE *******************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011 - 2013 XIAM Solutions B.V. (http://www.xiam.nl)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.annotation;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code ClassFileIterator} is used to iterate over all Java ClassFile files available within
|
|
||||||
* a specific context.
|
|
||||||
* <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 {
|
|
||||||
|
|
||||||
private FileIterator fileIter;
|
|
||||||
protected final String[] pkgNameFilter;
|
|
||||||
|
|
||||||
private ZipFileIterator zipIter;
|
|
||||||
private boolean isFile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@code ClassFileIterator} returning all Java ClassFile files available
|
|
||||||
* from the specified files and/or directories, including sub directories.
|
|
||||||
* <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) {
|
|
||||||
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>
|
|
||||||
* 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) {
|
|
||||||
this.fileIter = new FileIterator(filesOrDirectories);
|
|
||||||
this.pkgNameFilter = pkgNameFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the name of the Java ClassFile returned from the last call to {@link #next()}.
|
|
||||||
* The name is either the path name of a file or the name of an ZIP/JAR file entry.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
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 {@code true} if the current {@link InputStream} is reading from a plain
|
|
||||||
* {@link File}.
|
|
||||||
* Return {@code false} if the current {@link InputStream} is reading from a
|
|
||||||
* ZIP File Entry.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean isFile() {
|
|
||||||
return this.isFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the next Java ClassFile as an {@code InputStream}.
|
|
||||||
* <p>
|
|
||||||
* NOTICE: Client code MUST close the returned {@code InputStream}!
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
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 {
|
|
||||||
final String path = file.getPath();
|
|
||||||
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")) {
|
|
||||||
this.zipIter = new ZipFileIterator(file, this.pkgNameFilter);
|
|
||||||
} // else just ignore
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final InputStream is = this.zipIter.next(filter);
|
|
||||||
if (is == null) {
|
|
||||||
this.zipIter = null;
|
|
||||||
} else {
|
|
||||||
this.isFile = false;
|
|
||||||
return is;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// private
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
/* ClassFileIterator.java
|
|
||||||
*
|
|
||||||
* Created: 2011-10-10 (Year-Month-Day)
|
|
||||||
* Character encoding: UTF-8
|
|
||||||
*
|
|
||||||
****************************************** LICENSE *******************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011 - 2013 XIAM Solutions B.V. (http://www.xiam.nl)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.annotation;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code ClassFileIterator} is used to iterate over all Java ClassFile files available within
|
|
||||||
* a specific context.
|
|
||||||
* <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 {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the name of the Java ClassFile returned from the last call to {@link #next()}.
|
|
||||||
* The name is either the path name of a file or the name of an ZIP/JAR file entry.
|
|
||||||
*/
|
|
||||||
String getName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return {@code true} if the current {@link InputStream} is reading from a plain
|
|
||||||
* {@link File}.
|
|
||||||
* Return {@code false} if the current {@link InputStream} is reading from a
|
|
||||||
* ZIP File Entry.
|
|
||||||
*/
|
|
||||||
boolean isFile();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the next Java ClassFile as an {@code InputStream}.
|
|
||||||
* <p>
|
|
||||||
* NOTICE: Client code MUST close the returned {@code InputStream}!
|
|
||||||
*/
|
|
||||||
InputStream next(final FilenameFilter filter) throws IOException;
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
/* Cursor.java
|
|
||||||
*
|
|
||||||
* Created: 2014-06-15 (Year-Month-Day)
|
|
||||||
* Character encoding: UTF-8
|
|
||||||
*
|
|
||||||
****************************************** LICENSE *******************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 XIAM Solutions B.V. (http://www.xiam.nl)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.annotation;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.annotation.ElementType;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code Cursor} offers a "cursor interface" for working with {@link AnnotationDetector}.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
|
||||||
* @since annotation-detector 3.1.0
|
|
||||||
*/
|
|
||||||
public interface Cursor {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the type name of the currently reported Java Class File.
|
|
||||||
*/
|
|
||||||
String getTypeName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the Annotation Type currently reported.
|
|
||||||
*/
|
|
||||||
Class<? extends Annotation> getAnnotationType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the {@code ElementType} of the currently reported {@code Annotation}.
|
|
||||||
*/
|
|
||||||
ElementType getElementType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the member name of the currently reported {@code Annotation}.
|
|
||||||
* In case of an annotation on type level, "<clinit>" is reported.
|
|
||||||
*/
|
|
||||||
String getMemberName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the {@link Class type} of the currently reported Java Class File.
|
|
||||||
*/
|
|
||||||
Class<?> getType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the {@link Constructor} instance of the currently reported annotated Constructor.
|
|
||||||
*/
|
|
||||||
Constructor<?> getConstructor();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the {@link Field} instance of the currently reported annotated Field.
|
|
||||||
*/
|
|
||||||
Field getField();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the {@link Method} instance of the currently reported annotated Method.
|
|
||||||
*/
|
|
||||||
Method getMethod();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the {@code Annotation} of the reported Annotated Element.
|
|
||||||
*/
|
|
||||||
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
package dorkbox.util.annotation;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import dorkbox.util.FileUtil;
|
|
||||||
|
|
||||||
public class CustomClassloaderIterator implements ClassIterator {
|
|
||||||
|
|
||||||
private volatile 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 {
|
|
||||||
// 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>();
|
|
||||||
Iterator<URL> iterator = fileNames.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
URL url = iterator.next();
|
|
||||||
if (!url.getProtocol().equals("box")) {
|
|
||||||
try {
|
|
||||||
File file = FileUtil.normalize(new File(url.toURI()));
|
|
||||||
files.add(file);
|
|
||||||
iterator.remove();
|
|
||||||
} catch (URISyntaxException ex) {
|
|
||||||
throw new IOException(ex.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.isEmpty()) {
|
|
||||||
this.classFileIterator = null;
|
|
||||||
} else {
|
|
||||||
this.classFileIterator = new ClassFileIterator(files.toArray(new File[0]), packageNames);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.loaderFilesIterator = fileNames.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
// not needed
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFile() {
|
|
||||||
if (this.classFileIterator != null) {
|
|
||||||
return this.classFileIterator.isFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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 {
|
|
||||||
String name = this.classFileIterator.getName();
|
|
||||||
if (name.endsWith(".class")) {
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.loaderFilesIterator.hasNext()) {
|
|
||||||
URL next = this.loaderFilesIterator.next();
|
|
||||||
return next.openStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
/* FileIterator.java
|
|
||||||
*
|
|
||||||
* Created: 2011-10-10 (Year-Month-Day)
|
|
||||||
* Character encoding: UTF-8
|
|
||||||
*
|
|
||||||
****************************************** LICENSE *******************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011 - 2013 XIAM Solutions B.V. (http://www.xiam.nl)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.annotation;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Deque;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code FileIterator} enables iteration over all files in a directory and all its sub
|
|
||||||
* directories.
|
|
||||||
* <p>
|
|
||||||
* Usage:
|
|
||||||
* <pre>
|
|
||||||
* FileIterator iter = new FileIterator(new File("./src"));
|
|
||||||
* File f;
|
|
||||||
* while ((f = iter.next()) != null) {
|
|
||||||
* // do something with f
|
|
||||||
* assert f == iter.getCurrent();
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:rmuller@xiam.nl">Ronald K. Muller</a>
|
|
||||||
* @since annotation-detector 3.0.0
|
|
||||||
*/
|
|
||||||
final class FileIterator {
|
|
||||||
|
|
||||||
private final Deque<File> stack = new LinkedList<File>();
|
|
||||||
private int rootCount;
|
|
||||||
private File currentRoot;
|
|
||||||
private File current;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@code FileIterator} using the specified 'filesOrDirectories' as root.
|
|
||||||
* <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).
|
|
||||||
*
|
|
||||||
* @param filesOrDirectories Zero or more {@link File} objects, which are iterated
|
|
||||||
* in the specified order (depth first)
|
|
||||||
*/
|
|
||||||
FileIterator(final File... filesOrDirectories) {
|
|
||||||
addReverse(filesOrDirectories);
|
|
||||||
this.rootCount = this.stack.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the last returned file or {@code null} if no more files are available.
|
|
||||||
*
|
|
||||||
* @see #next()
|
|
||||||
*/
|
|
||||||
File getFile() {
|
|
||||||
return this.current;
|
|
||||||
}
|
|
||||||
|
|
||||||
File getRootFile() {
|
|
||||||
return this.currentRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Relativize the absolute full (file) 'path' against the current root file.
|
|
||||||
* <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>
|
|
||||||
* Note: the paths are not canonicalized!
|
|
||||||
*/
|
|
||||||
String relativize(final String path) {
|
|
||||||
assert path.startsWith(this.currentRoot.getPath());
|
|
||||||
return path.substring(this.currentRoot.getPath().length() + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return {@code true} if the current file is one of the files originally
|
|
||||||
* specified as one of the constructor file parameters, i.e. is a root file
|
|
||||||
* or directory.
|
|
||||||
*/
|
|
||||||
boolean isRootFile() {
|
|
||||||
if (this.current == null) {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
return this.stack.size() < this.rootCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the next {@link File} object or {@code null} if no more files are
|
|
||||||
* available.
|
|
||||||
*
|
|
||||||
* @see #getFile()
|
|
||||||
*/
|
|
||||||
File next() {
|
|
||||||
if (this.stack.isEmpty()) {
|
|
||||||
this.current = null;
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
this.current = this.stack.removeLast();
|
|
||||||
if (this.current.isDirectory()) {
|
|
||||||
if (this.stack.size() < this.rootCount) {
|
|
||||||
this.rootCount = this.stack.size();
|
|
||||||
this.currentRoot = this.current;
|
|
||||||
}
|
|
||||||
addReverse(this.current.listFiles());
|
|
||||||
return next();
|
|
||||||
} else {
|
|
||||||
return this.current;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// private
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the specified files in reverse order.
|
|
||||||
*/
|
|
||||||
private void addReverse(final File[] files) {
|
|
||||||
for (int i = files.length - 1; i >= 0; --i) {
|
|
||||||
this.stack.add(files[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/* Reporter.java
|
|
||||||
*
|
|
||||||
* Created: 2014-06-15 (Year-Month-Day)
|
|
||||||
* Character encoding: UTF-8
|
|
||||||
*
|
|
||||||
****************************************** LICENSE *******************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 XIAM Solutions B.V. (http://www.xiam.nl)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.annotation;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code Reporter} is used to report the detected annotations.
|
|
||||||
* <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>
|
|
||||||
* @since annotation-detector 3.1.0
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/* ReporterFunction.java
|
|
||||||
*
|
|
||||||
* Created: 2014-06-15 (Year-Month-Day)
|
|
||||||
* Character encoding: UTF-8
|
|
||||||
*
|
|
||||||
****************************************** LICENSE *******************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 XIAM Solutions B.V. (http://www.xiam.nl)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
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>
|
|
||||||
* @since annotation-detector 3.1.0
|
|
||||||
*/
|
|
||||||
public interface ReporterFunction<T> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called when an {@code Annotation} is detected.
|
|
||||||
* Invoke methods on the {@code Cursor} to get more specific information about the
|
|
||||||
* {@code Annotation}.
|
|
||||||
*/
|
|
||||||
T report(Cursor cursor);
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
/* ZipFileIterator.java
|
|
||||||
*
|
|
||||||
* Created: 2011-10-10 (Year-Month-Day)
|
|
||||||
* Character encoding: UTF-8
|
|
||||||
*
|
|
||||||
****************************************** LICENSE *******************************************
|
|
||||||
*
|
|
||||||
* Copyright (c) 2011 - 2013 XIAM Solutions B.V. (http://www.xiam.nl)
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.util.annotation;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
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>
|
|
||||||
* It is possible to specify an (optional) entry name filter.
|
|
||||||
* <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 {
|
|
||||||
|
|
||||||
private final File file;
|
|
||||||
private final ZipFile zipFile;
|
|
||||||
private final String[] entryNameFilter;
|
|
||||||
private final Enumeration<? extends ZipEntry> entries;
|
|
||||||
|
|
||||||
private ZipEntry current;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@code ZipFileIterator} instance.
|
|
||||||
*
|
|
||||||
* @param zipFile The ZIP file used to iterate over all entries
|
|
||||||
* @param entryNameFilter (optional) file name filter. Only entry names starting with
|
|
||||||
* one of the specified names in the filter are returned
|
|
||||||
*/
|
|
||||||
ZipFileIterator(final File file, final String[] entryNameFilter) throws IOException {
|
|
||||||
this.file = file;
|
|
||||||
this.zipFile = new ZipFile(file);
|
|
||||||
this.entryNameFilter = entryNameFilter;
|
|
||||||
|
|
||||||
this.entries = this.zipFile.entries();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ZipEntry getEntry() {
|
|
||||||
return this.current;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InputStream next(final FilenameFilter filter) throws IOException {
|
|
||||||
while (this.entries.hasMoreElements()) {
|
|
||||||
this.current = this.entries.nextElement();
|
|
||||||
if (filter == null || accept(this.current, filter)) {
|
|
||||||
return this.zipFile.getInputStream(this.current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no more entries in this ZipFile, so close ZipFile
|
|
||||||
try {
|
|
||||||
// zipFile is never null here
|
|
||||||
this.zipFile.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
// suppress IOException, otherwise close() is called twice
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean accept(final ZipEntry entry, final FilenameFilter filter) {
|
|
||||||
if (entry.isDirectory()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final String name = entry.getName();
|
|
||||||
if (name.endsWith(".class") && filter.accept(this.file, name)) {
|
|
||||||
if (this.entryNameFilter == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (final String entryName : this.entryNameFilter) {
|
|
||||||
if (name.startsWith(entryName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user