diff --git a/LICENSE-2.0.txt b/LICENSE-2.0.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/LICENSE-2.0.txt @@ -0,0 +1,202 @@ + + 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. + + "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. diff --git a/README.md b/README.md index e5e745a3..9cdb7076 100644 --- a/README.md +++ b/README.md @@ -1,152 +1,276 @@ -*Released `org.reflections:reflections:0.9.12` - with support for Java 8* +*Released `org.reflections:reflections:0.10`* -*Reflections library has over 2.5 million downloads per month from Maven Central, and is being used by thousands of projects and libraries. We're looking for maintainers to assist in reviewing pull requests and managing releases, please reach out.* +*Reflections library has ~4 million downloads per month from Maven Central, and is being used by thousands of [projects](https://github.com/ronmamo/reflections/network/dependents) and [libraries](https://mvnrepository.com/artifact/org.reflections/reflections/usages). +Thank you for your continuous support! And apologize for the issues. We're looking for community collaborators to assist in reviewing pull requests and issues, please reach out.* -## Java runtime metadata analysis, in the spirit of [Scannotations](http://bill.burkecentral.com/2008/01/14/scanning-java-annotations-at-runtime/) +# Java runtime metadata analysis, in the spirit of [Scannotations](http://bill.burkecentral.com/2008/01/14/scanning-java-annotations-at-runtime/) -Reflections scans your classpath, indexes the metadata, allows you to query it on runtime and may save and collect that information for many modules within your project. +[![Build Status](https://travis-ci.org/ronmamo/reflections.svg?branch=master)](https://travis-ci.org/ronmamo/reflections) -Using Reflections you can query your metadata such as: - * get all subtypes of some type - * get all types/members annotated with some annotation - * get all resources matching a regular expression - * get all methods with specific signature including parameters, parameter annotations and return type +Reflections scans and indexes your project's classpath metadata, allowing reverse transitive query of the type system on runtime. -[![Build Status](https://travis-ci.org/ronmamo/reflections.svg?branch=master)](https://travis-ci.org/ronmamo/reflections) +Using Reflections you can query for example: + * Subtypes of a type + * Types annotated with an annotation + * Methods with annotation, parameters, return type + * Resources found in classpath +And more... -### Intro -Add Reflections to your project. for maven projects just add this dependency: +## Usage +Add Reflections dependency to your project: ```xml +# Maven org.reflections reflections - 0.9.12 + 0.10 + +# Gradle +implementation 'org.reflections:reflections:0.10' ``` -A typical use of Reflections would be: +Create Reflections instance and use the query functions: ```java -Reflections reflections = new Reflections("my.project"); +Reflections reflections = new Reflections("com.my.project"); -Set> subTypes = reflections.getSubTypesOf(SomeType.class); +Set> subTypes = + reflections.get(SubTypes.of(SomeType.class).asClass()); -Set> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class); +Set> annotated = + reflections.get(TypesAnnotated.with(SomeAnnotation.class).asClass()); ``` -### Usage -Basically, to use Reflections first instantiate it with urls and scanners +*Note that there are some breaking changes with Reflections 0.10, along with performance improvements and more functional API. Migration is encouraged and should be easy though.* -```java -//scan urls that contain 'my.package', include inputs starting with 'my.package', use the default scanners -Reflections reflections = new Reflections("my.package"); - -//or using ConfigurationBuilder -new Reflections(new ConfigurationBuilder() - .setUrls(ClasspathHelper.forPackage("my.project.prefix")) - .setScanners(new SubTypesScanner(), - new TypeAnnotationsScanner().filterResultsBy(optionalFilter), ...), - .filterInputsBy(new FilterBuilder().includePackage("my.project.prefix")) - ...); -``` -Then use the convenient query methods: (depending on the scanners configured) +### Scan +Creating Reflections instance requires providing scanning configuration: ```java -//SubTypesScanner -Set> modules = - reflections.getSubTypesOf(com.google.inject.Module.class); +// scan for: +// urls in classpath that contain 'com.my.project' package +// filter types starting with 'com.my.project' +// use the default scanners SubTypes and TypesAnnotated +Reflections reflections = new Reflections( + new ConfigurationBuilder() + .forPackage("com.my.project") + .filterInputsBy(new FilterBuilder().includePackage("com.my.project"))); + +// or similarly +Reflections reflections = new Reflections("com.my.project"); + +// another example +Reflections reflections = new Reflections( + new ConfigurationBuilder() + .addUrls(ClasspathHelper.forPackage("com.my.project")) // same as forPackage + .setScanners(Scanners.values()) // all standard scanners + .filterInputsBy(new FilterBuilder() // optionally include/exclude packages + .includePackage("com.my.project") + .excludePackage("com.my.project.exclude"))); ``` + +*See more in [ConfigurationBuilder](src/main/java/org/reflections/util/ConfigurationBuilder.java).* + +Note that: +* **Scanners must be configured in order to be queried, otherwise an empty result is returned.** +If not specified, default scanners are `SubTypes` and `TypesAnnotated`. For all standard [Scanners](src/main/java/org/reflections/scanners/Scanners.java) use `Scanners.values()`. +* **All relevant URLs should be configured.** +If required, Reflections will [expand super types](http://ronmamo.github.io/reflections/org/reflections/Reflections.html#expandSuperTypes(Map)) in order to get the transitive closure metadata without scanning large 3rd party urls. +Consider adding inputs filter in case too many classes are scanned. +* Classloader can optionally be used for resolving runtime classes from names. + +### Query +Once Reflections was instantiated and scanning was successful, it can be used for querying the indexed metadata. +Standard [Scanners](src/main/java/org/reflections/scanners/Scanners.java) are provided for query using `reflections.get()`, for example: + ```java -//TypeAnnotationsScanner +import static org.reflections.scanners.Scanners.*; + +// SubTypes +Set> modules = + reflections.get(SubTypes.of(Module.class).asClass()); + +// TypesAnnotated Set> singletons = - reflections.getTypesAnnotatedWith(javax.inject.Singleton.class); -``` -```java -//ResourcesScanner -Set properties = - reflections.getResources(Pattern.compile(".*\\.properties")); -``` -```java -//MethodAnnotationsScanner + reflections.get(TypesAnnotated.with(Singleton.class).asClass()); + +// MethodAnnotated Set resources = - reflections.getMethodsAnnotatedWith(javax.ws.rs.Path.class); + reflections.get(MethodsAnnotated.with(GetMapping.class).as(Method.class)); + +// ConstructorsAnnotated Set injectables = - reflections.getConstructorsAnnotatedWith(javax.inject.Inject.class); -``` -```java -//FieldAnnotationsScanner + reflections.get(ConstructorsAnnotated.with(Inject.class).as(Constructor.class)); + +// FieldsAnnotated Set ids = - reflections.getFieldsAnnotatedWith(javax.persistence.Id.class); + reflections.get(FieldsAnnotated.with(Id.class).as(Field.class)); + +// Resources +Set properties = + reflections.get(Resources.with(".*\\.properties")); ``` + +Member scanners: + ```java -//MethodParameterScanner -Set someMethods = - reflections.getMethodsMatchParams(long.class, int.class); -Set voidMethods = - reflections.getMethodsReturn(void.class); -Set pathParamMethods = - reflections.getMethodsWithAnyParamAnnotated(PathParam.class); +// MethodsReturn +Set voidMethods = + reflections.get(MethodsReturn.with(void.class).as(Method.class)); + +// MethodsSignature +Set someMethods = + reflections.get(MethodsSignature.of(long.class, int.class).as(Method.class)); + +// MethodsParameter +Set pathParam = + reflections.get(MethodsParameter.of(PathParam.class).as(Method.class)); + +// ConstructorsSignature +Set someConstructors = + reflections.get(ConstructorsSignature.of(String.class).as(Constructor.class)); + +// ConstructorsParameter +Set pathParam = + reflections.get(ConstructorsParameter.of(PathParam.class).as(Constructor.class)); ``` + +*See more examples in [ReflectionsQueryTest](src/test/java/org/reflections/ReflectionsQueryTest.java).* + +Scanner queries return `Set` by default, if not using `as() / asClass()` mappers: ```java -//MethodParameterNamesScanner -List parameterNames = - reflections.getMethodParamNames(Method.class) +Set moduleNames = + reflections.get(SubTypes.of(Module.class)); + +Set singleNames = + reflections.get(TypesAnnotated.with(Singleton.class)); ``` +Note that previous 0.9.x API is still supported, for example: ```java -//MemberUsageScanner -Set usages = - reflections.getMethodUsages(Method.class) -``` +Set> modules = + reflections.getSubTypesOf(Module.class); - * If no scanners are configured, the default will be used - `SubTypesScanner` and `TypeAnnotationsScanner`. - * Classloader can also be configured, which will be used for resolving runtime classes from names. - * Reflections [expands super types](http://ronmamo.github.io/reflections/org/reflections/Reflections.html#expandSuperTypes()) by default. This solves some [problems](https://github.com/ronmamo/reflections/issues/65#issuecomment-95036047) with transitive urls are not scanned. +Set> singletons = + reflections.getTypesAnnotatedWith(Singleton.class); +``` +
+ Compare Scanners and previous 0.9.x API + +| Scanners | previous 0.9.x API | +| -------- | ------------------ | +| `get(SubType.of(T))` | getSubTypesOf(T) | +| `get(TypesAnnotated.with(A))` | getTypesAnnotatedWith(A) | +| `get(MethodsAnnotated.with(A))` | getMethodsAnnotatedWith(A) | +| `get(ConstructorsAnnotated.with(A))` | getConstructorsAnnotatedWith(A) | +| `get(FieldsAnnotated.with(A))` | getFieldsAnnotatedWith(A) | +| `get(Resources.with(regex))` | getResources(regex) | +| `get(MethodsParameter.with(P))` | getMethodsWithParameter(P) | +| `get(MethodsSignature.of(P, ...))` | getMethodsWithSignature(P, ...) | +| `get(MethodsReturn.of(T))` | getMethodsReturn(T) | +| `get(ConstructorsParameter.with(P))` | getConstructorsWithParameter(P) | +| `get(ConstructorsSignature.of(P, ...))` | getConstructorsWithSignature(P, ...) | + +*Note: `asClass()` and `as()` mappings were omitted* +
+ +## ReflectionUtils +Apart from scanning classpath metadata using [Javassist](https://github.com/jboss-javassist/javassist), +Java Reflection convenient methods are available using +[ReflectionsUtils](src/main/java/org/reflections/ReflectionUtils.java): -*Checkout the [javadoc](http://ronmamo.github.io/reflections/index.html?org/reflections/Reflections.html) for more info.* +```java +import static org.reflections.ReflectionUtils.*; -*Also, browse the [tests directory](https://github.com/ronmamo/reflections/tree/master/src/test/java/org/reflections) to see some more examples.* +Set> superTypes = get(SuperTypes.of(T)); +Set fields = get(Fields.of(T)); +Set constructors = get(Constructors.of(T)); +Set methods = get(Methods.of(T)); +Set annotations = get(Annotations.of(T)); +Set> annotationTypes = get(AnnotationTypess.of(T)); +``` -### ReflectionUtils -ReflectionsUtils contains some convenient Java reflection helper methods for getting types/constructors/methods/fields/annotations matching some predicates, generally in the form of *getAllXXX(type, withYYY) +*Previous ReflectionUtils 0.9.x API is still supported though marked for removal, more info in the javadocs.* -for example: +## QueryBuilder and QueryFunction +Each Scanner and ReflectionUtils function implements +[QueryBuilder](src/main/java/org/reflections/util/QueryBuilder.java), and supports: +* `get()` - function returns direct values +* `with()` or `of()` - function returns all transitive values -```java -import static org.reflections.ReflectionUtils.*; +*For example, `Scanners.SubTypes.get(T)` return direct subtypes, +while `Scanners.SubTypes.of(T)` return transitive subtypes hierarchy. +Same goes for `Scanners.TypesAnnotated` and `ReflectionUtils.SuperTypes` etc.* -Set getters = getAllMethods(someClass, - withModifier(Modifier.PUBLIC), withPrefix("get"), withParametersCount(0)); +Next, each function implements [QueryFunction](src/main/java/org/reflections/util/QueryFunction.java), +and provides fluent functional interface for composing `filter()`, `map()`, `flatMap()`, `as()` and more, such that: -//or -Set listMethodsFromCollectionToBoolean = - getAllMethods(List.class, - withParametersAssignableTo(Collection.class), withReturnType(boolean.class)); +```java +QueryFunction getters = + Methods.of(C1.class) + .filter(withModifier(Modifier.PUBLIC)) + .filter(withPrefix("get").and(withParametersCount(0))) + .as(Method.class); +``` -Set fields = getAllFields(SomeClass.class, withAnnotation(annotation), withTypeAssignableTo(type)); +Query functions can be composed, for example: +```java +// compose Scanner and ReflectionUtils functions +QueryFunction methods = + SubTypes.of(type).asClass() // <-- classpath scanned metadata + .flatMap(Methods::of); // <-- java reflection api + +// compose function of function +QueryFunction> queryAnnotations = + Annotations.of(Methods.of(C4.class)) + .map(Annotation::annotationType); ``` -*See more in the [ReflectionUtils javadoc](http://ronmamo.github.io/reflections/index.html?org/reflections/ReflectionUtils.html)* +See more in [ReflectionUtilsQueryTest](https://github.com/ronmamo/reflections/tree/master/src/test/java/org/reflections/ReflectionUtilsQueryTest.java) -### Integrating into your build lifecycle -Although scanning can be easily done on bootstrap time of your application - and shouldn't take long, it is sometime a good idea to integrate Reflections into your build lifecyle. -With simple Maven/Gradle/SBT/whatever configuration you can save all scanned metadata into xml/json files just after compile time. -Later on, when your project is bootstrapping you can let Reflections collect all those resources and re-create that metadata for you, -making it available at runtime without re-scanning the classpath. +A more complex example demonstrates getting merged annotations of rest controllers endpoints: +```java +// get all annotations of RequestMapping hierarchy (GetMapping, PostMapping, ...) +Set> metaAnnotations = + reflections.get(TypesAnnotated.getAllIncluding(RequestMapping.class.getName()).asClass()); + +QueryFunction> queryAnnotations = + // get all controller endpoint methods + MethodsAnnotated.with(metaAnnotations).as(Method.class) + .map(method -> + // get both method's + declaring class's RequestMapping annotations + get(Annotations.of(method.getDeclaringClass()) + .add(Annotations.of(method)) + .filter(a -> metaAnnotations.contains(a.annotationType()))) + .stream() + // merge annotations' member values into a single hash map + .collect(new AnnotationMergeCollector(method))); + +// apply query and map merged hashmap into java annotation proxy +Set mergedAnnotations = + reflections.get(mergedAnnotation + .map(map -> ReflectionUtils.toAnnotation(map, metaAnnotation))); +``` -*For Maven, see example using gmavenplus in the [reflections-maven](https://github.com/ronmamo/reflections-maven/) repository* +Check the [tests](src/test/java/org/reflections) folder for more examples and API usage -### Other use cases -*See the [UseCases](https://github.com/ronmamo/reflections/blob/gh-pages/UseCases.md) wiki page* +### What else? +- **Integrating with build lifecycle** +It is sometime useful to save the scanned metadata into xml/json as part of the build lifecycle for generating resources, +and then collect it on bootstrap with `Reflections.collect()` and avoid scanning. *See [reflections-maven](https://github.com/ronmamo/reflections-maven/) for example*. +- [JavaCodeSerializer](src/main/java/org/reflections/scanners/JavaCodeSerializer.java) - scanned metadata can be persisted into a generated Java source code. +Although less common, it can be useful for accessing types and members in a strongly typed manner. *(see [example](src/test/java/org/reflections/MyTestModelStore.java))* +- [AnnotationMergeCollector](src/main/java/org/reflections/util/AnnotationMergeCollector.java) - can be used to merge similar annotations, for example for finding effective REST controller endpoints. *(see [test](src/test/java/org/reflections/ReflectionUtilsQueryTest.java#L216))* +- [MemberUsageScanner](src/main/java/org/reflections/scanners/MemberUsageScanner.java) - experimental scanner allow querying for member usages `getMemberUsages()` of packages/types/elements in the classpath. +Can be used for finding usages between packages, layers, modules, types etc. ### Contribute Pull requests are welcomed!! -Apologize for not maintaining this repository continuously! We're looking for maintainers to assist in reviewing pull requests and managing releases, please reach out. - -The license is [WTFPL](http://www.wtfpl.net/), just do what the fuck you want to. - -This library is published as an act of giving and generosity, from developers to developers. +Dual licenced with Apache 2 and [WTFPL](http://www.wtfpl.net/), just do what the fuck you want to. -Please feel free to use it, and to contribute to the developers community in the same manner. [Dāna](http://en.wikipedia.org/wiki/D%C4%81na) -[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WLN75KYSR6HAY) +*This library is published as an act of giving and generosity, from developers to developers, +to promote knowledge sharing and a--hole free working environments. +Please feel free to use it, and to contribute to the developers' community in the same manner.* +[PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WLN75KYSR6HAY) / [Patreon](https://www.patreon.com/ronma) _Cheers_ diff --git a/pom.xml b/pom.xml index af6edbea..ebfef360 100644 --- a/pom.xml +++ b/pom.xml @@ -4,10 +4,11 @@ org.reflections reflections - 0.9.13-SNAPSHOT + 0.10-SNAPSHOT + jar Reflections - Reflections - a Java runtime metadata analysis + Reflections - Java runtime metadata analysis http://github.com/ronmamo/reflections @@ -16,8 +17,8 @@ http://www.wtfpl.net/ - The New BSD License - http://www.opensource.org/licenses/bsd-license.html + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt @@ -31,13 +32,6 @@ GitHub Issues - - - google-code-reflections - http://groups.google.com/group/google-code-reflections - - - ronmamo at gmail @@ -52,7 +46,7 @@ - 3.26.0-GA + 3.28.0-GA 1.8 none @@ -62,27 +56,33 @@ org.javassist javassist ${javassist.version} - false + false + + + + com.google.code.findbugs + jsr305 + 3.0.2 + compile org.slf4j slf4j-api - 1.7.30 - true + 1.7.32 org.dom4j dom4j - 2.1.1 + 2.1.3 true com.google.code.gson gson - 2.8.6 + 2.8.8 true @@ -94,26 +94,32 @@ true - org.slf4j slf4j-simple - 1.7.24 + 1.7.32 true org.jboss jboss-vfs - 3.2.12.Final + 3.2.15.Final provided true - junit - junit - 4.13 + org.junit.jupiter + junit-jupiter-engine + 5.8.0 + test + + + + org.hamcrest + hamcrest + 2.2 test @@ -229,6 +235,11 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + diff --git a/src/main/java/org/reflections/Configuration.java b/src/main/java/org/reflections/Configuration.java index 18115144..1a0b4d39 100644 --- a/src/main/java/org/reflections/Configuration.java +++ b/src/main/java/org/reflections/Configuration.java @@ -1,12 +1,10 @@ package org.reflections; -import org.reflections.adapters.MetadataAdapter; import org.reflections.scanners.Scanner; -import org.reflections.serializers.Serializer; import java.net.URL; +import java.util.Map; import java.util.Set; -import java.util.concurrent.ExecutorService; import java.util.function.Predicate; /** @@ -14,29 +12,22 @@ *

it is preferred to use {@link org.reflections.util.ConfigurationBuilder} */ public interface Configuration { - /** the scanner instances used for scanning different metadata */ + /** the scanner instances used for indexing metadata. defaults to {@code SubTypes} and {@code TypesAnnotated}. */ Set getScanners(); - /** the urls to be scanned */ + /** the urls to be scanned. required. */ Set getUrls(); - /** the metadata adapter used to fetch metadata from classes */ - @SuppressWarnings({"RawUseOfParameterizedType"}) - MetadataAdapter getMetadataAdapter(); - - /** get the fully qualified name filter used to filter types to be scanned */ + /** the fully qualified name filter used to filter types to be scanned. defaults to accept all inputs (if null). */ Predicate getInputsFilter(); - /** executor service used to scan files. if null, scanning is done in a simple for loop */ - ExecutorService getExecutorService(); - - /** the default serializer to use when saving Reflection */ - Serializer getSerializer(); + /** scan urls in parallel. defaults to true. */ + boolean isParallel(); - /** get class loaders, might be used for resolving methods/fields */ + /** optional class loaders used for resolving types. */ ClassLoader[] getClassLoaders(); /** if true (default), expand super types after scanning, for super types that were not scanned. - *

see {@link org.reflections.Reflections#expandSuperTypes()}*/ + *

see {@link org.reflections.Reflections#expandSuperTypes(Map)}*/ boolean shouldExpandSuperTypes(); } diff --git a/src/main/java/org/reflections/ReflectionUtils.java b/src/main/java/org/reflections/ReflectionUtils.java index fe1b8322..ee052f15 100644 --- a/src/main/java/org/reflections/ReflectionUtils.java +++ b/src/main/java/org/reflections/ReflectionUtils.java @@ -1,403 +1,259 @@ package org.reflections; -import org.reflections.util.ClasspathHelper; +import org.reflections.util.QueryFunction; +import org.reflections.util.ReflectionUtilsPredicates; +import org.reflections.util.UtilQueryBuilder; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.Member; import java.lang.reflect.Method; -import java.util.ArrayList; +import java.lang.reflect.Proxy; import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; -import java.util.Objects; +import java.util.Map; import java.util.Set; import java.util.function.Predicate; -import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static org.reflections.util.Utils.filter; - -/** convenient java reflection helper methods - *

- * 1. some helper methods to get type by name: {@link #forName(String, ClassLoader...)} and {@link #forNames(Collection, ClassLoader...)} )} +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toList; + +/** + * utils for querying java reflection meta types {@link #SuperTypes}, {@link #Annotations}, {@link #AnnotationTypes}, {@link #Methods}, {@link #Constructors}, {@link #Fields}. + *

{@code Set> supertypes = get(SuperTypes.of(type))
+ * Set annotations = get(Annotations.of(type))
+ * }
+ *

generally, apply {@link #get(QueryFunction)} on {@link QueryFunction} created by {@link UtilQueryBuilder}, and optionally use the functional methods in QueryFunction + *

{@code get(Methods.of(type)
+ *   .filter(withPublic().and(withPrefix("get")).and(withParameterCount(0)))
+ *   .as(Method.class)
+ *   .map(m -> ...))
+ * }
+ *

or (previously), use {@code getAllXXX(type/s, withYYY)} methods: + *

{@code getAllSuperTypes(), getAllFields(), getAllMethods(), getAllConstructors() }
+ * 
*

- * 2. some helper methods to get all types/methods/fields/constructors/properties matching some predicates, generally: - *

 Set<?> result = getAllXXX(type/s, withYYY) 
- *

where get methods are: - *

    - *
  • {@link #getAllSuperTypes(Class, java.util.function.Predicate...)} - *
  • {@link #getAllFields(Class, java.util.function.Predicate...)} - *
  • {@link #getAllMethods(Class, java.util.function.Predicate...)} - *
  • {@link #getAllConstructors(Class, java.util.function.Predicate...)} - *
- *

and predicates included here all starts with "with", such as - *

    - *
  • {@link #withAnnotation(java.lang.annotation.Annotation)} - *
  • {@link #withModifier(int)} - *
  • {@link #withName(String)} - *
  • {@link #withParameters(Class[])} - *
  • {@link #withAnyParameterAnnotation(Class)} - *
  • {@link #withParametersAssignableTo(Class[])} - *
  • {@link #withParametersAssignableFrom(Class[])} - *
  • {@link #withPrefix(String)} - *
  • {@link #withReturnType(Class)} - *
  • {@link #withType(Class)} - *
  • {@link #withTypeAssignableTo} - *
+ * some predicates included here: + *
    + *
  • {@link #withPublic()} + *
  • {@link #withParametersCount(int)}} + *
  • {@link #withAnnotation(java.lang.annotation.Annotation)} + *
  • {@link #withParameters(Class[])} + *
  • {@link #withModifier(int)} + *
  • {@link #withReturnType(Class)} + *
+ *
{@code
+ * import static org.reflections.ReflectionUtils.*;
  *
- *     


- * for example, getting all getters would be: - *

- *      Set<Method> getters = getAllMethods(someClasses, 
- *              Predicates.and(
- *                      withModifier(Modifier.PUBLIC), 
- *                      withPrefix("get"), 
- *                      withParametersCount(0)));
- *     
+ * Set getters = + * get(Methods(classes) + * .filter(withModifier(Modifier.PUBLIC).and(withPrefix("get")).and(withParametersCount(0))); + * + * get(Annotations.of(method) + * .filter(withAnnotation()) + * .map(annotation -> Methods.of(annotation) + * .map(method -> ))))) + * .stream()... + * }
* */ -@SuppressWarnings("unchecked") -public abstract class ReflectionUtils { - - /** would include {@code Object.class} when {@link #getAllSuperTypes(Class, java.util.function.Predicate[])}. default is false. */ - public static boolean includeObject = false; +@SuppressWarnings({"unchecked", "rawtypes"}) +public abstract class ReflectionUtils extends ReflectionUtilsPredicates { - /** get all super types of given {@code type}, including, optionally filtered by {@code predicates} - *

include {@code Object.class} if {@link #includeObject} is true */ - public static Set> getAllSuperTypes(final Class type, Predicate>... predicates) { - Set> result = new LinkedHashSet<>(); - if (type != null && (includeObject || !type.equals(Object.class))) { - result.add(type); - for (Class supertype : getSuperTypes(type)) { - result.addAll(getAllSuperTypes(supertype)); - } - } - return filter(result, predicates); + /** get type elements {@code } by applying {@link QueryFunction}

{@code get(SuperTypes.of(type))}
*/ + public static Set get(QueryFunction function) { + return function.apply(null); } - /** get the immediate supertype and interfaces of the given {@code type} */ - public static Set> getSuperTypes(Class type) { - Set> result = new LinkedHashSet<>(); - Class superclass = type.getSuperclass(); - Class[] interfaces = type.getInterfaces(); - if (superclass != null && (includeObject || !superclass.equals(Object.class))) result.add(superclass); - if (interfaces != null && interfaces.length > 0) result.addAll(Arrays.asList(interfaces)); - return result; + /** get type elements {@code } by applying {@link QueryFunction} and {@code predicates} */ + public static Set get(QueryFunction queryFunction, Predicate... predicates) { + return get(queryFunction.filter(Arrays.stream((Predicate[]) predicates).reduce(t -> true, Predicate::and))); } - /** get all methods of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} */ - public static Set getAllMethods(final Class type, Predicate... predicates) { - Set result = new HashSet<>(); - for (Class t : getAllSuperTypes(type)) { - result.addAll(getMethods(t, predicates)); - } - return result; - } - - /** get methods of given {@code type}, optionally filtered by {@code predicates} */ - public static Set getMethods(Class t, Predicate... predicates) { - return filter(t.isInterface() ? t.getMethods() : t.getDeclaredMethods(), predicates); - } - - /** get all constructors of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} */ - public static Set getAllConstructors(final Class type, Predicate... predicates) { - Set result = new HashSet<>(); - for (Class t : getAllSuperTypes(type)) { - result.addAll(getConstructors(t, predicates)); - } - return result; - } + private static final List objectMethodNames = + Arrays.asList("equals", "hashCode", "toString", "wait", "notify", "notifyAll"); - /** get constructors of given {@code type}, optionally filtered by {@code predicates} */ - public static Set getConstructors(Class t, Predicate... predicates) { - return filter(t.getDeclaredConstructors(), predicates); - } + /** predicate to filter out {@code Object} methods */ + public static final Predicate notObjectMethod = m -> !objectMethodNames.contains(m.getName()); - /** get all fields of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} */ - public static Set getAllFields(final Class type, Predicate... predicates) { - Set result = new HashSet<>(); - for (Class t : getAllSuperTypes(type)) result.addAll(getFields(t, predicates)); - return result; - } + /** query super class
{@code get(SuperClass.of(element)) -> Set>}
+ *

see also {@link ReflectionUtils#SuperTypes}, {@link ReflectionUtils#Interfaces} */ + public static final UtilQueryBuilder, Class> SuperClass = + element -> ctx -> { + Class superclass = element.getSuperclass(); + return superclass != null && !superclass.equals(Object.class) ? Collections.singleton(superclass) : Collections.emptySet(); + }; - /** get fields of given {@code type}, optionally filtered by {@code predicates} */ - public static Set getFields(Class type, Predicate... predicates) { - return filter(type.getDeclaredFields(), predicates); - } + /** query interfaces

{@code get(Interfaces.of(element)) -> Set>}
*/ + public static final UtilQueryBuilder, Class> Interfaces = + element -> ctx -> Stream.of(element.getInterfaces()).collect(Collectors.toCollection(LinkedHashSet::new)); - /** get all annotations of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} */ - public static Set getAllAnnotations(T type, Predicate... predicates) { - Set result = new LinkedHashSet<>(); - List keys = new ArrayList(); - if (type instanceof Class) { - keys.addAll(getAllSuperTypes((Class) type)); - } - for (int i = 0; i < keys.size(); i++) { - for (Annotation annotation : getAnnotations(keys.get(i), predicates)) { - if (result.add(annotation)) { - keys.add(annotation.annotationType()); - } + /** query super classes and interfaces including element
{@code get(SuperTypes.of(element)) -> Set> }
*/ + public static final UtilQueryBuilder, Class> SuperTypes = + new UtilQueryBuilder, Class>() { + @Override + public QueryFunction> get(Class element) { + return SuperClass.get(element).add(Interfaces.get(element)); } - } - return result; - } - - /** get annotations of given {@code type}, optionally honorInherited, optionally filtered by {@code predicates} */ - public static Set getAnnotations(T type, Predicate... predicates) { - return filter(type.getDeclaredAnnotations(), predicates); - } - - /** filter all given {@code elements} with {@code predicates}, if given */ - public static Set getAll(final Set elements, Predicate... predicates) { - return filter(elements, predicates); - } - - //predicates - /** where member name equals given {@code name} */ - public static Predicate withName(final String name) { - return input -> input != null && input.getName().equals(name); - } - - /** where member name startsWith given {@code prefix} */ - public static Predicate withPrefix(final String prefix) { - return input -> input != null && input.getName().startsWith(prefix); - } - - /** where member's {@code toString} matches given {@code regex} - *

for example: - *

-     *  getAllMethods(someClass, withPattern("public void .*"))
-     * 
- * */ - public static Predicate withPattern(final String regex) { - return input -> Pattern.matches(regex, input.toString()); - } - - /** where element is annotated with given {@code annotation} */ - public static Predicate withAnnotation(final Class annotation) { - return input -> input != null && input.isAnnotationPresent(annotation); - } - /** where element is annotated with given {@code annotations} */ - public static Predicate withAnnotations(final Class... annotations) { - return input -> input != null && Arrays.equals(annotations, annotationTypes(input.getAnnotations())); - } + @Override + public QueryFunction> of(Class element) { + return QueryFunction.>single(element).getAll(SuperTypes::get); + } + }; - /** where element is annotated with given {@code annotation}, including member matching */ - public static Predicate withAnnotation(final Annotation annotation) { - return input -> input != null && input.isAnnotationPresent(annotation.annotationType()) && - areAnnotationMembersMatching(input.getAnnotation(annotation.annotationType()), annotation); - } + /** query annotations
{@code get(Annotation.of(element)) -> Set }
*/ + public static final UtilQueryBuilder Annotations = + new UtilQueryBuilder() { + @Override + public QueryFunction get(AnnotatedElement element) { + return ctx -> Arrays.stream(element.getAnnotations()).collect(Collectors.toCollection(LinkedHashSet::new)); + } - /** where element is annotated with given {@code annotations}, including member matching */ - public static Predicate withAnnotations(final Annotation... annotations) { - return input -> { - if (input != null) { - Annotation[] inputAnnotations = input.getAnnotations(); - if (inputAnnotations.length == annotations.length) { - return IntStream.range(0, inputAnnotations.length) - .allMatch(i -> areAnnotationMembersMatching(inputAnnotations[i], annotations[i])); - } + @Override + public QueryFunction of(AnnotatedElement element) { + return ReflectionUtils.extendType().get(element).getAll(Annotations::get, Annotation::annotationType); } - return true; }; - } - /** when method/constructor parameter types equals given {@code types} */ - public static Predicate withParameters(final Class... types) { - return input -> Arrays.equals(parameterTypes(input), types); - } + /** query annotation types
{@code get(AnnotationTypes.of(element)) -> Set> }
*/ + public static final UtilQueryBuilder> AnnotationTypes = + new UtilQueryBuilder>() { + @Override + public QueryFunction> get(AnnotatedElement element) { + return Annotations.get(element).map(Annotation::annotationType); + } - /** when member parameter types assignable to given {@code types} */ - public static Predicate withParametersAssignableTo(final Class... types) { - return input -> isAssignable(types, parameterTypes(input)); - } + @Override + public QueryFunction> of(AnnotatedElement element) { + return ReflectionUtils.extendType().get(element).getAll(AnnotationTypes::get, a -> a); + } + }; - /** when method/constructor parameter types assignable from given {@code types} */ - public static Predicate withParametersAssignableFrom(final Class... types) { - return input -> isAssignable(parameterTypes(input), types); - } + /** query methods
{@code get(Methods.of(type)) -> Set}
*/ + public static final UtilQueryBuilder, Method> Methods = + element -> ctx -> Arrays.stream(element.getMethods()).filter(notObjectMethod).collect(Collectors.toCollection(LinkedHashSet::new)); - /** when method/constructor parameters count equal given {@code count} */ - public static Predicate withParametersCount(final int count) { - return input -> input != null && parameterTypes(input).length == count; - } + /** query constructors
{@code get(Constructors.of(type)) -> Set }
*/ + public static final UtilQueryBuilder, Constructor> Constructors = + element -> ctx -> Arrays.stream(element.getDeclaredConstructors()).collect(Collectors.toCollection(LinkedHashSet::new)); - /** when method/constructor has any parameter with an annotation matches given {@code annotations} */ - public static Predicate withAnyParameterAnnotation(final Class annotationClass) { - return input -> input != null && annotationTypes(parameterAnnotations(input)).stream().anyMatch(input1 -> input1.equals(annotationClass)); - } + /** query fields
{@code get(Fields.of(type)) -> Set }
*/ + public static final UtilQueryBuilder, Field> Fields = + element -> ctx -> Arrays.stream(element.getDeclaredFields()).collect(Collectors.toCollection(LinkedHashSet::new)); - /** when method/constructor has any parameter with an annotation matches given {@code annotations}, including member matching */ - public static Predicate withAnyParameterAnnotation(final Annotation annotation) { - return input -> input != null && parameterAnnotations(input).stream().anyMatch(input1 -> areAnnotationMembersMatching(annotation, input1)); + public static UtilQueryBuilder extendType() { + return element -> { + if (element instanceof Class && !((Class) element).isAnnotation()) { + QueryFunction> single = QueryFunction.single((Class) element); + return (QueryFunction) single.add(single.getAll(SuperTypes::get)); + } else { + return QueryFunction.single((T) element); + } + }; } - /** when field type equal given {@code type} */ - public static Predicate withType(final Class type) { - return input -> input != null && input.getType().equals(type); + /** get all annotations of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} + *

marked for removal, use instead {@code get(Annotations.of())} */ + public static Set getAllAnnotations(T type, Predicate... predicates) { + return get(Annotations.of(type), predicates); } - /** when field type assignable to given {@code type} */ - public static Predicate withTypeAssignableTo(final Class type) { - return input -> input != null && type.isAssignableFrom(input.getType()); + /** get all super types of given {@code type}, including, optionally filtered by {@code predicates} */ + public static Set> getAllSuperTypes(final Class type, Predicate>... predicates) { + Predicate>[] filter = predicates == null || predicates.length == 0 ? new Predicate[]{t -> !Object.class.equals(t)} : predicates; + return get(SuperTypes.of(type), filter); } - /** when method return type equal given {@code type} */ - public static Predicate withReturnType(final Class type) { - return input -> input != null && input.getReturnType().equals(type); + /** get the immediate supertype and interfaces of the given {@code type} + *

marked for removal, use instead {@code get(SuperTypes.get())} */ + public static Set> getSuperTypes(Class type) { + return get(SuperTypes.get(type)); } - /** when method return type assignable from given {@code type} */ - public static Predicate withReturnTypeAssignableTo(final Class type) { - return input -> input != null && type.isAssignableFrom(input.getReturnType()); + /** get all methods of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} + *

marked for removal, use instead {@code get(Methods.of())} */ + public static Set getAllMethods(final Class type, Predicate... predicates) { + return get(Methods.of(type), predicates); } - /** when member modifier matches given {@code mod} - *

for example: - *

-     * withModifier(Modifier.PUBLIC)
-     * 
- */ - public static Predicate withModifier(final int mod) { - return input -> input != null && (input.getModifiers() & mod) != 0; + /** get methods of given {@code type}, optionally filtered by {@code predicates} + *

marked for removal, use instead {@code get(Methods.get())} */ + public static Set getMethods(Class t, Predicate... predicates) { + return get(Methods.get(t), predicates); } - /** when class modifier matches given {@code mod} - *

for example: - *

-     * withModifier(Modifier.PUBLIC)
-     * 
- */ - public static Predicate> withClassModifier(final int mod) { - return input -> input != null && (input.getModifiers() & mod) != 0; + /** get all constructors of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} + *

marked for removal, use instead {@code get(Constructors.of())} */ + public static Set getAllConstructors(final Class type, Predicate... predicates) { + return get(Constructors.of(type), predicates); } - // - /** tries to resolve a java type name to a Class - *

if optional {@link ClassLoader}s are not specified, then both {@link org.reflections.util.ClasspathHelper#contextClassLoader()} and {@link org.reflections.util.ClasspathHelper#staticClassLoader()} are used - * */ - public static Class forName(String typeName, ClassLoader... classLoaders) { - if (getPrimitiveNames().contains(typeName)) { - return getPrimitiveTypes().get(getPrimitiveNames().indexOf(typeName)); - } else { - String type; - if (typeName.contains("[")) { - int i = typeName.indexOf("["); - type = typeName.substring(0, i); - String array = typeName.substring(i).replace("]", ""); - - if (getPrimitiveNames().contains(type)) { - type = getPrimitiveDescriptors().get(getPrimitiveNames().indexOf(type)); - } else { - type = "L" + type + ";"; - } - - type = array + type; - } else { - type = typeName; - } - - List reflectionsExceptions = new ArrayList<>(); - for (ClassLoader classLoader : ClasspathHelper.classLoaders(classLoaders)) { - if (type.contains("[")) { - try { return Class.forName(type, false, classLoader); } - catch (Throwable e) { - reflectionsExceptions.add(new ReflectionsException("could not get type for name " + typeName, e)); - } - } - try { return classLoader.loadClass(type); } - catch (Throwable e) { - reflectionsExceptions.add(new ReflectionsException("could not get type for name " + typeName, e)); - } - } - - if (Reflections.log != null && Reflections.log.isTraceEnabled()) { - for (ReflectionsException reflectionsException : reflectionsExceptions) { - Reflections.log.trace("could not get type for name " + typeName + " from any class loader", reflectionsException); - } - } - - return null; - } + /** get constructors of given {@code type}, optionally filtered by {@code predicates} + *

marked for removal, use instead {@code get(Constructors.get())} */ + public static Set getConstructors(Class t, Predicate... predicates) { + return get(Constructors.get(t), predicates); } - /** try to resolve all given string representation of types to a list of java types */ - public static Set> forNames(final Collection classes, ClassLoader... classLoaders) { - return classes.stream() - .map(className -> (Class) forName(className, classLoaders)) - .filter(Objects::nonNull) - .collect(Collectors.toCollection(LinkedHashSet::new)); + /** get all fields of given {@code type}, up the super class hierarchy, optionally filtered by {@code predicates} + *

marked for removal, use instead {@code get(Fields.of())} */ + public static Set getAllFields(final Class type, Predicate... predicates) { + return get(Fields.of(type), predicates); } - private static Class[] parameterTypes(Member member) { - return member != null ? - member.getClass() == Method.class ? ((Method) member).getParameterTypes() : - member.getClass() == Constructor.class ? ((Constructor) member).getParameterTypes() : null : null; + /** get fields of given {@code type}, optionally filtered by {@code predicates} + *

marked for removal, use instead {@code get(Fields.get())} */ + public static Set getFields(Class type, Predicate... predicates) { + return get(Fields.get(type), predicates); } - private static Set parameterAnnotations(Member member) { - Annotation[][] annotations = - member instanceof Method ? ((Method) member).getParameterAnnotations() : - member instanceof Constructor ? ((Constructor) member).getParameterAnnotations() : null; - return Arrays.stream(annotations).flatMap(Arrays::stream).collect(Collectors.toSet()); + /** get annotations of given {@code type}, optionally honorInherited, optionally filtered by {@code predicates} + *

marked for removal, use instead {@code get(Annotations.get())} */ + public static Set getAnnotations(T type, Predicate... predicates) { + return get(Annotations.get(type), predicates); } - private static Set> annotationTypes(Collection annotations) { - return annotations.stream().map(Annotation::annotationType).collect(Collectors.toSet()); + /** map {@code annotation} to hash map of member values recursively

{@code Annotations.of(type).map(ReflectionUtils::toMap)} 
*/ + public static Map toMap(Annotation annotation) { + return get(Methods.of(annotation.annotationType()) + .filter(notObjectMethod.and(withParametersCount(0)))) + .stream() + .collect(Collectors.toMap(Method::getName, m -> { + Object v1 = invoke(m, annotation); + return v1.getClass().isArray() && v1.getClass().getComponentType().isAnnotation() ? + Stream.of((Annotation[]) v1).map(ReflectionUtils::toMap).collect(toList()) : v1; + })); } - private static Class[] annotationTypes(Annotation[] annotations) { - return Arrays.stream(annotations).map(Annotation::annotationType).toArray(Class[]::new); + /** map {@code annotation} and {@code annotatedElement} to hash map of member values + *
{@code Annotations.of(type).map(a -> toMap(type, a))} 
*/ + public static Map toMap(Annotation annotation, AnnotatedElement element) { + Map map = toMap(annotation); + if (element != null) map.put("annotatedElement", element); + return map; } - // - private static List primitiveNames; - private static List primitiveTypes; - private static List primitiveDescriptors; - - private static void initPrimitives() { - if (primitiveNames == null) { - primitiveNames = Arrays.asList("boolean", "char", "byte", "short", "int", "long", "float", "double", "void"); - primitiveTypes = Arrays.asList(boolean.class, char.class, byte.class, short.class, int.class, long.class, float.class, double.class, void.class); - primitiveDescriptors = Arrays.asList("Z", "C", "B", "S", "I", "J", "F", "D", "V"); - } + /** create new annotation proxy with member values from the given {@code map}
{@code toAnnotation(Map.of("annotationType", annotationType, "value", ""))}
*/ + public static Annotation toAnnotation(Map map) { + return toAnnotation(map, (Class) map.get("annotationType")); } - private static List getPrimitiveNames() { initPrimitives(); return primitiveNames; } - private static List getPrimitiveTypes() { initPrimitives(); return primitiveTypes; } - private static List getPrimitiveDescriptors() { initPrimitives(); return primitiveDescriptors; } - - // - private static boolean areAnnotationMembersMatching(Annotation annotation1, Annotation annotation2) { - if (annotation2 != null && annotation1.annotationType() == annotation2.annotationType()) { - for (Method method : annotation1.annotationType().getDeclaredMethods()) { - try { - if (!method.invoke(annotation1).equals(method.invoke(annotation2))) return false; - } catch (Exception e) { - throw new ReflectionsException(String.format("could not invoke method %s on annotation %s", method.getName(), annotation1.annotationType()), e); - } - } - return true; - } - return false; + /** create new annotation proxy with member values from the given {@code map} and member values from the given {@code map} + *
{@code toAnnotation(Map.of("value", ""), annotationType)}
*/ + public static T toAnnotation(Map map, Class annotationType) { + return (T) Proxy.newProxyInstance(annotationType.getClassLoader(), new Class[]{annotationType}, + (proxy, method, args) -> notObjectMethod.test(method) ? map.get(method.getName()) : method.invoke(map)); } - - private static boolean isAssignable(Class[] childClasses, Class[] parentClasses) { - if (childClasses == null) { - return parentClasses == null || parentClasses.length == 0; - } - if (childClasses.length != parentClasses.length) { - return false; + /** invoke the given {@code method} with {@code args}, return either the result or an exception if occurred */ + public static Object invoke(Method method, Object obj, Object... args) { + try { + return method.invoke(obj, args); + } catch (Exception e) { + return e; } - return IntStream.range(0, childClasses.length) - .noneMatch(i -> !parentClasses[i].isAssignableFrom(childClasses[i]) || - parentClasses[i] == Object.class && childClasses[i] != Object.class); } } diff --git a/src/main/java/org/reflections/Reflections.java b/src/main/java/org/reflections/Reflections.java index 6697ca03..3a297077 100644 --- a/src/main/java/org/reflections/Reflections.java +++ b/src/main/java/org/reflections/Reflections.java @@ -1,291 +1,235 @@ package org.reflections; -import org.reflections.scanners.FieldAnnotationsScanner; +import javassist.bytecode.ClassFile; import org.reflections.scanners.MemberUsageScanner; -import org.reflections.scanners.MethodAnnotationsScanner; import org.reflections.scanners.MethodParameterNamesScanner; -import org.reflections.scanners.MethodParameterScanner; -import org.reflections.scanners.ResourcesScanner; import org.reflections.scanners.Scanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; +import org.reflections.scanners.Scanners; import org.reflections.serializers.Serializer; import org.reflections.serializers.XmlSerializer; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import org.reflections.util.FilterBuilder; -import org.reflections.util.Utils; +import org.reflections.util.NameHelper; +import org.reflections.util.QueryFunction; import org.reflections.vfs.Vfs; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; +import java.io.BufferedInputStream; +import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.annotation.Inherited; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.ThreadPoolExecutor; import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; import static java.lang.String.format; -import static org.reflections.ReflectionUtils.*; -import static org.reflections.util.Utils.*; +import static org.reflections.ReflectionUtils.withAnnotation; +import static org.reflections.ReflectionUtils.withAnyParameterAnnotation; +import static org.reflections.scanners.Scanners.*; /** * Reflections one-stop-shop object - *

Reflections scans your classpath, indexes the metadata, allows you to query it on runtime and may save and collect that information for many modules within your project. - *

Using Reflections you can query your metadata such as: + *

+ * Reflections scans and indexes your project's classpath, allowing reverse query of the type system metadata on runtime. + *

Using Reflections you can query for example: *

    - *
  • get all subtypes of some type - *
  • get all types/constructors/methods/fields annotated with some annotation, optionally with annotation parameters matching - *
  • get all resources matching matching a regular expression - *
  • get all methods with specific signature including parameters, parameter annotations and return type - *
  • get all methods parameter names - *
  • get all fields/methods/constructors usages in code + *
  • Subtypes of a type + *
  • Types annotated with an annotation + *
  • Methods with annotation, parameters, return type + *
  • Resources found in classpath + *
    And more... *
- *

A typical use of Reflections would be: - *

- *      Reflections reflections = new Reflections("my.project.prefix");
  *
- *      Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
+ * 

Create Reflections instance, preferably using {@link ConfigurationBuilder}: + *

{@code Reflections reflections = new Reflections(
+ *   new ConfigurationBuilder()
+ *     .forPackage("com.my.project"));
  *
- *      Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class);
- * 
- *

Basically, to use Reflections first instantiate it with one of the constructors, then depending on the scanners, use the convenient query methods: - *

- *      Reflections reflections = new Reflections("my.package.prefix");
- *      //or
- *      Reflections reflections = new Reflections(ClasspathHelper.forPackage("my.package.prefix"),
- *            new SubTypesScanner(), new TypesAnnotationScanner(), new FilterBuilder().include(...), ...);
+ * // or similarly
+ * Reflections reflections = new Reflections("com.my.project");
  *
- *       //or using the ConfigurationBuilder
- *       new Reflections(new ConfigurationBuilder()
- *            .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("my.project.prefix")))
- *            .setUrls(ClasspathHelper.forPackage("my.project.prefix"))
- *            .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner().filterResultsBy(optionalFilter), ...));
- * 
- * And then query, for example: - *
- *       Set<Class<? extends Module>> modules = reflections.getSubTypesOf(com.google.inject.Module.class);
- *       Set<Class<?>> singletons =             reflections.getTypesAnnotatedWith(javax.inject.Singleton.class);
+ * // another example
+ * Reflections reflections = new Reflections(
+ *   new ConfigurationBuilder()
+ *     .forPackage("com.my.project")
+ *     .setScanners(Scanners.values())     // all standard scanners
+ *     .filterInputsBy(new FilterBuilder().includePackage("com.my.project").excludePackage("com.my.project.exclude")));
+ * }
* - * Set<String> properties = reflections.getResources(Pattern.compile(".*\\.properties")); - * Set<Constructor> injectables = reflections.getConstructorsAnnotatedWith(javax.inject.Inject.class); - * Set<Method> deprecateds = reflections.getMethodsAnnotatedWith(javax.ws.rs.Path.class); - * Set<Field> ids = reflections.getFieldsAnnotatedWith(javax.persistence.Id.class); + *

All relevant URLs should be configured. + *
If required, Reflections will {@link #expandSuperTypes(Map)} in order to get the transitive closure metadata without scanning large 3rd party urls. + *

{@link Scanners} must be configured in order to be queried, otherwise an empty result is returned. + *
Default scanners are {@code SubTypes} and {@code TypesAnnotated}. + * For all standard scanners use {@code Scanners.values()}. + *

Classloader can optionally be used for resolving runtime classes from names. * - * Set<Method> someMethods = reflections.getMethodsMatchParams(long.class, int.class); - * Set<Method> voidMethods = reflections.getMethodsReturn(void.class); - * Set<Method> pathParamMethods = reflections.getMethodsWithAnyParamAnnotated(PathParam.class); - * Set<Method> floatToString = reflections.getConverters(Float.class, String.class); - * List<String> parameterNames = reflections.getMethodsParamNames(Method.class); + *

Query using {@link Reflections#get(QueryFunction)}, such as: + *
{@code Set> modules = reflections.get(SubTypes.of(Module.class).asClass());
+ * Set> singletons = reflections.get(TypesAnnotated.with(Singleton.class).asClass());
+ * Set properties   = reflections.get(Resources.with(".*\\.properties"));
+ * Set requests     = reflections.get(MethodsAnnotated.with(RequestMapping.class).as(Method.class));
+ * Set voidMethods  = reflections.get(MethodsReturn.with(void.class).as(Method.class));
+ * Set someMethods  = reflections.get(MethodsSignature.of(long.class, int.class).as(Method.class));
+ * }
* - * Set<Member> fieldUsage = reflections.getFieldUsage(Field.class); - * Set<Member> methodUsage = reflections.getMethodUsage(Method.class); - * Set<Member> constructorUsage = reflections.getConstructorUsage(Constructor.class); - *
- *

You can use other scanners defined in Reflections as well, such as: SubTypesScanner, TypeAnnotationsScanner (both default), - * ResourcesScanner, MethodAnnotationsScanner, ConstructorAnnotationsScanner, FieldAnnotationsScanner, - * MethodParameterScanner, MethodParameterNamesScanner, MemberUsageScanner or any custom scanner. - *

Use {@link #getStore()} to access and query the store directly - *

In order to save the store metadata, use {@link #save(String)} or {@link #save(String, org.reflections.serializers.Serializer)} - * for example with {@link org.reflections.serializers.XmlSerializer} or {@link org.reflections.serializers.JavaCodeSerializer} - *

In order to collect pre saved metadata and avoid re-scanning, use {@link #collect(String, java.util.function.Predicate, org.reflections.serializers.Serializer...)}} - *

Make sure to scan all the transitively relevant packages. - *
for instance, given your class C extends B extends A, and both B and A are located in another package than C, - * when only the package of C is scanned - then querying for sub types of A returns nothing (transitive), but querying for sub types of B returns C (direct). - * In that case make sure to scan all relevant packages a priori.
- *

For Javadoc, source code, and more information about Reflections Library, see http://github.com/ronmamo/reflections/ + * If not using {@code asClass()} or {@code as()} query results are strings, such that: + *

{@code Set modules    = reflections.get(SubTypes.of(Module.class));
+ * Set singletons = reflections.get(TypesAnnotated.with(Singleton.class));
+ * }
+ *

Note that previous 0.9.x API is still supported, for example: + *

{@code Set> modules = reflections.getSubTypesOf(Module.class);
+ * Set> singletons = reflections.getTypesAnnotatedWith(Singleton.class);
+ * }
+ *

Queries can combine {@link Scanners} and {@link ReflectionUtils} functions, and compose fluent functional methods from {@link QueryFunction}. + *

{@code }
+ *

Scanned metadata can be saved using {@link #save(String)}, and collected using {@link #collect(String, java.util.function.Predicate, org.reflections.serializers.Serializer)} + *

+ * For Javadoc, source code, and more information about Reflections Library, see http://github.com/ronmamo/reflections/ */ -public class Reflections { - public static Logger log = findLogger(Reflections.class); +public class Reflections implements NameHelper { + public final static Logger log = LoggerFactory.getLogger(Reflections.class); protected final transient Configuration configuration; - protected Store store; + protected final Store store; /** - * constructs a Reflections instance and scan according to given {@link org.reflections.Configuration} - *

it is preferred to use {@link org.reflections.util.ConfigurationBuilder} + * constructs Reflections instance and scan according to the given {@link org.reflections.Configuration} + *

it is preferred to use {@link org.reflections.util.ConfigurationBuilder}

{@code new Reflections(new ConfigurationBuilder()...)}
*/ - public Reflections(final Configuration configuration) { + public Reflections(Configuration configuration) { this.configuration = configuration; - store = new Store(configuration); - - if (configuration.getScanners() != null && !configuration.getScanners().isEmpty()) { - //inject to scanners - for (Scanner scanner : configuration.getScanners()) { - scanner.setConfiguration(configuration); - } - - scan(); - - if (configuration.shouldExpandSuperTypes()) { - expandSuperTypes(); - } + Map>> storeMap = scan(); + if (configuration.shouldExpandSuperTypes()) { + expandSuperTypes(storeMap.get(SubTypes.index())); } + store = new Store(storeMap); + } + + public Reflections(Store store) { + this.configuration = new ConfigurationBuilder(); + this.store = store; } /** - * a convenient constructor for scanning within a package prefix. - *

this actually create a {@link org.reflections.Configuration} with: - *
- urls that contain resources with name {@code prefix} - *
- filterInputsBy where name starts with the given {@code prefix} - *
- scanners set to the given {@code scanners}, otherwise defaults to {@link org.reflections.scanners.TypeAnnotationsScanner} and {@link org.reflections.scanners.SubTypesScanner}. - * @param prefix package prefix, to be used with {@link org.reflections.util.ClasspathHelper#forPackage(String, ClassLoader...)} )} - * @param scanners optionally supply scanners, otherwise defaults to {@link org.reflections.scanners.TypeAnnotationsScanner} and {@link org.reflections.scanners.SubTypesScanner} + * constructs Reflections instance and scan according to the given package {@code prefix} and optional {@code scanners} + *

{@code new Reflections("org.reflections")}
+ *

it is preferred to use {@link org.reflections.util.ConfigurationBuilder} instead, this is actually similar to: + *

{@code new Reflections(
+     *   new ConfigurationBuilder()
+     *     .forPackage(prefix)
+     *     .setScanners(scanners))
+     * }
+ *

uses {@link org.reflections.util.ClasspathHelper#forPackage(String, ClassLoader...)} to resolve urls from given {@code prefix} + *

optional {@code scanners} defaults to {@link Scanners#TypesAnnotated} and {@link Scanners#SubTypes} */ - public Reflections(final String prefix, final Scanner... scanners) { + public Reflections(String prefix, Scanner... scanners) { this((Object) prefix, scanners); } /** - * a convenient constructor for Reflections, where given {@code Object...} parameter types can be either: - *

    - *
  • {@link String} - would add urls using {@link org.reflections.util.ClasspathHelper#forPackage(String, ClassLoader...)} ()}
  • - *
  • {@link Class} - would add urls using {@link org.reflections.util.ClasspathHelper#forClass(Class, ClassLoader...)}
  • - *
  • {@link ClassLoader} - would use this classloaders in order to find urls in {@link org.reflections.util.ClasspathHelper#forPackage(String, ClassLoader...)} and {@link org.reflections.util.ClasspathHelper#forClass(Class, ClassLoader...)}
  • - *
  • {@link org.reflections.scanners.Scanner} - would use given scanner, overriding the default scanners
  • - *
  • {@link java.net.URL} - would add the given url for scanning
  • - *
  • {@link Object[]} - would use each element as above
  • - *
- * - * use any parameter type in any order. this constructor uses instanceof on each param and instantiate a {@link org.reflections.util.ConfigurationBuilder} appropriately. - * if you prefer the usual statically typed constructor, don't use this, although it can be very useful. - * - *

for example: - *
-     *     new Reflections("my.package", classLoader);
-     *     //or
-     *     new Reflections("my.package", someScanner, anotherScanner, classLoader);
-     *     //or
-     *     new Reflections(myUrl, myOtherUrl);
-     * 
- */ - public Reflections(final Object... params) { + * Convenient constructor for Reflections. + *

see the javadoc of {@link ConfigurationBuilder#build(Object...)} for details. + *

it is preferred to use {@link org.reflections.util.ConfigurationBuilder} instead. */ + public Reflections(Object... params) { this(ConfigurationBuilder.build(params)); } protected Reflections() { configuration = new ConfigurationBuilder(); - store = new Store(configuration); + store = new Store(new HashMap<>()); } - // - protected void scan() { - if (configuration.getUrls() == null || configuration.getUrls().isEmpty()) { - if (log != null) { - log.warn("given scan urls are empty. set urls in the configuration"); - } - return; - } - - if (log != null && log.isTraceEnabled()) { - log.trace("going to scan these urls: {}", configuration.getUrls()); - } - - long time = System.currentTimeMillis(); - int scannedUrls = 0; - ExecutorService executorService = configuration.getExecutorService(); - List> futures = new ArrayList<>(); + protected Map>> scan() { + long start = System.currentTimeMillis(); + Map>> collect = configuration.getScanners().stream().map(Scanner::index).distinct() + .collect(Collectors.toMap(s -> s, s -> Collections.synchronizedSet(new HashSet<>()))); + Set urls = configuration.getUrls(); - for (final URL url : configuration.getUrls()) { + (configuration.isParallel() ? urls.stream().parallel() : urls.stream()).forEach(url -> { + Vfs.Dir dir = null; try { - if (executorService != null) { - futures.add(executorService.submit(() -> { - if (log != null && log.isTraceEnabled()) { - log.trace("[{}] scanning {}", Thread.currentThread().toString(), url); + dir = Vfs.fromURL(url); + for (Vfs.File file : dir.getFiles()) { + if (doFilter(file, configuration.getInputsFilter())) { + ClassFile classFile = null; + for (Scanner scanner : configuration.getScanners()) { + try { + if (doFilter(file, scanner::acceptsInput)) { + List> entries = scanner.scan(file); + if (entries == null) { + if (classFile == null) classFile = getClassFile(file); + entries = scanner.scan(classFile); + } + if (entries != null) collect.get(scanner.index()).addAll(entries); + } + } catch (Exception e) { + if (log != null) log.trace("could not scan file {} with scanner {}", file.getRelativePath(), scanner.getClass().getSimpleName(), e); + } } - scan(url); - })); - } else { - scan(url); - } - scannedUrls++; - } catch (ReflectionsException e) { - if (log != null) { - log.warn("could not create Vfs.Dir from url. ignoring the exception and continuing", e); + } } + } catch (Exception e) { + if (log != null) log.warn("could not create Vfs.Dir from url. ignoring the exception and continuing", e); + } finally { + if (dir != null) dir.close(); } - } - - if (executorService != null) { - for (Future future : futures) { - try { - future.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } + }); + + // merge + Map>> storeMap = + collect.entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().stream().filter(e -> e.getKey() != null) + .collect(Collectors.groupingBy( + Map.Entry::getKey, + HashMap::new, + Collectors.mapping(Map.Entry::getValue, Collectors.toSet()))))); + if (log != null) { + int keys = 0, values = 0; + for (Map> map : storeMap.values()) { + keys += map.size(); + values += map.values().stream().mapToLong(Set::size).sum(); } + log.info(format("Reflections took %d ms to scan %d urls, producing %d keys and %d values", System.currentTimeMillis() - start, urls.size(), keys, values)); } + return storeMap; + } - //gracefully shutdown the parallel scanner executor service. - if (executorService != null) { - executorService.shutdown(); - } - - if (log != null) { - log.info(format("Reflections took %d ms to scan %d urls, producing %s %s", - System.currentTimeMillis() - time, scannedUrls, producingDescription(store), - executorService instanceof ThreadPoolExecutor ? - format("[using %d cores]", ((ThreadPoolExecutor) executorService).getMaximumPoolSize()) : "")); - } + private boolean doFilter(Vfs.File file, @Nullable Predicate predicate) { + String path = file.getRelativePath(); + String fqn = path.replace('/', '.'); + return predicate == null || predicate.test(path) || predicate.test(fqn); } - private static String producingDescription(Store store) { - int keys = 0; - int values = 0; - for (String index : store.keySet()) { - keys += store.keys(index).size(); - values += store.values(index).size(); - } - return String.format("%d keys and %d values", keys, values); - } - - protected void scan(URL url) { - Vfs.Dir dir = Vfs.fromURL(url); - - try { - for (final Vfs.File file : dir.getFiles()) { - // scan if inputs filter accepts file relative path or fqn - Predicate inputsFilter = configuration.getInputsFilter(); - String path = file.getRelativePath(); - String fqn = path.replace('/', '.'); - if (inputsFilter == null || inputsFilter.test(path) || inputsFilter.test(fqn)) { - Object classObject = null; - for (Scanner scanner : configuration.getScanners()) { - try { - if (scanner.acceptsInput(path) || scanner.acceptsInput(fqn)) { - classObject = scanner.scan(file, classObject, store); - } - } catch (Exception e) { - if (log != null && log.isTraceEnabled()) { - // SLF4J will filter out Throwables from the format string arguments. - log.trace("could not scan file {} in url {} with scanner {}", file.getRelativePath(), url.toExternalForm(), scanner.getClass().getSimpleName(), e); - } - } - } - } - } - } finally { - dir.close(); + private ClassFile getClassFile(Vfs.File file) { + try (DataInputStream dis = new DataInputStream(new BufferedInputStream(file.openInputStream()))) { + return new ClassFile(dis); + } catch (Exception e) { + throw new ReflectionsException("could not create class object from file " + file.getRelativePath(), e); } } @@ -294,344 +238,357 @@ protected void scan(URL url) { * and includes files matching the pattern .*-reflections.xml * */ public static Reflections collect() { - return collect("META-INF/reflections/", new FilterBuilder().include(".*-reflections.xml")); + return collect("META-INF/reflections/", new FilterBuilder().includePattern(".*-reflections\\.xml")); } /** - * collect saved Reflections resources from all urls that contains the given packagePrefix and matches the given resourceNameFilter - * and de-serializes them using the default serializer {@link org.reflections.serializers.XmlSerializer} or using the optionally supplied optionalSerializer - *

- * it is preferred to use a designated resource prefix (for example META-INF/reflections but not just META-INF), - * so that relevant urls could be found much faster - * @param optionalSerializer - optionally supply one serializer instance. if not specified or null, {@link org.reflections.serializers.XmlSerializer} will be used + * collect saved Reflections metadata from all urls that contains the given {@code packagePrefix} and matches the given {@code resourceNameFilter}, + * and deserialize using the default serializer {@link org.reflections.serializers.XmlSerializer} + *

{@code Reflections.collect("META-INF/reflections/",
+     *   new FilterBuilder().includePattern(".*-reflections\\.xml")}
+ * prefer using a designated directory (for example META-INF/reflections but not just META-INF), so that collect can work much faster */ - public static Reflections collect(final String packagePrefix, final Predicate resourceNameFilter, Serializer... optionalSerializer) { - Serializer serializer = optionalSerializer != null && optionalSerializer.length == 1 ? optionalSerializer[0] : new XmlSerializer(); + public static Reflections collect(String packagePrefix, Predicate resourceNameFilter) { + return collect(packagePrefix, resourceNameFilter, new XmlSerializer()); + } + /** + * collect saved Reflections metadata from all urls that contains the given {@code packagePrefix} and matches the given {@code resourceNameFilter}, + * and deserializes using the given {@code serializer} + *
{@code Reflections reflections = Reflections.collect(
+     *   "META-INF/reflections/",
+     *   new FilterBuilder().includePattern(".*-reflections\\.xml"),
+     *   new XmlSerializer())}
+ * prefer using a designated directory (for example META-INF/reflections but not just META-INF), so that collect can work much faster + */ + public static Reflections collect(String packagePrefix, Predicate resourceNameFilter, Serializer serializer) { Collection urls = ClasspathHelper.forPackage(packagePrefix); - if (urls.isEmpty()) return null; - long start = System.currentTimeMillis(); - final Reflections reflections = new Reflections(); Iterable files = Vfs.findFiles(urls, packagePrefix, resourceNameFilter); - for (final Vfs.File file : files) { - InputStream inputStream = null; - try { - inputStream = file.openInputStream(); - reflections.merge(serializer.read(inputStream)); - } catch (IOException e) { - throw new ReflectionsException("could not merge " + file, e); - } finally { - close(inputStream); - } - } - - if (log != null) { - log.info(format("Reflections took %d ms to collect %d url, producing %s", - System.currentTimeMillis() - start, urls.size(), producingDescription(reflections.store))); - } + Reflections reflections = new Reflections(); + StreamSupport.stream(files.spliterator(), false) + .forEach(file -> { + try (InputStream inputStream = file.openInputStream()) { + reflections.collect(inputStream, serializer); + } catch (IOException e) { + throw new ReflectionsException("could not merge " + file, e); + } + }); return reflections; } - /** merges saved Reflections resources from the given input stream, using the serializer configured in this instance's Configuration - *
useful if you know the serialized resource location and prefer not to look it up the classpath - * */ - public Reflections collect(final InputStream inputStream) { - try { - merge(configuration.getSerializer().read(inputStream)); - } catch (Exception ex) { - throw new ReflectionsException("could not merge input stream", ex); - } - - return this; + /** + * deserialize and merge saved Reflections metadata from the given {@code inputStream} and {@code serializer} + *

useful if you know the serialized resource location and prefer not to look it up the classpath + */ + public Reflections collect(InputStream inputStream, Serializer serializer) { + return merge(serializer.read(inputStream)); } - /** merges saved Reflections resources from the given file, using the serializer configured in this instance's Configuration - *

useful if you know the serialized resource location and prefer not to look it up the classpath - * */ - public Reflections collect(final File file) { - FileInputStream inputStream = null; - try { - inputStream = new FileInputStream(file); - return collect(inputStream); - } catch (FileNotFoundException e) { + /** + * deserialize and merge saved Reflections metadata from the given {@code file} and {@code serializer} + *

useful if you know the serialized resource location and prefer not to look it up the classpath + */ + public Reflections collect(File file, Serializer serializer) { + try (FileInputStream inputStream = new FileInputStream(file)) { + return collect(inputStream, serializer); + } catch (IOException e) { throw new ReflectionsException("could not obtain input stream from file " + file, e); - } finally { - Utils.close(inputStream); } } - /** - * merges a Reflections instance metadata into this instance - */ - public Reflections merge(final Reflections reflections) { - store.merge(reflections.store); + /** merges the given {@code reflections} instance metadata into this instance */ + public Reflections merge(Reflections reflections) { + reflections.store.forEach((index, map) -> this.store.merge(index, map, (m1, m2) -> { + m2.forEach((k, v) -> m1.merge(k, v, (s1, s2) -> { s1.addAll(s2); return s1;})); + return m1; + })); return this; } /** * expand super types after scanning, for super types that were not scanned. - * this is helpful in finding the transitive closure without scanning all 3rd party dependencies. - * it uses {@link ReflectionUtils#getSuperTypes(Class)}. - *

- * for example, for classes A,B,C where A supertype of B, B supertype of C: + *
this is helpful in finding the transitive closure without scanning all 3rd party dependencies. + *

+ * for example, for classes A,B,C where A supertype of B, B supertype of C (A -> B -> C): *
    - *
  • if scanning C resulted in B (B->C in store), but A was not scanned (although A supertype of B) - then getSubTypes(A) will not return C
  • + *
  • if scanning C resulted in B (B->C in store), but A was not scanned (although A is a supertype of B) - then getSubTypes(A) will not return C
  • *
  • if expanding supertypes, B will be expanded with A (A->B in store) - then getSubTypes(A) will return C
  • *
*/ - public void expandSuperTypes() { - String index = index(SubTypesScanner.class); - Set keys = store.keys(index); - keys.removeAll(store.values(index)); + public void expandSuperTypes(Map> map) { + if (map == null || map.isEmpty()) return; + Set keys = new LinkedHashSet<>(map.keySet()); + keys.removeAll(map.values().stream().flatMap(Collection::stream).collect(Collectors.toSet())); + keys.remove("java.lang.Object"); for (String key : keys) { - final Class type = forName(key, loaders()); + Class type = forClass(key, loaders()); if (type != null) { - expandSupertypes(store, key, type); + expandSupertypes(map, key, type); } } } - private void expandSupertypes(Store store, String key, Class type) { + private void expandSupertypes(Map> map, String key, Class type) { for (Class supertype : ReflectionUtils.getSuperTypes(type)) { - if (store.put(SubTypesScanner.class, supertype.getName(), key)) { - if (log != null && log.isTraceEnabled()) { - log.trace("expanded subtype {} -> {}", supertype.getName(), key); - } - expandSupertypes(store, supertype.getName(), supertype); + String supertypeName = supertype.getName(); + if (!map.containsKey(supertypeName)) { + map.computeIfAbsent(supertypeName, s -> new HashSet<>()).add(key); + expandSupertypes(map, supertypeName, supertype); } } } - //query /** - * gets all sub types in hierarchy of a given type - *

depends on SubTypesScanner configured + * apply {@link QueryFunction} on {@link Store} + *

{@code Set ts = get(query)}
+ *

use {@link Scanners} and {@link ReflectionUtils} query functions, such as: + *

{@code
+     * Set annotated = get(Scanners.TypesAnnotated.with(A.class))
+     * Set> subtypes = get(Scanners.SubTypes.of(B.class).asClass())
+     * Set methods = get(ReflectionUtils.Methods.of(B.class))
+     * }
*/ - public Set> getSubTypesOf(final Class type) { - return forNames(store.getAll(SubTypesScanner.class, type.getName()), loaders()); + public Set get(QueryFunction query) { + return query.apply(store); } /** - * get types annotated with a given annotation, both classes and annotations - *

{@link java.lang.annotation.Inherited} is not honored by default. - *

when honoring @Inherited, meta-annotation should only effect annotated super classes and its sub types - *

Note that this (@Inherited) meta-annotation type has no effect if the annotated type is used for anything other then a class. - * Also, this meta-annotation causes annotations to be inherited only from superclasses; annotations on implemented interfaces have no effect. - *

depends on TypeAnnotationsScanner and SubTypesScanner configured + * gets all subtypes in hierarchy of a given {@code type}. + *

similar to {@code get(SubTypes.of(type))} + *

depends on {@link Scanners#SubTypes} configured + */ + public Set> getSubTypesOf(Class type) { + //noinspection unchecked + return (Set>) get(SubTypes.of(type) + .as((Class) Class.class, loaders())); + } + + /** + * get types annotated with the given {@code annotation}, both classes and annotations + *

{@link java.lang.annotation.Inherited} is not honored by default, see {@link #getTypesAnnotatedWith(Class, boolean)}. + *

similar to {@code get(SubTypes.of(TypesAnnotated.with(annotation)))} + *

depends on {@link Scanners#TypesAnnotated} and {@link Scanners#SubTypes} configured */ - public Set> getTypesAnnotatedWith(final Class annotation) { - return getTypesAnnotatedWith(annotation, false); + public Set> getTypesAnnotatedWith(Class annotation) { + return get(SubTypes.of(TypesAnnotated.with(annotation)).asClass(loaders())); } /** - * get types annotated with a given annotation, both classes and annotations - *

{@link java.lang.annotation.Inherited} is honored according to given honorInherited. - *

when honoring @Inherited, meta-annotation should only effect annotated super classes and it's sub types + * get types annotated with the given {@code annotation}, both classes and annotations + *

{@link java.lang.annotation.Inherited} is honored according to the given {@code honorInherited}. + *

when honoring @Inherited, meta-annotation should only effect annotated super classes and subtypes *

when not honoring @Inherited, meta annotation effects all subtypes, including annotations interfaces and classes *

Note that this (@Inherited) meta-annotation type has no effect if the annotated type is used for anything other then a class. * Also, this meta-annotation causes annotations to be inherited only from superclasses; annotations on implemented interfaces have no effect. - *

depends on TypeAnnotationsScanner and SubTypesScanner configured + *

depends on {@link Scanners#TypesAnnotated} and {@link Scanners#SubTypes} configured */ - public Set> getTypesAnnotatedWith(final Class annotation, boolean honorInherited) { - Set annotated = store.get(TypeAnnotationsScanner.class, annotation.getName()); - annotated.addAll(getAllAnnotated(annotated, annotation, honorInherited)); - return forNames(annotated, loaders()); + public Set> getTypesAnnotatedWith(Class annotation, boolean honorInherited) { + if (!honorInherited) { + return getTypesAnnotatedWith(annotation); + } else { + if (annotation.isAnnotationPresent(Inherited.class)) { + return get(TypesAnnotated.get(annotation) + .add(SubTypes.of(TypesAnnotated.get(annotation) + .filter(c -> !forClass(c, loaders()).isInterface()))) + .asClass(loaders())); + } else { + return get(TypesAnnotated.get(annotation).asClass(loaders())); + } + } } /** - * get types annotated with a given annotation, both classes and annotations, including annotation member values matching - *

{@link java.lang.annotation.Inherited} is not honored by default - *

depends on TypeAnnotationsScanner configured + * get types annotated with the given {@code annotation}, both classes and annotations, including annotation member values matching + *

{@link java.lang.annotation.Inherited} is not honored by default, see {@link #getTypesAnnotatedWith(Annotation, boolean)}. + *

depends on {@link Scanners#TypesAnnotated} and {@link Scanners#SubTypes} configured */ - public Set> getTypesAnnotatedWith(final Annotation annotation) { - return getTypesAnnotatedWith(annotation, false); + public Set> getTypesAnnotatedWith(Annotation annotation) { + return get(SubTypes.of( + TypesAnnotated.of(TypesAnnotated.get(annotation.annotationType()) + .filter(c -> withAnnotation(annotation).test(forClass(c, loaders()))))) + .asClass(loaders())); } /** - * get types annotated with a given annotation, both classes and annotations, including annotation member values matching + * get types annotated with the given {@code annotation}, both classes and annotations, including annotation member values matching *

{@link java.lang.annotation.Inherited} is honored according to given honorInherited - *

depends on TypeAnnotationsScanner configured + *

depends on {@link Scanners#TypesAnnotated} and {@link Scanners#SubTypes} configured */ - public Set> getTypesAnnotatedWith(final Annotation annotation, boolean honorInherited) { - Set annotated = store.get(TypeAnnotationsScanner.class, annotation.annotationType().getName()); - Set> allAnnotated = filter(forNames(annotated, loaders()), withAnnotation(annotation)); - Set> classes = forNames(filter(getAllAnnotated(names(allAnnotated), annotation.annotationType(), honorInherited), s -> !annotated.contains(s)), loaders()); - allAnnotated.addAll(classes); - return allAnnotated; - } - - protected Collection getAllAnnotated(Collection annotated, Class annotation, boolean honorInherited) { - if (honorInherited) { - if (annotation.isAnnotationPresent(Inherited.class)) { - Set subTypes = store.get(SubTypesScanner.class, filter(annotated, input -> { - final Class type = forName(input, loaders()); - return type != null && !type.isInterface(); - })); - return store.getAllIncluding(SubTypesScanner.class, subTypes); + public Set> getTypesAnnotatedWith(Annotation annotation, boolean honorInherited) { + if (!honorInherited) { + return getTypesAnnotatedWith(annotation); + } else { + Class type = annotation.annotationType(); + if (type.isAnnotationPresent(Inherited.class)) { + return get(TypesAnnotated.with(type).asClass(loaders()).filter(withAnnotation(annotation)) + .add(SubTypes.of(TypesAnnotated.with(type).asClass(loaders()).filter(c -> !c.isInterface())))); } else { - return annotated; + return get(TypesAnnotated.with(type).asClass(loaders()).filter(withAnnotation(annotation))); } - } else { - Collection subTypes = store.getAllIncluding(TypeAnnotationsScanner.class, annotated); - return store.getAllIncluding(SubTypesScanner.class, subTypes); } } /** - * get all methods annotated with a given annotation - *

depends on MethodAnnotationsScanner configured + * get methods annotated with the given {@code annotation} + *

similar to {@code get(MethodsAnnotated.with(annotation))} + *

depends on {@link Scanners#MethodsAnnotated} configured */ - public Set getMethodsAnnotatedWith(final Class annotation) { - return getMethodsFromDescriptors(store.get(MethodAnnotationsScanner.class, annotation.getName()), loaders()); + public Set getMethodsAnnotatedWith(Class annotation) { + return get(MethodsAnnotated.with(annotation).as(Method.class, loaders())); } /** - * get all methods annotated with a given annotation, including annotation member values matching - *

depends on MethodAnnotationsScanner configured + * get methods annotated with the given {@code annotation}, including annotation member values matching + *

similar to {@code get(MethodsAnnotated.with(annotation))} + *

depends on {@link Scanners#MethodsAnnotated} configured */ - public Set getMethodsAnnotatedWith(final Annotation annotation) { - return filter(getMethodsAnnotatedWith(annotation.annotationType()), withAnnotation(annotation)); - } - - /** get methods with parameter types matching given {@code types}*/ - public Set getMethodsMatchParams(Class... types) { - return getMethodsFromDescriptors(store.get(MethodParameterScanner.class, names(types).toString()), loaders()); - } - - /** get methods with return type match given type */ - public Set getMethodsReturn(Class returnType) { - return getMethodsFromDescriptors(store.get(MethodParameterScanner.class, names(returnType)), loaders()); - } - - /** get methods with any parameter annotated with given annotation */ - public Set getMethodsWithAnyParamAnnotated(Class annotation) { - return getMethodsFromDescriptors(store.get(MethodParameterScanner.class, annotation.getName()), loaders()); - - } - - /** get methods with any parameter annotated with given annotation, including annotation member values matching */ - public Set getMethodsWithAnyParamAnnotated(Annotation annotation) { - return filter(getMethodsWithAnyParamAnnotated(annotation.annotationType()), withAnyParameterAnnotation(annotation)); + public Set getMethodsAnnotatedWith(Annotation annotation) { + return get(MethodsAnnotated.with(annotation.annotationType()).as(Method.class, loaders()) + .filter(withAnnotation(annotation))); } /** - * get all constructors annotated with a given annotation - *

depends on MethodAnnotationsScanner configured + * get methods with signature matching the given {@code types} + *

similar to {@code get(MethodsSignature.of(types))} + *

depends on {@link Scanners#MethodsSignature} configured */ - public Set getConstructorsAnnotatedWith(final Class annotation) { - return getConstructorsFromDescriptors(store.get(MethodAnnotationsScanner.class, annotation.getName()), loaders()); + public Set getMethodsWithSignature(Class... types) { + return get(MethodsSignature.with(types).as(Method.class, loaders())); } /** - * get all constructors annotated with a given annotation, including annotation member values matching - *

depends on MethodAnnotationsScanner configured + * get methods with any parameter matching the given {@code type}, either class or annotation + *

similar to {@code get(MethodsParameter.with(type))} + *

depends on {@link Scanners#MethodsParameter} configured */ - public Set getConstructorsAnnotatedWith(final Annotation annotation) { - return filter(getConstructorsAnnotatedWith(annotation.annotationType()), withAnnotation(annotation)); + public Set getMethodsWithParameter(AnnotatedElement type) { + return get(MethodsParameter.with(type).as(Method.class, loaders())); } - /** get constructors with parameter types matching given {@code types}*/ - public Set getConstructorsMatchParams(Class... types) { - return getConstructorsFromDescriptors(store.get(MethodParameterScanner.class, names(types).toString()), loaders()); + /** + * get methods with return type matching the given {@code returnType} + *

similar to {@code get(MethodsReturn.of(type))} + *

depends on {@link Scanners#MethodsParameter} configured + */ + public Set getMethodsReturn(Class type) { + return get(MethodsReturn.of(type).as(Method.class, loaders())); } - /** get constructors with any parameter annotated with given annotation */ - public Set getConstructorsWithAnyParamAnnotated(Class annotation) { - return getConstructorsFromDescriptors(store.get(MethodParameterScanner.class, annotation.getName()), loaders()); + /** + * get constructors annotated with the given {@code annotation} + *

similar to {@code get(ConstructorsAnnotated.with(annotation))} + *

depends on {@link Scanners#ConstructorsAnnotated} configured + */ + public Set getConstructorsAnnotatedWith(Class annotation) { + return get(ConstructorsAnnotated.with(annotation).as(Constructor.class, loaders())); } - /** get constructors with any parameter annotated with given annotation, including annotation member values matching */ - public Set getConstructorsWithAnyParamAnnotated(Annotation annotation) { - return filter(getConstructorsWithAnyParamAnnotated(annotation.annotationType()), withAnyParameterAnnotation(annotation)); + /** + * get constructors annotated with the given {@code annotation}, including annotation member values matching + *

similar to {@code get(ConstructorsAnnotated.with(annotation))} + *

depends on {@link Scanners#ConstructorsAnnotated} configured + */ + public Set getConstructorsAnnotatedWith(Annotation annotation) { + return get(ConstructorsAnnotated.with(annotation.annotationType()).as(Constructor.class, loaders()) + .filter(withAnyParameterAnnotation(annotation))); } /** - * get all fields annotated with a given annotation - *

depends on FieldAnnotationsScanner configured + * get constructors with signature matching the given {@code types} + *

similar to {@code get(ConstructorsSignature.with(types))} + *

depends on {@link Scanners#ConstructorsSignature} configured */ - public Set getFieldsAnnotatedWith(final Class annotation) { - return store.get(FieldAnnotationsScanner.class, annotation.getName()).stream() - .map(annotated -> getFieldFromString(annotated, loaders())) - .collect(Collectors.toSet()); + public Set getConstructorsWithSignature(Class... types) { + return get(ConstructorsSignature.with(types).as(Constructor.class, loaders())); } /** - * get all methods annotated with a given annotation, including annotation member values matching - *

depends on FieldAnnotationsScanner configured + * get constructors with any parameter matching the given {@code type}, either class or annotation + *

similar to {@code get(ConstructorsParameter.with(types))} + *

depends on {@link Scanners#ConstructorsParameter} configured */ - public Set getFieldsAnnotatedWith(final Annotation annotation) { - return filter(getFieldsAnnotatedWith(annotation.annotationType()), withAnnotation(annotation)); + public Set getConstructorsWithParameter(AnnotatedElement type) { + return get(ConstructorsParameter.of(type).as(Constructor.class, loaders())); } - /** get resources relative paths where simple name (key) matches given namePredicate - *

depends on ResourcesScanner configured - * */ - public Set getResources(final Predicate namePredicate) { - Set resources = filter(store.keys(index(ResourcesScanner.class)), namePredicate); - return store.get(ResourcesScanner.class, resources); + /** + * get fields annotated with the given {@code annotation} + *

similar to {@code get(FieldsAnnotated.with(annotation))} + *

depends on {@link Scanners#FieldsAnnotated} configured + */ + public Set getFieldsAnnotatedWith(Class annotation) { + return get(FieldsAnnotated.with(annotation).as(Field.class, loaders())); } - /** get resources relative paths where simple name (key) matches given regular expression - *

depends on ResourcesScanner configured - *

Set xmls = reflections.getResources(".*\\.xml");
+ /** + * get fields annotated with the given {@code annotation}, including annotation member values matching + *

similar to {@code get(FieldsAnnotated.with(annotation))} + *

depends on {@link Scanners#FieldsAnnotated} configured */ - public Set getResources(final Pattern pattern) { - return getResources(input -> pattern.matcher(input).matches()); + public Set getFieldsAnnotatedWith(Annotation annotation) { + return get(FieldsAnnotated.with(annotation.annotationType()).as(Field.class, loaders()) + .filter(withAnnotation(annotation))); } - /** get parameter names of given {@code method} - *

depends on MethodParameterNamesScanner configured + /** + * get resources matching the given {@code pattern} regex

{@code Set xmls = reflections.getResources(".*\\.xml")}
+ *

similar to {@code get(Resources.with(pattern))} + *

depends on {@link Scanners#Resources} configured */ - public List getMethodParamNames(Method method) { - Set names = store.get(MethodParameterNamesScanner.class, name(method)); - return names.size() == 1 ? Arrays.asList(names.iterator().next().split(", ")) : Collections.emptyList(); + public Set getResources(String pattern) { + return get(Resources.with(pattern)); } - /** get parameter names of given {@code constructor} - *

depends on MethodParameterNamesScanner configured + /** + * get resources matching the given {@code pattern} regex

{@code Set xmls = reflections.getResources(Pattern.compile(".*\\.xml"))}
+ *

similar to {@code get(Resources.with(pattern))} + *

depends on {@link Scanners#Resources} configured */ - public List getConstructorParamNames(Constructor constructor) { - Set names = store.get(MethodParameterNamesScanner.class, Utils.name(constructor)); - return names.size() == 1 ? Arrays.asList(names.iterator().next().split(", ")) : Collections.emptyList(); + public Set getResources(Pattern pattern) { + return getResources(pattern.pattern()); } - /** get all given {@code field} usages in methods and constructors - *

depends on MemberUsageScanner configured + /** + * get parameter names of the given {@code member}, either method or constructor + *

depends on {@link MethodParameterNamesScanner} configured */ - public Set getFieldUsage(Field field) { - return getMembersFromDescriptors(store.get(MemberUsageScanner.class, name(field))); + public List getMemberParameterNames(Member member) { + return store.getOrDefault(MethodParameterNamesScanner.class.getSimpleName(), Collections.emptyMap()).getOrDefault(toName((AnnotatedElement) member), Collections.emptySet()) + .stream().flatMap(s -> Stream.of(s.split(", "))).collect(Collectors.toList()); } - /** get all given {@code method} usages in methods and constructors - *

depends on MemberUsageScanner configured + /** + * get code usages for the given {@code member}, either field, method or constructor + *

depends on {@link MemberUsageScanner} configured */ - public Set getMethodUsage(Method method) { - return getMembersFromDescriptors(store.get(MemberUsageScanner.class, name(method))); + public Collection getMemberUsage(Member member) { + Set usages = store.getOrDefault(MemberUsageScanner.class.getSimpleName(), Collections.emptyMap()).getOrDefault(toName((AnnotatedElement) member), Collections.emptySet()); + return forNames(usages, Member.class, loaders()); } - /** get all given {@code constructors} usages in methods and constructors - *

depends on MemberUsageScanner configured + /** + * returns all keys and values scanned by {@link Scanners#SubTypes} scanner + *

using this api is discouraged, it is better to get elements by specific criteria such as {@code SubTypes.of(Class)} or {@code TypesAnnotated.with(Class)} + *

deprecated, use {@link #getAll(Scanner)} instead */ - public Set getConstructorUsage(Constructor constructor) { - return getMembersFromDescriptors(store.get(MemberUsageScanner.class, name(constructor))); + @Deprecated + public Set getAllTypes() { + return getAll(SubTypes); } - /** get all types scanned. this is effectively similar to getting all subtypes of Object. - *

depends on SubTypesScanner configured with {@code SubTypesScanner(false)}, otherwise {@code ReflectionsException} is thrown - *

note using this might be a bad practice. it is better to get types matching some criteria, - * such as {@link #getSubTypesOf(Class)} or {@link #getTypesAnnotatedWith(Class)} - * @return Set of String, and not of Class, in order to avoid definition of all types in PermGen + /** + * returns all key and values scanned by the given {@code scanner}

{@code Set all = reflections.getAll(SubTypes)}
+ *

using this is discouraged, it is better to get elements by specific criteria such as {@code SubTypes.of(Class)} or {@code TypesAnnotated.with(Class)} */ - public Set getAllTypes() { - Set allTypes = new HashSet<>(store.getAll(SubTypesScanner.class, Object.class.getName())); - if (allTypes.isEmpty()) { - throw new ReflectionsException("Couldn't find subtypes of Object. " + - "Make sure SubTypesScanner initialized to include Object class - new SubTypesScanner(false)"); - } - return allTypes; + public Set getAll(Scanner scanner) { + Map> map = store.getOrDefault(scanner.index(), Collections.emptyMap()); + return Stream.concat(map.keySet().stream(), map.values().stream().flatMap(Collection::stream)).collect(Collectors.toCollection(LinkedHashSet::new)); } - /** returns the {@link org.reflections.Store} used for storing and querying the metadata */ + /** + * returns the {@link org.reflections.Store} object used for storing and querying the metadata + *

{@code Store} is basically {@code Map>>} + */ public Store getStore() { return store; } @@ -642,23 +599,20 @@ public Configuration getConfiguration() { } /** - * serialize to a given directory and filename - *

* it is preferred to specify a designated directory (for example META-INF/reflections), - * so that it could be found later much faster using the load method - *

see the documentation for the save method on the configured {@link org.reflections.serializers.Serializer} + * serialize metadata to the given {@code filename} + *

prefer using a designated directory (for example META-INF/reflections but not just META-INF), so that {@link Reflections#collect(String, Predicate)} can work much faster */ - public File save(final String filename) { - return save(filename, configuration.getSerializer()); + public File save(String filename) { + return save(filename, new XmlSerializer()); } /** - * serialize to a given directory and filename using given serializer - *

* it is preferred to specify a designated directory (for example META-INF/reflections), - * so that it could be found later much faster using the load method + * serialize metadata to the given {@code filename} and {@code serializer} + *

prefer using a designated directory (for example META-INF/reflections but not just META-INF), so that {@link Reflections#collect(String, Predicate, Serializer)} can work much faster */ - public File save(final String filename, final Serializer serializer) { + public File save(String filename, Serializer serializer) { return serializer.save(this, filename); } - private ClassLoader[] loaders() { return configuration.getClassLoaders(); } + ClassLoader[] loaders() { return configuration.getClassLoaders(); } } diff --git a/src/main/java/org/reflections/Store.java b/src/main/java/org/reflections/Store.java index 35edefa4..56a75c9f 100644 --- a/src/main/java/org/reflections/Store.java +++ b/src/main/java/org/reflections/Store.java @@ -1,142 +1,13 @@ package org.reflections; -import org.reflections.scanners.Scanner; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; +import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -import static org.reflections.util.Utils.index; - -/** - * stores metadata information in multimaps - *

use the different query methods (getXXX) to query the metadata - *

the query methods are string based, and does not cause the class loader to define the types - *

use {@link org.reflections.Reflections#getStore()} to access this store - */ -public class Store { - - private final ConcurrentHashMap>> storeMap; - - protected Store(Configuration configuration) { - storeMap = new ConcurrentHashMap<>(); - for (Scanner scanner : configuration.getScanners()) { - String index = index(scanner.getClass()); - storeMap.computeIfAbsent(index, s -> new ConcurrentHashMap<>()); - } - } - - /** return all indices */ - public Set keySet() { - return storeMap.keySet(); - } - - /** get the multimap object for the given {@code index}, otherwise throws a {@link org.reflections.ReflectionsException} */ - private Map> get(String index) { - Map> mmap = storeMap.get(index); - if (mmap == null) { - throw new ReflectionsException("Scanner " + index + " was not configured"); - } - return mmap; - } - - /** get the values stored for the given {@code index} and {@code keys} */ - public Set get(Class scannerClass, String key) { - return get(index(scannerClass), Collections.singletonList(key)); - } - - /** get the values stored for the given {@code index} and {@code keys} */ - public Set get(String index, String key) { - return get(index, Collections.singletonList(key)); - } - - /** get the values stored for the given {@code index} and {@code keys} */ - public Set get(Class scannerClass, Collection keys) { - return get(index(scannerClass), keys); - } - - /** get the values stored for the given {@code index} and {@code keys} */ - private Set get(String index, Collection keys) { - Map> mmap = get(index); - Set result = new LinkedHashSet<>(); - for (String key : keys) { - Collection values = mmap.get(key); - if (values != null) { - result.addAll(values); - } - } - return result; - } - - /** recursively get the values stored for the given {@code index} and {@code keys}, including keys */ - public Set getAllIncluding(Class scannerClass, Collection keys) { - String index = index(scannerClass); - Map> mmap = get(index); - List workKeys = new ArrayList<>(keys); - - Set result = new HashSet<>(); - for (int i = 0; i < workKeys.size(); i++) { - String key = workKeys.get(i); - if (result.add(key)) { - Collection values = mmap.get(key); - if (values != null) { - workKeys.addAll(values); - } - } - } - return result; - } - - /** recursively get the values stored for the given {@code index} and {@code keys}, not including keys */ - public Set getAll(Class scannerClass, String key) { - return getAllIncluding(scannerClass, get(scannerClass, key)); - } - - /** recursively get the values stored for the given {@code index} and {@code keys}, not including keys */ - public Set getAll(Class scannerClass, Collection keys) { - return getAllIncluding(scannerClass, get(scannerClass, keys)); - } - - public Set keys(String index) { - Map> map = storeMap.get(index); - return map != null ? new HashSet<>(map.keySet()) : Collections.emptySet(); - } - - public Set values(String index) { - Map> map = storeMap.get(index); - return map != null ? map.values().stream().flatMap(Collection::stream).collect(Collectors.toSet()) : Collections.emptySet(); - } - - // - public boolean put(Class scannerClass, String key, String value) { - return put(index(scannerClass), key, value); - } - - public boolean put(String index, String key, String value) { - return storeMap.computeIfAbsent(index, s -> new ConcurrentHashMap<>()) - .computeIfAbsent(key, s -> Collections.synchronizedList(new ArrayList<>())) - .add(value); - } - void merge(Store store) { - if (store != null) { - for (String indexName : store.keySet()) { - Map> index = store.get(indexName); - if (index != null) { - for (String key : index.keySet()) { - for (String string : index.get(key)) { - put(indexName, key, string); - } - } - } - } - } - } +/** stores string key/value pairs per scanner index in a multimap {@code Map>>} + *

{@code Set values = reflections.getStore().get("index").get("key")}
+ * {@code Store} multimap is not copy protected, preferably use {@link org.reflections.util.QueryBuilder} to safely rich query the metadata */ +public class Store extends HashMap>> { + public Store() {} + public Store(Map>> storeMap) { super(storeMap); } } diff --git a/src/main/java/org/reflections/adapters/JavaReflectionAdapter.java b/src/main/java/org/reflections/adapters/JavaReflectionAdapter.java deleted file mode 100644 index aab18adf..00000000 --- a/src/main/java/org/reflections/adapters/JavaReflectionAdapter.java +++ /dev/null @@ -1,142 +0,0 @@ -package org.reflections.adapters; - -import org.reflections.util.Utils; -import org.reflections.vfs.Vfs; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import static org.reflections.ReflectionUtils.forName; -import static org.reflections.util.Utils.join; - -/** */ -public class JavaReflectionAdapter implements MetadataAdapter { - - public List getFields(Class cls) { - return Arrays.asList(cls.getDeclaredFields()); - } - - public List getMethods(Class cls) { - List methods = new ArrayList<>(); - methods.addAll(Arrays.asList(cls.getDeclaredMethods())); - methods.addAll(Arrays.asList(cls.getDeclaredConstructors())); - return methods; - } - - public String getMethodName(Member method) { - return method instanceof Method ? method.getName() : - method instanceof Constructor ? "" : null; - } - - public List getParameterNames(final Member member) { - Class[] parameterTypes = member instanceof Method ? ((Method) member).getParameterTypes() : - member instanceof Constructor ? ((Constructor) member).getParameterTypes() : null; - - return parameterTypes != null ? Arrays.stream(parameterTypes).map(JavaReflectionAdapter::getName).collect(Collectors.toList()) : Collections.emptyList(); - } - - public List getClassAnnotationNames(Class aClass) { - return getAnnotationNames(aClass.getDeclaredAnnotations()); - } - - public List getFieldAnnotationNames(Field field) { - return getAnnotationNames(field.getDeclaredAnnotations()); - } - - public List getMethodAnnotationNames(Member method) { - Annotation[] annotations = - method instanceof Method ? ((Method) method).getDeclaredAnnotations() : - method instanceof Constructor ? ((Constructor) method).getDeclaredAnnotations() : null; - return getAnnotationNames(annotations); - } - - public List getParameterAnnotationNames(Member method, int parameterIndex) { - Annotation[][] annotations = - method instanceof Method ? ((Method) method).getParameterAnnotations() : - method instanceof Constructor ? ((Constructor) method).getParameterAnnotations() : null; - - return getAnnotationNames(annotations != null ? annotations[parameterIndex] : null); - } - - public String getReturnTypeName(Member method) { - return ((Method) method).getReturnType().getName(); - } - - public String getFieldName(Field field) { - return field.getName(); - } - - public Class getOrCreateClassObject(Vfs.File file) throws Exception { - return getOrCreateClassObject(file, null); - } - - public Class getOrCreateClassObject(Vfs.File file, ClassLoader... loaders) throws Exception { - String name = file.getRelativePath().replace("/", ".").replace(".class", ""); - return forName(name, loaders); - } - - public String getMethodModifier(Member method) { - return Modifier.toString(method.getModifiers()); - } - - public String getMethodKey(Class cls, Member method) { - return getMethodName(method) + "(" + join(getParameterNames(method), ", ") + ")"; - } - - public String getMethodFullKey(Class cls, Member method) { - return getClassName(cls) + "." + getMethodKey(cls, method); - } - - public boolean isPublic(Object o) { - Integer mod = - o instanceof Class ? ((Class) o).getModifiers() : - o instanceof Member ? ((Member) o).getModifiers() : null; - - return mod != null && Modifier.isPublic(mod); - } - - public String getClassName(Class cls) { - return cls.getName(); - } - - public String getSuperclassName(Class cls) { - Class superclass = cls.getSuperclass(); - return superclass != null ? superclass.getName() : ""; - } - - public List getInterfacesNames(Class cls) { - Class[] classes = cls.getInterfaces(); - return classes != null ? Arrays.stream(classes).map(Class::getName).collect(Collectors.toList()) : Collections.emptyList(); - } - - public boolean acceptsInput(String file) { - return file.endsWith(".class"); - } - - // - private List getAnnotationNames(Annotation[] annotations) { - return Arrays.stream(annotations).map(annotation -> annotation.annotationType().getName()).collect(Collectors.toList()); - } - - public static String getName(Class type) { - if (type.isArray()) { - try { - Class cl = type; - int dim = 0; while (cl.isArray()) { dim++; cl = cl.getComponentType(); } - return cl.getName() + Utils.repeat("[]", dim); - } catch (Throwable e) { - // - } - } - return type.getName(); - } -} diff --git a/src/main/java/org/reflections/adapters/JavassistAdapter.java b/src/main/java/org/reflections/adapters/JavassistAdapter.java deleted file mode 100644 index dc5cd93c..00000000 --- a/src/main/java/org/reflections/adapters/JavassistAdapter.java +++ /dev/null @@ -1,193 +0,0 @@ -package org.reflections.adapters; - -import javassist.bytecode.AccessFlag; -import javassist.bytecode.AnnotationsAttribute; -import javassist.bytecode.ClassFile; -import javassist.bytecode.Descriptor; -import javassist.bytecode.FieldInfo; -import javassist.bytecode.MethodInfo; -import javassist.bytecode.ParameterAnnotationsAttribute; -import javassist.bytecode.annotation.Annotation; -import org.reflections.ReflectionsException; -import org.reflections.util.Utils; -import org.reflections.vfs.Vfs; - -import java.io.BufferedInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static javassist.bytecode.AccessFlag.isPrivate; -import static javassist.bytecode.AccessFlag.isProtected; -import static org.reflections.util.Utils.join; - -/** - * - */ -public class JavassistAdapter implements MetadataAdapter { - - /**setting this to false will result in returning only visible annotations from the relevant methods here (only {@link java.lang.annotation.RetentionPolicy#RUNTIME})*/ - public static boolean includeInvisibleTag = true; - - public List getFields(final ClassFile cls) { - return cls.getFields(); - } - - public List getMethods(final ClassFile cls) { - return cls.getMethods(); - } - - public String getMethodName(final MethodInfo method) { - return method.getName(); - } - - public List getParameterNames(final MethodInfo method) { - String descriptor = method.getDescriptor(); - descriptor = descriptor.substring(descriptor.indexOf("(") + 1, descriptor.lastIndexOf(")")); - return splitDescriptorToTypeNames(descriptor); - } - - public List getClassAnnotationNames(final ClassFile aClass) { - return getAnnotationNames((AnnotationsAttribute) aClass.getAttribute(AnnotationsAttribute.visibleTag), - includeInvisibleTag ? (AnnotationsAttribute) aClass.getAttribute(AnnotationsAttribute.invisibleTag) : null); - } - - public List getFieldAnnotationNames(final FieldInfo field) { - return getAnnotationNames((AnnotationsAttribute) field.getAttribute(AnnotationsAttribute.visibleTag), - includeInvisibleTag ? (AnnotationsAttribute) field.getAttribute(AnnotationsAttribute.invisibleTag) : null); - } - - public List getMethodAnnotationNames(final MethodInfo method) { - return getAnnotationNames((AnnotationsAttribute) method.getAttribute(AnnotationsAttribute.visibleTag), - includeInvisibleTag ? (AnnotationsAttribute) method.getAttribute(AnnotationsAttribute.invisibleTag) : null); - } - - public List getParameterAnnotationNames(final MethodInfo method, final int parameterIndex) { - List result = new ArrayList<>(); - - List parameterAnnotationsAttributes = Arrays.asList( - (ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.visibleTag), - (ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.invisibleTag)); - - for (ParameterAnnotationsAttribute parameterAnnotationsAttribute : parameterAnnotationsAttributes) { - if (parameterAnnotationsAttribute != null) { - Annotation[][] annotations = parameterAnnotationsAttribute.getAnnotations(); - if (parameterIndex < annotations.length) { - Annotation[] annotation = annotations[parameterIndex]; - result.addAll(getAnnotationNames(annotation)); - } - } - } - - return result; - } - - public String getReturnTypeName(final MethodInfo method) { - String descriptor = method.getDescriptor(); - descriptor = descriptor.substring(descriptor.lastIndexOf(")") + 1); - return splitDescriptorToTypeNames(descriptor).get(0); - } - - public String getFieldName(final FieldInfo field) { - return field.getName(); - } - - public ClassFile getOrCreateClassObject(final Vfs.File file) { - InputStream inputStream = null; - try { - inputStream = file.openInputStream(); - DataInputStream dis = new DataInputStream(new BufferedInputStream(inputStream)); - return new ClassFile(dis); - } catch (IOException e) { - throw new ReflectionsException("could not create class file from " + file.getName(), e); - } finally { - Utils.close(inputStream); - } - } - - public String getMethodModifier(MethodInfo method) { - int accessFlags = method.getAccessFlags(); - return isPrivate(accessFlags) ? "private" : - isProtected(accessFlags) ? "protected" : - isPublic(accessFlags) ? "public" : ""; - } - - public String getMethodKey(ClassFile cls, MethodInfo method) { - return getMethodName(method) + "(" + join(getParameterNames(method), ", ") + ")"; - } - - public String getMethodFullKey(ClassFile cls, MethodInfo method) { - return getClassName(cls) + "." + getMethodKey(cls, method); - } - - public boolean isPublic(Object o) { - Integer accessFlags = - o instanceof ClassFile ? ((ClassFile) o).getAccessFlags() : - o instanceof FieldInfo ? ((FieldInfo) o).getAccessFlags() : - o instanceof MethodInfo ? ((MethodInfo) o).getAccessFlags() : null; - - return accessFlags != null && AccessFlag.isPublic(accessFlags); - } - - // - public String getClassName(final ClassFile cls) { - return cls.getName(); - } - - public String getSuperclassName(final ClassFile cls) { - return cls.getSuperclass(); - } - - public List getInterfacesNames(final ClassFile cls) { - return Arrays.asList(cls.getInterfaces()); - } - - public boolean acceptsInput(String file) { - return file.endsWith(".class"); - } - - // - private List getAnnotationNames(final AnnotationsAttribute... annotationsAttributes) { - if (annotationsAttributes != null) { - return Arrays.stream(annotationsAttributes) - .filter(Objects::nonNull) - .flatMap(annotationsAttribute -> Arrays.stream(annotationsAttribute.getAnnotations())) - .map(Annotation::getTypeName) - .collect(Collectors.toList()); - } else { - return Collections.emptyList(); - } - } - - private List getAnnotationNames(final Annotation[] annotations) { - return Arrays.stream(annotations).map(Annotation::getTypeName).collect(Collectors.toList()); - } - - private List splitDescriptorToTypeNames(final String descriptors) { - List result = new ArrayList<>(); - - if (descriptors != null && descriptors.length() != 0) { - - List indices = new ArrayList<>(); - Descriptor.Iterator iterator = new Descriptor.Iterator(descriptors); - while (iterator.hasNext()) { - indices.add(iterator.next()); - } - indices.add(descriptors.length()); - - result = IntStream.range(0, indices.size() - 1) - .mapToObj(i -> Descriptor.toString(descriptors.substring(indices.get(i), indices.get(i + 1)))) - .collect(Collectors.toList()); - - } - - return result; - } -} diff --git a/src/main/java/org/reflections/adapters/MetadataAdapter.java b/src/main/java/org/reflections/adapters/MetadataAdapter.java deleted file mode 100644 index 99737bbc..00000000 --- a/src/main/java/org/reflections/adapters/MetadataAdapter.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.reflections.adapters; - -import org.reflections.vfs.Vfs; - -import java.util.List; - -/** - * - */ -public interface MetadataAdapter { - - // - String getClassName(final C cls); - - String getSuperclassName(final C cls); - - List getInterfacesNames(final C cls); - - // - List getFields(final C cls); - - List getMethods(final C cls); - - String getMethodName(final M method); - - List getParameterNames(final M method); - - List getClassAnnotationNames(final C aClass); - - List getFieldAnnotationNames(final F field); - - List getMethodAnnotationNames(final M method); - - List getParameterAnnotationNames(final M method, final int parameterIndex); - - String getReturnTypeName(final M method); - - String getFieldName(final F field); - - C getOrCreateClassObject(Vfs.File file) throws Exception; - - String getMethodModifier(M method); - - String getMethodKey(C cls, M method); - - String getMethodFullKey(C cls, M method); - - boolean isPublic(Object o); - - boolean acceptsInput(String file); - -} diff --git a/src/main/java/org/reflections/scanners/AbstractScanner.java b/src/main/java/org/reflections/scanners/AbstractScanner.java index bcde3ca2..f35b85a9 100644 --- a/src/main/java/org/reflections/scanners/AbstractScanner.java +++ b/src/main/java/org/reflections/scanners/AbstractScanner.java @@ -1,81 +1,20 @@ package org.reflections.scanners; -import org.reflections.Configuration; -import org.reflections.ReflectionsException; -import org.reflections.Store; -import org.reflections.adapters.MetadataAdapter; -import org.reflections.util.Utils; -import org.reflections.vfs.Vfs; +import javassist.bytecode.ClassFile; -import java.util.function.Predicate; +import java.util.List; +import java.util.Map; -/** - * - */ -@SuppressWarnings({"RawUseOfParameterizedType"}) -public abstract class AbstractScanner implements Scanner { +@Deprecated +public class AbstractScanner implements Scanner { + protected final Scanner scanner; - private Configuration configuration; - private Predicate resultFilter = s -> true; //accept all by default - - public boolean acceptsInput(String file) { - return getMetadataAdapter().acceptsInput(file); - } - - public Object scan(Vfs.File file, Object classObject, Store store) { - if (classObject == null) { - try { - classObject = configuration.getMetadataAdapter().getOrCreateClassObject(file); - } catch (Exception e) { - throw new ReflectionsException("could not create class object from file " + file.getRelativePath(), e); - } - } - scan(classObject, store); - return classObject; - } - - public abstract void scan(Object cls, Store store); - - protected void put(Store store, String key, String value) { - store.put(Utils.index(getClass()), key, value); - } - - // - public Configuration getConfiguration() { - return configuration; - } - - public void setConfiguration(final Configuration configuration) { - this.configuration = configuration; - } - - public Predicate getResultFilter() { - return resultFilter; - } - - public void setResultFilter(Predicate resultFilter) { - this.resultFilter = resultFilter; - } - - public Scanner filterResultsBy(Predicate filter) { - this.setResultFilter(filter); return this; - } - - // - public boolean acceptResult(final String fqn) { - return fqn != null && resultFilter.test(fqn); + public AbstractScanner(Scanner scanner) { + this.scanner = scanner; } - protected MetadataAdapter getMetadataAdapter() { - return configuration.getMetadataAdapter(); + @Override + public List> scan(final ClassFile cls) { + return scanner.scan(cls); } - - // - @Override public boolean equals(Object o) { - return this == o || o != null && getClass() == o.getClass(); - } - - @Override public int hashCode() { - return getClass().hashCode(); - } } diff --git a/src/main/java/org/reflections/scanners/FieldAnnotationsScanner.java b/src/main/java/org/reflections/scanners/FieldAnnotationsScanner.java index 1f68470f..11c836af 100644 --- a/src/main/java/org/reflections/scanners/FieldAnnotationsScanner.java +++ b/src/main/java/org/reflections/scanners/FieldAnnotationsScanner.java @@ -1,24 +1,12 @@ package org.reflections.scanners; -import org.reflections.Store; - -import java.util.List; - -/** scans for field's annotations */ -@SuppressWarnings({"unchecked"}) +/** scan field annotations. + * {@code Deprecated}, use {@link Scanners#FieldsAnnotated} instead + * */ +@Deprecated public class FieldAnnotationsScanner extends AbstractScanner { - public void scan(final Object cls, Store store) { - final String className = getMetadataAdapter().getClassName(cls); - List fields = getMetadataAdapter().getFields(cls); - for (final Object field : fields) { - List fieldAnnotations = getMetadataAdapter().getFieldAnnotationNames(field); - for (String fieldAnnotation : fieldAnnotations) { - if (acceptResult(fieldAnnotation)) { - String fieldName = getMetadataAdapter().getFieldName(field); - put(store, fieldAnnotation, String.format("%s.%s", className, fieldName)); - } - } - } + public FieldAnnotationsScanner() { + super(Scanners.FieldsAnnotated); } } diff --git a/src/main/java/org/reflections/scanners/MemberUsageScanner.java b/src/main/java/org/reflections/scanners/MemberUsageScanner.java index df8967f2..e3983841 100644 --- a/src/main/java/org/reflections/scanners/MemberUsageScanner.java +++ b/src/main/java/org/reflections/scanners/MemberUsageScanner.java @@ -6,6 +6,7 @@ import javassist.CtClass; import javassist.LoaderClassPath; import javassist.NotFoundException; +import javassist.bytecode.ClassFile; import javassist.bytecode.MethodInfo; import javassist.expr.ConstructorCall; import javassist.expr.ExprEditor; @@ -13,72 +14,95 @@ import javassist.expr.MethodCall; import javassist.expr.NewExpr; import org.reflections.ReflectionsException; -import org.reflections.Store; import org.reflections.util.ClasspathHelper; +import org.reflections.util.JavassistHelper; -import static org.reflections.util.Utils.join; +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; -/** scans methods/constructors/fields usage - *

depends on {@link org.reflections.adapters.JavassistAdapter} configured */ -@SuppressWarnings("unchecked") -public class MemberUsageScanner extends AbstractScanner { - private ClassPool classPool; +/** scan methods/constructors/fields usage */ +public class MemberUsageScanner implements Scanner { + private Predicate resultFilter = s -> true; //accept all by default + private final ClassLoader[] classLoaders; + private volatile ClassPool classPool; + + public MemberUsageScanner() { + this(ClasspathHelper.classLoaders()); + } + + public MemberUsageScanner(@Nonnull ClassLoader[] classLoaders) { + this.classLoaders = classLoaders; + } @Override - public void scan(Object cls, Store store) { + public List> scan(ClassFile classFile) { + List> entries = new ArrayList<>(); + CtClass ctClass = null; try { - CtClass ctClass = getClassPool().get(getMetadataAdapter().getClassName(cls)); + ctClass = getClassPool().get(classFile.getName()); for (CtBehavior member : ctClass.getDeclaredConstructors()) { - scanMember(member, store); + scanMember(member, entries); } for (CtBehavior member : ctClass.getDeclaredMethods()) { - scanMember(member, store); + scanMember(member, entries); } - ctClass.detach(); } catch (Exception e) { - throw new ReflectionsException("Could not scan method usage for " + getMetadataAdapter().getClassName(cls), e); + throw new ReflectionsException("Could not scan method usage for " + classFile.getName(), e); + } finally { + if (ctClass != null) { + ctClass.detach(); + } } + return entries; + } + + public Scanner filterResultsBy(Predicate filter) { + this.resultFilter = filter; + return this; } - void scanMember(CtBehavior member, Store store) throws CannotCompileException { + private void scanMember(CtBehavior member, List> entries) throws CannotCompileException { //key contains this$/val$ means local field/parameter closure final String key = member.getDeclaringClass().getName() + "." + member.getMethodInfo().getName() + "(" + parameterNames(member.getMethodInfo()) + ")"; //+ " #" + member.getMethodInfo().getLineNumber(0) member.instrument(new ExprEditor() { @Override - public void edit(NewExpr e) throws CannotCompileException { + public void edit(NewExpr e) { try { - put(store, e.getConstructor().getDeclaringClass().getName() + "." + "" + - "(" + parameterNames(e.getConstructor().getMethodInfo()) + ")", e.getLineNumber(), key); + add(entries, e.getConstructor().getDeclaringClass().getName() + "." + "" + + "(" + parameterNames(e.getConstructor().getMethodInfo()) + ")", key + " #" + e.getLineNumber()); } catch (NotFoundException e1) { throw new ReflectionsException("Could not find new instance usage in " + key, e1); } } @Override - public void edit(MethodCall m) throws CannotCompileException { + public void edit(MethodCall m) { try { - put(store, m.getMethod().getDeclaringClass().getName() + "." + m.getMethodName() + - "(" + parameterNames(m.getMethod().getMethodInfo()) + ")", m.getLineNumber(), key); + add(entries, m.getMethod().getDeclaringClass().getName() + "." + m.getMethodName() + + "(" + parameterNames(m.getMethod().getMethodInfo()) + ")", key + " #" + m.getLineNumber()); } catch (NotFoundException e) { throw new ReflectionsException("Could not find member " + m.getClassName() + " in " + key, e); } } @Override - public void edit(ConstructorCall c) throws CannotCompileException { + public void edit(ConstructorCall c) { try { - put(store, c.getConstructor().getDeclaringClass().getName() + "." + "" + - "(" + parameterNames(c.getConstructor().getMethodInfo()) + ")", c.getLineNumber(), key); + add(entries, c.getConstructor().getDeclaringClass().getName() + "." + "" + + "(" + parameterNames(c.getConstructor().getMethodInfo()) + ")", key + " #" + c.getLineNumber()); } catch (NotFoundException e) { throw new ReflectionsException("Could not find member " + c.getClassName() + " in " + key, e); } } @Override - public void edit(FieldAccess f) throws CannotCompileException { + public void edit(FieldAccess f) { try { - put(store, f.getField().getDeclaringClass().getName() + "." + f.getFieldName(), f.getLineNumber(), key); + add(entries, f.getField().getDeclaringClass().getName() + "." + f.getFieldName(), key + " #" + f.getLineNumber()); } catch (NotFoundException e) { throw new ReflectionsException("Could not find member " + f.getFieldName() + " in " + key, e); } @@ -86,26 +110,24 @@ public void edit(FieldAccess f) throws CannotCompileException { }); } - private void put(Store store, String key, int lineNumber, String value) { - if (acceptResult(key)) { - put(store, key, value + " #" + lineNumber); + private void add(List> entries, String key, String value) { + if (resultFilter.test(key)) { + entries.add(entry(key, value)); } } - String parameterNames(MethodInfo info) { - return join(getMetadataAdapter().getParameterNames(info), ", "); + public static String parameterNames(MethodInfo info) { + return String.join(", ", JavassistHelper.getParameters(info)); } private ClassPool getClassPool() { if (classPool == null) { synchronized (this) { - classPool = new ClassPool(); - ClassLoader[] classLoaders = getConfiguration().getClassLoaders(); - if (classLoaders == null) { - classLoaders = ClasspathHelper.classLoaders(); - } - for (ClassLoader classLoader : classLoaders) { - classPool.appendClassPath(new LoaderClassPath(classLoader)); + if (classPool == null) { + classPool = new ClassPool(); + for (ClassLoader classLoader : classLoaders) { + classPool.appendClassPath(new LoaderClassPath(classLoader)); + } } } } diff --git a/src/main/java/org/reflections/scanners/MethodAnnotationsScanner.java b/src/main/java/org/reflections/scanners/MethodAnnotationsScanner.java index 92ffc8c8..dddb9320 100644 --- a/src/main/java/org/reflections/scanners/MethodAnnotationsScanner.java +++ b/src/main/java/org/reflections/scanners/MethodAnnotationsScanner.java @@ -1,19 +1,12 @@ package org.reflections.scanners; -import org.reflections.Store; - -import java.util.List; - -@SuppressWarnings({"unchecked"}) -/** scans for method's annotations */ +/** scan method annotations. + * {@code Deprecated}, use {@link Scanners#MethodsAnnotated} instead + * */ +@Deprecated public class MethodAnnotationsScanner extends AbstractScanner { - public void scan(final Object cls, Store store) { - for (Object method : getMetadataAdapter().getMethods(cls)) { - for (String methodAnnotation : (List) getMetadataAdapter().getMethodAnnotationNames(method)) { - if (acceptResult(methodAnnotation)) { - put(store, methodAnnotation, getMetadataAdapter().getMethodFullKey(cls, method)); - } - } - } + + public MethodAnnotationsScanner() { + super(Scanners.MethodsAnnotated); } } diff --git a/src/main/java/org/reflections/scanners/MethodParameterNamesScanner.java b/src/main/java/org/reflections/scanners/MethodParameterNamesScanner.java index 9823e3f9..8bb35b09 100644 --- a/src/main/java/org/reflections/scanners/MethodParameterNamesScanner.java +++ b/src/main/java/org/reflections/scanners/MethodParameterNamesScanner.java @@ -1,40 +1,44 @@ package org.reflections.scanners; +import javassist.bytecode.ClassFile; import javassist.bytecode.CodeAttribute; import javassist.bytecode.LocalVariableAttribute; import javassist.bytecode.MethodInfo; -import org.reflections.Store; -import org.reflections.adapters.MetadataAdapter; +import org.reflections.util.JavassistHelper; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.IntStream; -/** scans methods/constructors and indexes parameter names */ -@SuppressWarnings("unchecked") -public class MethodParameterNamesScanner extends AbstractScanner { +public class MethodParameterNamesScanner implements Scanner { @Override - public void scan(Object cls, Store store) { - final MetadataAdapter md = getMetadataAdapter(); - - for (Object method : md.getMethods(cls)) { - String key = md.getMethodFullKey(cls, method); - if (acceptResult(key)) { - CodeAttribute codeAttribute = ((MethodInfo) method).getCodeAttribute(); - LocalVariableAttribute table = codeAttribute != null ? (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag) : null; - int length = md.getParameterNames(method).size(); - if (length > 0) { - int shift = Modifier.isStatic(((MethodInfo) method).getAccessFlags()) ? 0 : 1; //skip this - String join = IntStream.range(shift, length + shift) - .mapToObj(i -> ((MethodInfo) method).getConstPool().getUtf8Info(table.nameIndex(i))) - .filter(name -> !name.startsWith("this$")) - .collect(Collectors.joining(", ")); - if (!join.isEmpty()) { - put(store, key, join); - } - } + public List> scan(ClassFile classFile) { + List> entries = new ArrayList<>(); + for (MethodInfo method : classFile.getMethods()) { + String key = JavassistHelper.methodName(classFile, method); + String value = getString(method); + if (!value.isEmpty()) { + entries.add(entry(key, value)); } } + return entries; + } + + private String getString(MethodInfo method) { + CodeAttribute codeAttribute = method.getCodeAttribute(); + LocalVariableAttribute table = codeAttribute != null ? (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag) : null; + int length = JavassistHelper.getParameters(method).size(); + if (length > 0) { + int shift = Modifier.isStatic(method.getAccessFlags()) ? 0 : 1; //skip this + return IntStream.range(shift, length + shift) + .mapToObj(i -> method.getConstPool().getUtf8Info(table.nameIndex(i))) + .filter(name -> !name.startsWith("this$")) + .collect(Collectors.joining(", ")); + } + return ""; } } diff --git a/src/main/java/org/reflections/scanners/MethodParameterScanner.java b/src/main/java/org/reflections/scanners/MethodParameterScanner.java index d106ad6e..b47579b4 100644 --- a/src/main/java/org/reflections/scanners/MethodParameterScanner.java +++ b/src/main/java/org/reflections/scanners/MethodParameterScanner.java @@ -1,38 +1,12 @@ package org.reflections.scanners; -import org.reflections.Store; -import org.reflections.adapters.MetadataAdapter; - -import java.util.List; - -/** scans methods/constructors and indexes parameters, return type and parameter annotations */ -@SuppressWarnings("unchecked") +/** scan methods/constructors and indexes parameters, return type and parameter annotations. + * {@code Deprecated}, use {@link Scanners#MethodsParameter} instead + * */ +@Deprecated public class MethodParameterScanner extends AbstractScanner { - @Override - public void scan(Object cls, Store store) { - final MetadataAdapter md = getMetadataAdapter(); - - for (Object method : md.getMethods(cls)) { - - String signature = md.getParameterNames(method).toString(); - if (acceptResult(signature)) { - put(store, signature, md.getMethodFullKey(cls, method)); - } - - String returnTypeName = md.getReturnTypeName(method); - if (acceptResult(returnTypeName)) { - put(store, returnTypeName, md.getMethodFullKey(cls, method)); - } - - List parameterNames = md.getParameterNames(method); - for (int i = 0; i < parameterNames.size(); i++) { - for (Object paramAnnotation : md.getParameterAnnotationNames(method, i)) { - if (acceptResult((String) paramAnnotation)) { - put(store, (String) paramAnnotation, md.getMethodFullKey(cls, method)); - } - } - } - } + public MethodParameterScanner() { + super(Scanners.MethodsParameter); } } diff --git a/src/main/java/org/reflections/scanners/ResourcesScanner.java b/src/main/java/org/reflections/scanners/ResourcesScanner.java index 5810c56a..8e20c305 100644 --- a/src/main/java/org/reflections/scanners/ResourcesScanner.java +++ b/src/main/java/org/reflections/scanners/ResourcesScanner.java @@ -1,21 +1,13 @@ package org.reflections.scanners; -import org.reflections.Store; -import org.reflections.vfs.Vfs; - /** collects all resources that are not classes in a collection - *

key: value - {web.xml: WEB-INF/web.xml} */ + *

key: value - {web.xml: WEB-INF/web.xml}

+ * {@code Deprecated}, use {@link Scanners#Resources} instead + * */ +@Deprecated public class ResourcesScanner extends AbstractScanner { - public boolean acceptsInput(String file) { - return !file.endsWith(".class") && !file.endsWith(".groovy") && !file.endsWith(".scala") && !file.endsWith(".kt"); //not a class - } - - @Override public Object scan(Vfs.File file, Object classObject, Store store) { - put(store, file.getName(), file.getRelativePath()); - return classObject; - } - public void scan(Object cls, Store store) { - throw new UnsupportedOperationException(); //shouldn't get here + public ResourcesScanner() { + super(Scanners.Resources); } } diff --git a/src/main/java/org/reflections/scanners/Scanner.java b/src/main/java/org/reflections/scanners/Scanner.java index e6557ae8..42c91eaa 100644 --- a/src/main/java/org/reflections/scanners/Scanner.java +++ b/src/main/java/org/reflections/scanners/Scanner.java @@ -1,23 +1,54 @@ package org.reflections.scanners; -import org.reflections.Configuration; -import org.reflections.Store; +import javassist.bytecode.ClassFile; import org.reflections.vfs.Vfs; -import java.util.function.Predicate; +import javax.annotation.Nullable; +import java.util.AbstractMap; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** - * - */ + * Scanner {@link #scan(ClassFile)} method receives a {@link ClassFile} and produce a list of {@link Map.Entry}. + * These key/values will be stored under {@link #index()} for querying. + *

see more in {@link Scanners} + * */ public interface Scanner { - void setConfiguration(Configuration configuration); + /** scan the given {@code classFile} and produces list of {@link Map.Entry} key/values */ + List> scan(ClassFile classFile); - Scanner filterResultsBy(Predicate filter); + /** scan the given {@code file} and produces list of {@link Map.Entry} key/values */ + @Nullable + default List> scan(Vfs.File file) { + return null; + } - boolean acceptsInput(String file); + /** unique index name for scanner */ + default String index() { + return getClass().getSimpleName(); + } - Object scan(Vfs.File file, Object classObject, Store store); + default boolean acceptsInput(String file) { + return file.endsWith(".class"); + } - boolean acceptResult(String fqn); + default Map.Entry entry(String key, String value) { + return new AbstractMap.SimpleEntry<>(key, value); + } + + default List> entries(Collection keys, String value) { + return keys.stream().map(key -> entry(key, value)).collect(Collectors.toList()); + } + + default List> entries(String key, String value) { + return Collections.singletonList(entry(key, value)); + } + + default List> entries(String key, Collection values) { + return values.stream().map(value -> entry(key, value)).collect(Collectors.toList()); + } } diff --git a/src/main/java/org/reflections/scanners/Scanners.java b/src/main/java/org/reflections/scanners/Scanners.java new file mode 100644 index 00000000..b47d88aa --- /dev/null +++ b/src/main/java/org/reflections/scanners/Scanners.java @@ -0,0 +1,205 @@ +package org.reflections.scanners; + +import javassist.bytecode.ClassFile; +import org.reflections.Store; +import org.reflections.util.FilterBuilder; +import org.reflections.util.NameHelper; +import org.reflections.util.QueryBuilder; +import org.reflections.util.QueryFunction; +import org.reflections.vfs.Vfs; + +import java.lang.annotation.Inherited; +import java.lang.reflect.AnnotatedElement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.reflections.util.JavassistHelper.*; + +/** + * base Reflections {@link Scanner}s such as: + *
    + *
  • {@link #SubTypes}
  • + *
  • {@link #TypesAnnotated}
  • + *
  • {@link #MethodsAnnotated}
  • + *
  • {@link #FieldsAnnotated}
  • + *
  • {@link #Resources}
  • + *
  • {@link #MethodsParameter}
  • + *
  • {@link #MethodsSignature}
  • + *
  • {@link #MethodsReturn}
  • + *
+ * note that scanners must be configured in {@link org.reflections.Configuration} in order to be queried + * */ +public enum Scanners implements Scanner, QueryBuilder, NameHelper { + + /** scan type superclasses and interfaces + *

+ * Note that {@code Object} class is excluded by default, in order to reduce store size. + *
Use {@link #filterResultsBy(Predicate)} to change, for example {@code SubTypes.filterResultsBy(c -> true)}
+ * */ + SubTypes { + /* Object class is excluded by default from subtypes indexing */ + { filterResultsBy(new FilterBuilder().excludePattern("java\\.lang\\.Object")); } + + @Override + public void scan(ClassFile classFile, List> entries) { + entries.add(entry(classFile.getSuperclass(), classFile.getName())); + entries.addAll(entries(Arrays.asList(classFile.getInterfaces()), classFile.getName())); + } + }, + + /** scan type annotations */ + TypesAnnotated { + @Override + public boolean acceptResult(String annotation) { + return super.acceptResult(annotation) || annotation.equals(Inherited.class.getName()); + } + + @Override + public void scan(ClassFile classFile, List> entries) { + entries.addAll(entries(getAnnotations(classFile::getAttribute), classFile.getName())); + } + }, + + /** scan method annotations */ + MethodsAnnotated { + @Override + public void scan(ClassFile classFile, List> entries) { + getMethods(classFile).forEach(method -> + entries.addAll(entries(getAnnotations(method::getAttribute), methodName(classFile, method)))); + } + }, + + /** scan constructor annotations */ + ConstructorsAnnotated { + @Override + public void scan(ClassFile classFile, List> entries) { + getConstructors(classFile).forEach(constructor -> + entries.addAll(entries(getAnnotations(constructor::getAttribute), methodName(classFile, constructor)))); + } + }, + + /** scan field annotations */ + FieldsAnnotated { + @Override + public void scan(ClassFile classFile, List> entries) { + classFile.getFields().forEach(field -> + entries.addAll(entries(getAnnotations(field::getAttribute), fieldName(classFile, field)))); + } + }, + + /** scan non .class files such as xml or properties files */ + Resources { + @Override + public boolean acceptsInput(String file) { + return !file.endsWith(".class"); + } + + @Override + public List> scan(Vfs.File file) { + return Collections.singletonList(entry(file.getName(), file.getRelativePath())); + } + + @Override + public void scan(ClassFile classFile, List> entries) { + throw new IllegalStateException(); + } + + @Override + public QueryFunction with(String pattern) { + return store -> store.get(index()).entrySet().stream().filter(entry -> entry.getKey().matches(pattern)) + .flatMap(entry -> entry.getValue().stream()).collect(Collectors.toCollection(LinkedHashSet::new)); + } + }, + + /** scan method parameters types and annotations */ + MethodsParameter { + @Override + public void scan(ClassFile classFile, List> entries) { + getMethods(classFile).forEach(method -> { + String value = methodName(classFile, method); + entries.addAll(entries(getParameters(method), value)); + getParametersAnnotations(method).forEach(annotations -> entries.addAll(entries(annotations, value))); + }); + } + }, + + /** scan constructor parameters types and annotations */ + ConstructorsParameter { + @Override + public void scan(ClassFile classFile, List> entries) { + getConstructors(classFile).forEach(constructor -> { + String value = methodName(classFile, constructor); + entries.addAll(entries(getParameters(constructor), value)); + getParametersAnnotations(constructor).forEach(annotations -> entries.addAll(entries(annotations, value))); + }); + } + }, + + /** scan methods signature */ + MethodsSignature { + @Override + public void scan(ClassFile classFile, List> entries) { + getMethods(classFile).forEach(method -> + entries.add(entry(getParameters(method).toString(), methodName(classFile, method)))); + } + + @Override + public QueryFunction with(AnnotatedElement... keys) { + return QueryFunction.single(toNames(keys).toString()).getAll(this::get); + } + }, + + /** scan constructors signature */ + ConstructorsSignature { + @Override + public void scan(ClassFile classFile, List> entries) { + getConstructors(classFile).forEach(constructor -> + entries.add(entry(getParameters(constructor).toString(), methodName(classFile, constructor)))); + } + + @Override + public QueryFunction with(AnnotatedElement... keys) { + return QueryFunction.single(toNames(keys).toString()).getAll(this::get); + } + }, + + /** scan method return type */ + MethodsReturn { + @Override + public void scan(ClassFile classFile, List> entries) { + getMethods(classFile).forEach(method -> + entries.add(entry(getReturnType(method), methodName(classFile, method)))); + } + }; + + private Predicate resultFilter = s -> true; //accept all by default + + @Override + public String index() { + return name(); + } + + public Scanners filterResultsBy(Predicate filter) { + this.resultFilter = filter; + return this; + } + + @Override + public final List> scan(ClassFile classFile) { + List> entries = new ArrayList<>(); + scan(classFile, entries); + return entries.stream().filter(a -> acceptResult(a.getKey())).collect(Collectors.toList()); + } + + abstract void scan(ClassFile classFile, List> entries); + + protected boolean acceptResult(String fqn) { + return fqn != null && resultFilter.test(fqn); + } +} diff --git a/src/main/java/org/reflections/scanners/SubTypesScanner.java b/src/main/java/org/reflections/scanners/SubTypesScanner.java index 355421b0..4dcb4c43 100644 --- a/src/main/java/org/reflections/scanners/SubTypesScanner.java +++ b/src/main/java/org/reflections/scanners/SubTypesScanner.java @@ -1,39 +1,27 @@ package org.reflections.scanners; -import org.reflections.Store; -import org.reflections.util.FilterBuilder; +import javassist.bytecode.ClassFile; import java.util.List; +import java.util.Map; -/** scans for superclass and interfaces of a class, allowing a reverse lookup for subtypes */ +/** scan superclass and interfaces of a class, allowing a reverse lookup for subtypes. + * {@code Deprecated}, use {@link Scanners#SubTypes} instead + * */ +@Deprecated public class SubTypesScanner extends AbstractScanner { - /** created new SubTypesScanner. will exclude direct Object subtypes */ + /** create new SubTypesScanner. will exclude direct Object subtypes */ public SubTypesScanner() { - this(true); //exclude direct Object subtypes by default + super(Scanners.SubTypes); } - /** created new SubTypesScanner. - * @param excludeObjectClass if false, include direct {@link Object} subtypes in results. */ + /** create new SubTypesScanner. include direct {@link Object} subtypes in results. */ public SubTypesScanner(boolean excludeObjectClass) { - if (excludeObjectClass) { - filterResultsBy(new FilterBuilder().exclude(Object.class.getName())); //exclude direct Object subtypes - } + super(excludeObjectClass ? Scanners.SubTypes : Scanners.SubTypes.filterResultsBy(s -> true)); } - @SuppressWarnings({"unchecked"}) - public void scan(final Object cls, Store store) { - String className = getMetadataAdapter().getClassName(cls); - String superclass = getMetadataAdapter().getSuperclassName(cls); - - if (acceptResult(superclass)) { - put(store, superclass, className); - } - - for (String anInterface : (List) getMetadataAdapter().getInterfacesNames(cls)) { - if (acceptResult(anInterface)) { - put(store, anInterface, className); - } - } + public List> scan(final ClassFile cls) { + return scanner.scan(cls); } } diff --git a/src/main/java/org/reflections/scanners/TypeAnnotationsScanner.java b/src/main/java/org/reflections/scanners/TypeAnnotationsScanner.java index 3d1d8941..7777c872 100644 --- a/src/main/java/org/reflections/scanners/TypeAnnotationsScanner.java +++ b/src/main/java/org/reflections/scanners/TypeAnnotationsScanner.java @@ -1,23 +1,12 @@ package org.reflections.scanners; -import org.reflections.Store; - -import java.lang.annotation.Inherited; -import java.util.List; - -/** scans for class's annotations, where @Retention(RetentionPolicy.RUNTIME) */ -@SuppressWarnings({"unchecked"}) +/** scan class annotations, where @Retention(RetentionPolicy.RUNTIME). + * {@code Deprecated}, use {@link Scanners#TypesAnnotated} instead + **/ +@Deprecated public class TypeAnnotationsScanner extends AbstractScanner { - public void scan(final Object cls, Store store) { - final String className = getMetadataAdapter().getClassName(cls); - for (String annotationType : (List) getMetadataAdapter().getClassAnnotationNames(cls)) { - - if (acceptResult(annotationType) || - annotationType.equals(Inherited.class.getName())) { //as an exception, accept Inherited as well - put(store, annotationType, className); - } - } + public TypeAnnotationsScanner() { + super(Scanners.TypesAnnotated); } - } diff --git a/src/main/java/org/reflections/scanners/TypeElementsScanner.java b/src/main/java/org/reflections/scanners/TypeElementsScanner.java index c8e39af8..920d77fa 100644 --- a/src/main/java/org/reflections/scanners/TypeElementsScanner.java +++ b/src/main/java/org/reflections/scanners/TypeElementsScanner.java @@ -1,48 +1,50 @@ package org.reflections.scanners; -import org.reflections.Store; +import javassist.bytecode.ClassFile; +import org.reflections.util.JavassistHelper; -import static org.reflections.util.Utils.join; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; -/** scans fields and methods and stores fqn as key and elements as values */ -@SuppressWarnings({"unchecked"}) -public class TypeElementsScanner extends AbstractScanner { +/** scan types, annotations, methods and fields, and stores fqn as key and elements as values */ +public class TypeElementsScanner implements Scanner { private boolean includeFields = true; private boolean includeMethods = true; private boolean includeAnnotations = true; private boolean publicOnly = true; - - public void scan(Object cls, Store store) { - String className = getMetadataAdapter().getClassName(cls); - if (!acceptResult(className)) return; - - put(store, className, ""); - - if (includeFields) { - for (Object field : getMetadataAdapter().getFields(cls)) { - String fieldName = getMetadataAdapter().getFieldName(field); - put(store, className, fieldName); + private Predicate resultFilter = s -> true; //accept all by default + + public List> scan(ClassFile classFile) { + List> entries = new ArrayList<>(); + String className = classFile.getName(); + if (resultFilter.test(className) && isPublic(classFile)) { + entries.add(entry(className, "")); + if (includeFields) { + classFile.getFields().forEach(field -> entries.add(entry(className, field.getName()))); } - } - - if (includeMethods) { - for (Object method : getMetadataAdapter().getMethods(cls)) { - if (!publicOnly || getMetadataAdapter().isPublic(method)) { - String methodKey = getMetadataAdapter().getMethodName(method) + "(" + - join(getMetadataAdapter().getParameterNames(method), ", ") + ")"; - put(store, className, methodKey); - } + if (includeMethods) { + classFile.getMethods().stream().filter(this::isPublic) + .forEach(method -> entries.add(entry(className, method.getName() + "(" + String.join(", ", JavassistHelper.getParameters(method)) + ")"))); } - } - - if (includeAnnotations) { - for (Object annotation : getMetadataAdapter().getClassAnnotationNames(cls)) { - put(store, className, "@" + annotation); + if (includeAnnotations) { + JavassistHelper.getAnnotations(classFile::getAttribute).stream().filter(resultFilter) + .forEach(annotation -> entries.add(entry(className, "@" + annotation))); } } + return entries; + } + + private boolean isPublic(Object object) { + return !publicOnly || JavassistHelper.isPublic(object); + } + + public TypeElementsScanner filterResultsBy(Predicate filter) { + this.resultFilter = filter; + return this; } - // public TypeElementsScanner includeFields() { return includeFields(true); } public TypeElementsScanner includeFields(boolean include) { includeFields = include; return this; } public TypeElementsScanner includeMethods() { return includeMethods(true); } diff --git a/src/main/java/org/reflections/serializers/JavaCodeSerializer.java b/src/main/java/org/reflections/serializers/JavaCodeSerializer.java index 0249ec5a..8ab5dc45 100644 --- a/src/main/java/org/reflections/serializers/JavaCodeSerializer.java +++ b/src/main/java/org/reflections/serializers/JavaCodeSerializer.java @@ -1,62 +1,49 @@ package org.reflections.serializers; -import org.reflections.ReflectionUtils; import org.reflections.Reflections; -import org.reflections.ReflectionsException; import org.reflections.scanners.TypeElementsScanner; -import org.reflections.util.Utils; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.nio.charset.Charset; import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Date; -import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -import static org.reflections.Reflections.log; -import static org.reflections.util.Utils.*; - -/** Serialization of Reflections to java code - *

Serializes types and types elements into interfaces respectively to fully qualified name, - *

For example, after saving with JavaCodeSerializer: - *

- *   reflections.save(filename, new JavaCodeSerializer());
- * 
- *

Saved file should look like: - *

- *     public interface MyModel {
- *      public interface my {
- *       public interface package1 {
- *        public interface MyClass1 {
- *         public interface fields {
- *          public interface f1 {}
- *          public interface f2 {}
+import java.util.stream.IntStream;
+
+/** source code serialization for {@link org.reflections.Reflections} 
{@code reflections.save(file, new JavaCodeSerializer())}
+ *

an example of produced java source: + *
{@code
+ * public interface MyTestModelStore {
+ *   interface org {
+ *     interface reflections {
+ *       interface TestModel$C4 {
+ *         interface fields {
+ *           interface f1 {}
+ *           interface f2 {}
+ *         }
+ *         interface methods {
+ *           interface m1 {}
+ *           interface add {}
  *         }
- *         public interface methods {
- *          public interface m1 {}
- *          public interface m2 {}
+ *         interface annotations {
+ *           ...
  *         }
- *	...
+ *       }
+ *     }
+ *   }
  * }
- * 
- *

Use the different resolve methods to resolve the serialized element into Class, Field or Method. for example: - *

- *  Class m1Ref = MyModel.my.package1.MyClass1.methods.m1.class;
- *  Method method = JavaCodeSerializer.resolve(m1Ref);
- * 
- *

The {@link #save(org.reflections.Reflections, String)} method filename should be in the pattern: path/path/path/package.package.classname - *

depends on Reflections configured with {@link org.reflections.scanners.TypeElementsScanner} - * */ + * }

+ *

this allows strongly typed access by fqn to type elements - packages, classes, annotations, fields and methods: + *

{@code MyTestModelStore.org.reflections.TestModel$C1.methods.m1.class}
+ *

depends on {@link org.reflections.scanners.TypeElementsScanner} configured + */ public class JavaCodeSerializer implements Serializer { private static final String pathSeparator = "_"; @@ -65,14 +52,17 @@ public class JavaCodeSerializer implements Serializer { private static final String arrayDescriptor = "$$"; private static final String tokenSeparator = "_"; + private StringBuilder sb; + private List prevPaths; + private int indent; + public Reflections read(InputStream inputStream) { throw new UnsupportedOperationException("read is not implemented on JavaCodeSerializer"); } /** - * name should be in the pattern: path/path/path/package.package.classname, - * for example

/data/projects/my/src/main/java/org.my.project.MyStore
- * would create class MyStore in package org.my.project in the path /data/projects/my/src/main/java + * serialize and save to java source code + * @param name should be in the pattern {@code path/path/path/package.package.classname}, */ public File save(Reflections reflections, String name) { if (name.endsWith("/")) { @@ -81,7 +71,7 @@ public File save(Reflections reflections, String name) { //prepare file String filename = name.replace('.', '/').concat(".java"); - File file = prepareFile(filename); + File file = Serializer.prepareFile(filename); //get package and class names String packageName; @@ -97,16 +87,14 @@ public File save(Reflections reflections, String name) { //generate try { - StringBuilder sb = new StringBuilder(); - sb.append("//generated using Reflections JavaCodeSerializer") - .append(" [").append(new Date()).append("]") - .append("\n"); + sb = new StringBuilder(); + sb.append("//generated using Reflections JavaCodeSerializer").append(" [").append(new Date()).append("]").append("\n"); if (packageName.length() != 0) { sb.append("package ").append(packageName).append(";\n"); sb.append("\n"); } sb.append("public interface ").append(className).append(" {\n\n"); - sb.append(toString(reflections)); + toString(reflections); sb.append("}\n"); Files.write(new File(filename).toPath(), sb.toString().getBytes(Charset.defaultCharset())); @@ -118,207 +106,123 @@ public File save(Reflections reflections, String name) { return file; } - public String toString(Reflections reflections) { - if (reflections.getStore().keys(index(TypeElementsScanner.class)).isEmpty()) { - if (log != null) log.warn("JavaCodeSerializer needs TypeElementsScanner configured"); - } + private void toString(Reflections reflections) { + Map> map = reflections.getStore().get(TypeElementsScanner.class.getSimpleName()); + prevPaths = new ArrayList<>(); + indent = 1; - StringBuilder sb = new StringBuilder(); - - List prevPaths = new ArrayList<>(); - int indent = 1; - - List keys = new ArrayList<>(reflections.getStore().keys(index(TypeElementsScanner.class))); - Collections.sort(keys); - for (String fqn : keys) { + map.keySet().stream().sorted().forEach(fqn -> { List typePaths = Arrays.asList(fqn.split("\\.")); - - //skip indention - int i = 0; - while (i < Math.min(typePaths.size(), prevPaths.size()) && typePaths.get(i).equals(prevPaths.get(i))) { - i++; - } - - //indent left - for (int j = prevPaths.size(); j > i; j--) { - sb.append(repeat("\t", --indent)).append("}\n"); - } - - //indent right - add packages - for (int j = i; j < typePaths.size() - 1; j++) { - sb.append(repeat("\t", indent++)).append("public interface ").append(getNonDuplicateName(typePaths.get(j), typePaths, j)).append(" {\n"); - } - - //indent right - add class - String className = typePaths.get(typePaths.size() - 1); - - //get fields and methods - List annotations = new ArrayList<>(); + String className = fqn.substring(fqn.lastIndexOf('.') + 1); List fields = new ArrayList<>(); List methods = new ArrayList<>(); - - Iterable members = reflections.getStore().get(index(TypeElementsScanner.class), fqn); - List sorted = StreamSupport.stream(members.spliterator(), false).sorted().collect(Collectors.toList()); - for (String element : sorted) { + List annotations = new ArrayList<>(); + map.get(fqn).stream().sorted().forEach(element -> { if (element.startsWith("@")) { annotations.add(element.substring(1)); } else if (element.contains("(")) { - //method if (!element.startsWith("<")) { - int i1 = element.indexOf('('); - String name = element.substring(0, i1); - String params = element.substring(i1 + 1, element.indexOf(")")); - - String paramsDescriptor = ""; - if (params.length() != 0) { - paramsDescriptor = tokenSeparator + params.replace(dotSeparator, tokenSeparator).replace(", ", doubleSeparator).replace("[]", arrayDescriptor); - } - String normalized = name + paramsDescriptor; - if (!methods.contains(name)) { - methods.add(name); - } else { - methods.add(normalized); - } + int i = element.indexOf('('); + String name = element.substring(0, i); + String params = element.substring(i + 1, element.indexOf(")")); + String paramsDescriptor = params.length() != 0 ? tokenSeparator + params.replace(dotSeparator, tokenSeparator).replace(", ", doubleSeparator).replace("[]", arrayDescriptor) : ""; + methods.add(!methods.contains(name) ? name : name + paramsDescriptor); } - } else if (!Utils.isEmpty(element)) { - //field + } else if (!element.isEmpty()) { fields.add(element); } - } - - //add class and it's fields and methods - sb.append(repeat("\t", indent++)).append("public interface ").append(getNonDuplicateName(className, typePaths, typePaths.size() - 1)).append(" {\n"); - - //add fields - if (!fields.isEmpty()) { - sb.append(repeat("\t", indent++)).append("public interface fields {\n"); - for (String field : fields) { - sb.append(repeat("\t", indent)).append("public interface ").append(getNonDuplicateName(field, typePaths)).append(" {}\n"); - } - sb.append(repeat("\t", --indent)).append("}\n"); - } - - //add methods - if (!methods.isEmpty()) { - sb.append(repeat("\t", indent++)).append("public interface methods {\n"); - for (String method : methods) { - String methodName = getNonDuplicateName(method, fields); - - sb.append(repeat("\t", indent)).append("public interface ").append(getNonDuplicateName(methodName, typePaths)).append(" {}\n"); - } - sb.append(repeat("\t", --indent)).append("}\n"); - } + }); - //add annotations - if (!annotations.isEmpty()) { - sb.append(repeat("\t", indent++)).append("public interface annotations {\n"); - for (String annotation : annotations) { - String nonDuplicateName = annotation; - nonDuplicateName = getNonDuplicateName(nonDuplicateName, typePaths); - sb.append(repeat("\t", indent)).append("public interface ").append(nonDuplicateName).append(" {}\n"); - } - sb.append(repeat("\t", --indent)).append("}\n"); - } + int i = indentOpen(typePaths, prevPaths); + addPackages(typePaths, i); + addClass(typePaths, className); + addFields(typePaths, fields); + addMethods(typePaths, fields, methods); + addAnnotations(typePaths, annotations); prevPaths = typePaths; - } + }); + indentClose(prevPaths); + } - //close indention - for (int j = prevPaths.size(); j >= 1; j--) { - sb.append(repeat("\t", j)).append("}\n"); + protected int indentOpen(List typePaths, List prevPaths) { + int i = 0; + while (i < Math.min(typePaths.size(), prevPaths.size()) && typePaths.get(i).equals(prevPaths.get(i))) { + i++; } - - return sb.toString(); + for (int j = prevPaths.size(); j > i; j--) { + sb.append(indent(--indent)).append("}\n"); + } + return i; } - private String getNonDuplicateName(String candidate, List prev, int offset) { - String normalized = normalize(candidate); - for (int i = 0; i < offset; i++) { - if (normalized.equals(prev.get(i))) { - return getNonDuplicateName(normalized + tokenSeparator, prev, offset); - } + protected void indentClose(List prevPaths) { + for (int j = prevPaths.size(); j >= 1; j--) { + sb.append(indent(j)).append("}\n"); } - - return normalized; } - private String normalize(String candidate) { - return candidate.replace(dotSeparator, pathSeparator); + protected void addPackages(List typePaths, int i) { + for (int j = i; j < typePaths.size() - 1; j++) { + sb.append(indent(indent++)).append("interface ").append(uniqueName(typePaths.get(j), typePaths, j)).append(" {\n"); + } } - private String getNonDuplicateName(String candidate, List prev) { - return getNonDuplicateName(candidate, prev, prev.size()); + protected void addClass(List typePaths, String className) { + sb.append(indent(indent++)).append("interface ").append(uniqueName(className, typePaths, typePaths.size() - 1)).append(" {\n"); } - // - public static Class resolveClassOf(final Class element) throws ClassNotFoundException { - Class cursor = element; - LinkedList ognl = new LinkedList<>(); - - while (cursor != null) { - ognl.addFirst(cursor.getSimpleName()); - cursor = cursor.getDeclaringClass(); + protected void addFields(List typePaths, List fields) { + if (!fields.isEmpty()) { + sb.append(indent(indent++)).append("interface fields {\n"); + for (String field : fields) { + sb.append(indent(indent)).append("interface ").append(uniqueName(field, typePaths)).append(" {}\n"); + } + sb.append(indent(--indent)).append("}\n"); } - - String classOgnl = join(ognl.subList(1, ognl.size()), ".").replace(".$", "$"); - return Class.forName(classOgnl); } - public static Class resolveClass(final Class aClass) { - try { - return resolveClassOf(aClass); - } catch (Exception e) { - throw new ReflectionsException("could not resolve to class " + aClass.getName(), e); + protected void addMethods(List typePaths, List fields, List methods) { + if (!methods.isEmpty()) { + sb.append(indent(indent++)).append("interface methods {\n"); + for (String method : methods) { + String methodName = uniqueName(method, fields); + sb.append(indent(indent)).append("interface ").append(uniqueName(methodName, typePaths)).append(" {}\n"); + } + sb.append(indent(--indent)).append("}\n"); } } - public static Field resolveField(final Class aField) { - try { - String name = aField.getSimpleName(); - Class declaringClass = aField.getDeclaringClass().getDeclaringClass(); - return resolveClassOf(declaringClass).getDeclaredField(name); - } catch (Exception e) { - throw new ReflectionsException("could not resolve to field " + aField.getName(), e); + protected void addAnnotations(List typePaths, List annotations) { + if (!annotations.isEmpty()) { + sb.append(indent(indent++)).append("interface annotations {\n"); + for (String annotation : annotations) { + sb.append(indent(indent)).append("interface ").append(uniqueName(annotation, typePaths)).append(" {}\n"); + } + sb.append(indent(--indent)).append("}\n"); } } - public static Annotation resolveAnnotation(Class annotation) { - try { - String name = annotation.getSimpleName().replace(pathSeparator, dotSeparator); - Class declaringClass = annotation.getDeclaringClass().getDeclaringClass(); - Class aClass = resolveClassOf(declaringClass); - Class aClass1 = (Class) ReflectionUtils.forName(name); - Annotation annotation1 = aClass.getAnnotation(aClass1); - return annotation1; - } catch (Exception e) { - throw new ReflectionsException("could not resolve to annotation " + annotation.getName(), e); + private String uniqueName(String candidate, List prev, int offset) { + String normalized = normalize(candidate); + for (int i = 0; i < offset; i++) { + if (normalized.equals(prev.get(i))) { + return uniqueName(normalized + tokenSeparator, prev, offset); + } } + return normalized; } - public static Method resolveMethod(final Class aMethod) { - String methodOgnl = aMethod.getSimpleName(); + private String normalize(String candidate) { + return candidate.replace(dotSeparator, pathSeparator); + } - try { - String methodName; - Class[] paramTypes; - if (methodOgnl.contains(tokenSeparator)) { - methodName = methodOgnl.substring(0, methodOgnl.indexOf(tokenSeparator)); - String[] params = methodOgnl.substring(methodOgnl.indexOf(tokenSeparator) + 1).split(doubleSeparator); - paramTypes = new Class[params.length]; - for (int i = 0; i < params.length; i++) { - String typeName = params[i].replace(arrayDescriptor, "[]").replace(pathSeparator, dotSeparator); - paramTypes[i] = ReflectionUtils.forName(typeName); - } - } else { - methodName = methodOgnl; - paramTypes = null; - } + private String uniqueName(String candidate, List prev) { + return uniqueName(candidate, prev, prev.size()); + } - Class declaringClass = aMethod.getDeclaringClass().getDeclaringClass(); - return resolveClassOf(declaringClass).getDeclaredMethod(methodName, paramTypes); - } catch (Exception e) { - throw new ReflectionsException("could not resolve to method " + aMethod.getName(), e); - } + private String indent(int times) { + return IntStream.range(0, times).mapToObj(i -> " ").collect(Collectors.joining()); } } diff --git a/src/main/java/org/reflections/serializers/JsonSerializer.java b/src/main/java/org/reflections/serializers/JsonSerializer.java index 371676e8..ce8dde27 100644 --- a/src/main/java/org/reflections/serializers/JsonSerializer.java +++ b/src/main/java/org/reflections/serializers/JsonSerializer.java @@ -1,9 +1,7 @@ package org.reflections.serializers; -import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.reflections.Reflections; -import org.reflections.util.Utils; import java.io.File; import java.io.IOException; @@ -12,42 +10,45 @@ import java.nio.charset.Charset; import java.nio.file.Files; -/** serialization of Reflections to json - * - *

an example of produced json: - *

- * {"store":{"storeMap":
- *    {"org.reflections.scanners.TypeAnnotationsScanner":{
- *       "org.reflections.TestModel$AC1":["org.reflections.TestModel$C1"],
- *       "org.reflections.TestModel$AC2":["org.reflections.TestModel$I3",
- * ...
- * 
+/** + * json serialization for {@link org.reflections.Reflections}
{@code reflections.save(file, new JsonSerializer())}
+ *

an example of produced json: + *
{@code
+ * {
+ *   "store": {
+ *     "SubTypes": {
+ *       "org.reflections.TestModel$C1": [
+ *         "org.reflections.TestModel$C2",
+ *         "org.reflections.TestModel$C3"
+ *       ]
+ *     },
+ *     "TypesAnnotated": {
+ *       "org.reflections.TestModel$AC2": [
+ *         "org.reflections.TestModel$C2",
+ *         "org.reflections.TestModel$C3"
+ *       ]
+ *     }
+ *   }
+ * }
+ * }
* */ public class JsonSerializer implements Serializer { - private Gson gson; + @Override public Reflections read(InputStream inputStream) { - return getGson().fromJson(new InputStreamReader(inputStream), Reflections.class); + return new GsonBuilder().setPrettyPrinting().create() + .fromJson(new InputStreamReader(inputStream), Reflections.class); } + @Override public File save(Reflections reflections, String filename) { try { - File file = Utils.prepareFile(filename); - Files.write(file.toPath(), toString(reflections).getBytes(Charset.defaultCharset())); + File file = Serializer.prepareFile(filename); + String json = new GsonBuilder().setPrettyPrinting().create().toJson(reflections); + Files.write(file.toPath(), json.getBytes(Charset.defaultCharset())); return file; } catch (IOException e) { throw new RuntimeException(e); } } - - public String toString(Reflections reflections) { - return getGson().toJson(reflections); - } - - private Gson getGson() { - if (gson == null) { - gson = new GsonBuilder().setPrettyPrinting().create(); - } - return gson; - } } diff --git a/src/main/java/org/reflections/serializers/Serializer.java b/src/main/java/org/reflections/serializers/Serializer.java index 35e0fbdd..91287f14 100644 --- a/src/main/java/org/reflections/serializers/Serializer.java +++ b/src/main/java/org/reflections/serializers/Serializer.java @@ -5,7 +5,8 @@ import java.io.File; import java.io.InputStream; -/** Serilizer of a {@link org.reflections.Reflections} instance */ +/** de/serialization for {@link org.reflections.Reflections} instance metadata + *

see {@link XmlSerializer}, {@link JsonSerializer}, {@link JavaCodeSerializer} */ public interface Serializer { /** reads the input stream into a new Reflections instance, populating it's store */ Reflections read(InputStream inputStream); @@ -13,6 +14,13 @@ public interface Serializer { /** saves a Reflections instance into the given filename */ File save(Reflections reflections, String filename); - /** returns a string serialization of the given Reflections instance */ - String toString(Reflections reflections); + static File prepareFile(String filename) { + File file = new File(filename); + File parent = file.getAbsoluteFile().getParentFile(); + if (!parent.exists()) { + //noinspection ResultOfMethodCallIgnored + parent.mkdirs(); + } + return file; + } } diff --git a/src/main/java/org/reflections/serializers/XmlSerializer.java b/src/main/java/org/reflections/serializers/XmlSerializer.java index 50f61298..bb335d48 100644 --- a/src/main/java/org/reflections/serializers/XmlSerializer.java +++ b/src/main/java/org/reflections/serializers/XmlSerializer.java @@ -1,124 +1,90 @@ package org.reflections.serializers; import org.dom4j.Document; -import org.dom4j.DocumentException; import org.dom4j.DocumentFactory; import org.dom4j.Element; +import org.dom4j.Node; import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; import org.reflections.Reflections; import org.reflections.ReflectionsException; import org.reflections.Store; -import org.reflections.util.ConfigurationBuilder; -import org.reflections.util.Utils; import java.io.File; import java.io.FileOutputStream; -import java.io.IOException; import java.io.InputStream; -import java.io.StringWriter; -import java.lang.reflect.Constructor; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; -/** serialization of Reflections to xml - * - *

an example of produced xml: - *

- * <?xml version="1.0" encoding="UTF-8"?>
- *
- * <Reflections>
- *  <SubTypesScanner>
- *      <entry>
- *          <key>com.google.inject.Module</key>
- *          <values>
- *              <value>fully.qualified.name.1</value>
- *              <value>fully.qualified.name.2</value>
- * ...
- * 
- * */ +/** + * xml serialization for {@link org.reflections.Reflections}
{@code reflections.save(file, new XmlSerializer())}
+ *

an example of produced xml: + *
{@code
+ * 
+ *   
+ *     
+ *       org.reflections.TestModel$C1
+ *       
+ *         org.reflections.TestModel$C2
+ *         org.reflections.TestModel$C3
+ *       
+ *     
+ *   
+ *   
+ *     
+ *       org.reflections.TestModel$AC2
+ *       
+ *         org.reflections.TestModel$C2
+ *         org.reflections.TestModel$C3
+ *       
+ *     
+ *   
+ * 
+ * }
+ */ public class XmlSerializer implements Serializer { - public Reflections read(InputStream inputStream) { - Reflections reflections; - try { - Constructor constructor = Reflections.class.getDeclaredConstructor(); - constructor.setAccessible(true); - reflections = constructor.newInstance(); - } catch (Exception e) { - reflections = new Reflections(new ConfigurationBuilder()); - } + public Reflections read(InputStream inputStream) { + try { + Document document = new SAXReader().read(inputStream); + Map>> storeMap = + document.getRootElement() + .elements().stream() + .collect(Collectors.toMap(Node::getName, + index -> index.elements().stream().collect(Collectors.toMap( + entry -> entry.element("key").getText(), + entry -> entry.element("values").elements().stream().map(Element::getText).collect(Collectors.toSet()))))); + return new Reflections(new Store(storeMap)); + } catch (Exception e) { + throw new ReflectionsException("could not read.", e); + } + } - try { - Document document = new SAXReader().read(inputStream); - for (Object e1 : document.getRootElement().elements()) { - Element index = (Element) e1; - for (Object e2 : index.elements()) { - Element entry = (Element) e2; - Element key = entry.element("key"); - Element values = entry.element("values"); - for (Object o3 : values.elements()) { - Element value = (Element) o3; - reflections.getStore().put(index.getName(), key.getText(), value.getText()); - } - } - } - } catch (DocumentException e) { - throw new ReflectionsException("could not read.", e); - } catch (Throwable e) { - throw new RuntimeException("Could not read. Make sure relevant dependencies exist on classpath.", e); - } + public File save(Reflections reflections, String filename) { + File file = Serializer.prepareFile(filename); + try (FileOutputStream out = new FileOutputStream(file)) { + new XMLWriter(out, OutputFormat.createPrettyPrint()) + .write(createDocument(reflections.getStore())); + } catch (Exception e) { + throw new ReflectionsException("could not save to file " + filename, e); + } + return file; + } - return reflections; - } - - public File save(final Reflections reflections, final String filename) { - File file = Utils.prepareFile(filename); - - - try { - Document document = createDocument(reflections); - XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(file), OutputFormat.createPrettyPrint()); - xmlWriter.write(document); - xmlWriter.close(); - } catch (IOException e) { - throw new ReflectionsException("could not save to file " + filename, e); - } catch (Throwable e) { - throw new RuntimeException("Could not save to file " + filename + ". Make sure relevant dependencies exist on classpath.", e); - } - - return file; - } - - public String toString(final Reflections reflections) { - Document document = createDocument(reflections); - - try { - StringWriter writer = new StringWriter(); - XMLWriter xmlWriter = new XMLWriter(writer, OutputFormat.createPrettyPrint()); - xmlWriter.write(document); - xmlWriter.close(); - return writer.toString(); - } catch (IOException e) { - throw new RuntimeException(); - } - } - - private Document createDocument(final Reflections reflections) { - Store map = reflections.getStore(); - - Document document = DocumentFactory.getInstance().createDocument(); - Element root = document.addElement("Reflections"); - for (String indexName : map.keySet()) { - Element indexElement = root.addElement(indexName); - for (String key : map.keys(indexName)) { - Element entryElement = indexElement.addElement("entry"); - entryElement.addElement("key").setText(key); - Element valuesElement = entryElement.addElement("values"); - for (String value : map.get(indexName, key)) { - valuesElement.addElement("value").setText(value); - } - } - } - return document; - } + private Document createDocument(Store store) { + Document document = DocumentFactory.getInstance().createDocument(); + Element root = document.addElement("Reflections"); + store.forEach((index, map) -> { + Element indexElement = root.addElement(index); + map.forEach((key, values) -> { + Element entryElement = indexElement.addElement("entry"); + entryElement.addElement("key").setText(key); + Element valuesElement = entryElement.addElement("values"); + values.forEach(value -> valuesElement.addElement("value").setText(value)); + }); + }); + return document; + } } diff --git a/src/main/java/org/reflections/util/AnnotationMergeCollector.java b/src/main/java/org/reflections/util/AnnotationMergeCollector.java new file mode 100644 index 00000000..8df1bfbd --- /dev/null +++ b/src/main/java/org/reflections/util/AnnotationMergeCollector.java @@ -0,0 +1,105 @@ +package org.reflections.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; +import java.util.stream.Stream; + +import static org.reflections.ReflectionUtils.toMap; + +/** + * merge annotations by mapping {@link org.reflections.ReflectionUtils#toMap(Annotation)} + * and reduce using the given {@code mergeFunction} + *

{@code mergeFunction} defaults to {@link AnnotationMergeCollector#concatValues(Object, Object)}. + *

optional {@code annotatedElement} used by {@link org.reflections.ReflectionUtils#toMap(Annotation, AnnotatedElement)} + *

{@code get(Annotations.of(...))
+ *   .stream()
+ *   .collect(new AnnotationMergeCollector())}
+ */ +public class AnnotationMergeCollector implements Collector, Map> { + + private final AnnotatedElement annotatedElement; + private final BiFunction mergeFunction; + + public AnnotationMergeCollector(AnnotatedElement annotatedElement, BiFunction mergeFunction) { + this.annotatedElement = annotatedElement; + this.mergeFunction = mergeFunction; + } + + public AnnotationMergeCollector() { + this(null); + } + + public AnnotationMergeCollector(AnnotatedElement annotatedElement) { + this(annotatedElement, AnnotationMergeCollector::concatValues); + } + + @Override + public Supplier> supplier() { + return HashMap::new; + } + + @Override + public BiConsumer, Annotation> accumulator() { + return (acc, ann) -> mergeMaps(acc, toMap(ann, annotatedElement)); + } + + @Override + public BinaryOperator> combiner() { + return this::mergeMaps; + } + + @Override + public Function, Map> finisher() { + return Function.identity(); + } + + @Override + public Set characteristics() { + return Collections.emptySet(); + } + + // + private Map mergeMaps(Map m1, Map m2) { + m2.forEach((k1, v1) -> m1.merge(k1, v1, mergeFunction)); + return m1; + } + + private static Object concatValues(Object v1, Object v2) { + if (v1.getClass().isArray()) { + if (v2.getClass().getComponentType().equals(String.class)) { + return stringArrayConcat((String[]) v1, (String[]) v2); + } else { + return arrayAdd(((Object[]) v1), ((Object[]) v2)); + } + } else if (v2.getClass().equals(String.class)) { + return stringConcat(((String) v1), ((String) v2)); + } else { + return v2; // override + } + } + + private static Object[] arrayAdd(Object[] o1, Object[] o2) { + return o2.length == 0 ? o1 : o1.length == 0 ? o2 : + Stream.concat(Stream.of(o1), Stream.of(o2)).toArray(Object[]::new); + } + + private static Object stringArrayConcat(String[] v1, String[] v2) { + return v2.length == 0 ? v1 : v1.length == 0 ? v2 : + Arrays.stream(v2).flatMap(s2 -> Arrays.stream(v1).map(s1 -> s2 + s1)).toArray(String[]::new); + } + + private static Object stringConcat(String v1, String v2) { + return v2.isEmpty() ? v1 : v1.isEmpty() ? v2 : v1 + v2; + } +} diff --git a/src/main/java/org/reflections/util/ConfigurationBuilder.java b/src/main/java/org/reflections/util/ConfigurationBuilder.java index 38049076..aff76c4f 100644 --- a/src/main/java/org/reflections/util/ConfigurationBuilder.java +++ b/src/main/java/org/reflections/util/ConfigurationBuilder.java @@ -1,16 +1,9 @@ package org.reflections.util; import org.reflections.Configuration; -import org.reflections.Reflections; import org.reflections.ReflectionsException; -import org.reflections.adapters.JavaReflectionAdapter; -import org.reflections.adapters.JavassistAdapter; -import org.reflections.adapters.MetadataAdapter; import org.reflections.scanners.Scanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; -import org.reflections.serializers.Serializer; -import org.reflections.serializers.XmlSerializer; +import org.reflections.scanners.Scanners; import java.net.URL; import java.util.ArrayList; @@ -18,301 +11,228 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.stream.Stream; /** - * a fluent builder for {@link org.reflections.Configuration}, to be used for constructing a {@link org.reflections.Reflections} instance - *

usage: - *

- *      new Reflections(
- *          new ConfigurationBuilder()
- *              .filterInputsBy(new FilterBuilder().include("your project's common package prefix here..."))
- *              .setUrls(ClasspathHelper.forClassLoader())
- *              .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner().filterResultsBy(myClassAnnotationsFilter)));
- * 
- *
{@link #executorService} is used optionally used for parallel scanning. if value is null then scanning is done in a simple for loop - *

defaults: accept all for {@link #inputsFilter}, - * {@link #executorService} is null, - * {@link #serializer} is {@link org.reflections.serializers.XmlSerializer} + * {@link Configuration} builder for instantiating Reflections + *

{@code
+ * // add urls for package prefix, use default scanners
+ * new Reflections(
+ *   new ConfigurationBuilder()
+ *     .forPackage("org.reflections"))
+ *
+ * new Reflections(
+ *   new ConfigurationBuilder()
+ *     .addUrls(ClasspathHelper.forPackage("org.reflections"))   // add urls for package prefix
+ *     .addScanners(Scanners.values())                           // use all standard scanners
+ *     .filterInputsBy(new FilterBuilder().includePackage(...))) // optionally filter inputs
+ * }
+ *

defaults scanners: {@link Scanners#SubTypes} and {@link Scanners#TypesAnnotated} + *

(breaking changes) Inputs filter will NOT be set automatically, consider adding in case too many classes are scanned. */ public class ConfigurationBuilder implements Configuration { + public static final Set DEFAULT_SCANNERS = new HashSet<>(Arrays.asList(Scanners.TypesAnnotated, Scanners.SubTypes)); + public static final Predicate DEFAULT_INPUTS_FILTER = t -> true; + private Set scanners; private Set urls; - /*lazy*/ protected MetadataAdapter metadataAdapter; private Predicate inputsFilter; - /*lazy*/ private Serializer serializer; - private ExecutorService executorService; + private boolean isParallel = true; private ClassLoader[] classLoaders; private boolean expandSuperTypes = true; public ConfigurationBuilder() { - scanners = new HashSet<>(Arrays.asList(new TypeAnnotationsScanner(), new SubTypesScanner())); urls = new HashSet<>(); } - /** constructs a {@link ConfigurationBuilder} using the given parameters, in a non statically typed way. - * that is, each element in {@code params} is guessed by it's type and populated into the configuration. + /** constructs a {@link ConfigurationBuilder}. + *

each parameter in {@code params} is referred by its type: *

    - *
  • {@link String} - add urls using {@link ClasspathHelper#forPackage(String, ClassLoader...)} ()}
  • - *
  • {@link Class} - add urls using {@link ClasspathHelper#forClass(Class, ClassLoader...)}
  • - *
  • {@link ClassLoader} - use these classloaders in order to find urls in ClasspathHelper.forPackage(), ClasspathHelper.forClass() and for resolving types
  • - *
  • {@link Scanner} - use given scanner, overriding the default scanners
  • - *
  • {@link URL} - add the given url for scanning
  • - *
  • {@code Object[]} - flatten and use each element as above
  • + *
  • {@link String} - add urls using {@link ClasspathHelper#forPackage(String, ClassLoader...)} and an input filter + *
  • {@link Class} - add urls using {@link ClasspathHelper#forClass(Class, ClassLoader...)} and an input filter + *
  • {@link Scanner} - use scanner, overriding default scanners + *
  • {@link URL} - add url for scanning + *
  • {@link Predicate} - set/override inputs filter + *
  • {@link ClassLoader} - use these classloaders in order to find urls using ClasspathHelper and for resolving types + *
  • {@code Object[]} - flatten and use each element as above *
- * - * an input {@link FilterBuilder} will be set according to given packages. - *

use any parameter type in any order. this constructor uses instanceof on each param and instantiate a {@link ConfigurationBuilder} appropriately. - * */ - @SuppressWarnings("unchecked") - public static ConfigurationBuilder build(final Object... params) { - ConfigurationBuilder builder = new ConfigurationBuilder(); - - //flatten + * input filter will be set according to given packages + *

prefer using the explicit accessor methods instead: + *
{@code new ConfigurationBuilder().forPackage(...).setScanners(...)}
+ */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public static ConfigurationBuilder build(Object... params) { + final ConfigurationBuilder builder = new ConfigurationBuilder(); + + // flatten List parameters = new ArrayList<>(); - if (params != null) { - for (Object param : params) { - if (param != null) { - if (param.getClass().isArray()) { for (Object p : (Object[]) param) if (p != null) parameters.add(p); } - else if (param instanceof Iterable) { for (Object p : (Iterable) param) if (p != null) parameters.add(p); } - else parameters.add(param); - } - } + for (Object param : params) { + if (param.getClass().isArray()) { for (Object p : (Object[]) param) parameters.add(p); } + else if (param instanceof Iterable) { for (Object p : (Iterable) param) parameters.add(p); } + else parameters.add(param); } - List loaders = new ArrayList<>(); - for (Object param : parameters) if (param instanceof ClassLoader) loaders.add((ClassLoader) param); + ClassLoader[] loaders = Stream.of(params).filter(p -> p instanceof ClassLoader).distinct().toArray(ClassLoader[]::new); + if (loaders.length != 0) { builder.addClassLoaders(loaders); } - ClassLoader[] classLoaders = loaders.isEmpty() ? null : loaders.toArray(new ClassLoader[loaders.size()]); - FilterBuilder filter = new FilterBuilder(); - List scanners = new ArrayList<>(); + FilterBuilder inputsFilter = new FilterBuilder(); + builder.filterInputsBy(inputsFilter); for (Object param : parameters) { - if (param instanceof String) { - builder.addUrls(ClasspathHelper.forPackage((String) param, classLoaders)); - filter.includePackage((String) param); - } - else if (param instanceof Class) { - if (Scanner.class.isAssignableFrom((Class) param)) { - try { builder.addScanners(((Scanner) ((Class) param).newInstance())); } catch (Exception e) { /*fallback*/ } - } - builder.addUrls(ClasspathHelper.forClass((Class) param, classLoaders)); - filter.includePackage(((Class) param)); - } - else if (param instanceof Scanner) { scanners.add((Scanner) param); } - else if (param instanceof URL) { builder.addUrls((URL) param); } - else if (param instanceof ClassLoader) { /* already taken care */ } - else if (param instanceof Predicate) { filter.add((Predicate) param); } - else if (param instanceof ExecutorService) { builder.setExecutorService((ExecutorService) param); } - else throw new ReflectionsException("could not use param " + param); + if (param instanceof String && !((String) param).isEmpty()) { + builder.forPackage((String) param, loaders); + inputsFilter.includePackage((String) param); + } else if (param instanceof Class && !Scanner.class.isAssignableFrom((Class) param)) { + builder.addUrls(ClasspathHelper.forClass((Class) param, loaders)); + inputsFilter.includePackage(((Class) param).getPackage().getName()); + } else if (param instanceof URL) { + builder.addUrls((URL) param); + } else if (param instanceof Scanner) { + builder.addScanners((Scanner) param); + } else if (param instanceof Class && Scanner.class.isAssignableFrom((Class) param)) { + try { builder.addScanners(((Class) param).getDeclaredConstructor().newInstance()); } + catch (Exception e) { throw new RuntimeException(e); } + } else if (param instanceof Predicate) { + builder.filterInputsBy((Predicate) param); + } else throw new ReflectionsException("could not use param '" + param + "'"); } if (builder.getUrls().isEmpty()) { - if (classLoaders != null) { - builder.addUrls(ClasspathHelper.forClassLoader(classLoaders)); //default urls getResources("") - } else { - builder.addUrls(ClasspathHelper.forClassLoader()); //default urls getResources("") - } - if (builder.urls.isEmpty()) { - builder.addUrls(ClasspathHelper.forJavaClassPath()); - } + // scan all classpath if no urls provided todo avoid + builder.addUrls(ClasspathHelper.forClassLoader(loaders)); } - builder.filterInputsBy(filter); - if (!scanners.isEmpty()) { builder.setScanners(scanners.toArray(new Scanner[scanners.size()])); } - if (!loaders.isEmpty()) { builder.addClassLoaders(loaders); } - return builder; } + /** {@link #addUrls(URL...)} by applying {@link ClasspathHelper#forPackage(String, ClassLoader...)} for the given {@code pkg}*/ + public ConfigurationBuilder forPackage(String pkg, ClassLoader... classLoaders) { + return addUrls(ClasspathHelper.forPackage(pkg, classLoaders)); + } + + /** {@link #addUrls(URL...)} by applying {@link ClasspathHelper#forPackage(String, ClassLoader...)} for the given {@code packages}*/ public ConfigurationBuilder forPackages(String... packages) { - for (String pkg : packages) { - addUrls(ClasspathHelper.forPackage(pkg)); - } + for (String pkg : packages) forPackage(pkg); return this; } + @Override + /* @inherited */ public Set getScanners() { - return scanners; + return scanners != null ? scanners : DEFAULT_SCANNERS; } /** set the scanners instances for scanning different metadata */ - public ConfigurationBuilder setScanners(final Scanner... scanners) { - this.scanners.clear(); - return addScanners(scanners); + public ConfigurationBuilder setScanners(Scanner... scanners) { + this.scanners = new HashSet<>(Arrays.asList(scanners)); + return this; } /** set the scanners instances for scanning different metadata */ - public ConfigurationBuilder addScanners(final Scanner... scanners) { - this.scanners.addAll(Arrays.asList(scanners)); + public ConfigurationBuilder addScanners(Scanner... scanners) { + if (this.scanners == null) setScanners(scanners); else this.scanners.addAll(Arrays.asList(scanners)); return this; } + @Override + /* @inherited */ public Set getUrls() { return urls; } /** set the urls to be scanned - *

use {@link org.reflections.util.ClasspathHelper} convenient methods to get the relevant urls - * */ - public ConfigurationBuilder setUrls(final Collection urls) { + *

use {@link ClasspathHelper} convenient methods to get the relevant urls + *

see also {@link #forPackages(String...)} */ + public ConfigurationBuilder setUrls(Collection urls) { this.urls = new HashSet<>(urls); return this; } /** set the urls to be scanned - *

use {@link org.reflections.util.ClasspathHelper} convenient methods to get the relevant urls - * */ - public ConfigurationBuilder setUrls(final URL... urls) { - this.urls = new HashSet<>(Arrays.asList(urls)); - return this; + *

use {@link ClasspathHelper} convenient methods to get the relevant urls + *

see also {@link #forPackages(String...)} */ + public ConfigurationBuilder setUrls(URL... urls) { + return setUrls(Arrays.asList(urls)); } /** add urls to be scanned - *

use {@link org.reflections.util.ClasspathHelper} convenient methods to get the relevant urls - * */ - public ConfigurationBuilder addUrls(final Collection urls) { + *

use {@link ClasspathHelper} convenient methods to get the relevant urls + *

see also {@link #forPackages(String...)} */ + public ConfigurationBuilder addUrls(Collection urls) { this.urls.addAll(urls); return this; } /** add urls to be scanned - *

use {@link org.reflections.util.ClasspathHelper} convenient methods to get the relevant urls - * */ - public ConfigurationBuilder addUrls(final URL... urls) { - this.urls.addAll(new HashSet<>(Arrays.asList(urls))); - return this; - } - - /** returns the metadata adapter. - * if javassist library exists in the classpath, this method returns {@link JavassistAdapter} otherwise defaults to {@link JavaReflectionAdapter}. - *

the {@link JavassistAdapter} is preferred in terms of performance and class loading. */ - public MetadataAdapter getMetadataAdapter() { - if (metadataAdapter != null) return metadataAdapter; - else { - try { - return (metadataAdapter = new JavassistAdapter()); - } catch (Throwable e) { - if (Reflections.log != null) - Reflections.log.warn("could not create JavassistAdapter, using JavaReflectionAdapter", e); - return (metadataAdapter = new JavaReflectionAdapter()); - } - } - } - - /** sets the metadata adapter used to fetch metadata from classes */ - public ConfigurationBuilder setMetadataAdapter(final MetadataAdapter metadataAdapter) { - this.metadataAdapter = metadataAdapter; - return this; + *

use {@link ClasspathHelper} convenient methods to get the relevant urls + *

see also {@link #forPackages(String...)} */ + public ConfigurationBuilder addUrls(URL... urls) { + return addUrls(Arrays.asList(urls)); } + @Override + /* @inherited */ public Predicate getInputsFilter() { - return inputsFilter; + return inputsFilter != null ? inputsFilter : DEFAULT_INPUTS_FILTER; } /** sets the input filter for all resources to be scanned. - *

supply a {@link Predicate} or use the {@link FilterBuilder}*/ - public void setInputsFilter(Predicate inputsFilter) { + *

prefer using {@link FilterBuilder} */ + public ConfigurationBuilder setInputsFilter(Predicate inputsFilter) { this.inputsFilter = inputsFilter; + return this; } /** sets the input filter for all resources to be scanned. - *

supply a {@link Predicate} or use the {@link FilterBuilder}*/ + *

prefer using {@link FilterBuilder} */ public ConfigurationBuilder filterInputsBy(Predicate inputsFilter) { - this.inputsFilter = inputsFilter; - return this; - } - - public ExecutorService getExecutorService() { - return executorService; + return setInputsFilter(inputsFilter); } - /** sets the executor service used for scanning. */ - public ConfigurationBuilder setExecutorService(ExecutorService executorService) { - this.executorService = executorService; - return this; + @Override + /* @inherited */ + public boolean isParallel() { + return isParallel; } - /** sets the executor service used for scanning to ThreadPoolExecutor with core size as {@link java.lang.Runtime#availableProcessors()} - *

default is ThreadPoolExecutor with a single core */ - public ConfigurationBuilder useParallelExecutor() { - return useParallelExecutor(Runtime.getRuntime().availableProcessors()); + /** if true, scan urls in parallel. */ + public void setParallel(boolean parallel) { + isParallel = parallel; } - /** sets the executor service used for scanning to ThreadPoolExecutor with core size as the given availableProcessors parameter. - * the executor service spawns daemon threads by default. - *

default is ThreadPoolExecutor with a single core */ - public ConfigurationBuilder useParallelExecutor(final int availableProcessors) { - ThreadFactory threadFactory = new ThreadFactory() { - private final AtomicInteger threadNumber = new AtomicInteger(1); - - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setName("org.reflections-scanner-" + threadNumber.getAndIncrement()); - t.setDaemon(true); - return t; - } - }; - setExecutorService(Executors.newFixedThreadPool(availableProcessors, threadFactory)); - return this; + @Override + /* @inherited */ + public ClassLoader[] getClassLoaders() { + return classLoaders; } - public Serializer getSerializer() { - return serializer != null ? serializer : (serializer = new XmlSerializer()); //lazily defaults to XmlSerializer - } - /** sets the serializer used when issuing {@link org.reflections.Reflections#save} */ - public ConfigurationBuilder setSerializer(Serializer serializer) { - this.serializer = serializer; + /** set optional class loaders used for resolving types. */ + public ConfigurationBuilder setClassLoaders(ClassLoader[] classLoaders) { + this.classLoaders = classLoaders; return this; } - /** get class loader, might be used for scanning or resolving methods/fields */ - public ClassLoader[] getClassLoaders() { - return classLoaders; + /** add optional class loaders used for resolving types. */ + public ConfigurationBuilder addClassLoaders(ClassLoader... classLoaders) { + this.classLoaders = this.classLoaders == null ? classLoaders : + Stream.concat(Arrays.stream(this.classLoaders), Arrays.stream(classLoaders)).distinct().toArray(ClassLoader[]::new); + return this; } @Override + /* @inherited */ public boolean shouldExpandSuperTypes() { return expandSuperTypes; } - /** - * if set to true, Reflections will expand super types after scanning. - *

see {@link org.reflections.Reflections#expandSuperTypes()} - */ + /** if set to true, Reflections will expand super types after scanning. + *

see {@link org.reflections.Reflections#expandSuperTypes(Map)} */ public ConfigurationBuilder setExpandSuperTypes(boolean expandSuperTypes) { this.expandSuperTypes = expandSuperTypes; return this; } - - /** set class loader, might be used for resolving methods/fields */ - public ConfigurationBuilder setClassLoaders(ClassLoader[] classLoaders) { - this.classLoaders = classLoaders; - return this; - } - - /** add class loader, might be used for resolving methods/fields */ - public ConfigurationBuilder addClassLoader(ClassLoader classLoader) { - return addClassLoaders(classLoader); - } - - /** add class loader, might be used for resolving methods/fields */ - public ConfigurationBuilder addClassLoaders(ClassLoader... classLoaders) { - this.classLoaders = this.classLoaders == null - ? classLoaders - : Stream.concat(Arrays.stream(this.classLoaders), Arrays.stream(classLoaders)).toArray(ClassLoader[]::new); - return this; - } - - /** add class loader, might be used for resolving methods/fields */ - public ConfigurationBuilder addClassLoaders(Collection classLoaders) { - return addClassLoaders(classLoaders.toArray(new ClassLoader[classLoaders.size()])); - } } diff --git a/src/main/java/org/reflections/util/FilterBuilder.java b/src/main/java/org/reflections/util/FilterBuilder.java index 6ed7455f..af63171c 100644 --- a/src/main/java/org/reflections/util/FilterBuilder.java +++ b/src/main/java/org/reflections/util/FilterBuilder.java @@ -5,60 +5,93 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.function.Predicate; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** - * Builds include/exclude filters for Reflections. - *

- * For example: - *

- * Predicate filter1 = FilterBuilder.parsePackages("-java, "-javax");
- * Predicate filter2 = new FilterBuilder().include(".*").exclude("java.*");
- * 
+ * include/exclude filter builder + *
{@code
+ * new FilterBuilder().includePackage("java").excludePackage("java.lang")
+ *
+ * FilterBuilder.parsePackages("+java, -java.lang")
+ *
+ * new FilterBuilder().includePattern("java\\..*").excludePackage("java\\.lang\\..*")
+ * }
+ * note that includePackage/excludePackage value is mapped into a prefix pattern with a trailing dot, for example: {@code includePackage("a.b")} is equivalent to {@code includePattern("a\\.b\\..*)} */ public class FilterBuilder implements Predicate { - private final List> chain; + private final List> chain = new ArrayList<>(); - public FilterBuilder() {chain = new ArrayList<>();} - private FilterBuilder(final Collection> filters) {chain = new ArrayList<>(filters);} + public FilterBuilder() {} - /** include a regular expression */ - public FilterBuilder include(final String regex) {return add(new Include(regex));} - - /** exclude a regular expression*/ - public FilterBuilder exclude(final String regex) {add(new Exclude(regex)); return this;} + private FilterBuilder(Collection> filters) { + chain.addAll(filters); + } - /** add a Predicate to the chain of predicates*/ - public FilterBuilder add(Predicate filter) {chain.add(filter); return this;} + /** include package prefix
{@code new FilterBuilder().includePackage("java.lang")}
+ * note that the {@code value} is mapped into a prefix pattern with a trailing dot, for example {@code "a.b" == "a\\.b\\..*} + *

see more in {@link #prefixPattern(String)} */ + public FilterBuilder includePackage(String value) { + return includePattern(prefixPattern(value)); + } - /** include a package of a given class */ - public FilterBuilder includePackage(final Class aClass) {return add(new Include(packageNameRegex(aClass)));} + /** exclude package prefix

{@code new FilterBuilder().excludePackage("java.lang")}
+ * note that the {@code value} is mapped into a prefix pattern with a trailing dot, for example {@code "a.b" == "a\\.b\\..*} + *

see more in {@link #prefixPattern(String)} */ + public FilterBuilder excludePackage(String value) { + return excludePattern(prefixPattern(value)); + } - /** exclude a package of a given class */ - public FilterBuilder excludePackage(final Class aClass) {return add(new Exclude(packageNameRegex(aClass)));} + /** include regular expression

{@code new FilterBuilder().includePattern("java\\.lang\\..*")}
+ * see also {@link #includePackage(String)}*/ + public FilterBuilder includePattern(String regex) { + return add(new FilterBuilder.Include(regex)); + } - /** include packages of given prefixes */ - public FilterBuilder includePackage(final String... prefixes) { - for (String prefix : prefixes) { - add(new Include(prefix(prefix))); - } - return this; + /** exclude regular expression
{@code new FilterBuilder().excludePattern("java\\.lang\\..*")}
+ * see also {@link #excludePackage(String)}*/ + public FilterBuilder excludePattern(String regex) { + return add(new FilterBuilder.Exclude(regex)); } - /** exclude packages of a given prefix */ - public FilterBuilder excludePackage(final String... prefixes) { - for (String prefix : prefixes) { - add(new Exclude(prefix(prefix))); - } - return this; + /** include a regular expression

deprecated, use {@link #includePattern(String)} */ + @Deprecated + public FilterBuilder include(String regex) { + return add(new Include(regex)); } - private static String packageNameRegex(Class aClass) {return prefix(aClass.getPackage().getName() + ".");} + /** exclude a regular expression

deprecated, use {@link #excludePattern(String)} */ + @Deprecated + public FilterBuilder exclude(String regex) { + add(new Exclude(regex)); return this; + } - public static String prefix(String qualifiedName) {return qualifiedName.replace(".","\\.") + ".*";} + /** + * Parses csv of include/exclude package prefix filter, where each value starting with +/- + *

{@code FilterBuilder.parsePackages("-java, -javax, +java.util")}
+ * each value is mapped into a prefix pattern with a trailing dot, for example {@code "a.b" == "a\\.b\\..*}. see more in {@link #prefixPattern(String)} + */ + public static FilterBuilder parsePackages(String includeExcludeString) { + List> filters = new ArrayList<>(); + for (String string : includeExcludeString.split(",")) { + String trimmed = string.trim(); + char prefix = trimmed.charAt(0); + String pattern = prefixPattern(trimmed.substring(1)); + switch (prefix) { + case '+': filters.add(new Include(pattern)); break; + case '-': filters.add(new Exclude(pattern)); break; + default: throw new ReflectionsException("includeExclude should start with either + or -"); + } + } + return new FilterBuilder(filters); + } - @Override public String toString() {return Utils.join(chain, ", ");} + public FilterBuilder add(Predicate filter) { + chain.add(filter); + return this; + } public boolean test(String regex) { boolean accept = chain.isEmpty() || chain.get(0) instanceof Exclude; @@ -72,108 +105,47 @@ public boolean test(String regex) { return accept; } - public abstract static class Matcher implements Predicate { - final Pattern pattern; - public Matcher(final String regex) {pattern = Pattern.compile(regex);} - @Override public String toString() {return pattern.pattern();} + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + return Objects.equals(chain, ((FilterBuilder) o).chain); } - public static class Include extends Matcher { - public Include(final String patternString) {super(patternString);} - @Override public boolean test(final String regex) {return pattern.matcher(regex).matches();} - @Override public String toString() {return "+" + super.toString();} + @Override + public int hashCode() { + return Objects.hash(chain); } - public static class Exclude extends Matcher { - public Exclude(final String patternString) {super(patternString);} - @Override public boolean test(final String regex) {return !pattern.matcher(regex).matches();} - @Override public String toString() {return "-" + super.toString();} + @Override public String toString() { + return chain.stream().map(Object::toString).collect(Collectors.joining(", ")); } - /** - * Parses a string representation of an include/exclude filter. - *

- * The given includeExcludeString is a comma separated list of regexes, - * each starting with either + or - to indicate include/exclude. - *

- * For example parsePackages("-java\\..*, -javax\\..*, -sun\\..*, -com\\.sun\\..*") - * or parse("+com\\.myn\\..*,-com\\.myn\\.excluded\\..*"). - * Note that "-java\\..*" will block "java.foo" but not "javax.foo". - *

- * See also the more useful {@link FilterBuilder#parsePackages(String)} method. - */ - public static FilterBuilder parse(String includeExcludeString) { - List> filters = new ArrayList<>(); - - if (!Utils.isEmpty(includeExcludeString)) { - for (String string : includeExcludeString.split(",")) { - String trimmed = string.trim(); - char prefix = trimmed.charAt(0); - String pattern = trimmed.substring(1); - - Predicate filter; - switch (prefix) { - case '+': - filter = new Include(pattern); - break; - case '-': - filter = new Exclude(pattern); - break; - default: - throw new ReflectionsException("includeExclude should start with either + or -"); - } - - filters.add(filter); - } + /** maps fqn to prefix pattern with a trailing dot, for example {@code packageNamePrefix("a.b") == "a\\.b\\..*} */ + private static String prefixPattern(String fqn) { + if (!fqn.endsWith(".")) fqn += "."; + return fqn.replace(".", "\\.").replace("$", "\\$") + ".*"; + } - return new FilterBuilder(filters); - } else { - return new FilterBuilder(); + abstract static class Matcher implements Predicate { + final Pattern pattern; + Matcher(String regex) { pattern = Pattern.compile(regex); } + @Override public int hashCode() { return Objects.hash(pattern); } + @Override public boolean equals(Object o) { + return this == o || o != null && getClass() == o.getClass() && Objects.equals(pattern.pattern(), ((Matcher) o).pattern.pattern()); } + @Override public String toString() { return pattern.pattern(); } } - /** - * Parses a string representation of an include/exclude filter. - *

- * The given includeExcludeString is a comma separated list of package name segments, - * each starting with either + or - to indicate include/exclude. - *

- * For example parsePackages("-java, -javax, -sun, -com.sun") or parse("+com.myn,-com.myn.excluded"). - * Note that "-java" will block "java.foo" but not "javax.foo". - *

- * The input strings "-java" and "-java." are equivalent. - */ - public static FilterBuilder parsePackages(String includeExcludeString) { - List> filters = new ArrayList<>(); - - if (!Utils.isEmpty(includeExcludeString)) { - for (String string : includeExcludeString.split(",")) { - String trimmed = string.trim(); - char prefix = trimmed.charAt(0); - String pattern = trimmed.substring(1); - if (!pattern.endsWith(".")) { - pattern += "."; - } - pattern = prefix(pattern); - - Predicate filter; - switch (prefix) { - case '+': - filter = new Include(pattern); - break; - case '-': - filter = new Exclude(pattern); - break; - default: - throw new ReflectionsException("includeExclude should start with either + or -"); - } - - filters.add(filter); - } + static class Include extends Matcher { + Include(String regex) { super(regex); } + @Override public boolean test(String regex) { return pattern.matcher(regex).matches(); } + @Override public String toString() { return "+" + pattern; } + } - return new FilterBuilder(filters); - } else { - return new FilterBuilder(); - } + static class Exclude extends Matcher { + Exclude(String regex) { super(regex); } + @Override public boolean test(String regex) { return !pattern.matcher(regex).matches(); } + @Override public String toString() { return "-" + pattern; } } } diff --git a/src/main/java/org/reflections/util/JavassistHelper.java b/src/main/java/org/reflections/util/JavassistHelper.java new file mode 100644 index 00000000..e3a71e3a --- /dev/null +++ b/src/main/java/org/reflections/util/JavassistHelper.java @@ -0,0 +1,91 @@ +package org.reflections.util; + +import javassist.bytecode.AccessFlag; +import javassist.bytecode.AnnotationsAttribute; +import javassist.bytecode.AttributeInfo; +import javassist.bytecode.ClassFile; +import javassist.bytecode.Descriptor; +import javassist.bytecode.FieldInfo; +import javassist.bytecode.MethodInfo; +import javassist.bytecode.ParameterAnnotationsAttribute; +import javassist.bytecode.annotation.Annotation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class JavassistHelper { + /** setting this static to false will result in returning only {@link java.lang.annotation.RetentionPolicy#RUNTIME} visible annotation */ + public static boolean includeInvisibleTag = true; + + public static List getAnnotations(Function function) { + List list = getAnnotations((AnnotationsAttribute) function.apply(AnnotationsAttribute.visibleTag)); + if (includeInvisibleTag) list.addAll(getAnnotations((AnnotationsAttribute) function.apply(AnnotationsAttribute.invisibleTag))); + return list; + } + + public static String fieldName(ClassFile classFile, FieldInfo object) { + return String.format("%s.%s", classFile.getName(), object.getName()); + } + + public static String methodName(ClassFile classFile, MethodInfo object) { + return String.format("%s.%s(%s)", classFile.getName(), object.getName(), String.join(", ", getParameters(object))); + } + + public static boolean isPublic(Object object) { + if (object instanceof ClassFile) return AccessFlag.isPublic(((ClassFile) object).getAccessFlags()); + if (object instanceof FieldInfo) return AccessFlag.isPublic(((FieldInfo) object).getAccessFlags()); + if (object instanceof MethodInfo) return AccessFlag.isPublic(((MethodInfo) object).getAccessFlags()); + return false; + } + + public static Stream getMethods(ClassFile classFile) { + return classFile.getMethods().stream().filter(MethodInfo::isMethod); + } + + public static Stream getConstructors(ClassFile classFile) { + return classFile.getMethods().stream().filter(methodInfo -> !methodInfo.isMethod()); + } + + public static List getParameters(MethodInfo method) { + List result = new ArrayList<>(); + String descriptor = method.getDescriptor().substring(1); + Descriptor.Iterator iterator = new Descriptor.Iterator(descriptor); + Integer prev = null; + while (iterator.hasNext()) { + int cur = iterator.next(); + if (prev != null) result.add(Descriptor.toString(descriptor.substring(prev, cur))); + prev = cur; + } + return result; + } + + public static String getReturnType(MethodInfo method) { + String descriptor = method.getDescriptor(); + descriptor = descriptor.substring(descriptor.lastIndexOf(")") + 1); + return Descriptor.toString(descriptor); + } + + public static List> getParametersAnnotations(MethodInfo method) { + List> list = getAnnotations((ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.visibleTag)); + if (includeInvisibleTag) list.addAll(getAnnotations((ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.invisibleTag))); + return list; + } + + private static List> getAnnotations(ParameterAnnotationsAttribute attribute) { + return mapList(attribute, ParameterAnnotationsAttribute::getAnnotations, aa -> mapList(aa, a -> a, Annotation::getTypeName)); + } + + private static List getAnnotations(AnnotationsAttribute attribute) { + return mapList(attribute, AnnotationsAttribute::getAnnotations, Annotation::getTypeName); + } + + // todo inline & simplify + private static List mapList(T t, Function f1, Function f2) { + return t != null ? Arrays.stream(f1.apply(t)).map(f2).collect(Collectors.toList()) : Collections.emptyList(); + } +} diff --git a/src/main/java/org/reflections/util/NameHelper.java b/src/main/java/org/reflections/util/NameHelper.java new file mode 100644 index 00000000..ef30289e --- /dev/null +++ b/src/main/java/org/reflections/util/NameHelper.java @@ -0,0 +1,172 @@ +package org.reflections.util; + +import org.reflections.ReflectionsException; + +import javax.annotation.Nullable; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Helper methods for converting between annotated elements and their names + */ +public interface NameHelper { + + List primitiveNames = Arrays.asList("boolean", "char", "byte", "short", "int", "long", "float", "double", "void"); + List> primitiveTypes = Arrays.asList(boolean.class, char.class, byte.class, short.class, int.class, long.class, float.class, double.class, void.class); + List primitiveDescriptors = Arrays.asList("Z", "C", "B", "S", "I", "J", "F", "D", "V"); + + // toName + default String toName(AnnotatedElement element) { + return element.getClass().equals(Class.class) ? toName((Class) element) : + element.getClass().equals(Constructor.class) ? toName((Constructor) element) : + element.getClass().equals(Method.class) ? toName((Method) element) : + element.getClass().equals(Field.class) ? toName((Field) element) : null; + } + + default String toName(Class type) { + int dim = 0; + while (type.isArray()) { dim++; type = type.getComponentType(); } + return type.getName() + String.join("", Collections.nCopies(dim, "[]")); + } + + default String toName(Constructor constructor) { + return String.format("%s.(%s)", constructor.getName(), String.join(", ", toNames(constructor.getParameterTypes()))); + } + + default String toName(Method method) { + return String.format("%s.%s(%s)", method.getDeclaringClass().getName(), method.getName(), String.join(", ", toNames(method.getParameterTypes()))); + } + + default String toName(Field field) { + return String.format("%s.%s", field.getDeclaringClass().getName(), field.getName()); + } + + default Collection toNames(Collection elements) { + return elements.stream().map(this::toName).filter(Objects::nonNull).collect(Collectors.toList()); + } + + default Collection toNames(AnnotatedElement... elements) { + return toNames(Arrays.asList(elements)); + } + + // forName + @SuppressWarnings("unchecked") + default T forName(String name, Class resultType, ClassLoader... loaders) { + return resultType.equals(Class.class) ? (T) forClass(name, loaders) : + resultType.equals(Constructor.class) ? (T) forConstructor(name, loaders) : + resultType.equals(Method.class) ? (T) forMethod(name, loaders) : + resultType.equals(Field.class) ? (T) forField(name, loaders) : + resultType.equals(Member.class) ? (T) forMember(name, loaders) : null; + } + + /** tries to resolve a java type name to a Class + *

if optional {@link ClassLoader}s are not specified, then both {@link org.reflections.util.ClasspathHelper#contextClassLoader()} and {@link org.reflections.util.ClasspathHelper#staticClassLoader()} are used + * */ + default Class forClass(String typeName, ClassLoader... loaders) { + if (primitiveNames.contains(typeName)) { + return primitiveTypes.get(primitiveNames.indexOf(typeName)); + } else { + String type; + if (typeName.contains("[")) { + int i = typeName.indexOf("["); + type = typeName.substring(0, i); + String array = typeName.substring(i).replace("]", ""); + if (primitiveNames.contains(type)) { + type = primitiveDescriptors.get(primitiveNames.indexOf(type)); + } else { + type = "L" + type + ";"; + } + type = array + type; + } else { + type = typeName; + } + + for (ClassLoader classLoader : ClasspathHelper.classLoaders(loaders)) { + if (type.contains("[")) { + try { return Class.forName(type, false, classLoader); } + catch (Throwable ignored) {} + } + try { return classLoader.loadClass(type); } + catch (Throwable ignored) {} + } + return null; + } + } + + default Member forMember(String descriptor, ClassLoader... loaders) throws ReflectionsException { + int p0 = descriptor.lastIndexOf('('); + String memberKey = p0 != -1 ? descriptor.substring(0, p0) : descriptor; + String methodParameters = p0 != -1 ? descriptor.substring(p0 + 1, descriptor.lastIndexOf(')')) : ""; + + int p1 = Math.max(memberKey.lastIndexOf('.'), memberKey.lastIndexOf("$")); + String className = memberKey.substring(0, p1); + String memberName = memberKey.substring(p1 + 1); + + Class[] parameterTypes = null; + if (!methodParameters.isEmpty()) { + String[] parameterNames = methodParameters.split(","); + parameterTypes = Arrays.stream(parameterNames).map(name -> forClass(name.trim(), loaders)).toArray(Class[]::new); + } + + Class aClass; + try { + aClass = forClass(className, loaders); + } catch (Exception e) { + return null; + } + while (aClass != null) { + try { + if (!descriptor.contains("(")) { + return aClass.isInterface() ? aClass.getField(memberName) : aClass.getDeclaredField(memberName); + } else if (descriptor.contains("init>")) { + return aClass.isInterface() ? aClass.getConstructor(parameterTypes) : aClass.getDeclaredConstructor(parameterTypes); + } else { + return aClass.isInterface() ? aClass.getMethod(memberName, parameterTypes) : aClass.getDeclaredMethod(memberName, parameterTypes); + } + } catch (Exception e) { + aClass = aClass.getSuperclass(); + } + } + return null; + } + + @Nullable + default T forElement(String descriptor, Class resultType, ClassLoader[] loaders) { + Member member = forMember(descriptor, loaders); + //noinspection unchecked + return member != null && member.getClass().equals(resultType) ? (T) member : null; + } + + @Nullable + default Method forMethod(String descriptor, ClassLoader... loaders) throws ReflectionsException { + return forElement(descriptor, Method.class, loaders); + } + + default Constructor forConstructor(String descriptor, ClassLoader... loaders) throws ReflectionsException { + return forElement(descriptor, Constructor.class, loaders); + } + + @Nullable + default Field forField(String descriptor, ClassLoader... loaders) { + return forElement(descriptor, Field.class, loaders); + } + + default Collection forNames(Collection names, Class resultType, ClassLoader... loaders) { + return names.stream().map(name -> forName(name, resultType, loaders)).filter(Objects::nonNull).collect(Collectors.toCollection(LinkedHashSet::new)); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + default Collection> forNames(Collection names, ClassLoader... loaders) { + return forNames(names, (Class) Class.class, loaders); + } +} diff --git a/src/main/java/org/reflections/util/QueryBuilder.java b/src/main/java/org/reflections/util/QueryBuilder.java new file mode 100644 index 00000000..dd15af68 --- /dev/null +++ b/src/main/java/org/reflections/util/QueryBuilder.java @@ -0,0 +1,70 @@ +package org.reflections.util; + +import org.reflections.Store; + +import java.lang.reflect.AnnotatedElement; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.function.Function; + +/** builder for store query

{@code QueryBuilder builder = element -> store -> Set}
*/ +public interface QueryBuilder extends NameHelper { + + default String index() { return getClass().getSimpleName(); } + + /** direct values indexed for {@code key} String + *

safely returns an empty {@code Set} if {@code index/key} not found + *

this is the only function accessing the {@link Store} multimap */ + default QueryFunction get(String key) { + return store -> new LinkedHashSet<>(store.getOrDefault(index(), Collections.emptyMap()).getOrDefault(key, Collections.emptySet())); + } + + // get/getAll/getAllIncluding + /** direct values indexed for {@code AnnotatedElement} */ + default QueryFunction get(AnnotatedElement element) { return get(toName(element)); } + + /** direct values indexed for {@code keys} String collection */ + default QueryFunction get(Collection keys) { return keys.stream().map(this::get).reduce(QueryFunction::add).get(); } + + /** transitive values indexed for {@code keys} String collection, not including {@code keys} */ + default QueryFunction getAll(Collection keys) { return QueryFunction.set(keys).getAll(this::get); } + + /** transitive values indexed for {@code key} String, including {@code key} */ + default QueryFunction getAllIncluding(String key) { return QueryFunction.single(key).add(QueryFunction.single(key).getAll(this::get)); } + + /** transitive values indexed for {@code keys} String collection, including {@code keys} */ + default QueryFunction getAllIncluding(Collection keys) { return QueryFunction.set(keys).add(QueryFunction.set(keys).getAll(this::get)); } + + // of/with syntactics + /** transitive values indexed for {@code keys} String collection, not including {@code keys} */ + default QueryFunction of(Collection keys) { return getAll(keys); } + + /** transitive values indexed for {@code key} String, not including {@code key} */ + default QueryFunction of(String key) { return getAll(Collections.singletonList(key)); } + + /** transitive values indexed for {@code AnnotatedElement} varargs, not including */ + default QueryFunction of(AnnotatedElement... elements) { return getAll(toNames(elements)); } + + /** transitive values indexed for {@code AnnotatedElement} set, not including */ + default QueryFunction of(Set elements) { return getAll(toNames(elements)); } + + /** transitive values indexed for {@code keys} String collection, not including {@code keys}.

same as {@link #of(Collection)} */ + default QueryFunction with(Collection keys) { return of(keys); } + + /** transitive values indexed for {@code key} String, not including {@code key}.

same as {@link #of(String)} */ + default QueryFunction with(String key) { return of(key); } + + /** transitive values indexed for {@code AnnotatedElements} varargs, not including.

same as {@link #of(AnnotatedElement...)} */ + default QueryFunction with(AnnotatedElement... keys) { return of(keys); } + + /** transitive values indexed for {@code AnnotatedElements} set, not including.

same as {@link #of(Set)} */ + default QueryFunction with(Set keys) { return of(keys); } + + // compose QueryFunction + /** transitive {@link QueryFunction#getAll(java.util.function.Function)} values by applying this {@link #get(String)} on each {@code queryFunction} value, including */ + default QueryFunction of(QueryFunction queryFunction) { + return queryFunction.add(queryFunction.getAll((Function>) this::get)); + } +} diff --git a/src/main/java/org/reflections/util/QueryFunction.java b/src/main/java/org/reflections/util/QueryFunction.java new file mode 100644 index 00000000..2941abe0 --- /dev/null +++ b/src/main/java/org/reflections/util/QueryFunction.java @@ -0,0 +1,101 @@ +package org.reflections.util; + +import org.reflections.Store; + +import java.lang.reflect.AnnotatedElement; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * sam function for store query {@code apply(C) -> Set} + *

{@code QueryFunction query = ctx -> ctx.get(key) }
+ *

supports functional composition {@link #filter(Predicate)}, {@link #map(Function)}, {@link #flatMap(Function)}, ... + */ +public interface QueryFunction extends Function>, NameHelper { + /* @inherited */ + Set apply(C ctx); + + static QueryFunction empty() { return ctx -> Collections.emptySet(); } + static QueryFunction single(T element) { return ctx -> Collections.singleton(element); } + static QueryFunction set(Collection elements) { return ctx -> new LinkedHashSet<>(elements); } + + /** filter by predicate

{@code SubTypes.of(type).filter(withPrefix("org"))}
*/ + default QueryFunction filter(Predicate predicate) { + return ctx -> apply(ctx).stream().filter(predicate).collect(Collectors.toCollection(LinkedHashSet::new)); + } + + /** map by function
{@code TypesAnnotated.with(annotation).asClass().map(Annotation::annotationType)}
*/ + default QueryFunction map(Function function) { + return ctx -> apply(ctx).stream().map(function).collect(Collectors.toCollection(LinkedHashSet::new)); + } + + /** flatmap by function
{@code QueryFunction methods = SubTypes.of(type).asClass().flatMap(Methods::of)}
*/ + default QueryFunction flatMap(Function>> function) { + return ctx -> apply(ctx).stream().flatMap(t -> function.apply(t).apply(ctx).stream()).collect(Collectors.toCollection(LinkedHashSet::new)); + } + + /** transitively get all by {@code builder}
{@code SuperTypes.of(type).getAll(Annotations::get)}
*/ + default QueryFunction getAll(Function> builder) { + return getAll(builder, t -> t); + } + + /** transitively get all by {@code builder}
{@code SuperTypes.of(type).getAll(Annotations::get)}
*/ + default QueryFunction getAll(Function> builder, Function traverse) { + return ctx -> { + List workKeys = new ArrayList<>(apply(ctx)); + Set result = new LinkedHashSet<>(); + for (int i = 0; i < workKeys.size(); i++) { + T key = workKeys.get(i); + Set apply = builder.apply(key).apply(ctx); + for (R r : apply) if (result.add(r)) workKeys.add(traverse.apply(r)); + } + return result; + }; + } + + /** concat elements from function
{@code Annotations.of(method).add(Annotations.of(type))}
*/ + default QueryFunction add(QueryFunction function) { + return ctx -> Stream.of(apply(ctx), function.apply(ctx)) + .flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new)); + } + + /** convert to given {@code type}, uses {@link NameHelper#forName(java.lang.String, java.lang.Class, java.lang.ClassLoader...)} + *
{@code Methods.of(type).as(Method.class)}
*/ + default QueryFunction as(Class type, ClassLoader... loaders) { + return ctx -> { + Set apply = apply(ctx); + //noinspection unchecked + return (Set) apply.stream().findFirst().map(first -> + type.isAssignableFrom(first.getClass()) ? apply : + first instanceof String ? ((Set) forNames((Collection) apply, type, loaders)) : + first instanceof AnnotatedElement ? ((Set) forNames(toNames((Collection) apply), type, loaders)) : + apply.stream().map(t -> (R) t).collect(Collectors.toCollection(LinkedHashSet::new)) + ).orElse(apply); + }; + } + + /** convert elements to {@code Class} using {@link NameHelper#forName(java.lang.String, java.lang.Class, java.lang.ClassLoader...)} + *
{@code SubTypes.of(type).asClass()}
*/ + default QueryFunction> asClass(ClassLoader... loaders) { + // noinspection unchecked + return ctx -> ((Set>) forNames((Set) apply(ctx), Class.class, loaders)); + } + + /** convert elements to String using {@link NameHelper#toName(AnnotatedElement)}*/ + default QueryFunction asString() { + return ctx -> new LinkedHashSet<>(toNames((AnnotatedElement) apply(ctx))); + } + + /** cast elements as {@code } unsafe */ + default QueryFunction> as() { + return ctx -> new LinkedHashSet<>((Set>) apply(ctx)); + } +} diff --git a/src/main/java/org/reflections/util/ReflectionUtilsPredicates.java b/src/main/java/org/reflections/util/ReflectionUtilsPredicates.java new file mode 100644 index 00000000..cc83aee8 --- /dev/null +++ b/src/main/java/org/reflections/util/ReflectionUtilsPredicates.java @@ -0,0 +1,234 @@ +package org.reflections.util; + +import org.reflections.ReflectionUtils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collection; +import java.util.Set; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.IntStream; + +import static java.util.stream.Collectors.toSet; + +/** helper predicates for java meta types*/ +public class ReflectionUtilsPredicates { + /** + * where member name equals given {@code name} + */ + public static Predicate withName(final String name) { + return input -> input != null && input.getName().equals(name); + } + + /** + * where member name startsWith given {@code prefix} + */ + public static Predicate withPrefix(final String prefix) { + return input -> input != null && input.getName().startsWith(prefix); + } + + /** + * where member's {@code toString} matches given {@code regex} + *
 get(Methods.of(someClass).filter(withPattern("public void .*"))) 
+ */ + public static Predicate withPattern(final String regex) { + return input -> Pattern.matches(regex, input.toString()); + } + + /** + * where element is annotated with given {@code annotation} + */ + public static Predicate withAnnotation(final Class annotation) { + return input -> input != null && input.isAnnotationPresent(annotation); + } + + /** + * where element is annotated with given {@code annotations} + */ + public static Predicate withAnnotations(final Class... annotations) { + return input -> input != null && Arrays.equals(annotations, ReflectionUtilsPredicates.annotationTypes(input.getAnnotations())); + } + + /** + * where element is annotated with given {@code annotation}, including member matching + */ + public static Predicate withAnnotation(final Annotation annotation) { + return input -> input != null && input.isAnnotationPresent(annotation.annotationType()) && + ReflectionUtilsPredicates.areAnnotationMembersMatching(input.getAnnotation(annotation.annotationType()), annotation); + } + + /** + * where element is annotated with given {@code annotations}, including member matching + */ + public static Predicate withAnnotations(final Annotation... annotations) { + return input -> { + if (input != null) { + Annotation[] inputAnnotations = input.getAnnotations(); + if (inputAnnotations.length == annotations.length) { + return IntStream.range(0, inputAnnotations.length) + .allMatch(i -> ReflectionUtilsPredicates.areAnnotationMembersMatching(inputAnnotations[i], annotations[i])); + } + } + return true; + }; + } + + /** + * when method/constructor parameter types equals given {@code types} + */ + public static Predicate withParameters(final Class... types) { + return input -> Arrays.equals(ReflectionUtilsPredicates.parameterTypes(input), types); + } + + /** + * when member parameter types assignable to given {@code types} + */ + public static Predicate withParametersAssignableTo(final Class... types) { + return input -> ReflectionUtilsPredicates.isAssignable(types, ReflectionUtilsPredicates.parameterTypes(input)); + } + + /** + * when method/constructor parameter types assignable from given {@code types} + */ + public static Predicate withParametersAssignableFrom(final Class... types) { + return input -> ReflectionUtilsPredicates.isAssignable(ReflectionUtilsPredicates.parameterTypes(input), types); + } + + /** + * when method/constructor parameters count equal given {@code count} + */ + public static Predicate withParametersCount(final int count) { + return input -> input != null && ReflectionUtilsPredicates.parameterTypes(input).length == count; + } + + /** + * when method/constructor has any parameter with an annotation matches given {@code annotations} + */ + public static Predicate withAnyParameterAnnotation(final Class annotationClass) { + return input -> input != null && ReflectionUtilsPredicates.annotationTypes(ReflectionUtilsPredicates.parameterAnnotations(input)).stream().anyMatch(input1 -> input1.equals(annotationClass)); + } + + /** + * when method/constructor has any parameter with an annotation matches given {@code annotations}, including member matching + */ + public static Predicate withAnyParameterAnnotation(final Annotation annotation) { + return input -> input != null && ReflectionUtilsPredicates.parameterAnnotations(input).stream().anyMatch(input1 -> ReflectionUtilsPredicates.areAnnotationMembersMatching(annotation, input1)); + } + + /** + * when field type equal given {@code type} + */ + public static Predicate withType(final Class type) { + return input -> input != null && input.getType().equals(type); + } + + /** + * when field type assignable to given {@code type} + */ + public static Predicate withTypeAssignableTo(final Class type) { + return input -> input != null && type.isAssignableFrom(input.getType()); + } + + /** + * when method return type equal given {@code type} + */ + public static Predicate withReturnType(final Class type) { + return input -> input != null && input.getReturnType().equals(type); + } + + /** + * when method return type assignable from given {@code type} + */ + public static Predicate withReturnTypeAssignableFrom(final Class type) { + return input -> input != null && type.isAssignableFrom(input.getReturnType()); + } + + /** + * when member modifier matches given {@code mod} + *

for example: + *

+	 * withModifier(Modifier.PUBLIC)
+	 * 
+ */ + public static Predicate withModifier(final int mod) { + return input -> input != null && (input.getModifiers() & mod) != 0; + } + + /** + * when member modifier is public + */ + public static Predicate withPublic() { + return ReflectionUtilsPredicates.withModifier(Modifier.PUBLIC); + } + + public static Predicate withStatic() { + return ReflectionUtilsPredicates.withModifier(Modifier.STATIC); + } + + public static Predicate withInterface() { + return ReflectionUtilsPredicates.withModifier(Modifier.INTERFACE); + } + + /** + * when class modifier matches given {@code mod} + *

for example: + *

+	 * withModifier(Modifier.PUBLIC)
+	 * 
+ */ + public static Predicate> withClassModifier(final int mod) { + return input -> input != null && (input.getModifiers() & mod) != 0; + } + + public static boolean isAssignable(Class[] childClasses, Class[] parentClasses) { + if (childClasses == null || childClasses.length == 0) { + return parentClasses == null || parentClasses.length == 0; + } + if (childClasses.length != parentClasses.length) { + return false; + } + return IntStream.range(0, childClasses.length) + .noneMatch(i -> !parentClasses[i].isAssignableFrom(childClasses[i]) || + parentClasses[i] == Object.class && childClasses[i] != Object.class); + } + + // + private static Class[] parameterTypes(Member member) { + return member != null ? + member.getClass() == Method.class ? ((Method) member).getParameterTypes() : + member.getClass() == Constructor.class ? ((Constructor) member).getParameterTypes() : null : null; + } + + private static Set parameterAnnotations(Member member) { + Annotation[][] annotations = + member instanceof Method ? ((Method) member).getParameterAnnotations() : + member instanceof Constructor ? ((Constructor) member).getParameterAnnotations() : null; + return Arrays.stream(annotations != null ? annotations : new Annotation[0][]).flatMap(Arrays::stream).collect(toSet()); + } + + private static Set> annotationTypes(Collection annotations) { + return annotations.stream().map(Annotation::annotationType).collect(toSet()); + } + + private static Class[] annotationTypes(Annotation[] annotations) { + return Arrays.stream(annotations).map(Annotation::annotationType).toArray(Class[]::new); + } + + private static boolean areAnnotationMembersMatching(Annotation annotation1, Annotation annotation2) { + if (annotation2 != null && annotation1.annotationType() == annotation2.annotationType()) { + for (Method method : annotation1.annotationType().getDeclaredMethods()) { + if (!ReflectionUtils.invoke(method, annotation1).equals(ReflectionUtils.invoke(method, annotation2))) + return false; + } + return true; + } + return false; + } +} diff --git a/src/main/java/org/reflections/util/UtilQueryBuilder.java b/src/main/java/org/reflections/util/UtilQueryBuilder.java new file mode 100644 index 00000000..91d974e2 --- /dev/null +++ b/src/main/java/org/reflections/util/UtilQueryBuilder.java @@ -0,0 +1,35 @@ +package org.reflections.util; + +import org.reflections.ReflectionUtils; +import org.reflections.Store; + +import java.lang.reflect.AnnotatedElement; +import java.util.LinkedHashSet; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * query builder for {@link QueryFunction} + *
{@code UtilQueryBuilder builder =
+ *   element -> store -> element.getDeclaredAnnotations()} 
+ */ +public interface UtilQueryBuilder extends NameHelper { + /** get direct values of given element */ + QueryFunction get(F element); + + /** get transitive values of given element */ + default QueryFunction of(final F element) { + return of(ReflectionUtils.>extendType().get((AnnotatedElement) element)); + } + + /** get transitive value of given element filtered by predicate */ + default QueryFunction of(final F element, Predicate predicate) { + return of(element).filter(predicate); + } + + /** compose given function */ + default QueryFunction of(QueryFunction function) { + return store -> function.apply(store).stream() + .flatMap(t -> get((F) t).apply(store).stream()).collect(Collectors.toCollection(LinkedHashSet::new)); + } +} diff --git a/src/main/java/org/reflections/util/Utils.java b/src/main/java/org/reflections/util/Utils.java deleted file mode 100644 index fe55f2ca..00000000 --- a/src/main/java/org/reflections/util/Utils.java +++ /dev/null @@ -1,210 +0,0 @@ -package org.reflections.util; - -import org.reflections.Reflections; -import org.reflections.ReflectionsException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Member; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static org.reflections.ReflectionUtils.forName; - -/** - * a garbage can of convenient methods - */ -public abstract class Utils { - - public static String repeat(String string, int times) { - return IntStream.range(0, times).mapToObj(i -> string).collect(Collectors.joining()); - } - - /** - * isEmpty compatible with Java 5 - */ - public static boolean isEmpty(String s) { - return s == null || s.length() == 0; - } - - public static File prepareFile(String filename) { - File file = new File(filename); - File parent = file.getAbsoluteFile().getParentFile(); - if (!parent.exists()) { - //noinspection ResultOfMethodCallIgnored - parent.mkdirs(); - } - return file; - } - - public static Member getMemberFromDescriptor(String descriptor, ClassLoader... classLoaders) throws ReflectionsException { - int p0 = descriptor.lastIndexOf('('); - String memberKey = p0 != -1 ? descriptor.substring(0, p0) : descriptor; - String methodParameters = p0 != -1 ? descriptor.substring(p0 + 1, descriptor.lastIndexOf(')')) : ""; - - int p1 = Math.max(memberKey.lastIndexOf('.'), memberKey.lastIndexOf("$")); - String className = memberKey.substring(0, p1); - String memberName = memberKey.substring(p1 + 1); - - Class[] parameterTypes = null; - if (!isEmpty(methodParameters)) { - String[] parameterNames = methodParameters.split(","); - parameterTypes = Arrays.stream(parameterNames).map(name -> forName(name.trim(), classLoaders)).toArray(Class[]::new); - } - - Class aClass = forName(className, classLoaders); - while (aClass != null) { - try { - if (!descriptor.contains("(")) { - return aClass.isInterface() ? aClass.getField(memberName) : aClass.getDeclaredField(memberName); - } else if (isConstructor(descriptor)) { - return aClass.isInterface() ? aClass.getConstructor(parameterTypes) : aClass.getDeclaredConstructor(parameterTypes); - } else { - return aClass.isInterface() ? aClass.getMethod(memberName, parameterTypes) : aClass.getDeclaredMethod(memberName, parameterTypes); - } - } catch (Exception e) { - aClass = aClass.getSuperclass(); - } - } - throw new ReflectionsException("Can't resolve member named " + memberName + " for class " + className); - } - - public static Set getMethodsFromDescriptors(Iterable annotatedWith, ClassLoader... classLoaders) { - Set result = new HashSet<>(); - for (String annotated : annotatedWith) { - if (!isConstructor(annotated)) { - Method member = (Method) getMemberFromDescriptor(annotated, classLoaders); - if (member != null) result.add(member); - } - } - return result; - } - - public static Set getConstructorsFromDescriptors(Iterable annotatedWith, ClassLoader... classLoaders) { - Set result = new HashSet<>(); - for (String annotated : annotatedWith) { - if (isConstructor(annotated)) { - Constructor member = (Constructor) getMemberFromDescriptor(annotated, classLoaders); - if (member != null) result.add(member); - } - } - return result; - } - - public static Set getMembersFromDescriptors(Iterable values, ClassLoader... classLoaders) { - Set result = new HashSet<>(); - for (String value : values) { - try { - result.add(Utils.getMemberFromDescriptor(value, classLoaders)); - } catch (ReflectionsException e) { - throw new ReflectionsException("Can't resolve member named " + value, e); - } - } - return result; - } - - public static Field getFieldFromString(String field, ClassLoader... classLoaders) { - String className = field.substring(0, field.lastIndexOf('.')); - String fieldName = field.substring(field.lastIndexOf('.') + 1); - - try { - return forName(className, classLoaders).getDeclaredField(fieldName); - } catch (NoSuchFieldException e) { - throw new ReflectionsException("Can't resolve field named " + fieldName, e); - } - } - - public static void close(InputStream closeable) { - try { if (closeable != null) closeable.close(); } - catch (IOException e) { - if (Reflections.log != null) { - Reflections.log.warn("Could not close InputStream", e); - } - } - } - - public static Logger findLogger(Class aClass) { - try { - // This is to check whether an optional SLF4J binding is available. While SLF4J recommends that libraries - // "should not declare a dependency on any SLF4J binding but only depend on slf4j-api", doing so forces - // users of the library to either add a binding to the classpath (even if just slf4j-nop) or to set the - // "slf4j.suppressInitError" system property in order to avoid the warning, which both is inconvenient. - Class.forName("org.slf4j.impl.StaticLoggerBinder"); - return LoggerFactory.getLogger(aClass); - } catch (Throwable e) { - return null; - } - } - - public static boolean isConstructor(String fqn) { - return fqn.contains("init>"); - } - - public static String name(Class type) { - if (!type.isArray()) { - return type.getName(); - } else { - int dim = 0; - while (type.isArray()) { - dim++; - type = type.getComponentType(); - } - return type.getName() + repeat("[]", dim); - } - } - - - public static List names(Collection> types) { - return types.stream().map(Utils::name).collect(Collectors.toList()); - } - - public static List names(Class... types) { - return names(Arrays.asList(types)); - } - - public static String name(Constructor constructor) { - return constructor.getName() + "." + "" + "(" + join(names(constructor.getParameterTypes()), ", ") + ")"; - } - - public static String name(Method method) { - return method.getDeclaringClass().getName() + "." + method.getName() + "(" + join(names(method.getParameterTypes()), ", ") + ")"; - } - - public static String name(Field field) { - return field.getDeclaringClass().getName() + "." + field.getName(); - } - - public static String index(Class scannerClass) { return scannerClass.getSimpleName(); } - - public static Predicate and(Predicate... predicates) { - return Arrays.stream(predicates).reduce(t -> true, Predicate::and); - } - - public static String join(Collection elements, String delimiter) { - return elements.stream().map(Object::toString).collect(Collectors.joining(delimiter)); - } - - public static Set filter(Collection result, Predicate... predicates) { - return result.stream().filter(and(predicates)).collect(Collectors.toSet()); - } - - public static Set filter(Collection result, Predicate predicate) { - return result.stream().filter(predicate).collect(Collectors.toSet()); - } - - public static Set filter(T[] result, Predicate... predicates) { - return Arrays.stream(result).filter(and(predicates)).collect(Collectors.toSet()); - } -} diff --git a/src/main/java/org/reflections/vfs/JarInputDir.java b/src/main/java/org/reflections/vfs/JarInputDir.java index 01917287..b5d61682 100644 --- a/src/main/java/org/reflections/vfs/JarInputDir.java +++ b/src/main/java/org/reflections/vfs/JarInputDir.java @@ -1,17 +1,15 @@ package org.reflections.vfs; +import org.reflections.Reflections; import org.reflections.ReflectionsException; -import org.reflections.util.Utils; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.Iterator; import java.util.jar.JarInputStream; import java.util.zip.ZipEntry; -/** - * - */ public class JarInputDir implements Vfs.Dir { private final URL url; JarInputStream jarInputStream; @@ -28,7 +26,6 @@ public String getPath() { public Iterable getFiles() { return () -> new Iterator() { - { try { jarInputStream = new JarInputStream(url.openConnection().getInputStream()); } catch (Exception e) { throw new ReflectionsException("Could not open url connection", e); } @@ -71,6 +68,11 @@ private Vfs.File computeNext() { } public void close() { - Utils.close(jarInputStream); + try { if (jarInputStream != null) ((InputStream) jarInputStream).close(); } + catch (IOException e) { + if (Reflections.log != null) { + Reflections.log.warn("Could not close InputStream", e); + } + } } } diff --git a/src/main/java/org/reflections/vfs/JarInputFile.java b/src/main/java/org/reflections/vfs/JarInputFile.java index 4c6ed6fc..8e6a7eab 100644 --- a/src/main/java/org/reflections/vfs/JarInputFile.java +++ b/src/main/java/org/reflections/vfs/JarInputFile.java @@ -29,7 +29,7 @@ public String getRelativePath() { return entry.getName(); } - public InputStream openInputStream() throws IOException { + public InputStream openInputStream() { return new InputStream() { @Override public int read() throws IOException { diff --git a/src/main/java/org/reflections/vfs/JbossDir.java b/src/main/java/org/reflections/vfs/JbossDir.java index e7fbb384..a777003f 100644 --- a/src/main/java/org/reflections/vfs/JbossDir.java +++ b/src/main/java/org/reflections/vfs/JbossDir.java @@ -2,7 +2,6 @@ import org.jboss.vfs.VirtualFile; -import java.io.IOException; import java.net.URL; import java.util.Iterator; import java.util.Stack; @@ -12,7 +11,7 @@ public class JbossDir implements Vfs.Dir { private final VirtualFile virtualFile; - private JbossDir(VirtualFile virtualFile) throws IOException { + private JbossDir(VirtualFile virtualFile) { this.virtualFile = virtualFile; } @@ -61,15 +60,8 @@ private Vfs.File computeNext() { return new JbossFile(JbossDir.this, file); } } - return null; } }; } - - @Override - public void close() { - - } - } diff --git a/src/main/java/org/reflections/vfs/SystemDir.java b/src/main/java/org/reflections/vfs/SystemDir.java index 32c9fb75..3d85d07e 100644 --- a/src/main/java/org/reflections/vfs/SystemDir.java +++ b/src/main/java/org/reflections/vfs/SystemDir.java @@ -17,21 +17,15 @@ public SystemDir(File file) { if (file != null && (!file.isDirectory() || !file.canRead())) { throw new RuntimeException("cannot use dir " + file); } - this.file = file; } public String getPath() { - if (file == null) { - return "/NO-SUCH-DIRECTORY/"; - } - return file.getPath().replace("\\", "/"); + return file != null ? file.getPath().replace("\\", "/") : "/NO-SUCH-DIRECTORY/"; } public Iterable getFiles() { - if (file == null || !file.exists()) { - return Collections.emptyList(); - } + if (file == null || !file.exists()) return Collections.emptyList(); return () -> { try { return Files.walk(file.toPath()) @@ -43,12 +37,4 @@ public Iterable getFiles() { } }; } - - public void close() { - } - - @Override - public String toString() { - return getPath(); - } } diff --git a/src/main/java/org/reflections/vfs/UrlTypeVFS.java b/src/main/java/org/reflections/vfs/UrlTypeVFS.java index 116e1547..d568d16d 100644 --- a/src/main/java/org/reflections/vfs/UrlTypeVFS.java +++ b/src/main/java/org/reflections/vfs/UrlTypeVFS.java @@ -50,7 +50,7 @@ public Dir createDir(final URL url) { public URL adaptURL(URL url) throws MalformedURLException { if (VFSZIP.equals(url.getProtocol())) { - return replaceZipSeparators(url.getPath(), realFile); + return replaceZipSeparators(url.getPath(), file -> file.exists() && file.isFile()); } else if (VFSFILE.equals(url.getProtocol())) { return new URL(url.toString().replace(VFSFILE, "file")); } else { @@ -83,8 +83,6 @@ int findFirstMatchOfDeployableExtention(String path, int pos) { } } - Predicate realFile = file -> file.exists() && file.isFile(); - URL replaceZipSeparatorStartingFrom(String path, int pos) throws MalformedURLException { String zipFile = path.substring(0, pos - 1); diff --git a/src/main/java/org/reflections/vfs/Vfs.java b/src/main/java/org/reflections/vfs/Vfs.java index e2b1a4e2..a64732a4 100644 --- a/src/main/java/org/reflections/vfs/Vfs.java +++ b/src/main/java/org/reflections/vfs/Vfs.java @@ -3,7 +3,6 @@ import org.reflections.Reflections; import org.reflections.ReflectionsException; import org.reflections.util.ClasspathHelper; -import org.reflections.util.Utils; import java.io.IOException; import java.io.InputStream; @@ -60,7 +59,7 @@ public abstract class Vfs { public interface Dir { String getPath(); Iterable getFiles(); - void close(); + default void close() {} } /** an abstract vfs file */ @@ -128,12 +127,11 @@ public static Iterable findFiles(final Collection inUrls, final Strin String path = file.getRelativePath(); if (path.startsWith(packagePrefix)) { String filename = path.substring(path.indexOf(packagePrefix) + packagePrefix.length()); - return !Utils.isEmpty(filename) && nameFilter.test(filename.substring(1)); + return !filename.isEmpty() && nameFilter.test(filename.substring(1)); } else { return false; } }; - return findFiles(inUrls, fileNamePredicate); } @@ -149,8 +147,7 @@ public static Iterable findFiles(final Collection urls, final Predica } return Stream.of(); } - }) - .filter(filePredicate).iterator(); + }).filter(filePredicate).iterator(); } /**try to get {@link java.io.File} from url*/ @@ -191,7 +188,7 @@ public static java.io.File getFile(URL url) { } private static boolean hasJarFileInPath(URL url) { - return url.toExternalForm().matches(".*\\.jar(\\!.*|$)"); + return url.toExternalForm().matches(".*\\.jar(!.*|$)"); } /** default url types used by {@link org.reflections.vfs.Vfs#fromURL(java.net.URL)} diff --git a/src/main/java/org/reflections/vfs/ZipDir.java b/src/main/java/org/reflections/vfs/ZipDir.java index 0414d12c..88d79d1d 100644 --- a/src/main/java/org/reflections/vfs/ZipDir.java +++ b/src/main/java/org/reflections/vfs/ZipDir.java @@ -14,10 +14,7 @@ public ZipDir(JarFile jarFile) { } public String getPath() { - if (jarFile == null) { - return "/NO-SUCH-DIRECTORY/"; - } - return jarFile.getName().replace("\\", "/"); + return jarFile != null ? jarFile.getName().replace("\\", "/") : "/NO-SUCH-DIRECTORY/"; } public Iterable getFiles() { diff --git a/src/test/java/org/reflections/ClasspathHelperTest.java b/src/test/java/org/reflections/ClasspathHelperTest.java index e6924f11..5ab35427 100644 --- a/src/test/java/org/reflections/ClasspathHelperTest.java +++ b/src/test/java/org/reflections/ClasspathHelperTest.java @@ -1,7 +1,6 @@ package org.reflections; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.reflections.util.ClasspathHelper; import java.net.MalformedURLException; @@ -12,6 +11,8 @@ import java.util.Collections; import java.util.List; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + /** * Test ClasspathHelper utility class */ @@ -22,14 +23,14 @@ public void testForClassLoaderShouldntReorderUrls() throws MalformedURLException final URL[] urls1 = {new URL("file", "foo", 1111, "foo"), new URL("file", "bar", 1111, "bar"),new URL("file", "baz", 1111, "baz")}; final List urlsList2 = Arrays.asList(urls1); Collections.reverse(urlsList2); - final URL[] urls2 = urlsList2.toArray(new URL[urlsList2.size()]); + final URL[] urls2 = urlsList2.toArray(new URL[0]); final URLClassLoader urlClassLoader1 = new URLClassLoader(urls1, null); final URLClassLoader urlClassLoader2 = new URLClassLoader(urls2, null); final Collection resultUrls1 = ClasspathHelper.forClassLoader(urlClassLoader1); final Collection resultUrls2 = ClasspathHelper.forClassLoader(urlClassLoader2); - Assert.assertArrayEquals("URLs returned from forClassLoader should be in the same order as source URLs", urls1, resultUrls1.toArray()); - Assert.assertArrayEquals("URLs returned from forClassLoader should be in the same order as source URLs", urls2, resultUrls2.toArray()); + assertArrayEquals(urls1, resultUrls1.toArray(), "URLs returned from forClassLoader should be in the same order as source URLs"); + assertArrayEquals(urls2, resultUrls2.toArray(), "URLs returned from forClassLoader should be in the same order as source URLs"); } } diff --git a/src/test/java/org/reflections/CombinedTestModel.java b/src/test/java/org/reflections/CombinedTestModel.java new file mode 100644 index 00000000..a6062b74 --- /dev/null +++ b/src/test/java/org/reflections/CombinedTestModel.java @@ -0,0 +1,47 @@ +package org.reflections; + +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +interface CombinedTestModel { + @Retention(RUNTIME) + @interface Alias { String value();} + + @Retention(RUNTIME) + @interface Requests { + Request[] value(); + } + + @Retention(RUNTIME) + @Repeatable(Requests.class) + @interface Request { + @Alias("path") String value() default ""; + String method() default ""; + } + + @Retention(RUNTIME) @Request(method = "Get") + @interface Get { String value();} + + @Retention(RUNTIME) @Request(method = "Post") + @interface Post { String value();} + + + @Request("/base") + interface Controller { + @Get("/get") void get(); + @Post("/post") + void post(Object object); + } + + abstract class Abstract implements Controller { + @Override public void get() {} + } + + class Impl extends Abstract { + @Requests({@Request(method = "PUT", value = "/another"), + @Request(method = "PATCH", value = "/another")}) + @Override public void post(Object object) {} + } +} diff --git a/src/test/java/org/reflections/ConfigurationBuilderTest.java b/src/test/java/org/reflections/ConfigurationBuilderTest.java new file mode 100644 index 00000000..23f36dfb --- /dev/null +++ b/src/test/java/org/reflections/ConfigurationBuilderTest.java @@ -0,0 +1,48 @@ +package org.reflections; + +import org.junit.jupiter.api.Test; +import org.reflections.scanners.Scanners; +import org.reflections.util.ClasspathHelper; +import org.reflections.util.ConfigurationBuilder; +import org.reflections.util.FilterBuilder; + +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.function.Predicate; + +import static org.junit.jupiter.api.Assertions.*; + +public class ConfigurationBuilderTest { + + @Test + public void buildForConfig() { + assertConfig(ConfigurationBuilder.build("org.reflections"), + ClasspathHelper.forPackage("org.reflections"), + new FilterBuilder().includePackage("org.reflections")); + + assertConfig(ConfigurationBuilder.build("org"), + ClasspathHelper.forPackage("org"), + new FilterBuilder().includePackage("org")); + } + + @Test + public void buildFor() { + assertThrows(ReflectionsException.class, () -> ConfigurationBuilder.build("")); + + assertConfig(ConfigurationBuilder.build(), + ClasspathHelper.forClassLoader(), + new FilterBuilder()); + + assertConfig(ConfigurationBuilder.build("not.exist"), + ClasspathHelper.forClassLoader(), + new FilterBuilder().includePackage("not.exist")); + } + + private void assertConfig(ConfigurationBuilder config, Collection urls, Predicate inputsFilter) { + assertEquals(config.getUrls(), new HashSet<>(urls)); + assertEquals(config.getInputsFilter(), inputsFilter); + assertEquals(config.getScanners(), new HashSet<>(Arrays.asList(Scanners.SubTypes, Scanners.TypesAnnotated))); + } +} \ No newline at end of file diff --git a/src/test/java/org/reflections/FilterBuilderTest.java b/src/test/java/org/reflections/FilterBuilderTest.java index de813dd4..f4f49ef4 100644 --- a/src/test/java/org/reflections/FilterBuilderTest.java +++ b/src/test/java/org/reflections/FilterBuilderTest.java @@ -1,173 +1,54 @@ package org.reflections; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.reflections.util.FilterBuilder; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -/** - * Test filtering - */ public class FilterBuilderTest { - @Test - public void test_include() { - FilterBuilder filter = new FilterBuilder().include("org\\.reflections.*"); - assertTrue(filter.test("org.reflections.Reflections")); - assertTrue(filter.test("org.reflections.foo.Reflections")); - assertFalse(filter.test("org.foobar.Reflections")); - } - - @Test - public void test_includePackage() { - FilterBuilder filter = new FilterBuilder().includePackage("org.reflections"); - assertTrue(filter.test("org.reflections.Reflections")); - assertTrue(filter.test("org.reflections.foo.Reflections")); - assertFalse(filter.test("org.foobar.Reflections")); - } - - @Test - public void test_includePackageMultiple() { - FilterBuilder filter = new FilterBuilder().includePackage("org.reflections", "org.foo"); - assertTrue(filter.test("org.reflections.Reflections")); - assertTrue(filter.test("org.reflections.foo.Reflections")); - assertTrue(filter.test("org.foo.Reflections")); - assertTrue(filter.test("org.foo.bar.Reflections")); - assertFalse(filter.test("org.bar.Reflections")); - } - - @Test - public void test_includePackagebyClass() { - FilterBuilder filter = new FilterBuilder().includePackage(Reflections.class); - assertTrue(filter.test("org.reflections.Reflections")); - assertTrue(filter.test("org.reflections.foo.Reflections")); - assertFalse(filter.test("org.foobar.Reflections")); - } - - //----------------------------------------------------------------------- - @Test - public void test_exclude() { - FilterBuilder filter = new FilterBuilder().exclude("org\\.reflections.*"); - assertFalse(filter.test("org.reflections.Reflections")); - assertFalse(filter.test("org.reflections.foo.Reflections")); - assertTrue(filter.test("org.foobar.Reflections")); - } - - @Test - public void test_excludePackage() { - FilterBuilder filter = new FilterBuilder().excludePackage("org.reflections"); - assertFalse(filter.test("org.reflections.Reflections")); - assertFalse(filter.test("org.reflections.foo.Reflections")); - assertTrue(filter.test("org.foobar.Reflections")); - } - - @Test - public void test_excludePackageMultiple() { - FilterBuilder filter = new FilterBuilder().excludePackage("org.reflections", "org.foo"); - assertFalse(filter.test("org.reflections.Reflections")); - assertFalse(filter.test("org.reflections.foo.Reflections")); - assertFalse(filter.test("org.foo.Reflections")); - assertFalse(filter.test("org.foo.bar.Reflections")); - assertTrue(filter.test("org.bar.Reflections")); - } - - @Test - public void test_excludePackageByClass() { - FilterBuilder filter = new FilterBuilder().excludePackage(Reflections.class); - assertFalse(filter.test("org.reflections.Reflections")); - assertFalse(filter.test("org.reflections.foo.Reflections")); - assertTrue(filter.test("org.foobar.Reflections")); - } - - //----------------------------------------------------------------------- @Test - public void test_parse_include() { - FilterBuilder filter = FilterBuilder.parse("+org.reflections.*"); - assertTrue(filter.test("org.reflections.Reflections")); - assertTrue(filter.test("org.reflections.foo.Reflections")); - assertFalse(filter.test("org.foobar.Reflections")); - assertTrue(filter.test("org.reflectionsplus.Reflections")); - } + public void includeExcludePackage() { + FilterBuilder filter = new FilterBuilder() + .includePackage("org.reflections") + .excludePackage("org.reflections.exclude") + .includePackage("org.foo"); - @Test - public void test_parse_include_notRegex() { - FilterBuilder filter = FilterBuilder.parse("+org.reflections"); - assertFalse(filter.test("org.reflections.Reflections")); - assertFalse(filter.test("org.reflections.foo.Reflections")); - assertFalse(filter.test("org.foobar.Reflections")); - assertFalse(filter.test("org.reflectionsplus.Reflections")); + doAssert(filter); } @Test - public void test_parse_exclude() { - FilterBuilder filter = FilterBuilder.parse("-org.reflections.*"); - assertFalse(filter.test("org.reflections.Reflections")); - assertFalse(filter.test("org.reflections.foo.Reflections")); - assertTrue(filter.test("org.foobar.Reflections")); - assertFalse(filter.test("org.reflectionsplus.Reflections")); - } + public void parsePackages() { + FilterBuilder filter = FilterBuilder + .parsePackages("+org.reflections , -org.reflections.exclude,+org.foo"); // not trimmed - @Test - public void test_parse_exclude_notRegex() { - FilterBuilder filter = FilterBuilder.parse("-org.reflections"); - assertTrue(filter.test("org.reflections.Reflections")); - assertTrue(filter.test("org.reflections.foo.Reflections")); - assertTrue(filter.test("org.foobar.Reflections")); - assertTrue(filter.test("org.reflectionsplus.Reflections")); + doAssert(filter); } @Test - public void test_parse_include_exclude() { - FilterBuilder filter = FilterBuilder.parse("+org.reflections.*, -org.reflections.foo.*"); - assertTrue(filter.test("org.reflections.Reflections")); - assertFalse(filter.test("org.reflections.foo.Reflections")); - assertFalse(filter.test("org.foobar.Reflections")); - } + public void includeExcludePattern() { + FilterBuilder filter = new FilterBuilder() + .includePattern("org\\.reflections\\..*") + .excludePattern("org\\.reflections\\.exclude\\..*") + .includePattern("org\\.foo\\..*"); - //----------------------------------------------------------------------- - @Test - public void test_parsePackages_include() { - FilterBuilder filter = FilterBuilder.parsePackages("+org.reflections"); - assertTrue(filter.test("org.reflections.Reflections")); - assertTrue(filter.test("org.reflections.foo.Reflections")); - assertFalse(filter.test("org.foobar.Reflections")); - assertFalse(filter.test("org.reflectionsplus.Reflections")); + doAssert(filter); } - @Test - public void test_parsePackages_include_trailingDot() { - FilterBuilder filter = FilterBuilder.parsePackages("+org.reflections."); + private void doAssert(FilterBuilder filter) { + assertFalse(filter.test("")); + assertFalse(filter.test("org")); + assertFalse(filter.test("org.")); + assertFalse(filter.test("org.reflections")); + assertTrue(filter.test("org.reflections.")); assertTrue(filter.test("org.reflections.Reflections")); assertTrue(filter.test("org.reflections.foo.Reflections")); - assertFalse(filter.test("org.foobar.Reflections")); - assertFalse(filter.test("org.reflectionsplus.Reflections")); - } - - @Test - public void test_parsePackages_exclude() { - FilterBuilder filter = FilterBuilder.parsePackages("-org.reflections"); - assertFalse(filter.test("org.reflections.Reflections")); - assertFalse(filter.test("org.reflections.foo.Reflections")); - assertTrue(filter.test("org.foobar.Reflections")); - assertTrue(filter.test("org.reflectionsplus.Reflections")); - } - - @Test - public void test_parsePackages_exclude_trailingDot() { - FilterBuilder filter = FilterBuilder.parsePackages("-org.reflections."); - assertFalse(filter.test("org.reflections.Reflections")); - assertFalse(filter.test("org.reflections.foo.Reflections")); - assertTrue(filter.test("org.foobar.Reflections")); - assertTrue(filter.test("org.reflectionsplus.Reflections")); - } - - @Test - public void test_parsePackages_include_exclude() { - FilterBuilder filter = FilterBuilder.parsePackages("+org.reflections, -org.reflections.foo"); - assertTrue(filter.test("org.reflections.Reflections")); - assertFalse(filter.test("org.reflections.foo.Reflections")); - assertFalse(filter.test("org.foobar.Reflections")); + assertFalse(filter.test("org.reflections.exclude.it")); + assertFalse(filter.test("org.foo")); + assertTrue(filter.test("org.foo.")); + assertTrue(filter.test("org.foo.bar")); + assertFalse(filter.test("org.bar.Reflections")); } } diff --git a/src/test/java/org/reflections/JavaCodeSerializerTest.java b/src/test/java/org/reflections/JavaCodeSerializerTest.java index d80e6d6a..0e827885 100644 --- a/src/test/java/org/reflections/JavaCodeSerializerTest.java +++ b/src/test/java/org/reflections/JavaCodeSerializerTest.java @@ -1,60 +1,30 @@ package org.reflections; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.reflections.scanners.TypeElementsScanner; import org.reflections.serializers.JavaCodeSerializer; -import org.reflections.util.ClasspathHelper; -import org.reflections.util.ConfigurationBuilder; import org.reflections.util.FilterBuilder; - -import java.util.Collections; -import java.util.function.Predicate; - -import static org.junit.Assert.assertEquals; -import static org.reflections.TestModel.*; - -/** */ -public class JavaCodeSerializerTest { - - @BeforeClass - public static void generateAndSave() { - Predicate filter = new FilterBuilder().include("org.reflections.TestModel\\$.*"); - - Reflections reflections = new Reflections(new ConfigurationBuilder() - .filterInputsBy(filter) - .setScanners(new TypeElementsScanner().includeFields().publicOnly(false)) - .setUrls(Collections.singletonList(ClasspathHelper.forClass(TestModel.class)))); - - //save - String filename = ReflectionsTest.getUserDir() + "/src/test/java/org.reflections.MyTestModelStore"; - reflections.save(filename, new JavaCodeSerializer()); - } - - @Test - public void resolve() throws NoSuchMethodException, NoSuchFieldException { - //class - assertEquals(C1.class, - JavaCodeSerializer.resolveClass(MyTestModelStore.org.reflections.TestModel$C1.class)); - - //method - assertEquals(C4.class.getDeclaredMethod("m1"), - JavaCodeSerializer.resolveMethod(MyTestModelStore.org.reflections.TestModel$C4.methods.m1.class)); - - //overloaded method with parameters - assertEquals(C4.class.getDeclaredMethod("m1", int.class, String[].class), - JavaCodeSerializer.resolveMethod(MyTestModelStore.org.reflections.TestModel$C4.methods.m1_int__java_lang_String$$.class)); - - //overloaded method with parameters and multi dimensional array - assertEquals(C4.class.getDeclaredMethod("m1", int[][].class, String[][].class), - JavaCodeSerializer.resolveMethod(MyTestModelStore.org.reflections.TestModel$C4.methods.m1_int$$$$__java_lang_String$$$$.class)); - - //field - assertEquals(C4.class.getDeclaredField("f1"), - JavaCodeSerializer.resolveField(MyTestModelStore.org.reflections.TestModel$C4.fields.f1.class)); - - //annotation - assertEquals(C2.class.getAnnotation(AC2.class), - JavaCodeSerializer.resolveAnnotation(MyTestModelStore.org.reflections.TestModel$C2.annotations.org_reflections_TestModel$AC2.class)); - } +import org.reflections.util.NameHelper; + +public class JavaCodeSerializerTest implements NameHelper { + + public JavaCodeSerializerTest() { + FilterBuilder filterBuilder = new FilterBuilder().includePattern("org\\.reflections\\.TestModel\\$.*"); + Reflections reflections = new Reflections( + TestModel.class, + new TypeElementsScanner().filterResultsBy(filterBuilder), + filterBuilder); + + String filename = ReflectionsTest.getUserDir() + "/src/test/java/org.reflections.MyTestModelStore"; + reflections.save(filename, new JavaCodeSerializer()); + } + + @Test + public void check() { + // MyTestModelStore contains TestModel type elements + Class c1 = MyTestModelStore.org.reflections.TestModel$C1.class; + Class ac1 = MyTestModelStore.org.reflections.TestModel$C1.annotations.org_reflections_TestModel$AC1.class; + Class f1 = MyTestModelStore.org.reflections.TestModel$C4.fields.f1.class; + Class m1 = MyTestModelStore.org.reflections.TestModel$C4.methods.m1.class; + } } diff --git a/src/test/java/org/reflections/MoreTests.java b/src/test/java/org/reflections/MoreTests.java index ca62e4ad..16dbbf77 100644 --- a/src/test/java/org/reflections/MoreTests.java +++ b/src/test/java/org/reflections/MoreTests.java @@ -1,27 +1,28 @@ package org.reflections; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.reflections.scanners.MethodParameterNamesScanner; -import org.reflections.scanners.ResourcesScanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; +import org.reflections.scanners.Scanners; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; -import org.reflections.util.FilterBuilder; import java.lang.annotation.Annotation; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.*; import static org.reflections.MoreTestsModel.*; +import static org.reflections.ReflectionUtils.Annotations; +import static org.reflections.ReflectionUtils.SuperTypes; import static org.reflections.ReflectionUtilsTest.toStringSorted; import static org.reflections.ReflectionsTest.are; +import static org.reflections.scanners.Scanners.SubTypes; public class MoreTests { @@ -34,21 +35,23 @@ public void test_cyclic_annotation() { @Test public void no_exception_when_configured_scanner_store_is_empty() { - Reflections reflections = new Reflections(new ConfigurationBuilder() - .setUrls(ClasspathHelper.forPackage("my.project.prefix")) - .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner()) - .filterInputsBy(new FilterBuilder().includePackage("my.project.prefix"))); - - reflections.getSubTypesOf(String.class); + Reflections reflections = new Reflections( + new ConfigurationBuilder() + .setUrls(ClasspathHelper.forClass(TestModel.class)) + .setScanners(Scanners.Resources)); + + assertNull(reflections.getStore().get(SubTypes.index())); + assertTrue(reflections.getSubTypesOf(TestModel.C1.class).isEmpty()); + assertTrue(reflections.get(SubTypes.of(TestModel.C1.class)).isEmpty()); } @Test public void getAllAnnotated_returns_meta_annotations() { Reflections reflections = new Reflections(MoreTestsModel.class); for (Class type: reflections.getTypesAnnotatedWith(Meta.class)) { - Set allAnnotations = ReflectionUtils.getAllAnnotations(type); + Set allAnnotations = ReflectionUtils.get(Annotations.of(type)); List> collect = allAnnotations.stream().map(Annotation::annotationType).collect(Collectors.toList()); - Assert.assertTrue(collect.contains(Meta.class)); + assertTrue(collect.contains(Meta.class)); } Meta meta = new Meta() { @@ -56,21 +59,12 @@ public void getAllAnnotated_returns_meta_annotations() { @Override public Class annotationType() { return Meta.class; } }; for (Class type: reflections.getTypesAnnotatedWith(meta)) { - Set allAnnotations = ReflectionUtils.getAllAnnotations(type); + Set allAnnotations = ReflectionUtils.get(Annotations.of(type)); List> collect = allAnnotations.stream().map(Annotation::annotationType).collect(Collectors.toList()); - Assert.assertTrue(collect.contains(Meta.class)); + assertTrue(collect.contains(Meta.class)); } } - @Test - public void test_java_9_subtypes_of_Object() { - Reflections reflections = new Reflections(new ConfigurationBuilder() - .setUrls(ClasspathHelper.forClass(Object.class)) - .setScanners(new SubTypesScanner(false))); - Set components = reflections.getSubTypesOf(Object.class); - assertFalse(components.isEmpty()); - } - @Test public void test_custom_url_class_loader() throws MalformedURLException { URL externalUrl = new URL("jar:file:" + ReflectionsTest.getUserDir() + "/src/test/resources/another-project.jar!/"); @@ -94,24 +88,23 @@ public void test_reflection_utils_with_custom_loader() throws MalformedURLExcept final URLClassLoader classLoader = new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader()); Class aClass = Class.forName("another.project.AnotherTestModel$C2", true, classLoader); - assertEquals(toStringSorted(ReflectionUtils.getAllSuperTypes(aClass)), - "[class another.project.AnotherTestModel$C2, " + - "class org.reflections.TestModel$C1, " + + assertEquals(toStringSorted(ReflectionUtils.get(SuperTypes.of(aClass), t -> !Object.class.equals(t))), + "[class org.reflections.TestModel$C1, " + "interface org.reflections.TestModel$I1, " + "interface org.reflections.TestModel$I2]"); } @Test public void resources_scanner_filters_classes() { - Reflections reflections = new Reflections(new ResourcesScanner()); - Set keys = reflections.getStore().keys(ResourcesScanner.class.getSimpleName()); - assertTrue(keys.stream().noneMatch(res -> res.endsWith(".class"))); + Reflections reflections = new Reflections(Scanners.Resources); + Collection resources = reflections.getResources(".*"); + assertTrue(resources.stream().noneMatch(res -> res.endsWith(".class"))); } @Test public void test_repeatable() { Reflections ref = new Reflections(MoreTestsModel.class); - Set> clazzes = ref.getTypesAnnotatedWith(Name.class); + Collection> clazzes = ref.getTypesAnnotatedWith(Name.class); assertTrue(clazzes.contains(SingleName.class)); assertFalse(clazzes.contains(MultiName.class)); @@ -125,13 +118,13 @@ public void test_method_param_names_not_local_vars() throws NoSuchMethodExceptio Reflections reflections = new Reflections(MoreTestsModel.class, new MethodParameterNamesScanner()); Class clazz = ParamNames.class; - assertEquals(reflections.getConstructorParamNames(clazz.getConstructor(String.class)).toString(), + assertEquals(reflections.getMemberParameterNames(clazz.getConstructor(String.class)).toString(), "[param1]"); - assertEquals(reflections.getMethodParamNames(clazz.getMethod("test", String.class, String.class)).toString(), + assertEquals(reflections.getMemberParameterNames(clazz.getMethod("test", String.class, String.class)).toString(), "[testParam1, testParam2]"); - assertEquals(reflections.getMethodParamNames(clazz.getMethod("test", String.class)).toString(), + assertEquals(reflections.getMemberParameterNames(clazz.getMethod("test", String.class)).toString(), "[testParam]"); - assertEquals(reflections.getMethodParamNames(clazz.getMethod("test2", String.class)).toString(), + assertEquals(reflections.getMemberParameterNames(clazz.getMethod("test2", String.class)).toString(), "[testParam]"); } diff --git a/src/test/java/org/reflections/MoreTestsModel.java b/src/test/java/org/reflections/MoreTestsModel.java index ed353a69..bce8173e 100644 --- a/src/test/java/org/reflections/MoreTestsModel.java +++ b/src/test/java/org/reflections/MoreTestsModel.java @@ -3,7 +3,6 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -11,7 +10,8 @@ public class MoreTestsModel { @CyclicAnnotation - public @Retention(RUNTIME) @interface CyclicAnnotation {} + @Retention(RUNTIME) + public @interface CyclicAnnotation {} @Target(ElementType.TYPE) @Retention(RUNTIME) @@ -27,27 +27,22 @@ public class MoreTestsModel { @Retention(RUNTIME) @interface B {} - @A - class A1 {} - @B - class B1 {} - @A - class A2 {} + @A class A1 {} + @B class B1 {} + @A class A2 {} - @Retention(RetentionPolicy.RUNTIME) + @Retention(RUNTIME) public @interface TestAnnotation { - public String value(); + String value(); } @TestAnnotation("foo foo foo") public class ActualFunctionalityClass { @TestAnnotation("bar bar bar") - class Thing { - } + class Thing { } } // repeatable - @Repeatable(Names.class) @Retention(RUNTIME) @Target({ElementType.TYPE}) @@ -57,8 +52,7 @@ class Thing { @Name(name = "foo") @Name(name = "bar") - public static class MultiName { - } + public static class MultiName { } @Retention(RUNTIME) @Target({ElementType.TYPE}) @@ -67,23 +61,19 @@ public static class MultiName { } @Name(name = "foo") - public static class SingleName { - } + public static class SingleName { } // public static class ParamNames { public ParamNames() { String testLocal = "local"; } - public ParamNames(String param1) { String testLocal = "local"; } - public void test(String testParam) { String testLocal = "local"; } - public void test(String testParam1, String testParam2) { String testLocal1 = "local"; String testLocal2 = "local"; diff --git a/src/test/java/org/reflections/MyTestModelStore.java b/src/test/java/org/reflections/MyTestModelStore.java index 4b2a6310..3ea7b834 100644 --- a/src/test/java/org/reflections/MyTestModelStore.java +++ b/src/test/java/org/reflections/MyTestModelStore.java @@ -1,144 +1,98 @@ -//generated using Reflections JavaCodeSerializer [Mon Jan 06 09:39:37 ICT 2020] +//generated using Reflections JavaCodeSerializer [Sat Sep 04 04:04:04 JST 2021] package org.reflections; public interface MyTestModelStore { - public interface org { - public interface reflections { - public interface TestModel$AC1 { - public interface annotations { - public interface java_lang_annotation_Inherited {} - public interface java_lang_annotation_Retention {} - } - } - public interface TestModel$AC1n { - public interface annotations { - public interface java_lang_annotation_Retention {} - } - } - public interface TestModel$AC2 { - public interface methods { - public interface value {} - } - public interface annotations { - public interface java_lang_annotation_Retention {} - } - } - public interface TestModel$AC3 { - public interface annotations { - public interface java_lang_annotation_Retention {} - public interface org_reflections_TestModel$AC2 {} - } - } - public interface TestModel$AF1 { - public interface methods { - public interface value {} - } - public interface annotations { - public interface java_lang_annotation_Retention {} - } - } - public interface TestModel$AI1 { - public interface annotations { - public interface java_lang_annotation_Retention {} - public interface org_reflections_TestModel$MAI1 {} - } - } - public interface TestModel$AI2 { - public interface annotations { - public interface java_lang_annotation_Inherited {} - public interface java_lang_annotation_Retention {} - } - } - public interface TestModel$AM1 { - public interface methods { - public interface value {} - } - public interface annotations { - public interface java_lang_annotation_Retention {} - } - } - public interface TestModel$C1 { - public interface annotations { - public interface org_reflections_TestModel$AC1 {} - public interface org_reflections_TestModel$AC1n {} - } - } - public interface TestModel$C2 { - public interface annotations { - public interface org_reflections_TestModel$AC2 {} - } - } - public interface TestModel$C3 { - public interface annotations { - public interface org_reflections_TestModel$AC2 {} - } - } - public interface TestModel$C4 { - public interface fields { - public interface f1 {} - public interface f2 {} - public interface f3 {} - } - public interface methods { - public interface add {} - public interface c2toC3 {} - public interface m1 {} - public interface m1_int__java_lang_String$$ {} - public interface m1_int$$$$__java_lang_String$$$$ {} - public interface m3 {} - public interface m4 {} - } - } - public interface TestModel$C5 { - } - public interface TestModel$C6 { - } - public interface TestModel$C7 { - public interface annotations { - public interface org_reflections_TestModel$AC3 {} - } - } - public interface TestModel$I1 { - public interface annotations { - public interface org_reflections_TestModel$AI1 {} - } - } - public interface TestModel$I2 { - public interface annotations { - public interface org_reflections_TestModel$AI2 {} - } - } - public interface TestModel$I3 { - public interface annotations { - public interface org_reflections_TestModel$AC2 {} - } - } - public interface TestModel$MAI1 { - public interface annotations { - public interface java_lang_annotation_Inherited {} - public interface java_lang_annotation_Retention {} - } - } - public interface TestModel$Usage { - } - public interface TestModel$Usage$C1 { - public interface fields { - public interface c2 {} - } - public interface methods { - public interface method {} - public interface method_java_lang_String {} - } - } - public interface TestModel$Usage$C2 { - public interface fields { - public interface c1 {} - } - public interface methods { - public interface method {} - } - } - } - } + interface org { + interface reflections { + interface TestModel$AC1 { + } + interface TestModel$AC1n { + } + interface TestModel$AC2 { + interface methods { + interface value {} + } + } + interface TestModel$AC3 { + interface annotations { + interface org_reflections_TestModel$AC2 {} + } + } + interface TestModel$AF1 { + interface methods { + interface value {} + } + } + interface TestModel$AI1 { + interface annotations { + interface org_reflections_TestModel$MAI1 {} + } + } + interface TestModel$AI2 { + } + interface TestModel$AM1 { + interface methods { + interface value {} + } + } + interface TestModel$C1 { + interface annotations { + interface org_reflections_TestModel$AC1 {} + interface org_reflections_TestModel$AC1n {} + } + } + interface TestModel$C2 { + interface annotations { + interface org_reflections_TestModel$AC2 {} + } + } + interface TestModel$C3 { + interface annotations { + interface org_reflections_TestModel$AC2 {} + } + } + interface TestModel$C4 { + interface fields { + interface f1 {} + interface f2 {} + interface f3 {} + } + interface methods { + interface add {} + interface c2toC3 {} + interface m1 {} + interface m1_int$$$$__java_lang_String$$$$ {} + interface m3 {} + interface m4 {} + } + } + interface TestModel$C5 { + } + interface TestModel$C6 { + } + interface TestModel$C7 { + interface annotations { + interface org_reflections_TestModel$AC3 {} + } + } + interface TestModel$I1 { + interface annotations { + interface org_reflections_TestModel$AI1 {} + } + } + interface TestModel$I2 { + interface annotations { + interface org_reflections_TestModel$AI2 {} + } + } + interface TestModel$I3 { + interface annotations { + interface org_reflections_TestModel$AC2 {} + } + } + interface TestModel$MAI1 { + } + } + } } diff --git a/src/test/java/org/reflections/NameHelperTest.java b/src/test/java/org/reflections/NameHelperTest.java new file mode 100644 index 00000000..901ec04d --- /dev/null +++ b/src/test/java/org/reflections/NameHelperTest.java @@ -0,0 +1,73 @@ +package org.reflections; + +import org.junit.jupiter.api.Test; +import org.reflections.util.NameHelper; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +@SuppressWarnings({"unchecked"}) +public class NameHelperTest implements NameHelper { + + @Test + public void testClass() { + assertToFor(String.class, this::toName, this::forClass); + assertToFor(String[].class, this::toName, this::forClass); + assertToFor(boolean.class, this::toName, this::forClass); + assertNull(forClass("no.exist")); + } + + @Test + public void testConstructor() throws NoSuchMethodException { + assertToFor(String.class.getDeclaredConstructor(), this::toName, this::forConstructor); + assertToFor(String.class.getDeclaredConstructor(String.class), this::toName, this::forConstructor); + } + + @Test + public void testMethod() throws NoSuchMethodException { + assertToFor(String.class.getDeclaredMethod("length"), this::toName, this::forMethod); + assertToFor(String.class.getDeclaredMethod("charAt", int.class), this::toName, this::forMethod); + } + + @Test + public void testField() throws NoSuchFieldException { + assertToFor(String.class.getDeclaredField("value"), this::toName, this::forField); + } + + @Test + public void testToForNames() throws NoSuchFieldException, NoSuchMethodException { + Class CLASS = String.class; + Constructor CONST = CLASS.getDeclaredConstructor(); + Method METHOD = CLASS.getDeclaredMethod("length"); + Field FIELD = CLASS.getDeclaredField("value"); + + Set elements = set(CLASS, CONST, METHOD, FIELD); + Collection names = toNames(elements); + + assertEquals(set(CLASS), forNames(names)); + assertEquals(set(CLASS), forNames(names, Class.class)); + assertEquals(set(CONST), forNames(names, Constructor.class)); + assertEquals(set(METHOD), forNames(names, Method.class)); + assertEquals(set(FIELD), forNames(names, Field.class)); + assertEquals(set(CONST, METHOD, FIELD), forNames(names, Member.class)); + } + + void assertToFor(T type, Function toName, Function forName) { + assertEquals(forName.apply(toName.apply(type)), type); + } + + private Set set(T... ts) { + return new HashSet<>(Arrays.asList(ts)); + } +} \ No newline at end of file diff --git a/src/test/java/org/reflections/ReflectionUtilsQueryTest.java b/src/test/java/org/reflections/ReflectionUtilsQueryTest.java new file mode 100644 index 00000000..33e3f47c --- /dev/null +++ b/src/test/java/org/reflections/ReflectionUtilsQueryTest.java @@ -0,0 +1,244 @@ +package org.reflections; + +import org.junit.jupiter.api.Test; +import org.reflections.scanners.Scanners; +import org.reflections.util.AnnotationMergeCollector; +import org.reflections.util.QueryFunction; + +import java.lang.annotation.Annotation; +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.reflections.ReflectionUtils.*; +import static org.reflections.ReflectionsQueryTest.equalTo; +import static org.reflections.TestModel.*; +import static org.reflections.scanners.Scanners.MethodsAnnotated; +import static org.reflections.scanners.Scanners.TypesAnnotated; + +public class ReflectionUtilsQueryTest { + + @Test + public void testTypes() throws NoSuchMethodException { + assertThat( + get(SuperTypes.of(C3.class)), + equalTo(C1.class, I2.class, I1.class)); + + assertThat( + get(SuperTypes.of(C3.class) + .filter(withAnnotation(AI1.class))), + equalTo(I1.class)); + + assertThat( + get(Interfaces.get(C1.class)), + equalTo(I2.class)); + + assertThat( + get(Interfaces.of(C3.class)), + equalTo(I2.class, I1.class)); + + assertThat( + get(SuperClass.of(C5.class)), + equalTo(C3.class, C1.class)); + + assertThat( + get(Annotations.of(TestModel.C3.class) + .map(Annotation::annotationType)), + equalTo( + Retention.class, Target.class, Documented.class, Inherited.class, + AC1.class, AC1n.class, AC2.class, AI1.class, AI2.class, MAI1.class)); + + assertThat( + get(AnnotationTypes.of(C3.class) + .filter(a -> !a.getName().startsWith("java."))), + equalTo( + AC1.class, AC1n.class, AC2.class, AI1.class, AI2.class, MAI1.class)); + + assertThat( + get(Annotations.of(C4.class.getDeclaredMethod("m4", String.class)) + .map(Annotation::annotationType)), + equalTo()); + } + + @Test + public void testMembers() throws NoSuchMethodException, NoSuchFieldException { + assertThat( + get(Methods.of(C4.class, withName("m4"))), + equalTo(C4.class.getDeclaredMethod("m4", String.class))); + + assertThat( + get(Methods.of(C4.class, withParameters(String.class))), + equalTo(C4.class.getDeclaredMethod("m4", String.class))); + + assertThat( + get(Methods.of(C4.class) + .filter(withPattern("public.*.void .*")) + .map(Method::getName)), + equalTo("m1")); + + assertThat( + get(Methods.of(C4.class, withAnyParameterAnnotation(AM1.class))), + equalTo(C4.class.getDeclaredMethod("m4", String.class))); + + assertThat( + get(Methods.of(Class.class) + .filter(withReturnType(Method.class).and(withPublic())) + .map(Method::getName)), + equalTo("getMethod", "getDeclaredMethod", "getEnclosingMethod")); + + assertThat( + get(Fields.of(C4.class, withAnnotation(AF1.class))), + equalTo(C4.class.getDeclaredField("f1"), + C4.class.getDeclaredField("f2"))); + + AF1 af12 = new AF1() { + public String value() { return "2"; } + public Class annotationType() { return AF1.class; } + }; + assertThat( + get(Fields.of(C4.class) + .filter(withAnnotation(af12))), + equalTo(C4.class.getDeclaredField("f2"))); + + assertThat( + get(Fields.of(C4.class) + .filter(withTypeAssignableTo(String.class))), + equalTo(C4.class.getDeclaredField("f1"), + C4.class.getDeclaredField("f2"), + C4.class.getDeclaredField("f3"))); + + assertThat( + get(Constructors.of(C4.class) + .filter(withParametersCount(0))), + equalTo(C4.class.getDeclaredConstructor())); + } + + @Test + public void nestedQuery() { + Set> annotations = + get(Annotations.of( + Methods.of(C4.class)) + .map(Annotation::annotationType) + .filter(a -> !a.getName().startsWith("java.")) + .as()); + + assertThat(annotations, + equalTo(AM1.class)); + } + + @Test + public void addQuery() { + Set> annotations = + get(AnnotationTypes.of(C1.class) + .add(AnnotationTypes.of(C2.class))); + + assertThat(annotations, + equalTo( + Retention.class, Target.class, Documented.class, Inherited.class, + AC1.class, AC2.class, AC1n.class, AI2.class, AI1.class, MAI1.class)); + } + + @Test + public void singleQuery() { + QueryFunction> single = + QueryFunction.single(CombinedTestModel.Impl.class); + assertThat(single.apply(null), + equalTo(CombinedTestModel.Impl.class)); + + QueryFunction> second = + single.add( + QueryFunction.single(CombinedTestModel.Controller.class)); + assertThat(second.apply(null), + equalTo(CombinedTestModel.Impl.class, CombinedTestModel.Controller.class)); + } + + @Test + public void getAllQuery() { + QueryFunction> single = + QueryFunction.single(CombinedTestModel.Impl.class); + + QueryFunction> allIncluding = + single.add( + single.getAll(SuperTypes::get)); + assertThat(allIncluding.apply(null), + equalTo(CombinedTestModel.Impl.class, CombinedTestModel.Abstract.class, CombinedTestModel.Controller.class)); + } + + @Test + public void flatMapQuery() throws NoSuchMethodException { + Set query = + get(Annotations.of( + Methods.of(CombinedTestModel.Impl.class)) + .flatMap(annotation -> + Methods.of(annotation.annotationType()))); + + Set query1 = + get(AnnotationTypes.of(Methods.of(CombinedTestModel.Impl.class)).flatMap(Methods::of)); + + assertThat(query, + equalTo( + CombinedTestModel.Post.class.getDeclaredMethod("value"), + CombinedTestModel.Requests.class.getDeclaredMethod("value"), + CombinedTestModel.Get.class.getDeclaredMethod("value"), + Annotation.class.getDeclaredMethod("annotationType"))); + + assertEquals(query, query1); + } + + @Test + public void annotationToMap() { + Set> valueMaps = + get(Annotations.of( + Methods.of(CombinedTestModel.Impl.class)) + .map(ReflectionUtils::toMap)); + + // todo proper assert + Set collect = valueMaps.stream().map(Object::toString).sorted().collect(Collectors.toCollection(LinkedHashSet::new)); + assertThat(collect, + equalTo( + "{annotationType=interface org.reflections.CombinedTestModel$Get, value=/get}", + "{annotationType=interface org.reflections.CombinedTestModel$Post, value=/post}", + "{annotationType=interface org.reflections.CombinedTestModel$Requests, value=[" + + "{method=PUT, annotationType=interface org.reflections.CombinedTestModel$Request, value=/another}, " + + "{method=PATCH, annotationType=interface org.reflections.CombinedTestModel$Request, value=/another}]}" + )); + } + + @Test + public void mergedAnnotations() { + Class metaAnnotation = CombinedTestModel.Request.class; + + Reflections reflections = new Reflections(metaAnnotation, Scanners.values()); + + Set> metaAnnotations = + reflections.get(TypesAnnotated.getAllIncluding(metaAnnotation.getName()).asClass()); + + QueryFunction mergedAnnotations = + MethodsAnnotated.with(metaAnnotations) + .as(Method.class) + .map(method -> + get(Annotations.of(method.getDeclaringClass()) + .add(Annotations.of(method)) + .filter(a -> metaAnnotations.contains(a.annotationType()))) + .stream() + .collect(new AnnotationMergeCollector(method))) + .map(map -> ReflectionUtils.toAnnotation(map, metaAnnotation)); + + assertThat( + reflections.get(mergedAnnotations.map(CombinedTestModel.Request::value)), + equalTo("/base/post", "/base/get")); + + assertThat( + reflections.get(mergedAnnotations.map(CombinedTestModel.Request::method)), + equalTo("Post", "Get")); + } +} + diff --git a/src/test/java/org/reflections/ReflectionUtilsTest.java b/src/test/java/org/reflections/ReflectionUtilsTest.java index d3881817..944887a3 100644 --- a/src/test/java/org/reflections/ReflectionUtilsTest.java +++ b/src/test/java/org/reflections/ReflectionUtilsTest.java @@ -2,8 +2,8 @@ import org.hamcrest.BaseMatcher; import org.hamcrest.Description; -import org.junit.Test; -import org.reflections.scanners.FieldAnnotationsScanner; +import org.junit.jupiter.api.Test; +import org.reflections.scanners.Scanners; import java.lang.annotation.Annotation; import java.lang.reflect.Field; @@ -17,13 +17,11 @@ import java.util.Set; import java.util.stream.Collectors; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.*; import static org.reflections.ReflectionUtils.*; import static org.reflections.ReflectionsTest.are; -/** - * @author mamo - */ @SuppressWarnings("unchecked") public class ReflectionUtilsTest { @@ -57,7 +55,7 @@ public void getAllTest() { "@java.lang.annotation.Target(value=ANNOTATION_TYPE), " + "@org.reflections.TestModel$AC1(), " + "@org.reflections.TestModel$AC1n(), " + - "@org.reflections.TestModel$AC2(value=ugh?!), " + + "@org.reflections.TestModel$AC2(value=ac2), " + "@org.reflections.TestModel$AI1(), " + "@org.reflections.TestModel$AI2(), " + "@org.reflections.TestModel$MAI1()]"); @@ -110,7 +108,7 @@ public void withParametersAssignableFromTest() throws Exception { } @Test public void withReturn() { - Set returnMember = getAllMethods(Class.class, withReturnTypeAssignableTo(Member.class)); + Set returnMember = getAllMethods(Class.class, withReturnTypeAssignableFrom(Member.class)); Set returnsAssignableToMember = getAllMethods(Class.class, withReturnType(Method.class)); assertTrue(returnMember.containsAll(returnsAssignableToMember)); @@ -123,10 +121,10 @@ public void withParametersAssignableFromTest() throws Exception { @Test public void getAllAndReflections() { - Reflections reflections = new Reflections(TestModel.class, new FieldAnnotationsScanner()); + Reflections reflections = new Reflections(TestModel.class, Scanners.FieldsAnnotated); - Set af1 = reflections.getFieldsAnnotatedWith(TestModel.AF1.class); - Set allFields = ReflectionUtils.getAll(af1, withModifier(Modifier.PROTECTED)); + Set allFields = reflections.getFieldsAnnotatedWith(TestModel.AF1.class) + .stream().filter(withModifier(Modifier.PROTECTED)).collect(Collectors.toSet()); assertEquals(1, allFields.size()); assertThat(allFields, names("f2")); } @@ -149,7 +147,7 @@ public void describeTo(Description description) { }; } - public static String toStringSorted(Set set) { + public static String toStringSorted(Collection set) { return set.stream() .map(o -> o.toString().replace("[", "").replace("]", "").replace("{", "").replace("}", "").replace("\"", "")) .sorted().collect(Collectors.toList()).toString(); diff --git a/src/test/java/org/reflections/ReflectionsCollectTest.java b/src/test/java/org/reflections/ReflectionsCollectTest.java index a71b3de2..9845606d 100644 --- a/src/test/java/org/reflections/ReflectionsCollectTest.java +++ b/src/test/java/org/reflections/ReflectionsCollectTest.java @@ -1,73 +1,38 @@ package org.reflections; -import org.junit.BeforeClass; -import org.junit.Test; -import org.reflections.scanners.MemberUsageScanner; -import org.reflections.scanners.MethodAnnotationsScanner; -import org.reflections.scanners.MethodParameterNamesScanner; -import org.reflections.scanners.MethodParameterScanner; -import org.reflections.scanners.ResourcesScanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; +import org.junit.jupiter.api.Test; +import org.reflections.scanners.Scanners; import org.reflections.serializers.JsonSerializer; -import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import org.reflections.util.FilterBuilder; -import java.util.Collections; -import java.util.Set; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -import static org.junit.Assert.assertThat; -import static org.reflections.util.Utils.index; - -/** */ -public class ReflectionsCollectTest extends ReflectionsTest { - - @BeforeClass - public static void init() { - Reflections ref = new Reflections(new ConfigurationBuilder() - .addUrls(ClasspathHelper.forClass(TestModel.class)) - .filterInputsBy(TestModelFilter) - .setScanners( - new SubTypesScanner(false), - new TypeAnnotationsScanner(), - new MethodAnnotationsScanner(), - new MethodParameterNamesScanner(), - new MemberUsageScanner())); - - ref.save(getUserDir() + "/target/test-classes" + "/META-INF/reflections/testModel-reflections.xml"); - - ref = new Reflections(new ConfigurationBuilder() - .setUrls(Collections.singletonList(ClasspathHelper.forClass(TestModel.class))) - .filterInputsBy(TestModelFilter) - .setScanners( - new MethodParameterScanner())); - - final JsonSerializer serializer = new JsonSerializer(); - ref.save(getUserDir() + "/target/test-classes" + "/META-INF/reflections/testModel-reflections.json", serializer); - - reflections = Reflections - .collect() - .merge(Reflections.collect("META-INF/reflections", - new FilterBuilder().include(".*-reflections.json"), - serializer)); - } - - @Test - public void testResourcesScanner() { - Predicate filter = new FilterBuilder().include(".*\\.xml").include(".*\\.json"); - Reflections reflections = new Reflections(new ConfigurationBuilder() - .filterInputsBy(filter) - .setScanners(new ResourcesScanner()) - .setUrls(Collections.singletonList(ClasspathHelper.forClass(TestModel.class)))); - - Set resolved = reflections.getResources(Pattern.compile(".*resource1-reflections\\.xml")); - assertThat(resolved, are("META-INF/reflections/resource1-reflections.xml")); - - Set resources = reflections.getStore().keys(index(ResourcesScanner.class)); - assertThat(resources, are("resource1-reflections.xml", "resource2-reflections.xml", - "testModel-reflections.xml", "testModel-reflections.json")); - } +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.reflections.ReflectionsTest.getUserDir; + +public class ReflectionsCollectTest { + + @Test + public void testCollect() { + Reflections reflections = new Reflections( + new ConfigurationBuilder() + .forPackage("org.reflections") + .filterInputsBy(new FilterBuilder() + .includePattern("org\\.reflections\\.TestModel\\$.*") + .includePattern(".*\\.xml")) + .addScanners(Scanners.values())); + + String targetDir = getUserDir() + "/target/test-classes"; + + // xml + reflections.save(targetDir + "/META-INF/reflections/saved-testModel-reflections.xml"); + assertEquals( + Reflections.collect("/META-INF/reflections/testModel-reflections.xml", a -> true).getStore(), + Reflections.collect("/META-INF/reflections/saved-testModel-reflections.xml", a -> true).getStore()); + + // json + reflections.save(targetDir + "/META-INF/reflections/saved-testModel-reflections.json", new JsonSerializer()); + assertEquals( + Reflections.collect("/META-INF/reflections/testModel-reflections.json", a -> true).getStore(), + Reflections.collect("/META-INF/reflections/saved-testModel-reflections.json", a -> true).getStore()); + } } diff --git a/src/test/java/org/reflections/ReflectionsExpandSupertypesTest.java b/src/test/java/org/reflections/ReflectionsExpandSupertypesTest.java index f0ce1dac..3123ac65 100644 --- a/src/test/java/org/reflections/ReflectionsExpandSupertypesTest.java +++ b/src/test/java/org/reflections/ReflectionsExpandSupertypesTest.java @@ -1,54 +1,51 @@ package org.reflections; -import org.junit.Assert; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import org.reflections.util.FilterBuilder; -import java.util.Set; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ReflectionsExpandSupertypesTest { - private final static String packagePrefix = - "org.reflections.ReflectionsExpandSupertypesTest\\$TestModel\\$ScannedScope\\$.*"; - private FilterBuilder inputsFilter = new FilterBuilder().include(packagePrefix); + private final FilterBuilder inputsFilter = new FilterBuilder() + .includePattern("org\\.reflections\\.ReflectionsExpandSupertypesTest\\$TestModel\\$ScannedScope\\$.*"); + @SuppressWarnings("unused") public interface TestModel { - interface A { - } // outside of scanned scope - - interface B extends A { - } // outside of scanned scope, but immediate supertype + interface A { } // outside of scanned scope + interface B extends A { } // outside of scanned scope, but immediate supertype interface ScannedScope { - interface C extends B { - } - - interface D extends B { - } + interface C extends B { } + interface D extends B { } } } @Test - public void testExpandSupertypes() throws Exception { + public void testExpandSupertypes() { Reflections refExpand = new Reflections(new ConfigurationBuilder(). setUrls(ClasspathHelper.forClass(TestModel.ScannedScope.C.class)). filterInputsBy(inputsFilter)); - Assert.assertTrue(refExpand.getConfiguration().shouldExpandSuperTypes()); - Set> subTypesOf = refExpand.getSubTypesOf(TestModel.A.class); - Assert.assertTrue("expanded", subTypesOf.contains(TestModel.B.class)); - Assert.assertTrue("transitivity", subTypesOf.containsAll(refExpand.getSubTypesOf(TestModel.B.class))); + assertTrue(refExpand.getConfiguration().shouldExpandSuperTypes()); + Collection> subTypesOf = refExpand.getSubTypesOf(TestModel.A.class); + assertTrue(subTypesOf.contains(TestModel.B.class), "expanded"); + assertTrue(subTypesOf.containsAll(refExpand.getSubTypesOf(TestModel.B.class)), "transitivity"); } @Test - public void testNotExpandSupertypes() throws Exception { - Reflections refDontExpand = new Reflections(new ConfigurationBuilder(). - setUrls(ClasspathHelper.forClass(TestModel.ScannedScope.C.class)). - filterInputsBy(inputsFilter). + public void testNotExpandSupertypes() { + Reflections refDontExpand = new Reflections( + new ConfigurationBuilder() + .forPackage("org.reflections") + .filterInputsBy(inputsFilter). setExpandSuperTypes(false)); - Assert.assertFalse(refDontExpand.getConfiguration().shouldExpandSuperTypes()); - Set> subTypesOf1 = refDontExpand.getSubTypesOf(TestModel.A.class); - Assert.assertFalse(subTypesOf1.contains(TestModel.B.class)); + assertFalse(refDontExpand.getConfiguration().shouldExpandSuperTypes()); + Collection> subTypesOf1 = refDontExpand.getSubTypesOf(TestModel.A.class); + assertFalse(subTypesOf1.contains(TestModel.B.class)); } } diff --git a/src/test/java/org/reflections/ReflectionsParallelTest.java b/src/test/java/org/reflections/ReflectionsParallelTest.java deleted file mode 100644 index 8d74972a..00000000 --- a/src/test/java/org/reflections/ReflectionsParallelTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.reflections; - -import org.junit.BeforeClass; -import org.reflections.scanners.FieldAnnotationsScanner; -import org.reflections.scanners.MemberUsageScanner; -import org.reflections.scanners.MethodAnnotationsScanner; -import org.reflections.scanners.MethodParameterNamesScanner; -import org.reflections.scanners.MethodParameterScanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; -import org.reflections.util.ClasspathHelper; -import org.reflections.util.ConfigurationBuilder; - -import java.util.Collections; - -/** */ -public class ReflectionsParallelTest extends ReflectionsTest { - - @BeforeClass - public static void init() { - reflections = new Reflections(new ConfigurationBuilder() - .setUrls(Collections.singletonList(ClasspathHelper.forClass(TestModel.class))) - .filterInputsBy(TestModelFilter) - .setScanners( - new SubTypesScanner(false), - new TypeAnnotationsScanner(), - new FieldAnnotationsScanner(), - new MethodAnnotationsScanner(), - new MethodParameterScanner(), - new MethodParameterNamesScanner(), - new MemberUsageScanner()) - .useParallelExecutor()); - } -} diff --git a/src/test/java/org/reflections/ReflectionsQueryTest.java b/src/test/java/org/reflections/ReflectionsQueryTest.java new file mode 100644 index 00000000..d3e340f6 --- /dev/null +++ b/src/test/java/org/reflections/ReflectionsQueryTest.java @@ -0,0 +1,327 @@ +package org.reflections; + +import org.hamcrest.Matcher; +import org.hamcrest.core.IsEqual; +import org.junit.jupiter.api.Test; +import org.reflections.scanners.Scanners; +import org.reflections.util.ConfigurationBuilder; +import org.reflections.util.FilterBuilder; +import org.reflections.util.NameHelper; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashSet; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.reflections.ReflectionUtils.withAnnotation; +import static org.reflections.ReflectionUtils.withAnyParameterAnnotation; +import static org.reflections.TestModel.*; +import static org.reflections.scanners.Scanners.*; + +public class ReflectionsQueryTest implements NameHelper { + static Reflections reflections; + + public ReflectionsQueryTest() { + reflections = new Reflections( + new ConfigurationBuilder() + .forPackage("org.reflections") + .filterInputsBy(new FilterBuilder() + .includePattern("org\\.reflections\\.TestModel\\$.*") + .or(s -> s.endsWith(".xml"))) + .setScanners(Scanners.values())); + } + + @Test + public void testSubTypes() { + assertThat("direct subtypes of interface", + reflections.get(SubTypes.get(I1.class)), + equalToNames(I2.class)); + + assertThat("direct subtypes of class", + reflections.get(SubTypes.get(C1.class).asClass()), + equalTo(C2.class, C3.class)); + + assertThat("transitive subtypes of interface", + reflections.get(SubTypes.of(I1.class)), + equalToNames(I2.class, C1.class, C2.class, C3.class, C5.class)); + + assertThat("transitive subtypes of class", + reflections.get(SubTypes.of(C1.class).asClass()), + equalTo(C2.class, C3.class, C5.class)); + } + + @Test + public void testTypesAnnotated() { + assertThat("direct types annotated with meta annotation", + reflections.get(TypesAnnotated.get(MAI1.class).asClass()), + equalTo(AI1.class)); + + assertThat("transitive types annotated with meta annotation", + reflections.get(TypesAnnotated.of(MAI1.class).asClass()), + equalTo(AI1.class, I1.class)); + + assertThat("transitive subtypes of types annotated with meta annotation, including", + reflections.get(SubTypes.of(TypesAnnotated.with(MAI1.class)).asClass()), + equalTo(AI1.class, I1.class, I2.class, C1.class, C2.class, C3.class, C5.class)); + + assertThat("direct types annotated with annotation", + reflections.get(TypesAnnotated.get(AI1.class)), + equalToNames(I1.class)); + + assertThat("transitive types annotated with annotation", + reflections.get(TypesAnnotated.of(AI1.class)), + equalToNames(I1.class)); + + assertThat("transitive subtypes of types annotated with annotation", + reflections.get(SubTypes.of(TypesAnnotated.with(AI1.class))), + equalToNames(I1.class, I2.class, C1.class, C2.class, C3.class, C5.class)); + } + + @Test + public void testTypesAnnotatedWithMemberMatching() { + assertThat("direct types annotated with annotation", + reflections.get(TypesAnnotated.get(AC2.class).asClass()), + equalTo(C2.class, C3.class, I3.class, AC3.class)); + + assertThat("transitive types annotated with annotation", + reflections.get(TypesAnnotated.with(AC2.class).asClass()), + equalTo(C2.class, C3.class, I3.class, AC3.class, C7.class)); + + assertThat("transitive subtypes of types annotated with annotation", + reflections.get(SubTypes.of(TypesAnnotated.with(AC2.class)).asClass()), + equalTo(C2.class, C3.class, I3.class, AC3.class, C7.class, C5.class, C6.class)); + + AC2 ac2 = new AC2() { + public String value() { return "ac2"; } + public Class annotationType() { return AC2.class; } + }; + + assertThat("transitive types annotated with annotation filter by member matching", + reflections.get(TypesAnnotated.with(AC2.class).asClass().filter(withAnnotation(ac2))), + equalTo(C3.class, I3.class, AC3.class)); + + assertThat("transitive subtypes of types annotated with annotation filter by member matching", + reflections.get(SubTypes.of(TypesAnnotated.with(AC2.class).filter(a -> withAnnotation(ac2).test(forClass(a))))), + equalToNames(C3.class, I3.class, AC3.class, C5.class, C6.class)); + } + + @Test + public void testMethodsAnnotated() throws NoSuchMethodException { + assertThat("methods annotated with annotation", + reflections.get(MethodsAnnotated.with(AM1.class)), + equalToNames( + C4.class.getDeclaredMethod("m1"), + C4.class.getDeclaredMethod("m1", int.class, String[].class), + C4.class.getDeclaredMethod("m1", int[][].class, String[][].class), + C4.class.getDeclaredMethod("m3"))); + + AM1 am11 = new AM1() { + public String value() { + return "1"; + } + public Class annotationType() { + return AM1.class; + } + }; + + assertThat("methods annotated with annotation filter by member matching", + reflections.get(MethodsAnnotated.with(AM1.class).as(Method.class).filter(withAnnotation(am11))), + equalTo( + C4.class.getDeclaredMethod("m1"), + C4.class.getDeclaredMethod("m1", int.class, String[].class), + C4.class.getDeclaredMethod("m1", int[][].class, String[][].class))); + } + + @Test + public void testConstructorsAnnotated() throws NoSuchMethodException { + assertThat("constructors annotated with annotation", + reflections.get(ConstructorsAnnotated.with(AM1.class)), + equalToNames(C4.class.getDeclaredConstructor(String.class))); + + AM1 am12 = new AM1() { + public String value() { + return "2"; + } + public Class annotationType() { + return AM1.class; + } + }; + + assertThat("constructors annotated with annotation filter by member matching", + reflections.get(ConstructorsAnnotated.with(AM1.class) + .as(Constructor.class).filter(withAnnotation(am12))), + equalTo()); + } + + @Test + public void testFieldsAnnotated() throws NoSuchFieldException { + assertThat("fields annotated with annotation", + reflections.get(FieldsAnnotated.with(AF1.class)), + equalToNames( + C4.class.getDeclaredField("f1"), + C4.class.getDeclaredField("f2"))); + + AF1 af12 = new AF1() { + public String value() { + return "2"; + } + public Class annotationType() { + return AF1.class; + } + }; + + assertThat("fields annotated with annotation filter by member matching", + reflections.get(FieldsAnnotated.with(AF1.class) + .as(Field.class).filter(withAnnotation(af12))), + equalTo(C4.class.getDeclaredField("f2"))); + } + + @Test + public void testMethods() throws NoSuchMethodException { + assertThat("methods with any parameter", + reflections.get(MethodsParameter.with(String.class)), + equalToNames(C4.class.getDeclaredMethod("m4", String.class))); + + assertThat("methods with any parameter", + reflections.get(MethodsParameter.with(int.class)), + equalToNames( + C4.class.getDeclaredMethod("m1", int.class, String[].class), + C4.class.getDeclaredMethod("add", int.class, int.class))); + + assertThat("methods with signature single parameter", + reflections.get(MethodsSignature.with(String.class)), + equalToNames(C4.class.getDeclaredMethod("m4", String.class))); + + assertThat("methods with signature", + reflections.get(MethodsSignature.with(int.class, String[].class)), + equalToNames(C4.class.getDeclaredMethod("m1", int.class, String[].class))); + + assertThat("methods with signature no parameters", + reflections.get(MethodsSignature.with()), + equalToNames( + C4.class.getDeclaredMethod("m1"), + C4.class.getDeclaredMethod("m3"), + AC2.class.getMethod("value"), + AF1.class.getMethod("value"), + AM1.class.getMethod("value"))); + + assertThat("methods with return type", + reflections.get(MethodsReturn.of(String.class)), + equalToNames( + C4.class.getDeclaredMethod("m3"), + C4.class.getDeclaredMethod("m4", String.class), + AC2.class.getMethod("value"), + AF1.class.getMethod("value"), + AM1.class.getMethod("value"))); + + assertThat("methods with return type void", + reflections.get(MethodsReturn.of(void.class)), + equalToNames(C4.class.getDeclaredMethod("m1"), + C4.class.getDeclaredMethod("m1", int.class, String[].class), + C4.class.getDeclaredMethod("m1", int[][].class, String[][].class))); + + assertThat("methods with parameter annotation", + reflections.get(MethodsParameter.with(AM1.class)), + equalToNames(C4.class.getDeclaredMethod("m4", String.class))); + + AM1 am1 = new AM1() { + public String value() { + return "2"; + } + public Class annotationType() { + return AM1.class; + } + }; + + assertThat("methods with parameter annotation filter by member matching", + reflections.get(MethodsParameter.with(AM1.class).as(Method.class).filter(withAnyParameterAnnotation(am1))), + equalTo(C4.class.getDeclaredMethod("m4", String.class))); + } + + @Test + public void testConstructorParameter() throws NoSuchMethodException { + assertThat("constructors with parameter", + reflections.get(ConstructorsParameter.with(String.class)), + equalToNames(C4.class.getDeclaredConstructor(String.class))); + + assertThat("constructors with signature no parameters", + reflections.get(ConstructorsSignature.with()), + equalToNames( + C1.class.getDeclaredConstructor(), + C2.class.getDeclaredConstructor(), + C3.class.getDeclaredConstructor(), + C4.class.getDeclaredConstructor(), + C5.class.getDeclaredConstructor(), + C6.class.getDeclaredConstructor(), + C7.class.getDeclaredConstructor())); + + assertThat("constructors with parameter annotation", + reflections.get(ConstructorsParameter.with(AM1.class)), + equalToNames(C4.class.getDeclaredConstructor(String.class))); + + AM1 am1 = new AM1() { + public String value() { + return "1"; + } + public Class annotationType() { + return AM1.class; + } + }; + + assertThat("constructors with parameter annotation filter by member values", + reflections.get(ConstructorsParameter.with(AM1.class) + .as(Constructor.class) + .filter(withAnnotation(am1))), + equalTo(C4.class.getDeclaredConstructor(String.class))); + } + + @Test + public void testResourcesScanner() { + assertThat("resources matching pattern", + reflections.get(Resources.with(".*resource1-reflections\\.xml")), + equalTo("META-INF/reflections/resource1-reflections.xml")); + + assertThat("resources matching pattern any", + reflections.get(Resources.with(".*")), + equalTo( + "META-INF/reflections/testModel-reflections.xml", + "META-INF/reflections/saved-testModel-reflections.xml", + "META-INF/reflections/resource1-reflections.xml", + "META-INF/reflections/inner/resource2-reflections.xml")); + } + + @Test + public void testGetAll() { + reflections = new Reflections( + new ConfigurationBuilder() + .forPackage("org.reflections") + .filterInputsBy(new FilterBuilder().includePattern("org\\.reflections\\.TestModel\\$.*")) + .setScanners(Scanners.SubTypes.filterResultsBy(t -> true))); + + assertThat("all (sub) types", + reflections.getAll(SubTypes), + equalTo("java.lang.Object", "java.lang.annotation.Annotation", + "org.reflections.TestModel$MAI1", "org.reflections.TestModel$AI1", "org.reflections.TestModel$AI2", + "org.reflections.TestModel$I1", "org.reflections.TestModel$I2", "org.reflections.TestModel$I3", + "org.reflections.TestModel$AF1", "org.reflections.TestModel$AM1", + "org.reflections.TestModel$AC1", "org.reflections.TestModel$AC1n", "org.reflections.TestModel$AC2", "org.reflections.TestModel$AC3", + "org.reflections.TestModel$C1", "org.reflections.TestModel$C2", "org.reflections.TestModel$C3", "org.reflections.TestModel$C4", + "org.reflections.TestModel$C5", "org.reflections.TestModel$C6", "org.reflections.TestModel$C7")); + } + + // + @SafeVarargs + public static Matcher> equalTo(T... operand) { + return IsEqual.equalTo(new LinkedHashSet<>(Arrays.asList(operand))); + } + + @SafeVarargs + public final Matcher> equalToNames(T... operand) { + return IsEqual.equalTo(new LinkedHashSet<>(toNames(operand))); + } +} diff --git a/src/test/java/org/reflections/ReflectionsTest.java b/src/test/java/org/reflections/ReflectionsTest.java index f345dfe1..6f188203 100644 --- a/src/test/java/org/reflections/ReflectionsTest.java +++ b/src/test/java/org/reflections/ReflectionsTest.java @@ -3,59 +3,51 @@ import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; -import org.junit.BeforeClass; -import org.junit.Test; -import org.reflections.scanners.FieldAnnotationsScanner; +import org.hamcrest.core.IsEqual; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.reflections.scanners.MemberUsageScanner; -import org.reflections.scanners.MethodAnnotationsScanner; import org.reflections.scanners.MethodParameterNamesScanner; -import org.reflections.scanners.MethodParameterScanner; -import org.reflections.scanners.ResourcesScanner; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.scanners.TypeAnnotationsScanner; +import org.reflections.scanners.Scanners; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import org.reflections.util.FilterBuilder; +import org.reflections.util.NameHelper; import java.io.File; import java.lang.annotation.Annotation; -import java.util.ArrayList; +import java.lang.reflect.AnnotatedElement; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; -import java.util.Set; import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; -import static org.junit.Assert.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.*; import static org.reflections.TestModel.*; -import static org.reflections.util.Utils.index; -/** - * - */ @SuppressWarnings("unchecked") -public class ReflectionsTest { - public static final FilterBuilder TestModelFilter = new FilterBuilder().include("org.reflections.TestModel\\$.*"); +public class ReflectionsTest implements NameHelper { + private static final FilterBuilder TestModelFilter = new FilterBuilder() + .includePattern("org\\.reflections\\.TestModel\\$.*") + .includePattern("org\\.reflections\\.UsageTestModel\\$.*"); + static Reflections reflections; - @BeforeClass + @BeforeAll public static void init() { reflections = new Reflections(new ConfigurationBuilder() .setUrls(Collections.singletonList(ClasspathHelper.forClass(TestModel.class))) .filterInputsBy(TestModelFilter) .setScanners( - new SubTypesScanner(false), - new TypeAnnotationsScanner(), - new FieldAnnotationsScanner(), - new MethodAnnotationsScanner(), - new MethodParameterScanner(), - new MethodParameterNamesScanner(), - new MemberUsageScanner())); + new MethodParameterNamesScanner(), + new MemberUsageScanner()) + .addScanners(Scanners.values())); } @Test @@ -63,8 +55,7 @@ public void testSubTypesOf() { assertThat(reflections.getSubTypesOf(I1.class), are(I2.class, C1.class, C2.class, C3.class, C5.class)); assertThat(reflections.getSubTypesOf(C1.class), are(C2.class, C3.class, C5.class)); - assertFalse("getAllTypes should not be empty when Reflections is configured with SubTypesScanner(false)", - reflections.getAllTypes().isEmpty()); + assertFalse(reflections.getAllTypes().isEmpty(), "getAllTypes should not be empty when Reflections is configured with SubTypesScanner(false)"); } @Test @@ -80,21 +71,15 @@ public void testTypesAnnotatedWith() { assertThat(reflections.getTypesAnnotatedWith(AC1n.class, true), are(C1.class)); assertThat(reflections.getTypesAnnotatedWith(AC1n.class, true), annotatedWith(AC1n.class)); - assertThat(reflections.getTypesAnnotatedWith(MAI1.class), are(AI1.class, I1.class, I2.class, C1.class, C2.class, C3.class, C5.class)); - assertThat(reflections.getTypesAnnotatedWith(MAI1.class), metaAnnotatedWith(MAI1.class)); - assertThat(reflections.getTypesAnnotatedWith(AI1.class), are(I1.class, I2.class, C1.class, C2.class, C3.class, C5.class)); - assertThat(reflections.getTypesAnnotatedWith(AI1.class), metaAnnotatedWith(AI1.class)); - assertThat(reflections.getTypesAnnotatedWith(AI2.class), are(I2.class, C1.class, C2.class, C3.class, C5.class)); - assertThat(reflections.getTypesAnnotatedWith(AI2.class), metaAnnotatedWith(AI2.class)); assertThat(reflections.getTypesAnnotatedWith(AM1.class), isEmpty); //annotation member value matching AC2 ac2 = new AC2() { - public String value() {return "ugh?!";} + public String value() {return "ac2";} public Class annotationType() {return AC2.class;}}; assertThat(reflections.getTypesAnnotatedWith(ac2), are(C3.class, C5.class, I3.class, C6.class, AC3.class, C7.class)); @@ -103,73 +88,60 @@ public void testTypesAnnotatedWith() { } @Test - public void testMethodsAnnotatedWith() { - try { - assertThat(reflections.getMethodsAnnotatedWith(AM1.class), - are(C4.class.getDeclaredMethod("m1"), - C4.class.getDeclaredMethod("m1", int.class, String[].class), - C4.class.getDeclaredMethod("m1", int[][].class, String[][].class), - C4.class.getDeclaredMethod("m3"))); - - AM1 am1 = new AM1() { - public String value() {return "1";} - public Class annotationType() {return AM1.class;} - }; - assertThat(reflections.getMethodsAnnotatedWith(am1), - are(C4.class.getDeclaredMethod("m1"), - C4.class.getDeclaredMethod("m1", int.class, String[].class), - C4.class.getDeclaredMethod("m1", int[][].class, String[][].class))); - } catch (NoSuchMethodException e) { - fail(); - } + public void testMethodsAnnotatedWith() throws NoSuchMethodException { + assertThat(reflections.getMethodsAnnotatedWith(AM1.class), + are(C4.class.getDeclaredMethod("m1"), + C4.class.getDeclaredMethod("m1", int.class, String[].class), + C4.class.getDeclaredMethod("m1", int[][].class, String[][].class), + C4.class.getDeclaredMethod("m3"))); + + AM1 am1 = new AM1() { + public String value() {return "1";} + public Class annotationType() {return AM1.class;} + }; + assertThat(reflections.getMethodsAnnotatedWith(am1), + are(C4.class.getDeclaredMethod("m1"), + C4.class.getDeclaredMethod("m1", int.class, String[].class), + C4.class.getDeclaredMethod("m1", int[][].class, String[][].class))); } @Test - public void testConstructorsAnnotatedWith() { - try { - assertThat(reflections.getConstructorsAnnotatedWith(AM1.class), - are(C4.class.getDeclaredConstructor(String.class))); - - AM1 am1 = new AM1() { - public String value() {return "1";} - public Class annotationType() {return AM1.class;} - }; - assertThat(reflections.getConstructorsAnnotatedWith(am1), - are(C4.class.getDeclaredConstructor(String.class))); - } catch (NoSuchMethodException e) { - fail(); - } + public void testConstructorsAnnotatedWith() throws NoSuchMethodException { + assertThat(reflections.getConstructorsAnnotatedWith(AM1.class), + are(C4.class.getDeclaredConstructor(String.class))); + + AM1 am1 = new AM1() { + public String value() {return "1";} + public Class annotationType() {return AM1.class;} + }; + assertThat(reflections.getConstructorsAnnotatedWith(am1), + are(C4.class.getDeclaredConstructor(String.class))); } @Test - public void testFieldsAnnotatedWith() { - try { - assertThat(reflections.getFieldsAnnotatedWith(AF1.class), - are(C4.class.getDeclaredField("f1"), - C4.class.getDeclaredField("f2") - )); - - assertThat(reflections.getFieldsAnnotatedWith(new AF1() { - public String value() {return "2";} - public Class annotationType() {return AF1.class;}}), - are(C4.class.getDeclaredField("f2"))); - } catch (NoSuchFieldException e) { - fail(); - } + public void testFieldsAnnotatedWith() throws NoSuchFieldException { + assertThat(reflections.getFieldsAnnotatedWith(AF1.class), + are(C4.class.getDeclaredField("f1"), + C4.class.getDeclaredField("f2") + )); + + assertThat(reflections.getFieldsAnnotatedWith(new AF1() { + public String value() {return "2";} + public Class annotationType() {return AF1.class;}}), + are(C4.class.getDeclaredField("f2"))); } @Test - public void testMethodParameter() { - try { - assertThat(reflections.getMethodsMatchParams(String.class), - are(C4.class.getDeclaredMethod("m4", String.class), Usage.C1.class.getDeclaredMethod("method", String.class))); + public void testMethodParameter() throws NoSuchMethodException { + assertThat(reflections.getMethodsWithParameter(String.class), + are(C4.class.getDeclaredMethod("m4", String.class), UsageTestModel.C1.class.getDeclaredMethod("method", String.class))); - assertThat(reflections.getMethodsMatchParams(), + assertThat(reflections.getMethodsWithSignature(), are(C4.class.getDeclaredMethod("m1"), C4.class.getDeclaredMethod("m3"), AC2.class.getMethod("value"), AF1.class.getMethod("value"), AM1.class.getMethod("value"), - Usage.C1.class.getDeclaredMethod("method"), Usage.C2.class.getDeclaredMethod("method"))); + UsageTestModel.C1.class.getDeclaredMethod("method"), UsageTestModel.C2.class.getDeclaredMethod("method"))); - assertThat(reflections.getMethodsMatchParams(int[][].class, String[][].class), + assertThat(reflections.getMethodsWithSignature(int[][].class, String[][].class), are(C4.class.getDeclaredMethod("m1", int[][].class, String[][].class))); assertThat(reflections.getMethodsReturn(int.class), @@ -181,107 +153,85 @@ public void testMethodParameter() { assertThat(reflections.getMethodsReturn(void.class), are(C4.class.getDeclaredMethod("m1"), C4.class.getDeclaredMethod("m1", int.class, String[].class), - C4.class.getDeclaredMethod("m1", int[][].class, String[][].class), Usage.C1.class.getDeclaredMethod("method"), - Usage.C1.class.getDeclaredMethod("method", String.class), Usage.C2.class.getDeclaredMethod("method"))); - - assertThat(reflections.getMethodsWithAnyParamAnnotated(AM1.class), - are(C4.class.getDeclaredMethod("m4", String.class))); + C4.class.getDeclaredMethod("m1", int[][].class, String[][].class), UsageTestModel.C1.class.getDeclaredMethod("method"), + UsageTestModel.C1.class.getDeclaredMethod("method", String.class), UsageTestModel.C2.class.getDeclaredMethod("method"))); - assertThat(reflections.getMethodsWithAnyParamAnnotated( - new AM1() { - public String value() { return "2"; } - public Class annotationType() { return AM1.class; } - }), + assertThat(reflections.getMethodsWithParameter(AM1.class), are(C4.class.getDeclaredMethod("m4", String.class))); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } } @Test public void testConstructorParameter() throws NoSuchMethodException { - assertThat(reflections.getConstructorsMatchParams(String.class), + assertThat(reflections.getConstructorsWithParameter(String.class), are(C4.class.getDeclaredConstructor(String.class))); - assertThat(reflections.getConstructorsMatchParams(), + assertThat(reflections.getConstructorsWithSignature(), are(C1.class.getDeclaredConstructor(), C2.class.getDeclaredConstructor(), C3.class.getDeclaredConstructor(), C4.class.getDeclaredConstructor(), C5.class.getDeclaredConstructor(), C6.class.getDeclaredConstructor(), - C7.class.getDeclaredConstructor(), Usage.C1.class.getDeclaredConstructor(), Usage.C2.class.getDeclaredConstructor())); - - assertThat(reflections.getConstructorsWithAnyParamAnnotated(AM1.class), - are(C4.class.getDeclaredConstructor(String.class))); + C7.class.getDeclaredConstructor(), UsageTestModel.C1.class.getDeclaredConstructor(), UsageTestModel.C2.class.getDeclaredConstructor())); - assertThat(reflections.getConstructorsWithAnyParamAnnotated( - new AM1() { - public String value() { return "1"; } - public Class annotationType() { return AM1.class; } - }), + assertThat(reflections.getConstructorsWithParameter(AM1.class), are(C4.class.getDeclaredConstructor(String.class))); } @Test public void testResourcesScanner() { - Predicate filter = new FilterBuilder().include(".*\\.xml").exclude(".*testModel-reflections\\.xml"); + Predicate filter = new FilterBuilder().includePattern(".*\\.xml").excludePattern(".*testModel-reflections\\.xml"); Reflections reflections = new Reflections(new ConfigurationBuilder() .filterInputsBy(filter) - .setScanners(new ResourcesScanner()) + .setScanners(Scanners.Resources) .setUrls(Collections.singletonList(ClasspathHelper.forClass(TestModel.class)))); - Set resolved = reflections.getResources(Pattern.compile(".*resource1-reflections\\.xml")); + Collection resolved = reflections.getResources(Pattern.compile(".*resource1-reflections\\.xml")); assertThat(resolved, are("META-INF/reflections/resource1-reflections.xml")); - Set resources = reflections.getStore().keys(index(ResourcesScanner.class)); - assertThat(resources, are("resource1-reflections.xml", "resource2-reflections.xml")); + Collection resources = reflections.getResources(".*"); + assertThat(resources, are("META-INF/reflections/resource1-reflections.xml", "META-INF/reflections/inner/resource2-reflections.xml")); } @Test public void testMethodParameterNames() throws NoSuchMethodException { - assertEquals(reflections.getMethodParamNames(C4.class.getDeclaredMethod("m3")), + assertEquals(reflections.getMemberParameterNames(C4.class.getDeclaredMethod("m3")), Collections.emptyList()); - assertEquals(reflections.getMethodParamNames(C4.class.getDeclaredMethod("m4", String.class)), + assertEquals(reflections.getMemberParameterNames(C4.class.getDeclaredMethod("m4", String.class)), Collections.singletonList("string")); - assertEquals(reflections.getMethodParamNames(C4.class.getDeclaredMethod("add", int.class, int.class)), + assertEquals(reflections.getMemberParameterNames(C4.class.getDeclaredMethod("add", int.class, int.class)), Arrays.asList("i1", "i2")); - assertEquals(reflections.getConstructorParamNames(C4.class.getDeclaredConstructor(String.class)), + assertEquals(reflections.getMemberParameterNames(C4.class.getDeclaredConstructor(String.class)), Collections.singletonList("f1")); } @Test public void testMemberUsageScanner() throws NoSuchFieldException, NoSuchMethodException { //field usage - assertThat(reflections.getFieldUsage(Usage.C1.class.getDeclaredField("c2")), - are(Usage.C1.class.getDeclaredConstructor(), - Usage.C1.class.getDeclaredConstructor(Usage.C2.class), - Usage.C1.class.getDeclaredMethod("method"), - Usage.C1.class.getDeclaredMethod("method", String.class))); + assertThat(reflections.getMemberUsage(UsageTestModel.C1.class.getDeclaredField("c2")), + are(UsageTestModel.C1.class.getDeclaredConstructor(), + UsageTestModel.C1.class.getDeclaredConstructor(UsageTestModel.C2.class), + UsageTestModel.C1.class.getDeclaredMethod("method"), + UsageTestModel.C1.class.getDeclaredMethod("method", String.class))); //method usage - assertThat(reflections.getMethodUsage(Usage.C1.class.getDeclaredMethod("method")), - are(Usage.C2.class.getDeclaredMethod("method"))); + assertThat(reflections.getMemberUsage(UsageTestModel.C1.class.getDeclaredMethod("method")), + are(UsageTestModel.C2.class.getDeclaredMethod("method"))); - assertThat(reflections.getMethodUsage(Usage.C1.class.getDeclaredMethod("method", String.class)), - are(Usage.C2.class.getDeclaredMethod("method"))); + assertThat(reflections.getMemberUsage(UsageTestModel.C1.class.getDeclaredMethod("method", String.class)), + are(UsageTestModel.C2.class.getDeclaredMethod("method"))); //constructor usage - assertThat(reflections.getConstructorUsage(Usage.C1.class.getDeclaredConstructor()), - are(Usage.C2.class.getDeclaredConstructor(), - Usage.C2.class.getDeclaredMethod("method"))); + assertThat(reflections.getMemberUsage(UsageTestModel.C1.class.getDeclaredConstructor()), + are(UsageTestModel.C2.class.getDeclaredConstructor(), + UsageTestModel.C2.class.getDeclaredMethod("method"))); - assertThat(reflections.getConstructorUsage(Usage.C1.class.getDeclaredConstructor(Usage.C2.class)), - are(Usage.C2.class.getDeclaredMethod("method"))); + assertThat(reflections.getMemberUsage(UsageTestModel.C1.class.getDeclaredConstructor(UsageTestModel.C2.class)), + are(UsageTestModel.C2.class.getDeclaredMethod("method"))); } @Test public void testScannerNotConfigured() { - try { - new Reflections(TestModel.class, TestModelFilter).getMethodsAnnotatedWith(AC1.class); - fail(); - } catch (ReflectionsException e) { - assertEquals(e.getMessage(), "Scanner " + MethodAnnotationsScanner.class.getSimpleName() + " was not configured"); - } + assertTrue(new Reflections(TestModel.class, TestModelFilter).getMethodsAnnotatedWith(AC1.class).isEmpty()); } // @@ -294,7 +244,7 @@ public static String getUserDir() { return file.getAbsolutePath(); } - private final BaseMatcher>> isEmpty = new BaseMatcher>>() { + private final BaseMatcher>> isEmpty = new BaseMatcher>>() { public boolean matches(Object o) { return ((Collection) o).isEmpty(); } @@ -308,9 +258,9 @@ private abstract static class Match extends BaseMatcher { public void describeTo(Description description) { } } - public static Matcher> are(final T... ts) { + public static Matcher> are(final T... ts) { final Collection c1 = Arrays.asList(ts); - return new Match>() { + return new Match>() { public boolean matches(Object o) { Collection c2 = (Collection) o; return c1.containsAll(c2) && c2.containsAll(c1); @@ -323,32 +273,21 @@ public void describeTo(Description description) { }; } - private Matcher>> annotatedWith(final Class annotation) { - return new Match>>() { - public boolean matches(Object o) { - for (Class c : (Iterable>) o) { - if (!annotationTypes(Arrays.asList(c.getAnnotations())).contains(annotation)) return false; - } - return true; - } - }; + @SafeVarargs + public static Matcher> equalTo(T... operand) { + return IsEqual.equalTo(new HashSet<>(Arrays.asList(operand))); } - private Matcher>> metaAnnotatedWith(final Class annotation) { - return new Match>>() { + @SafeVarargs + public final Matcher> equalToNames(T... operand) { + return IsEqual.equalTo(new HashSet<>(toNames(operand))); + } + + private Matcher>> annotatedWith(final Class annotation) { + return new Match>>() { public boolean matches(Object o) { for (Class c : (Iterable>) o) { - Set result = new HashSet<>(); - List stack = new ArrayList<>(ReflectionUtils.getAllSuperTypes(c)); - while (!stack.isEmpty()) { - Class next = stack.remove(0); - if (result.add(next)) { - for (Class ac : annotationTypes(Arrays.asList(next.getDeclaredAnnotations()))) { - if (!result.contains(ac) && !stack.contains(ac)) stack.add(ac); - } - } - } - if (!result.contains(annotation)) return false; + if (!annotationTypes(Arrays.asList(c.getAnnotations())).contains(annotation)) return false; } return true; } diff --git a/src/test/java/org/reflections/ReflectionsThreadSafenessTest.java b/src/test/java/org/reflections/ReflectionsThreadSafenessTest.java deleted file mode 100644 index b7f23e14..00000000 --- a/src/test/java/org/reflections/ReflectionsThreadSafenessTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.reflections; - -import org.junit.Test; -import org.reflections.scanners.SubTypesScanner; -import org.reflections.util.ClasspathHelper; -import org.reflections.util.ConfigurationBuilder; -import org.slf4j.Logger; - -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import static java.util.Collections.singletonList; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.junit.Assert.assertEquals; - -public class ReflectionsThreadSafenessTest { - - /** - * https://github.com/ronmamo/reflections/issues/81 - */ - @Test - public void reflections_scan_is_thread_safe() throws Exception { - - Callable>> callable = () -> { - final Reflections reflections = new Reflections(new ConfigurationBuilder() - .setUrls(singletonList(ClasspathHelper.forClass(Logger.class))) - .setScanners(new SubTypesScanner(false))); - - return reflections.getSubTypesOf(Logger.class); - }; - - final ExecutorService pool = Executors.newFixedThreadPool(2); - - final Future first = pool.submit(callable); - final Future second = pool.submit(callable); - - assertEquals(first.get(5, SECONDS), second.get(5, SECONDS)); - } -} diff --git a/src/test/java/org/reflections/TestModel.java b/src/test/java/org/reflections/TestModel.java index 1d059112..06ba7284 100644 --- a/src/test/java/org/reflections/TestModel.java +++ b/src/test/java/org/reflections/TestModel.java @@ -1,12 +1,10 @@ package org.reflections; -import java.lang.annotation.Retention; import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; + import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * - */ @SuppressWarnings({"ALL"}) public interface TestModel { public @Retention(RUNTIME) @Inherited @interface MAI1 {} @@ -22,8 +20,8 @@ public interface TestModel { public abstract String value(); } - public @AC2("grr...") class C2 extends C1 {} - public @AC2("ugh?!") class C3 extends C1 {} + public @AC2("") class C2 extends C1 {} + public @AC2("ac2") class C3 extends C1 {} public @Retention(RUNTIME) @interface AM1 { public abstract String value(); @@ -49,28 +47,10 @@ public C4() { } } public class C5 extends C3 {} - public @AC2("ugh?!") interface I3 {} + public @AC2("ac2") interface I3 {} public class C6 implements I3 {} - public @Retention(RUNTIME) @AC2("ugh?!") @interface AC3 { } + public @Retention(RUNTIME) @AC2("ac2") @interface AC3 { } public @AC3 class C7 {} - public interface Usage { - public static class C1 { - C2 c2 = new C2(); - public C1() { } - public C1(C2 c2) { this.c2 = c2; } - public void method() { c2.method(); } - public void method(String string) { c2.method(); } - } - public static class C2 { - C1 c1 = new C1(); - public void method() { - c1 = new C1(); - c1 = new C1(this); - c1.method(); - c1.method(""); - } - } - } } diff --git a/src/test/java/org/reflections/UsageTestModel.java b/src/test/java/org/reflections/UsageTestModel.java new file mode 100644 index 00000000..ec2d439f --- /dev/null +++ b/src/test/java/org/reflections/UsageTestModel.java @@ -0,0 +1,21 @@ +package org.reflections; + +public interface UsageTestModel { + class C1 { + C2 c2 = new C2(); + public C1() {} + public C1(C2 c2) {this.c2 = c2;} + public void method() {c2.method();} + public void method(String string) {c2.method();} + } + + class C2 { + C1 c1 = new C1(); + public void method() { + c1 = new C1(); + c1 = new C1(this); + c1.method(); + c1.method(""); + } + } +} diff --git a/src/test/java/org/reflections/VfsTest.java b/src/test/java/org/reflections/VfsTest.java index 99ccf49c..ea898c68 100644 --- a/src/test/java/org/reflections/VfsTest.java +++ b/src/test/java/org/reflections/VfsTest.java @@ -1,8 +1,6 @@ package org.reflections; -import javassist.bytecode.ClassFile; -import org.junit.Test; -import org.reflections.adapters.JavassistAdapter; +import org.junit.jupiter.api.Test; import org.reflections.util.ClasspathHelper; import org.reflections.vfs.SystemDir; import org.reflections.vfs.Vfs; @@ -11,14 +9,13 @@ import java.io.File; import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import static java.text.MessageFormat.format; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; -/** - * - */ public class VfsTest { @Test @@ -83,7 +80,7 @@ public void testJarInputStream() throws Exception { try { testVfsDir(Vfs.DefaultUrlTypes.jarInputStream.createDir(url)); fail(); - } catch (NullPointerException e) { + } catch (AssertionError e) { // expected } } @@ -118,17 +115,10 @@ public void vfsFromDirWithJarInName() throws MalformedURLException { } private void testVfsDir(Vfs.Dir dir) { - JavassistAdapter mdAdapter = new JavassistAdapter(); - Vfs.File file = null; - for (Vfs.File f : dir.getFiles()) { - if (f.getRelativePath().endsWith(".class")) { - file = f; - break; - } + List files = new ArrayList<>(); + for (Vfs.File file : dir.getFiles()) { + files.add(file); } - - ClassFile stringCF = mdAdapter.getOrCreateClassObject(file); - String className = mdAdapter.getClassName(stringCF); - assertFalse(className.isEmpty()); + assertFalse(files.isEmpty()); } } \ No newline at end of file diff --git a/src/test/java/org/reflections/util/ConfigurationBuilderTest.java b/src/test/java/org/reflections/util/ConfigurationBuilderTest.java deleted file mode 100644 index 46a169ad..00000000 --- a/src/test/java/org/reflections/util/ConfigurationBuilderTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.reflections.util; - -import org.junit.Test; - -import static org.junit.Assert.*; - -public class ConfigurationBuilderTest -{ - @Test - public void testCallingAddClassLoaderMoreThanOnce() - { - ClassLoader fooClassLoader = new ClassLoader() { }; - ClassLoader barClassLoader = new ClassLoader() { }; - - ConfigurationBuilder configurationBuilder = new ConfigurationBuilder() - .addClassLoader( fooClassLoader ); - - // Attempt to add a second class loader - configurationBuilder.addClassLoader( barClassLoader ); - assertTrue( true ); - } -} \ No newline at end of file diff --git a/src/test/resources/META-INF/reflections/inner/resource2-reflections.xml b/src/test/resources/META-INF/reflections/inner/resource2-reflections.xml index ea1acceb..b41f42a5 100644 --- a/src/test/resources/META-INF/reflections/inner/resource2-reflections.xml +++ b/src/test/resources/META-INF/reflections/inner/resource2-reflections.xml @@ -1,7 +1,7 @@ - + org.reflections.TestModel$AF1 @@ -9,5 +9,5 @@ org.reflections.TestModel$C4.f2 - + diff --git a/src/test/resources/META-INF/reflections/resource1-reflections.xml b/src/test/resources/META-INF/reflections/resource1-reflections.xml index 6815e64d..4b24e9d3 100644 --- a/src/test/resources/META-INF/reflections/resource1-reflections.xml +++ b/src/test/resources/META-INF/reflections/resource1-reflections.xml @@ -1,7 +1,7 @@ - + org.reflections.TestModel$AM1 @@ -12,5 +12,5 @@ org.reflections.TestModel$C4.m1() - + diff --git a/src/test/resources/META-INF/reflections/saved-testModel-reflections.json b/src/test/resources/META-INF/reflections/saved-testModel-reflections.json new file mode 100644 index 00000000..a0571eec --- /dev/null +++ b/src/test/resources/META-INF/reflections/saved-testModel-reflections.json @@ -0,0 +1,200 @@ +{ + "store": { + "TypesAnnotated": { + "org.reflections.TestModel$AC3": [ + "org.reflections.TestModel$C7" + ], + "org.reflections.TestModel$MAI1": [ + "org.reflections.TestModel$AI1" + ], + "org.reflections.TestModel$AC2": [ + "org.reflections.TestModel$C3", + "org.reflections.TestModel$C2", + "org.reflections.TestModel$AC3", + "org.reflections.TestModel$I3" + ], + "org.reflections.TestModel$AC1n": [ + "org.reflections.TestModel$C1" + ], + "org.reflections.TestModel$AC1": [ + "org.reflections.TestModel$C1" + ], + "org.reflections.TestModel$AI2": [ + "org.reflections.TestModel$I2" + ], + "java.lang.annotation.Inherited": [ + "org.reflections.TestModel$MAI1", + "org.reflections.TestModel$AC1", + "org.reflections.TestModel$AI2" + ], + "org.reflections.TestModel$AI1": [ + "org.reflections.TestModel$I1" + ], + "java.lang.annotation.Retention": [ + "org.reflections.TestModel$AC3", + "org.reflections.TestModel$MAI1", + "org.reflections.TestModel$AC1n", + "org.reflections.TestModel$AC2", + "org.reflections.TestModel$AC1", + "org.reflections.TestModel$AI2", + "org.reflections.TestModel$AI1", + "org.reflections.TestModel$AF1", + "org.reflections.TestModel$AM1" + ] + }, + "MethodsSignature": { + "[org.reflections.TestModel$C2]": [ + "org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2)" + ], + "[]": [ + "org.reflections.TestModel$C7.\u003cinit\u003e()", + "org.reflections.TestModel$AF1.value()", + "org.reflections.TestModel$C4.m1()", + "org.reflections.TestModel$C4.\u003cinit\u003e()", + "org.reflections.TestModel$C2.\u003cinit\u003e()", + "org.reflections.TestModel$C4.m3()", + "org.reflections.TestModel$C3.\u003cinit\u003e()", + "org.reflections.TestModel$C6.\u003cinit\u003e()", + "org.reflections.TestModel$C5.\u003cinit\u003e()", + "org.reflections.TestModel$AM1.value()", + "org.reflections.TestModel$AC2.value()", + "org.reflections.TestModel$C1.\u003cinit\u003e()" + ], + "[int, java.lang.String[]]": [ + "org.reflections.TestModel$C4.m1(int, java.lang.String[])" + ], + "[int, int]": [ + "org.reflections.TestModel$C4.add(int, int)" + ], + "[java.lang.String]": [ + "org.reflections.TestModel$C4.m4(java.lang.String)", + "org.reflections.TestModel$C4.\u003cinit\u003e(java.lang.String)" + ], + "[int[][], java.lang.String[][]]": [ + "org.reflections.TestModel$C4.m1(int[][], java.lang.String[][])" + ] + }, + "MethodsParameter": { + "org.reflections.TestModel$C2": [ + "org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2)" + ], + "int[][]": [ + "org.reflections.TestModel$C4.m1(int[][], java.lang.String[][])" + ], + "java.lang.String[][]": [ + "org.reflections.TestModel$C4.m1(int[][], java.lang.String[][])" + ], + "java.lang.String": [ + "org.reflections.TestModel$C4.m4(java.lang.String)", + "org.reflections.TestModel$C4.\u003cinit\u003e(java.lang.String)" + ], + "java.lang.String[]": [ + "org.reflections.TestModel$C4.m1(int, java.lang.String[])" + ], + "org.reflections.TestModel$AM1": [ + "org.reflections.TestModel$C4.m4(java.lang.String)", + "org.reflections.TestModel$C4.\u003cinit\u003e(java.lang.String)" + ], + "int": [ + "org.reflections.TestModel$C4.m1(int, java.lang.String[])", + "org.reflections.TestModel$C4.add(int, int)" + ] + }, + "SubTypes": { + "org.reflections.TestModel$C3": [ + "org.reflections.TestModel$C5" + ], + "org.reflections.TestModel$C1": [ + "org.reflections.TestModel$C3", + "org.reflections.TestModel$C2" + ], + "java.lang.annotation.Annotation": [ + "org.reflections.TestModel$AC3", + "org.reflections.TestModel$MAI1", + "org.reflections.TestModel$AC2", + "org.reflections.TestModel$AC1n", + "org.reflections.TestModel$AC1", + "org.reflections.TestModel$AI2", + "org.reflections.TestModel$AI1", + "org.reflections.TestModel$AF1", + "org.reflections.TestModel$AM1" + ], + "org.reflections.TestModel$I1": [ + "org.reflections.TestModel$I2" + ], + "org.reflections.TestModel$I3": [ + "org.reflections.TestModel$C6" + ], + "java.lang.Object": [ + "org.reflections.TestModel$AI2", + "org.reflections.TestModel$AI1", + "org.reflections.TestModel$AF1", + "org.reflections.TestModel$C7", + "org.reflections.TestModel$AM1", + "org.reflections.TestModel$C6", + "org.reflections.TestModel$AC3", + "org.reflections.TestModel$C4", + "org.reflections.TestModel$MAI1", + "org.reflections.TestModel$AC2", + "org.reflections.TestModel$C1", + "org.reflections.TestModel$AC1n", + "org.reflections.TestModel$AC1", + "org.reflections.TestModel$I1", + "org.reflections.TestModel$I3", + "org.reflections.TestModel$I2" + ], + "org.reflections.TestModel$I2": [ + "org.reflections.TestModel$C1" + ] + }, + "MethodsReturn": { + "org.reflections.TestModel$C3": [ + "org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2)" + ], + "void": [ + "org.reflections.TestModel$C4.m1(int, java.lang.String[])", + "org.reflections.TestModel$C4.m1()", + "org.reflections.TestModel$C4.m1(int[][], java.lang.String[][])" + ], + "java.lang.String": [ + "org.reflections.TestModel$AF1.value()", + "org.reflections.TestModel$C4.m4(java.lang.String)", + "org.reflections.TestModel$C4.m3()", + "org.reflections.TestModel$AM1.value()", + "org.reflections.TestModel$AC2.value()" + ], + "int": [ + "org.reflections.TestModel$C4.add(int, int)" + ] + }, + "MethodsAnnotated": { + "org.reflections.TestModel$AM1": [ + "org.reflections.TestModel$C4.m1(int, java.lang.String[])", + "org.reflections.TestModel$C4.m1()", + "org.reflections.TestModel$C4.m3()", + "org.reflections.TestModel$C4.m1(int[][], java.lang.String[][])", + "org.reflections.TestModel$C4.\u003cinit\u003e(java.lang.String)" + ] + }, + "FieldsAnnotated": { + "org.reflections.TestModel$AF1": [ + "org.reflections.TestModel$C4.f1", + "org.reflections.TestModel$C4.f2" + ] + }, + "Resources": { + "resource1-reflections.xml": [ + "META-INF/reflections/resource1-reflections.xml" + ], + "saved-testModel-reflections.xml": [ + "META-INF/reflections/saved-testModel-reflections.xml" + ], + "resource2-reflections.xml": [ + "META-INF/reflections/inner/resource2-reflections.xml" + ], + "testModel-reflections.xml": [ + "META-INF/reflections/testModel-reflections.xml" + ] + } + } +} \ No newline at end of file diff --git a/src/test/resources/META-INF/reflections/saved-testModel-reflections.xml b/src/test/resources/META-INF/reflections/saved-testModel-reflections.xml new file mode 100644 index 00000000..1a5ad280 --- /dev/null +++ b/src/test/resources/META-INF/reflections/saved-testModel-reflections.xml @@ -0,0 +1,317 @@ + + + + + + org.reflections.TestModel$AC3 + + org.reflections.TestModel$C7 + + + + org.reflections.TestModel$MAI1 + + org.reflections.TestModel$AI1 + + + + org.reflections.TestModel$AC2 + + org.reflections.TestModel$C3 + org.reflections.TestModel$C2 + org.reflections.TestModel$AC3 + org.reflections.TestModel$I3 + + + + org.reflections.TestModel$AC1n + + org.reflections.TestModel$C1 + + + + org.reflections.TestModel$AC1 + + org.reflections.TestModel$C1 + + + + org.reflections.TestModel$AI2 + + org.reflections.TestModel$I2 + + + + java.lang.annotation.Inherited + + org.reflections.TestModel$MAI1 + org.reflections.TestModel$AC1 + org.reflections.TestModel$AI2 + + + + org.reflections.TestModel$AI1 + + org.reflections.TestModel$I1 + + + + java.lang.annotation.Retention + + org.reflections.TestModel$AC3 + org.reflections.TestModel$MAI1 + org.reflections.TestModel$AC1n + org.reflections.TestModel$AC2 + org.reflections.TestModel$AC1 + org.reflections.TestModel$AI2 + org.reflections.TestModel$AI1 + org.reflections.TestModel$AF1 + org.reflections.TestModel$AM1 + + + + + + [org.reflections.TestModel$C2] + + org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2) + + + + [] + + org.reflections.TestModel$C7.<init>() + org.reflections.TestModel$AF1.value() + org.reflections.TestModel$C4.m1() + org.reflections.TestModel$C4.<init>() + org.reflections.TestModel$C2.<init>() + org.reflections.TestModel$C4.m3() + org.reflections.TestModel$C3.<init>() + org.reflections.TestModel$C6.<init>() + org.reflections.TestModel$C5.<init>() + org.reflections.TestModel$AM1.value() + org.reflections.TestModel$AC2.value() + org.reflections.TestModel$C1.<init>() + + + + [int, java.lang.String[]] + + org.reflections.TestModel$C4.m1(int, java.lang.String[]) + + + + [int, int] + + org.reflections.TestModel$C4.add(int, int) + + + + [java.lang.String] + + org.reflections.TestModel$C4.m4(java.lang.String) + org.reflections.TestModel$C4.<init>(java.lang.String) + + + + [int[][], java.lang.String[][]] + + org.reflections.TestModel$C4.m1(int[][], java.lang.String[][]) + + + + + + org.reflections.TestModel$C2 + + org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2) + + + + int[][] + + org.reflections.TestModel$C4.m1(int[][], java.lang.String[][]) + + + + java.lang.String[][] + + org.reflections.TestModel$C4.m1(int[][], java.lang.String[][]) + + + + java.lang.String + + org.reflections.TestModel$C4.m4(java.lang.String) + org.reflections.TestModel$C4.<init>(java.lang.String) + + + + java.lang.String[] + + org.reflections.TestModel$C4.m1(int, java.lang.String[]) + + + + org.reflections.TestModel$AM1 + + org.reflections.TestModel$C4.m4(java.lang.String) + org.reflections.TestModel$C4.<init>(java.lang.String) + + + + int + + org.reflections.TestModel$C4.m1(int, java.lang.String[]) + org.reflections.TestModel$C4.add(int, int) + + + + + + org.reflections.TestModel$C3 + + org.reflections.TestModel$C5 + + + + org.reflections.TestModel$C1 + + org.reflections.TestModel$C3 + org.reflections.TestModel$C2 + + + + java.lang.annotation.Annotation + + org.reflections.TestModel$AC3 + org.reflections.TestModel$MAI1 + org.reflections.TestModel$AC2 + org.reflections.TestModel$AC1n + org.reflections.TestModel$AC1 + org.reflections.TestModel$AI2 + org.reflections.TestModel$AI1 + org.reflections.TestModel$AF1 + org.reflections.TestModel$AM1 + + + + org.reflections.TestModel$I1 + + org.reflections.TestModel$I2 + + + + org.reflections.TestModel$I3 + + org.reflections.TestModel$C6 + + + + java.lang.Object + + org.reflections.TestModel$AI2 + org.reflections.TestModel$AI1 + org.reflections.TestModel$AF1 + org.reflections.TestModel$C7 + org.reflections.TestModel$AM1 + org.reflections.TestModel$C6 + org.reflections.TestModel$AC3 + org.reflections.TestModel$C4 + org.reflections.TestModel$MAI1 + org.reflections.TestModel$AC2 + org.reflections.TestModel$C1 + org.reflections.TestModel$AC1n + org.reflections.TestModel$AC1 + org.reflections.TestModel$I1 + org.reflections.TestModel$I3 + org.reflections.TestModel$I2 + + + + org.reflections.TestModel$I2 + + org.reflections.TestModel$C1 + + + + + + org.reflections.TestModel$C3 + + org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2) + + + + void + + org.reflections.TestModel$C4.m1(int, java.lang.String[]) + org.reflections.TestModel$C4.m1() + org.reflections.TestModel$C4.m1(int[][], java.lang.String[][]) + + + + java.lang.String + + org.reflections.TestModel$AF1.value() + org.reflections.TestModel$C4.m4(java.lang.String) + org.reflections.TestModel$C4.m3() + org.reflections.TestModel$AM1.value() + org.reflections.TestModel$AC2.value() + + + + int + + org.reflections.TestModel$C4.add(int, int) + + + + + + org.reflections.TestModel$AM1 + + org.reflections.TestModel$C4.m1(int, java.lang.String[]) + org.reflections.TestModel$C4.m1() + org.reflections.TestModel$C4.m3() + org.reflections.TestModel$C4.m1(int[][], java.lang.String[][]) + org.reflections.TestModel$C4.<init>(java.lang.String) + + + + + + org.reflections.TestModel$AF1 + + org.reflections.TestModel$C4.f1 + org.reflections.TestModel$C4.f2 + + + + + + resource1-reflections.xml + + META-INF/reflections/resource1-reflections.xml + + + + saved-testModel-reflections.xml + + META-INF/reflections/saved-testModel-reflections.xml + + + + resource2-reflections.xml + + META-INF/reflections/inner/resource2-reflections.xml + + + + testModel-reflections.xml + + META-INF/reflections/testModel-reflections.xml + + + + diff --git a/src/test/resources/META-INF/reflections/testModel-reflections.json b/src/test/resources/META-INF/reflections/testModel-reflections.json new file mode 100644 index 00000000..a0571eec --- /dev/null +++ b/src/test/resources/META-INF/reflections/testModel-reflections.json @@ -0,0 +1,200 @@ +{ + "store": { + "TypesAnnotated": { + "org.reflections.TestModel$AC3": [ + "org.reflections.TestModel$C7" + ], + "org.reflections.TestModel$MAI1": [ + "org.reflections.TestModel$AI1" + ], + "org.reflections.TestModel$AC2": [ + "org.reflections.TestModel$C3", + "org.reflections.TestModel$C2", + "org.reflections.TestModel$AC3", + "org.reflections.TestModel$I3" + ], + "org.reflections.TestModel$AC1n": [ + "org.reflections.TestModel$C1" + ], + "org.reflections.TestModel$AC1": [ + "org.reflections.TestModel$C1" + ], + "org.reflections.TestModel$AI2": [ + "org.reflections.TestModel$I2" + ], + "java.lang.annotation.Inherited": [ + "org.reflections.TestModel$MAI1", + "org.reflections.TestModel$AC1", + "org.reflections.TestModel$AI2" + ], + "org.reflections.TestModel$AI1": [ + "org.reflections.TestModel$I1" + ], + "java.lang.annotation.Retention": [ + "org.reflections.TestModel$AC3", + "org.reflections.TestModel$MAI1", + "org.reflections.TestModel$AC1n", + "org.reflections.TestModel$AC2", + "org.reflections.TestModel$AC1", + "org.reflections.TestModel$AI2", + "org.reflections.TestModel$AI1", + "org.reflections.TestModel$AF1", + "org.reflections.TestModel$AM1" + ] + }, + "MethodsSignature": { + "[org.reflections.TestModel$C2]": [ + "org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2)" + ], + "[]": [ + "org.reflections.TestModel$C7.\u003cinit\u003e()", + "org.reflections.TestModel$AF1.value()", + "org.reflections.TestModel$C4.m1()", + "org.reflections.TestModel$C4.\u003cinit\u003e()", + "org.reflections.TestModel$C2.\u003cinit\u003e()", + "org.reflections.TestModel$C4.m3()", + "org.reflections.TestModel$C3.\u003cinit\u003e()", + "org.reflections.TestModel$C6.\u003cinit\u003e()", + "org.reflections.TestModel$C5.\u003cinit\u003e()", + "org.reflections.TestModel$AM1.value()", + "org.reflections.TestModel$AC2.value()", + "org.reflections.TestModel$C1.\u003cinit\u003e()" + ], + "[int, java.lang.String[]]": [ + "org.reflections.TestModel$C4.m1(int, java.lang.String[])" + ], + "[int, int]": [ + "org.reflections.TestModel$C4.add(int, int)" + ], + "[java.lang.String]": [ + "org.reflections.TestModel$C4.m4(java.lang.String)", + "org.reflections.TestModel$C4.\u003cinit\u003e(java.lang.String)" + ], + "[int[][], java.lang.String[][]]": [ + "org.reflections.TestModel$C4.m1(int[][], java.lang.String[][])" + ] + }, + "MethodsParameter": { + "org.reflections.TestModel$C2": [ + "org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2)" + ], + "int[][]": [ + "org.reflections.TestModel$C4.m1(int[][], java.lang.String[][])" + ], + "java.lang.String[][]": [ + "org.reflections.TestModel$C4.m1(int[][], java.lang.String[][])" + ], + "java.lang.String": [ + "org.reflections.TestModel$C4.m4(java.lang.String)", + "org.reflections.TestModel$C4.\u003cinit\u003e(java.lang.String)" + ], + "java.lang.String[]": [ + "org.reflections.TestModel$C4.m1(int, java.lang.String[])" + ], + "org.reflections.TestModel$AM1": [ + "org.reflections.TestModel$C4.m4(java.lang.String)", + "org.reflections.TestModel$C4.\u003cinit\u003e(java.lang.String)" + ], + "int": [ + "org.reflections.TestModel$C4.m1(int, java.lang.String[])", + "org.reflections.TestModel$C4.add(int, int)" + ] + }, + "SubTypes": { + "org.reflections.TestModel$C3": [ + "org.reflections.TestModel$C5" + ], + "org.reflections.TestModel$C1": [ + "org.reflections.TestModel$C3", + "org.reflections.TestModel$C2" + ], + "java.lang.annotation.Annotation": [ + "org.reflections.TestModel$AC3", + "org.reflections.TestModel$MAI1", + "org.reflections.TestModel$AC2", + "org.reflections.TestModel$AC1n", + "org.reflections.TestModel$AC1", + "org.reflections.TestModel$AI2", + "org.reflections.TestModel$AI1", + "org.reflections.TestModel$AF1", + "org.reflections.TestModel$AM1" + ], + "org.reflections.TestModel$I1": [ + "org.reflections.TestModel$I2" + ], + "org.reflections.TestModel$I3": [ + "org.reflections.TestModel$C6" + ], + "java.lang.Object": [ + "org.reflections.TestModel$AI2", + "org.reflections.TestModel$AI1", + "org.reflections.TestModel$AF1", + "org.reflections.TestModel$C7", + "org.reflections.TestModel$AM1", + "org.reflections.TestModel$C6", + "org.reflections.TestModel$AC3", + "org.reflections.TestModel$C4", + "org.reflections.TestModel$MAI1", + "org.reflections.TestModel$AC2", + "org.reflections.TestModel$C1", + "org.reflections.TestModel$AC1n", + "org.reflections.TestModel$AC1", + "org.reflections.TestModel$I1", + "org.reflections.TestModel$I3", + "org.reflections.TestModel$I2" + ], + "org.reflections.TestModel$I2": [ + "org.reflections.TestModel$C1" + ] + }, + "MethodsReturn": { + "org.reflections.TestModel$C3": [ + "org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2)" + ], + "void": [ + "org.reflections.TestModel$C4.m1(int, java.lang.String[])", + "org.reflections.TestModel$C4.m1()", + "org.reflections.TestModel$C4.m1(int[][], java.lang.String[][])" + ], + "java.lang.String": [ + "org.reflections.TestModel$AF1.value()", + "org.reflections.TestModel$C4.m4(java.lang.String)", + "org.reflections.TestModel$C4.m3()", + "org.reflections.TestModel$AM1.value()", + "org.reflections.TestModel$AC2.value()" + ], + "int": [ + "org.reflections.TestModel$C4.add(int, int)" + ] + }, + "MethodsAnnotated": { + "org.reflections.TestModel$AM1": [ + "org.reflections.TestModel$C4.m1(int, java.lang.String[])", + "org.reflections.TestModel$C4.m1()", + "org.reflections.TestModel$C4.m3()", + "org.reflections.TestModel$C4.m1(int[][], java.lang.String[][])", + "org.reflections.TestModel$C4.\u003cinit\u003e(java.lang.String)" + ] + }, + "FieldsAnnotated": { + "org.reflections.TestModel$AF1": [ + "org.reflections.TestModel$C4.f1", + "org.reflections.TestModel$C4.f2" + ] + }, + "Resources": { + "resource1-reflections.xml": [ + "META-INF/reflections/resource1-reflections.xml" + ], + "saved-testModel-reflections.xml": [ + "META-INF/reflections/saved-testModel-reflections.xml" + ], + "resource2-reflections.xml": [ + "META-INF/reflections/inner/resource2-reflections.xml" + ], + "testModel-reflections.xml": [ + "META-INF/reflections/testModel-reflections.xml" + ] + } + } +} \ No newline at end of file diff --git a/src/test/resources/META-INF/reflections/testModel-reflections.xml b/src/test/resources/META-INF/reflections/testModel-reflections.xml new file mode 100644 index 00000000..1a5ad280 --- /dev/null +++ b/src/test/resources/META-INF/reflections/testModel-reflections.xml @@ -0,0 +1,317 @@ + + + + + + org.reflections.TestModel$AC3 + + org.reflections.TestModel$C7 + + + + org.reflections.TestModel$MAI1 + + org.reflections.TestModel$AI1 + + + + org.reflections.TestModel$AC2 + + org.reflections.TestModel$C3 + org.reflections.TestModel$C2 + org.reflections.TestModel$AC3 + org.reflections.TestModel$I3 + + + + org.reflections.TestModel$AC1n + + org.reflections.TestModel$C1 + + + + org.reflections.TestModel$AC1 + + org.reflections.TestModel$C1 + + + + org.reflections.TestModel$AI2 + + org.reflections.TestModel$I2 + + + + java.lang.annotation.Inherited + + org.reflections.TestModel$MAI1 + org.reflections.TestModel$AC1 + org.reflections.TestModel$AI2 + + + + org.reflections.TestModel$AI1 + + org.reflections.TestModel$I1 + + + + java.lang.annotation.Retention + + org.reflections.TestModel$AC3 + org.reflections.TestModel$MAI1 + org.reflections.TestModel$AC1n + org.reflections.TestModel$AC2 + org.reflections.TestModel$AC1 + org.reflections.TestModel$AI2 + org.reflections.TestModel$AI1 + org.reflections.TestModel$AF1 + org.reflections.TestModel$AM1 + + + + + + [org.reflections.TestModel$C2] + + org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2) + + + + [] + + org.reflections.TestModel$C7.<init>() + org.reflections.TestModel$AF1.value() + org.reflections.TestModel$C4.m1() + org.reflections.TestModel$C4.<init>() + org.reflections.TestModel$C2.<init>() + org.reflections.TestModel$C4.m3() + org.reflections.TestModel$C3.<init>() + org.reflections.TestModel$C6.<init>() + org.reflections.TestModel$C5.<init>() + org.reflections.TestModel$AM1.value() + org.reflections.TestModel$AC2.value() + org.reflections.TestModel$C1.<init>() + + + + [int, java.lang.String[]] + + org.reflections.TestModel$C4.m1(int, java.lang.String[]) + + + + [int, int] + + org.reflections.TestModel$C4.add(int, int) + + + + [java.lang.String] + + org.reflections.TestModel$C4.m4(java.lang.String) + org.reflections.TestModel$C4.<init>(java.lang.String) + + + + [int[][], java.lang.String[][]] + + org.reflections.TestModel$C4.m1(int[][], java.lang.String[][]) + + + + + + org.reflections.TestModel$C2 + + org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2) + + + + int[][] + + org.reflections.TestModel$C4.m1(int[][], java.lang.String[][]) + + + + java.lang.String[][] + + org.reflections.TestModel$C4.m1(int[][], java.lang.String[][]) + + + + java.lang.String + + org.reflections.TestModel$C4.m4(java.lang.String) + org.reflections.TestModel$C4.<init>(java.lang.String) + + + + java.lang.String[] + + org.reflections.TestModel$C4.m1(int, java.lang.String[]) + + + + org.reflections.TestModel$AM1 + + org.reflections.TestModel$C4.m4(java.lang.String) + org.reflections.TestModel$C4.<init>(java.lang.String) + + + + int + + org.reflections.TestModel$C4.m1(int, java.lang.String[]) + org.reflections.TestModel$C4.add(int, int) + + + + + + org.reflections.TestModel$C3 + + org.reflections.TestModel$C5 + + + + org.reflections.TestModel$C1 + + org.reflections.TestModel$C3 + org.reflections.TestModel$C2 + + + + java.lang.annotation.Annotation + + org.reflections.TestModel$AC3 + org.reflections.TestModel$MAI1 + org.reflections.TestModel$AC2 + org.reflections.TestModel$AC1n + org.reflections.TestModel$AC1 + org.reflections.TestModel$AI2 + org.reflections.TestModel$AI1 + org.reflections.TestModel$AF1 + org.reflections.TestModel$AM1 + + + + org.reflections.TestModel$I1 + + org.reflections.TestModel$I2 + + + + org.reflections.TestModel$I3 + + org.reflections.TestModel$C6 + + + + java.lang.Object + + org.reflections.TestModel$AI2 + org.reflections.TestModel$AI1 + org.reflections.TestModel$AF1 + org.reflections.TestModel$C7 + org.reflections.TestModel$AM1 + org.reflections.TestModel$C6 + org.reflections.TestModel$AC3 + org.reflections.TestModel$C4 + org.reflections.TestModel$MAI1 + org.reflections.TestModel$AC2 + org.reflections.TestModel$C1 + org.reflections.TestModel$AC1n + org.reflections.TestModel$AC1 + org.reflections.TestModel$I1 + org.reflections.TestModel$I3 + org.reflections.TestModel$I2 + + + + org.reflections.TestModel$I2 + + org.reflections.TestModel$C1 + + + + + + org.reflections.TestModel$C3 + + org.reflections.TestModel$C4.c2toC3(org.reflections.TestModel$C2) + + + + void + + org.reflections.TestModel$C4.m1(int, java.lang.String[]) + org.reflections.TestModel$C4.m1() + org.reflections.TestModel$C4.m1(int[][], java.lang.String[][]) + + + + java.lang.String + + org.reflections.TestModel$AF1.value() + org.reflections.TestModel$C4.m4(java.lang.String) + org.reflections.TestModel$C4.m3() + org.reflections.TestModel$AM1.value() + org.reflections.TestModel$AC2.value() + + + + int + + org.reflections.TestModel$C4.add(int, int) + + + + + + org.reflections.TestModel$AM1 + + org.reflections.TestModel$C4.m1(int, java.lang.String[]) + org.reflections.TestModel$C4.m1() + org.reflections.TestModel$C4.m3() + org.reflections.TestModel$C4.m1(int[][], java.lang.String[][]) + org.reflections.TestModel$C4.<init>(java.lang.String) + + + + + + org.reflections.TestModel$AF1 + + org.reflections.TestModel$C4.f1 + org.reflections.TestModel$C4.f2 + + + + + + resource1-reflections.xml + + META-INF/reflections/resource1-reflections.xml + + + + saved-testModel-reflections.xml + + META-INF/reflections/saved-testModel-reflections.xml + + + + resource2-reflections.xml + + META-INF/reflections/inner/resource2-reflections.xml + + + + testModel-reflections.xml + + META-INF/reflections/testModel-reflections.xml + + + +