Skip to content

Commit

Permalink
ref-0.10.1 - fix exception, docs, ci matrix, dependabot
Browse files Browse the repository at this point in the history
* 0.10.1 - fix exception in JavassistHelper.getParametersAnnotations, docs

* ci: Setup GitHub actions for basic PR CI (#333)

* Make the path format to be platform-compatible (#299)

* enable Dependabot v2 (#319)

* Enable automatic module name (#308)
  • Loading branch information
ronmamo committed Oct 7, 2021
1 parent ee52a9d commit b7e8f9a
Show file tree
Hide file tree
Showing 16 changed files with 158 additions and 55 deletions.
6 changes: 6 additions & 0 deletions .github/dependabot.yml
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "weekly"
42 changes: 42 additions & 0 deletions .github/workflows/maven-ci.yml
@@ -0,0 +1,42 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven

name: CI

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:
strategy:
fail-fast: false
matrix:
java: [ '8', '9', '11', '13', '15', '17']
os: [ ubuntu-latest ]
name: Java ${{ matrix.Java }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v2

- name: Setup Java
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: ${{ matrix.java }}

- name: Show version
run: mvn -version

- name: Cache Maven packages
uses: actions/cache@v1
with:
path: ~/.m2/repository
key: ${{ runner.os }}-m2
restore-keys: ${{ runner.os }}-m2

- name: Test with Maven
run: mvn -B clean package ${{ matrix.jdk }}
16 changes: 10 additions & 6 deletions README.md
@@ -1,9 +1,9 @@
*Released `org.reflections:reflections:0.10`*
*Released `org.reflections:reflections:0.10.1`*

*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

[![Build Status](https://travis-ci.org/ronmamo/reflections.svg?branch=master)](https://travis-ci.org/ronmamo/reflections)

Expand All @@ -16,18 +16,20 @@ Using Reflections you can query for example:
* Resources found in classpath
And more...

*Reflections was written in the spirit of [Scannotations](http://bill.burkecentral.com/2008/01/14/scanning-java-annotations-at-runtime/) library*

## Usage
Add Reflections dependency to your project:
```xml
# Maven
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10</version>
<version>0.10.1</version>
</dependency>

# Gradle
implementation 'org.reflections:reflections:0.10'
implementation 'org.reflections:reflections:0.10.1'
```

Create Reflections instance and use the query functions:
Expand All @@ -41,7 +43,7 @@ Set<Class<?>> annotated =
reflections.get(TypesAnnotated.with(SomeAnnotation.class).asClass());
```

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

### Scan
Creating Reflections instance requires providing scanning configuration:
Expand Down Expand Up @@ -263,7 +265,9 @@ Although less common, it can be useful for accessing types and members in a stro
Can be used for finding usages between packages, layers, modules, types etc.

### Contribute
Pull requests are welcomed!!
Pull requests are welcomed!!
Here are some issues labeled with [please contribute :heart:](https://github.com/ronmamo/reflections/issues?q=is%3Aissue+is%3Aopen+label%3A%22please+contribute+%E2%9D%A4%EF%B8%8F%22+label%3A%22good+first+issue%22)
*We're looking for community collaborators to assist in reviewing pull requests and issues, please reach out.*

Dual licenced with Apache 2 and [WTFPL](http://www.wtfpl.net/), just do what the fuck you want to.

Expand Down
5 changes: 4 additions & 1 deletion pom.xml
Expand Up @@ -4,7 +4,7 @@

<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.11-SNAPSHOT</version>
<version>0.10.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Reflections</name>
Expand Down Expand Up @@ -232,6 +232,9 @@
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
<manifestEntries>
<Automatic-Module-Name>org.reflections</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/org/reflections/ReflectionUtils.java
Expand Up @@ -23,11 +23,13 @@
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}.
* <pre>{@code Set<Class<?>> supertypes = get(SuperTypes.of(type))
* utils for querying java reflection meta types
* <p>see {@link #SuperTypes}, {@link #Annotations}, {@link #AnnotationTypes}, {@link #Methods}, {@link #Constructors} and {@link #Fields}.
* <pre>{@code
* Set<Class<?>> supertypes = get(SuperTypes.of(type))
* Set<Annotation> annotations = get(Annotations.of(type))
* }</pre>
* <p>generally, apply {@link #get(QueryFunction)} on {@link QueryFunction} created by {@link UtilQueryBuilder}, and optionally use the functional methods in QueryFunction
* <p>generally, apply {@link #get(QueryFunction)} on {@link QueryFunction} created by {@link UtilQueryBuilder}, and optionally use the functional methods in QueryFunction.
* <pre>{@code get(Methods.of(type)
* .filter(withPublic().and(withPrefix("get")).and(withParameterCount(0)))
* .as(Method.class)
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/org/reflections/Reflections.java
Expand Up @@ -64,7 +64,8 @@
* </ul>
*
* <p>Create Reflections instance, preferably using {@link ConfigurationBuilder}:
* <pre>{@code Reflections reflections = new Reflections(
* <pre>{@code
* Reflections reflections = new Reflections(
* new ConfigurationBuilder()
* .forPackage("com.my.project"));
*
Expand All @@ -87,7 +88,8 @@
* <p>Classloader can optionally be used for resolving runtime classes from names.
*
* <p></p>Query using {@link Reflections#get(QueryFunction)}, such as:
* <pre>{@code Set<Class<?>> modules = reflections.get(SubTypes.of(Module.class).asClass());
* <pre>{@code
* Set<Class<?>> modules = reflections.get(SubTypes.of(Module.class).asClass());
* Set<Class<?>> singletons = reflections.get(TypesAnnotated.with(Singleton.class).asClass());
* Set<String> properties = reflections.get(Resources.with(".*\\.properties"));
* Set<Method> requests = reflections.get(MethodsAnnotated.with(RequestMapping.class).as(Method.class));
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/reflections/scanners/Scanners.java
Expand Up @@ -27,10 +27,13 @@
* <li>{@link #SubTypes}</li>
* <li>{@link #TypesAnnotated}</li>
* <li>{@link #MethodsAnnotated}</li>
* <li>{@link #ConstructorsAnnotated}</li>
* <li>{@link #FieldsAnnotated}</li>
* <li>{@link #Resources}</li>
* <li>{@link #MethodsParameter}</li>
* <li>{@link #ConstructorsParameter}</li>
* <li>{@link #MethodsSignature}</li>
* <li>{@link #ConstructorsSignature}</li>
* <li>{@link #MethodsReturn}</li>
* </ul>
* <i>note that scanners must be configured in {@link org.reflections.Configuration} in order to be queried</i>
Expand Down
48 changes: 29 additions & 19 deletions src/main/java/org/reflections/util/JavassistHelper.java
Expand Up @@ -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;
Expand All @@ -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 List<String> getAnnotations(Function<String, AttributeInfo> function) {
List<String> 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());
}
Expand Down Expand Up @@ -70,22 +63,39 @@ public static String getReturnType(MethodInfo method) {
return Descriptor.toString(descriptor);
}

public static List<List<String>> getParametersAnnotations(MethodInfo method) {
List<List<String>> list = getAnnotations((ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.visibleTag));
if (includeInvisibleTag) list.addAll(getAnnotations((ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.invisibleTag)));
return list;
}
public static List<String> getAnnotations(Function<String, AttributeInfo> function) {
Function<String, List<String>> names = function
.andThen(attribute -> attribute != null ? ((AnnotationsAttribute) attribute).getAnnotations() : null)
.andThen(JavassistHelper::annotationNames);

private static List<List<String>> getAnnotations(ParameterAnnotationsAttribute attribute) {
return mapList(attribute, ParameterAnnotationsAttribute::getAnnotations, aa -> mapList(aa, a -> a, Annotation::getTypeName));
List<String> result = new ArrayList<>(names.apply(AnnotationsAttribute.visibleTag));
if (includeInvisibleTag) result.addAll(names.apply(AnnotationsAttribute.invisibleTag));
return result;
}

private static List<String> getAnnotations(AnnotationsAttribute attribute) {
return mapList(attribute, AnnotationsAttribute::getAnnotations, Annotation::getTypeName);
public static List<List<String>> getParametersAnnotations(MethodInfo method) {
Function<String, List<List<String>>> names = ((Function<String, AttributeInfo>) 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<List<String>> visibleAnnotations = names.apply(ParameterAnnotationsAttribute.visibleTag);
if (!includeInvisibleTag) return new ArrayList<>(visibleAnnotations);

List<List<String>> invisibleAnnotations = names.apply(ParameterAnnotationsAttribute.invisibleTag);
if (invisibleAnnotations.isEmpty()) return new ArrayList<>(visibleAnnotations);

// horror
List<List<String>> result = new ArrayList<>();
for (int i = 0; i < Math.max(visibleAnnotations.size(), invisibleAnnotations.size()); i++) {
List<String> 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 <T, A, R> List<R> mapList(T t, Function<T, A[]> f1, Function<A, R> f2) {
return t != null ? Arrays.stream(f1.apply(t)).map(f2).collect(Collectors.toList()) : Collections.emptyList();
private static List<String> annotationNames(Annotation[] annotations) {
return annotations != null ? Stream.of(annotations).map(Annotation::getTypeName).collect(Collectors.toList()) : Collections.emptyList();
}
}
15 changes: 15 additions & 0 deletions src/main/java/org/reflections/util/ReflectionUtilsPredicates.java
Expand Up @@ -34,6 +34,13 @@ public static <T extends Member> Predicate<T> withPrefix(final String prefix) {
return input -> input != null && input.getName().startsWith(prefix);
}

/**
* where annotated element name startsWith given {@code prefix}
*/
public static <T> Predicate<T> withNamePrefix(final String prefix) {
return input -> toName(input).startsWith(prefix);
}

/**
* where member's {@code toString} matches given {@code regex}
* <pre> get(Methods.of(someClass).filter(withPattern("public void .*"))) </pre>
Expand Down Expand Up @@ -200,6 +207,14 @@ public static boolean isAssignable(Class[] childClasses, Class[] parentClasses)
}

//
private static String toName(Object input) {
return input == null ? "" :
input.getClass().equals(Class.class) ? ((Class<?>) input).getName() :
input instanceof Member ? ((Member) input).getName() :
input instanceof Annotation ? ((Annotation) input).annotationType().getName() :
input.toString();
}

private static Class[] parameterTypes(Member member) {
return member != null ?
member.getClass() == Method.class ? ((Method) member).getParameterTypes() :
Expand Down
2 changes: 2 additions & 0 deletions src/test/java/org/reflections/MyTestModelStore.java
Expand Up @@ -36,6 +36,8 @@ interface methods {
interface value {}
}
}
interface TestModel$AM2 {
}
interface TestModel$C1 {
interface annotations {
interface org_reflections_TestModel$AC1 {}
Expand Down
7 changes: 3 additions & 4 deletions src/test/java/org/reflections/ReflectionUtilsQueryTest.java
Expand Up @@ -124,11 +124,9 @@ public void testMembers() throws NoSuchMethodException, NoSuchFieldException {
@Test
public void nestedQuery() {
Set<Class<? extends Annotation>> annotations =
get(Annotations.of(
get(AnnotationTypes.of(
Methods.of(C4.class))
.map(Annotation::annotationType)
.filter(a -> !a.getName().startsWith("java."))
.as());
.filter(withNamePrefix("org.reflections")));

assertThat(annotations,
equalTo(AM1.class));
Expand Down Expand Up @@ -198,6 +196,7 @@ public void annotationToMap() {
Set<Map<String, Object>> valueMaps =
get(Annotations.of(
Methods.of(CombinedTestModel.Impl.class))
.filter(withNamePrefix("org.reflections"))
.map(ReflectionUtils::toMap));

// todo proper assert
Expand Down
22 changes: 11 additions & 11 deletions src/test/java/org/reflections/ReflectionUtilsTest.java
Expand Up @@ -6,6 +6,10 @@
import org.reflections.scanners.Scanners;

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.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
Expand All @@ -14,13 +18,15 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.reflections.ReflectionUtils.*;
import static org.reflections.ReflectionsTest.are;
import static org.reflections.ReflectionsTest.equalTo;

@SuppressWarnings("unchecked")
public class ReflectionUtilsTest {
Expand Down Expand Up @@ -48,17 +54,11 @@ public void getAllTest() {

assertThat(getAllConstructors(TestModel.C4.class, withParametersCount(0)), names(TestModel.C4.class.getName()));

assertEquals(toStringSorted(getAllAnnotations(TestModel.C3.class)),
"[@java.lang.annotation.Documented(), " +
"@java.lang.annotation.Inherited(), " +
"@java.lang.annotation.Retention(value=RUNTIME), " +
"@java.lang.annotation.Target(value=ANNOTATION_TYPE), " +
"@org.reflections.TestModel$AC1(), " +
"@org.reflections.TestModel$AC1n(), " +
"@org.reflections.TestModel$AC2(value=ac2), " +
"@org.reflections.TestModel$AI1(), " +
"@org.reflections.TestModel$AI2(), " +
"@org.reflections.TestModel$MAI1()]");
Set<Annotation> allAnnotations = getAllAnnotations(TestModel.C3.class);
assertThat(allAnnotations.stream().map(Annotation::annotationType).collect(Collectors.toSet()),
equalTo(Documented.class, Inherited.class, Retention.class, Target.class,
TestModel.MAI1.class, TestModel.AI1.class, TestModel.AI2.class,
TestModel.AC1.class, TestModel.AC1n.class, TestModel.AC2.class));

Method m4 = getMethods(TestModel.C4.class, withName("m4")).iterator().next();
assertEquals(m4.getName(), "m4");
Expand Down

0 comments on commit b7e8f9a

Please sign in to comment.