Initial import of annotations project
This commit is contained in:
commit
20913ad79c
8
.classpath
Normal file
8
.classpath
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/logging/slf4j-api-1.7.5.jar" sourcepath="/Dependencies/logging/slf4j-api-1.7.5-sources.zip"/>
|
||||
<classpathentry kind="lib" path="libs/dorkboxUtil_v1.0.jar"/>
|
||||
<classpathentry kind="output" path="classes"/>
|
||||
</classpath>
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/classes/
|
17
.project
Normal file
17
.project
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>Annotations</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
33
LICENSE
Normal file
33
LICENSE
Normal file
@ -0,0 +1,33 @@
|
||||
- Dorkbox Annotations - Apache 2.0 License
|
||||
https://github.com/dorkbox
|
||||
Copyright 2014, dorkbox, llc
|
||||
|
||||
|
||||
- AnnotationDetector - Apache 2.0 License
|
||||
https://github.com/rmuller/infomas-asl
|
||||
Copyright 2011 - 2014, XIAM Solutions B.V. (http://www.xiam.nl)
|
||||
|
||||
|
||||
- Dorkbox Utils - Apache 2.0 License
|
||||
https://github.com/dorkbox
|
||||
Copyright 2010, dorkbox, llc
|
||||
|
||||
|
||||
- FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
|
||||
http://commons.apache.org/proper/commons-io/
|
||||
Copyright 2013, ASF
|
||||
Kevin A. Burton
|
||||
Scott Sanders
|
||||
Daniel Rall
|
||||
Christoph.Reck
|
||||
Peter Donald
|
||||
Jeff Turner
|
||||
Matthew Hawthorne
|
||||
Martin Cooper
|
||||
Jeremias Maerki
|
||||
Stephen Colebourne
|
||||
|
||||
|
||||
- SLF4J - MIT License
|
||||
http://www.slf4j.org
|
||||
Copyright 2004-2008, QOS.ch
|
218
LICENSE.Apachev2
Normal file
218
LICENSE.Apachev2
Normal file
@ -0,0 +1,218 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
21
LICENSE.MIT
Normal file
21
LICENSE.MIT
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
76
src/dorkbox/util/annotation/AnnotationDefaults.java
Normal file
76
src/dorkbox/util/annotation/AnnotationDefaults.java
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2014 XIAM Solutions B.V. The Netherlands (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;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
};
|
||||
}
|
954
src/dorkbox/util/annotation/AnnotationDetector.java
Normal file
954
src/dorkbox/util/annotation/AnnotationDetector.java
Normal file
@ -0,0 +1,954 @@
|
||||
/* 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));
|
||||
}
|
||||
|
||||
}
|
119
src/dorkbox/util/annotation/Builder.java
Normal file
119
src/dorkbox/util/annotation/Builder.java
Normal file
@ -0,0 +1,119 @@
|
||||
/* 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;
|
||||
}
|
240
src/dorkbox/util/annotation/ClassFileBuffer.java
Normal file
240
src/dorkbox/util/annotation/ClassFileBuffer.java
Normal file
@ -0,0 +1,240 @@
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
135
src/dorkbox/util/annotation/ClassFileIterator.java
Normal file
135
src/dorkbox/util/annotation/ClassFileIterator.java
Normal file
@ -0,0 +1,135 @@
|
||||
/* 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);
|
||||
}
|
||||
}
|
62
src/dorkbox/util/annotation/ClassIterator.java
Normal file
62
src/dorkbox/util/annotation/ClassIterator.java
Normal file
@ -0,0 +1,62 @@
|
||||
/* 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.
|
||||
*
|
||||
* Modified 2014, dorkbox, llc
|
||||
*/
|
||||
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;
|
||||
}
|
84
src/dorkbox/util/annotation/Cursor.java
Normal file
84
src/dorkbox/util/annotation/Cursor.java
Normal file
@ -0,0 +1,84 @@
|
||||
/* 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);
|
||||
|
||||
}
|
106
src/dorkbox/util/annotation/CustomClassloaderIterator.java
Normal file
106
src/dorkbox/util/annotation/CustomClassloaderIterator.java
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2014 dorkbox, llc
|
||||
*
|
||||
* 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.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;
|
||||
}
|
||||
|
||||
}
|
144
src/dorkbox/util/annotation/FileIterator.java
Normal file
144
src/dorkbox/util/annotation/FileIterator.java
Normal file
@ -0,0 +1,144 @@
|
||||
/* 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]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
46
src/dorkbox/util/annotation/Reporter.java
Normal file
46
src/dorkbox/util/annotation/Reporter.java
Normal file
@ -0,0 +1,46 @@
|
||||
/* 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);
|
||||
|
||||
}
|
40
src/dorkbox/util/annotation/ReporterFunction.java
Normal file
40
src/dorkbox/util/annotation/ReporterFunction.java
Normal file
@ -0,0 +1,40 @@
|
||||
/* 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);
|
||||
}
|
105
src/dorkbox/util/annotation/ZipFileIterator.java
Normal file
105
src/dorkbox/util/annotation/ZipFileIterator.java
Normal file
@ -0,0 +1,105 @@
|
||||
/* 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