diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/AnnotationDefaults.java b/Dorkbox-Util/src/dorkbox/util/annotation/AnnotationDefaults.java deleted file mode 100644 index 26b8a17..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/AnnotationDefaults.java +++ /dev/null @@ -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 getTypeName = new ReporterFunction() { - @Override - public String report(Cursor cursor) { - return cursor.getTypeName(); - } - }; - public static final ReporterFunction> getAnnotationType = new ReporterFunction>() { - @Override - public Class report(Cursor cursor) { - return cursor.getAnnotationType(); - } - }; - public static final ReporterFunction getElementType = new ReporterFunction() { - @Override - public ElementType report(Cursor cursor) { - return cursor.getElementType(); - } - }; - public static final ReporterFunction getMemberName = new ReporterFunction() { - @Override - public String report(Cursor cursor) { - return cursor.getMemberName(); - } - }; - public static final ReporterFunction> getType = new ReporterFunction>() { - @Override - public Class report(Cursor cursor) { - return cursor.getType(); - } - }; - public static final ReporterFunction> getConstructor = new ReporterFunction>() { - @Override - public Constructor report(Cursor cursor) { - return cursor.getConstructor(); - } - }; - public static final ReporterFunction getField = new ReporterFunction() { - @Override - public Field report(Cursor cursor) { - return cursor.getField(); - } - }; - public static final ReporterFunction getMethod = new ReporterFunction() { - @Override - public Method report(Cursor cursor) { - return cursor.getMethod(); - } - }; -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/AnnotationDetector.java b/Dorkbox-Util/src/dorkbox/util/annotation/AnnotationDetector.java deleted file mode 100644 index 8509331..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/AnnotationDetector.java +++ /dev/null @@ -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. - *

- * 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}. - *

- * A class file consists of a single ClassFile structure: - *

- * 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}.
- * 
- * References: - * - *

- * Similar projects / libraries: - *

- *

- * All above mentioned projects make use of a byte code manipulation library (like BCEL, - * ASM or Javassist). - * - * @author Ronald K. Muller - * @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 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> annotations; - private FilenameFilter filter; - private Reporter reporter; - - // The current annotation reported - private Class 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 fileNames; - - // scanning the classpath - if (packageNames.length == 0) { - pkgNameFilter = null; - List asList = Arrays.asList(System.getProperty("java.class.path").split(File.pathSeparator)); - fileNames = new ArrayList(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(); - for (final String packageName : pkgNameFilter) { - final Enumeration 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 files = new HashSet(); - - 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 annotation) { - this.annotations = new HashMap>(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... annotations) { - this.annotations = new HashMap>(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 List collect(final ReporterFunction reporter) throws IOException { - final List list = new ArrayList(); - 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 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 getAnnotation(final Class 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 files) - throws IOException { - - final Enumeration 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, "".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> 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>(); - } - } - 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)); - } - -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/Builder.java b/Dorkbox-Util/src/dorkbox/util/annotation/Builder.java deleted file mode 100644 index 32e4ef3..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/Builder.java +++ /dev/null @@ -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 Ronald K. Muller - * @since annotation-detector 3.1.0 - */ -public interface Builder { - - /** - * Specify the annotation types to report. - */ - @SuppressWarnings("unchecked") - Builder forAnnotations(final Class... annotations); - - /** - * Specify the annotation types to report. - */ - Builder forAnnotations(Class annotation); - - - /** - * Specify the Element Types to scan. If this method is not called, - * {@link ElementType#TYPE} is used as default. - *

