{@code Set> supertypes = get(SuperTypes.of(type)) + * utils for querying java reflection meta types + * see {@link #SuperTypes}, {@link #Annotations}, {@link #AnnotationTypes}, {@link #Methods}, {@link #Constructors} and {@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 + *
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) diff --git a/src/main/java/org/reflections/Reflections.java b/src/main/java/org/reflections/Reflections.java index 3a297077..71d39d07 100644 --- a/src/main/java/org/reflections/Reflections.java +++ b/src/main/java/org/reflections/Reflections.java @@ -64,7 +64,8 @@ * * *Create Reflections instance, preferably using {@link ConfigurationBuilder}: - *
{@code Reflections reflections = new Reflections( + *{@code + * Reflections reflections = new Reflections( * new ConfigurationBuilder() * .forPackage("com.my.project")); * @@ -87,7 +88,8 @@ *Classloader can optionally be used for resolving runtime classes from names. * *
Query using {@link Reflections#get(QueryFunction)}, such as: - *{@code Set> modules = reflections.get(SubTypes.of(Module.class).asClass()); + * {@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)); diff --git a/src/main/java/org/reflections/scanners/Scanners.java b/src/main/java/org/reflections/scanners/Scanners.java index b47d88aa..37b0eb9e 100644 --- a/src/main/java/org/reflections/scanners/Scanners.java +++ b/src/main/java/org/reflections/scanners/Scanners.java @@ -27,10 +27,13 @@ * {@link #SubTypes} *{@link #TypesAnnotated} *{@link #MethodsAnnotated} + *{@link #ConstructorsAnnotated} *{@link #FieldsAnnotated} *{@link #Resources} *{@link #MethodsParameter} + *{@link #ConstructorsParameter} *{@link #MethodsSignature} + *{@link #ConstructorsSignature} *{@link #MethodsReturn} * * note that scanners must be configured in {@link org.reflections.Configuration} in order to be queried diff --git a/src/main/java/org/reflections/util/JavassistHelper.java b/src/main/java/org/reflections/util/JavassistHelper.java index e3a71e3a..ac04bfc1 100644 --- a/src/main/java/org/reflections/util/JavassistHelper.java +++ b/src/main/java/org/reflections/util/JavassistHelper.java @@ -11,7 +11,6 @@ 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; @@ -22,12 +21,6 @@ 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 ListgetAnnotations(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()); } @@ -70,22 +63,39 @@ public static String getReturnType(MethodInfo method) { 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; - } + public static List
getAnnotations(Function function) { + Function > names = function + .andThen(attribute -> attribute != null ? ((AnnotationsAttribute) attribute).getAnnotations() : null) + .andThen(JavassistHelper::annotationNames); - private static List > getAnnotations(ParameterAnnotationsAttribute attribute) { - return mapList(attribute, ParameterAnnotationsAttribute::getAnnotations, aa -> mapList(aa, a -> a, Annotation::getTypeName)); + List
result = new ArrayList<>(names.apply(AnnotationsAttribute.visibleTag)); + if (includeInvisibleTag) result.addAll(names.apply(AnnotationsAttribute.invisibleTag)); + return result; } - private static List getAnnotations(AnnotationsAttribute attribute) { - return mapList(attribute, AnnotationsAttribute::getAnnotations, Annotation::getTypeName); + public static List > getParametersAnnotations(MethodInfo method) { + Function
>> names = ((Function ) method::getAttribute) + .andThen(attribute -> attribute != null ? ((ParameterAnnotationsAttribute) attribute).getAnnotations() : null) + .andThen((Annotation[][] aa) -> aa != null ? Stream.of(aa).map(JavassistHelper::annotationNames).collect(Collectors.toList()) : Collections.emptyList()); + + List > visibleAnnotations = names.apply(ParameterAnnotationsAttribute.visibleTag); + if (!includeInvisibleTag) return new ArrayList<>(visibleAnnotations); + + List
> invisibleAnnotations = names.apply(ParameterAnnotationsAttribute.invisibleTag); + if (invisibleAnnotations.isEmpty()) return new ArrayList<>(visibleAnnotations); + + // horror + List
> result = new ArrayList<>(); + for (int i = 0; i < Math.max(visibleAnnotations.size(), invisibleAnnotations.size()); i++) { + List
concat = new ArrayList<>(); + if (i < visibleAnnotations.size()) concat.addAll(visibleAnnotations.get(i)); + if (i < invisibleAnnotations.size()) concat.addAll(invisibleAnnotations.get(i)); + result.add(concat); + } + return result; } - // 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(); + private static List annotationNames(Annotation[] annotations) { + return annotations != null ? Stream.of(annotations).map(Annotation::getTypeName).collect(Collectors.toList()) : Collections.emptyList(); } } diff --git a/src/test/java/org/reflections/MyTestModelStore.java b/src/test/java/org/reflections/MyTestModelStore.java index 3ea7b834..1c7126df 100644 --- a/src/test/java/org/reflections/MyTestModelStore.java +++ b/src/test/java/org/reflections/MyTestModelStore.java @@ -36,6 +36,8 @@ interface methods { interface value {} } } + interface TestModel$AM2 { + } interface TestModel$C1 { interface annotations { interface org_reflections_TestModel$AC1 {} diff --git a/src/test/java/org/reflections/ReflectionsQueryTest.java b/src/test/java/org/reflections/ReflectionsQueryTest.java index d3e340f6..4a69f0fe 100644 --- a/src/test/java/org/reflections/ReflectionsQueryTest.java +++ b/src/test/java/org/reflections/ReflectionsQueryTest.java @@ -92,6 +92,10 @@ public void testTypesAnnotatedWithMemberMatching() { reflections.get(TypesAnnotated.with(AC2.class).asClass()), equalTo(C2.class, C3.class, I3.class, AC3.class, C7.class)); + assertThat("direct types annotated with annotation with Retention(CLASS)", + reflections.get(TypesAnnotated.get(AC3.class).asClass()), + equalTo(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)); @@ -221,7 +225,8 @@ public void testMethods() throws NoSuchMethodException { assertThat("methods with return type void", reflections.get(MethodsReturn.of(void.class)), - equalToNames(C4.class.getDeclaredMethod("m1"), + equalToNames( + C4.class.getDeclaredMethod("m1"), C4.class.getDeclaredMethod("m1", int.class, String[].class), C4.class.getDeclaredMethod("m1", int[][].class, String[][].class))); @@ -241,6 +246,12 @@ public Class extends Annotation> annotationType() { 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))); + + assertThat("methods with parameter annotation visible/invisible", + reflections.get(MethodsParameter.with(AM2.class)), + equalToNames( + C4.class.getDeclaredMethod("m4", String.class), + C4.class.getDeclaredMethod("m1", int.class, String[].class))); } @Test @@ -308,7 +319,7 @@ public void testGetAll() { 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$AF1", "org.reflections.TestModel$AM1", "org.reflections.TestModel$AM2", "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")); diff --git a/src/test/java/org/reflections/ReflectionsTest.java b/src/test/java/org/reflections/ReflectionsTest.java index 6f188203..f497cd44 100644 --- a/src/test/java/org/reflections/ReflectionsTest.java +++ b/src/test/java/org/reflections/ReflectionsTest.java @@ -158,6 +158,10 @@ public void testMethodParameter() throws NoSuchMethodException { assertThat(reflections.getMethodsWithParameter(AM1.class), are(C4.class.getDeclaredMethod("m4", String.class))); + + assertThat(reflections.getMethodsWithParameter(AM2.class), + are(C4.class.getDeclaredMethod("m4", String.class), + C4.class.getDeclaredMethod("m1", int.class, String[].class))); } @Test diff --git a/src/test/java/org/reflections/TestModel.java b/src/test/java/org/reflections/TestModel.java index 06ba7284..68b7b9e7 100644 --- a/src/test/java/org/reflections/TestModel.java +++ b/src/test/java/org/reflections/TestModel.java @@ -3,7 +3,7 @@ import java.lang.annotation.Inherited; import java.lang.annotation.Retention; -import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.RetentionPolicy.*; @SuppressWarnings({"ALL"}) public interface TestModel { @@ -26,6 +26,7 @@ public interface TestModel { public @Retention(RUNTIME) @interface AM1 { public abstract String value(); } + public @interface AM2 {} public @Retention(RUNTIME) @interface AF1 { public abstract String value(); } @@ -38,10 +39,10 @@ public C4() { } @AM1("1") public C4(@AM1("1") String f1) { this.f1 = f1; } @AM1("1") protected void m1() {} - @AM1("1") public void m1(int integer, String... strings) {} + @AM1("1") public void m1(int integer, @AM2 String... strings) {} @AM1("1") public void m1(int[][] integer, String[][] strings) {} @AM1("2") public String m3() {return null;} - public String m4(@AM1("2") String string) {return null;} + public String m4(@AM1("2") @AM2 String string) {return null;} public C3 c2toC3(C2 c2) {return null;} public int add(int i1, int i2) { return i1+i2; } } @@ -50,7 +51,6 @@ public class C5 extends C3 {} public @AC2("ac2") interface I3 {} public class C6 implements I3 {} - public @Retention(RUNTIME) @AC2("ac2") @interface AC3 { } + public @AC2("ac2") @interface AC3 { } // not @Retention(RUNTIME) public @AC3 class C7 {} - }