- * Valid types are: - *

    - *
  • {@link ElementType#TYPE} - *
  • {@link ElementType#METHOD} - *
  • {@link ElementType#FIELD} - *
- * 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. - *

- * Valid types are: - *

    - *
  • {@link ElementType#TYPE} - *
  • {@link ElementType#METHOD} - *
  • {@link ElementType#FIELD} - *
- * 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. - *

- * 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. - *

- * 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. - *

- * 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}. - *

- * 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) - */ - List collect(final ReporterFunction reporter) throws IOException; -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/ClassFileBuffer.java b/Dorkbox-Util/src/dorkbox/util/annotation/ClassFileBuffer.java deleted file mode 100644 index a678a17..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/ClassFileBuffer.java +++ /dev/null @@ -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. - *

- * Note that Java ClassFile files can grow really big, - * {@code com.sun.corba.se.impl.logging.ORBUtilSystemException} is 128.2 kb! - * - * @author Ronald K. Muller - * @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; - } - } - -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/ClassFileIterator.java b/Dorkbox-Util/src/dorkbox/util/annotation/ClassFileIterator.java deleted file mode 100644 index 5f2641b..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/ClassFileIterator.java +++ /dev/null @@ -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. - *

- * For every Java ClassFile ({@code .class}) an {@link InputStream} is returned. - * - * @author Ronald K. Muller - * @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. - *

- * 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. - *

- * 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}. - *

- * 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); - } -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/ClassIterator.java b/Dorkbox-Util/src/dorkbox/util/annotation/ClassIterator.java deleted file mode 100644 index 862072a..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/ClassIterator.java +++ /dev/null @@ -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. - *

- * For every Java ClassFile ({@code .class}) an {@link InputStream} is returned. - * - * @author Ronald K. Muller - * @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}. - *

- * NOTICE: Client code MUST close the returned {@code InputStream}! - */ - InputStream next(final FilenameFilter filter) throws IOException; -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/Cursor.java b/Dorkbox-Util/src/dorkbox/util/annotation/Cursor.java deleted file mode 100644 index acaa969..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/Cursor.java +++ /dev/null @@ -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 Ronald K. Muller - * @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 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 getAnnotation(Class annotationClass); - -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/CustomClassloaderIterator.java b/Dorkbox-Util/src/dorkbox/util/annotation/CustomClassloaderIterator.java deleted file mode 100644 index 8ad3ff6..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/CustomClassloaderIterator.java +++ /dev/null @@ -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 loaderFilesIterator; - private ClassFileIterator classFileIterator; - - // have to support - // 1 - scanning the classpath - // 2 - scanning a specific package - public CustomClassloaderIterator(List 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 files = new HashSet(); - Iterator 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; - } - -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/FileIterator.java b/Dorkbox-Util/src/dorkbox/util/annotation/FileIterator.java deleted file mode 100644 index 5b5668b..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/FileIterator.java +++ /dev/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. - *

- * Usage: - *

- * FileIterator iter = new FileIterator(new File("./src"));
- * File f;
- * while ((f = iter.next()) != null) {
- *     // do something with f
- *     assert f == iter.getCurrent();
- * }
- * 
- * - * @author Ronald K. Muller - * @since annotation-detector 3.0.0 - */ -final class FileIterator { - - private final Deque stack = new LinkedList(); - private int rootCount; - private File currentRoot; - private File current; - - /** - * Create a new {@code FileIterator} using the specified 'filesOrDirectories' as root. - *

- * 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. - *

- * Example:
- * Let current root be "/path/to/dir". - * Then {@code relativize("/path/to/dir/with/file.ext")} equals "with/file.ext" (without - * leading '/'). - *

- * 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]); - } - } - -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/Reporter.java b/Dorkbox-Util/src/dorkbox/util/annotation/Reporter.java deleted file mode 100644 index a737b74..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/Reporter.java +++ /dev/null @@ -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. - *

- * 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 Ronald K. Muller - * @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); - -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/ReporterFunction.java b/Dorkbox-Util/src/dorkbox/util/annotation/ReporterFunction.java deleted file mode 100644 index 5261fdc..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/ReporterFunction.java +++ /dev/null @@ -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 Ronald K. Muller - * @since annotation-detector 3.1.0 - */ -public interface ReporterFunction { - - /** - * 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); -} diff --git a/Dorkbox-Util/src/dorkbox/util/annotation/ZipFileIterator.java b/Dorkbox-Util/src/dorkbox/util/annotation/ZipFileIterator.java deleted file mode 100644 index 98eccbc..0000000 --- a/Dorkbox-Util/src/dorkbox/util/annotation/ZipFileIterator.java +++ /dev/null @@ -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. - *

- * It is possible to specify an (optional) entry name filter. - *

- * The most efficient way of iterating is used, see benchmark in test classes. - * - * @author Ronald K. Muller - * @since annotation-detector 3.0.0 - */ -final class ZipFileIterator { - - private final File file; - private final ZipFile zipFile; - private final String[] entryNameFilter; - private final Enumeration 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; - } -}