names = new ArrayList<>(length - i);
- while (i < length) names.add(((MethodInfo) method).getConstPool().getUtf8Info(table.nameIndex(i++)));
- put(store, key, join(names, ", "));
+ 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);
+ }
}
}
}
diff --git a/src/main/java/org/reflections/scanners/ResourcesScanner.java b/src/main/java/org/reflections/scanners/ResourcesScanner.java
index 5d632708..5810c56a 100644
--- a/src/main/java/org/reflections/scanners/ResourcesScanner.java
+++ b/src/main/java/org/reflections/scanners/ResourcesScanner.java
@@ -7,7 +7,7 @@
* key: value - {web.xml: WEB-INF/web.xml} */
public class ResourcesScanner extends AbstractScanner {
public boolean acceptsInput(String file) {
- return !file.endsWith(".class"); //not a class
+ 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) {
diff --git a/src/main/java/org/reflections/util/ConfigurationBuilder.java b/src/main/java/org/reflections/util/ConfigurationBuilder.java
index b61a28b3..38049076 100644
--- a/src/main/java/org/reflections/util/ConfigurationBuilder.java
+++ b/src/main/java/org/reflections/util/ConfigurationBuilder.java
@@ -110,7 +110,7 @@ else if (param instanceof Class) {
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 if (Reflections.log != null) { throw new ReflectionsException("could not use param " + param); }
+ else throw new ReflectionsException("could not use param " + param);
}
if (builder.getUrls().isEmpty()) {
@@ -119,6 +119,9 @@ else if (param instanceof ClassLoader) { /* already taken care */ }
} else {
builder.addUrls(ClasspathHelper.forClassLoader()); //default urls getResources("")
}
+ if (builder.urls.isEmpty()) {
+ builder.addUrls(ClasspathHelper.forJavaClassPath());
+ }
}
builder.filterInputsBy(filter);
@@ -290,8 +293,9 @@ public ConfigurationBuilder setExpandSuperTypes(boolean expandSuperTypes) {
}
/** set class loader, might be used for resolving methods/fields */
- public void setClassLoaders(ClassLoader[] classLoaders) {
+ public ConfigurationBuilder setClassLoaders(ClassLoader[] classLoaders) {
this.classLoaders = classLoaders;
+ return this;
}
/** add class loader, might be used for resolving methods/fields */
@@ -301,9 +305,9 @@ public ConfigurationBuilder addClassLoader(ClassLoader classLoader) {
/** add class loader, might be used for resolving methods/fields */
public ConfigurationBuilder addClassLoaders(ClassLoader... classLoaders) {
- this.classLoaders = this.classLoaders == null ? classLoaders :
- Stream.concat(Stream.concat(Arrays.stream(this.classLoaders), Arrays.stream(classLoaders)), Stream.of(ClassLoader.class))
- .distinct().toArray(ClassLoader[]::new);
+ this.classLoaders = this.classLoaders == null
+ ? classLoaders
+ : Stream.concat(Arrays.stream(this.classLoaders), Arrays.stream(classLoaders)).toArray(ClassLoader[]::new);
return this;
}
diff --git a/src/main/java/org/reflections/util/FilterBuilder.java b/src/main/java/org/reflections/util/FilterBuilder.java
index f85bef9e..6ed7455f 100644
--- a/src/main/java/org/reflections/util/FilterBuilder.java
+++ b/src/main/java/org/reflections/util/FilterBuilder.java
@@ -46,8 +46,13 @@ public FilterBuilder includePackage(final String... prefixes) {
return this;
}
- /** exclude a package of a given prefix */
- public FilterBuilder excludePackage(final String prefix) {return add(new Exclude(prefix(prefix)));}
+ /** exclude packages of a given prefix */
+ public FilterBuilder excludePackage(final String... prefixes) {
+ for (String prefix : prefixes) {
+ add(new Exclude(prefix(prefix)));
+ }
+ return this;
+ }
private static String packageNameRegex(Class> aClass) {return prefix(aClass.getPackage().getName() + ".");}
diff --git a/src/main/java/org/reflections/util/Utils.java b/src/main/java/org/reflections/util/Utils.java
index b9402635..fe55f2ca 100644
--- a/src/main/java/org/reflections/util/Utils.java
+++ b/src/main/java/org/reflections/util/Utils.java
@@ -55,7 +55,7 @@ public static Member getMemberFromDescriptor(String descriptor, ClassLoader... c
String methodParameters = p0 != -1 ? descriptor.substring(p0 + 1, descriptor.lastIndexOf(')')) : "";
int p1 = Math.max(memberKey.lastIndexOf('.'), memberKey.lastIndexOf("$"));
- String className = memberKey.substring(memberKey.lastIndexOf(' ') + 1, p1);
+ String className = memberKey.substring(0, p1);
String memberName = memberKey.substring(p1 + 1);
Class>[] parameterTypes = null;
diff --git a/src/main/java/org/reflections/vfs/JbossDir.java b/src/main/java/org/reflections/vfs/JbossDir.java
new file mode 100644
index 00000000..e7fbb384
--- /dev/null
+++ b/src/main/java/org/reflections/vfs/JbossDir.java
@@ -0,0 +1,75 @@
+package org.reflections.vfs;
+
+import org.jboss.vfs.VirtualFile;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Stack;
+import java.util.jar.JarFile;
+
+public class JbossDir implements Vfs.Dir {
+
+ private final VirtualFile virtualFile;
+
+ private JbossDir(VirtualFile virtualFile) throws IOException {
+ this.virtualFile = virtualFile;
+ }
+
+ public static Vfs.Dir createDir(URL url) throws Exception {
+ VirtualFile virtualFile = (VirtualFile) url.openConnection().getContent();
+ if(virtualFile.isFile()) {
+ return new ZipDir(new JarFile(virtualFile.getPhysicalFile()));
+ }
+ return new JbossDir(virtualFile);
+ }
+
+
+ @Override
+ public String getPath() {
+ return virtualFile.getPathName();
+ }
+
+ @Override
+ public Iterable getFiles() {
+ return () -> new Iterator() {
+ final Stack stack = new Stack<>();
+ Vfs.File entry = null;
+
+ {
+ stack.addAll(virtualFile.getChildren());
+ }
+
+ @Override
+ public boolean hasNext() {
+ return entry != null || (entry = computeNext()) != null;
+ }
+
+ @Override
+ public Vfs.File next() {
+ Vfs.File next = entry;
+ entry = null;
+ return next;
+ }
+
+ private Vfs.File computeNext() {
+ while (!stack.isEmpty()) {
+ final VirtualFile file = stack.pop();
+ if (file.isDirectory()) {
+ stack.addAll(file.getChildren());
+ } else {
+ return new JbossFile(JbossDir.this, file);
+ }
+ }
+
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+}
diff --git a/src/main/java/org/reflections/vfs/JbossFile.java b/src/main/java/org/reflections/vfs/JbossFile.java
new file mode 100644
index 00000000..c14fa364
--- /dev/null
+++ b/src/main/java/org/reflections/vfs/JbossFile.java
@@ -0,0 +1,37 @@
+package org.reflections.vfs;
+
+import org.jboss.vfs.VirtualFile;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class JbossFile implements Vfs.File {
+
+ private final JbossDir root;
+ private final VirtualFile virtualFile;
+
+ public JbossFile(final JbossDir root, VirtualFile virtualFile) {
+ this.root = root;
+ this.virtualFile = virtualFile;
+ }
+
+ @Override
+ public String getName() {
+ return virtualFile.getName();
+ }
+
+ @Override
+ public String getRelativePath() {
+ String filepath = virtualFile.getPathName();
+ if(filepath.startsWith(root.getPath())) {
+ return filepath.substring(root.getPath().length() + 1);
+ }
+
+ return null;
+ }
+
+ @Override
+ public InputStream openInputStream() throws IOException {
+ return virtualFile.openStream();
+ }
+}
diff --git a/src/main/java/org/reflections/vfs/UrlTypeVFS.java b/src/main/java/org/reflections/vfs/UrlTypeVFS.java
index b3d07c5f..116e1547 100644
--- a/src/main/java/org/reflections/vfs/UrlTypeVFS.java
+++ b/src/main/java/org/reflections/vfs/UrlTypeVFS.java
@@ -42,7 +42,6 @@ public Dir createDir(final URL url) {
} catch (IOException e1) {
if (Reflections.log != null) {
Reflections.log.warn("Could not get URL", e);
- Reflections.log.warn("Could not get URL", e1);
}
}
}
diff --git a/src/main/java/org/reflections/vfs/Vfs.java b/src/main/java/org/reflections/vfs/Vfs.java
index ad4d93b3..e2b1a4e2 100644
--- a/src/main/java/org/reflections/vfs/Vfs.java
+++ b/src/main/java/org/reflections/vfs/Vfs.java
@@ -255,13 +255,7 @@ public boolean matches(URL url) {
}
public Vfs.Dir createDir(URL url) throws Exception {
- Object content = url.openConnection().getContent();
- Class> virtualFile = ClasspathHelper.contextClassLoader().loadClass("org.jboss.vfs.VirtualFile");
- java.io.File physicalFile = (java.io.File) virtualFile.getMethod("getPhysicalFile").invoke(content);
- String name = (String) virtualFile.getMethod("getName").invoke(content);
- java.io.File file = new java.io.File(physicalFile.getParentFile(), name);
- if (!file.exists() || !file.canRead()) file = physicalFile;
- return file.isDirectory() ? new SystemDir(file) : new ZipDir(new JarFile(file));
+ return JbossDir.createDir(url);
}
},
diff --git a/src/main/java/org/reflections/vfs/ZipDir.java b/src/main/java/org/reflections/vfs/ZipDir.java
index 5e0dfff4..0414d12c 100644
--- a/src/main/java/org/reflections/vfs/ZipDir.java
+++ b/src/main/java/org/reflections/vfs/ZipDir.java
@@ -14,7 +14,10 @@ public ZipDir(JarFile jarFile) {
}
public String getPath() {
- return jarFile.getName();
+ if (jarFile == null) {
+ return "/NO-SUCH-DIRECTORY/";
+ }
+ return jarFile.getName().replace("\\", "/");
}
public Iterable getFiles() {
diff --git a/src/test/java/org/reflections/FilterBuilderTest.java b/src/test/java/org/reflections/FilterBuilderTest.java
index 0a959df1..de813dd4 100644
--- a/src/test/java/org/reflections/FilterBuilderTest.java
+++ b/src/test/java/org/reflections/FilterBuilderTest.java
@@ -62,6 +62,16 @@ public void test_excludePackage() {
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);
diff --git a/src/test/java/org/reflections/MoreTests.java b/src/test/java/org/reflections/MoreTests.java
new file mode 100644
index 00000000..ca62e4ad
--- /dev/null
+++ b/src/test/java/org/reflections/MoreTests.java
@@ -0,0 +1,138 @@
+package org.reflections;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.reflections.scanners.MethodParameterNamesScanner;
+import org.reflections.scanners.ResourcesScanner;
+import org.reflections.scanners.SubTypesScanner;
+import org.reflections.scanners.TypeAnnotationsScanner;
+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.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.*;
+import static org.reflections.MoreTestsModel.*;
+import static org.reflections.ReflectionUtilsTest.toStringSorted;
+import static org.reflections.ReflectionsTest.are;
+
+public class MoreTests {
+
+ @Test
+ public void test_cyclic_annotation() {
+ Reflections reflections = new Reflections(MoreTestsModel.class);
+ assertThat(reflections.getTypesAnnotatedWith(CyclicAnnotation.class),
+ are(CyclicAnnotation.class));
+ }
+
+ @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);
+ }
+
+ @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);
+ List extends Class extends Annotation>> collect = allAnnotations.stream().map(Annotation::annotationType).collect(Collectors.toList());
+ Assert.assertTrue(collect.contains(Meta.class));
+ }
+
+ Meta meta = new Meta() {
+ @Override public String value() { return "a"; }
+ @Override public Class extends Annotation> annotationType() { return Meta.class; }
+ };
+ for (Class> type: reflections.getTypesAnnotatedWith(meta)) {
+ Set allAnnotations = ReflectionUtils.getAllAnnotations(type);
+ List extends Class extends Annotation>> collect = allAnnotations.stream().map(Annotation::annotationType).collect(Collectors.toList());
+ Assert.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!/");
+ URLClassLoader externalClassLoader = new URLClassLoader(new URL[]{externalUrl}, Thread.currentThread().getContextClassLoader());
+
+ Reflections reflections = new Reflections(new ConfigurationBuilder()
+ .addUrls(ClasspathHelper.forClass(TestModel.class))
+ .addUrls(externalUrl)
+ .addClassLoaders(externalClassLoader));
+
+ assertEquals(toStringSorted(reflections.getSubTypesOf(TestModel.C1.class)),
+ "[class another.project.AnotherTestModel$C2, " +
+ "class org.reflections.TestModel$C2, " +
+ "class org.reflections.TestModel$C3, " +
+ "class org.reflections.TestModel$C5]");
+ }
+
+ @Test
+ public void test_reflection_utils_with_custom_loader() throws MalformedURLException, ClassNotFoundException {
+ URL url = new URL("jar:file:" + ReflectionsTest.getUserDir() + "/src/test/resources/another-project.jar!/");
+ 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, " +
+ "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")));
+ }
+
+ @Test
+ public void test_repeatable() {
+ Reflections ref = new Reflections(MoreTestsModel.class);
+ Set> clazzes = ref.getTypesAnnotatedWith(Name.class);
+ assertTrue(clazzes.contains(SingleName.class));
+ assertFalse(clazzes.contains(MultiName.class));
+
+ clazzes = ref.getTypesAnnotatedWith(Names.class);
+ assertFalse(clazzes.contains(SingleName.class));
+ assertTrue(clazzes.contains(MultiName.class));
+ }
+
+ @Test
+ public void test_method_param_names_not_local_vars() throws NoSuchMethodException {
+ Reflections reflections = new Reflections(MoreTestsModel.class, new MethodParameterNamesScanner());
+
+ Class clazz = ParamNames.class;
+ assertEquals(reflections.getConstructorParamNames(clazz.getConstructor(String.class)).toString(),
+ "[param1]");
+ assertEquals(reflections.getMethodParamNames(clazz.getMethod("test", String.class, String.class)).toString(),
+ "[testParam1, testParam2]");
+ assertEquals(reflections.getMethodParamNames(clazz.getMethod("test", String.class)).toString(),
+ "[testParam]");
+ assertEquals(reflections.getMethodParamNames(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
new file mode 100644
index 00000000..ed353a69
--- /dev/null
+++ b/src/test/java/org/reflections/MoreTestsModel.java
@@ -0,0 +1,96 @@
+package org.reflections;
+
+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;
+
+public class MoreTestsModel {
+
+ @CyclicAnnotation
+ public @Retention(RUNTIME) @interface CyclicAnnotation {}
+
+ @Target(ElementType.TYPE)
+ @Retention(RUNTIME)
+ @interface Meta {
+ String value();
+ }
+
+ @Meta("a")
+ @Retention(RUNTIME)
+ @interface A {}
+
+ @Meta("b")
+ @Retention(RUNTIME)
+ @interface B {}
+
+ @A
+ class A1 {}
+ @B
+ class B1 {}
+ @A
+ class A2 {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface TestAnnotation {
+ public String value();
+ }
+
+ @TestAnnotation("foo foo foo")
+ public class ActualFunctionalityClass {
+ @TestAnnotation("bar bar bar")
+ class Thing {
+ }
+ }
+
+ // repeatable
+
+ @Repeatable(Names.class)
+ @Retention(RUNTIME)
+ @Target({ElementType.TYPE})
+ public @interface Name {
+ String name();
+ }
+
+ @Name(name = "foo")
+ @Name(name = "bar")
+ public static class MultiName {
+ }
+
+ @Retention(RUNTIME)
+ @Target({ElementType.TYPE})
+ public @interface Names {
+ Name[] value() default {};
+ }
+
+ @Name(name = "foo")
+ 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";
+ }
+
+ public static void test2(String testParam) {
+ String testLocal = "local";
+ }
+ }
+}
diff --git a/src/test/java/org/reflections/ReflectionUtilsTest.java b/src/test/java/org/reflections/ReflectionUtilsTest.java
index fd4fddce..d3881817 100644
--- a/src/test/java/org/reflections/ReflectionUtilsTest.java
+++ b/src/test/java/org/reflections/ReflectionUtilsTest.java
@@ -50,7 +50,17 @@ public void getAllTest() {
assertThat(getAllConstructors(TestModel.C4.class, withParametersCount(0)), names(TestModel.C4.class.getName()));
- assertEquals(getAllAnnotations(TestModel.C3.class).size(), 5);
+ 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=ugh?!), " +
+ "@org.reflections.TestModel$AI1(), " +
+ "@org.reflections.TestModel$AI2(), " +
+ "@org.reflections.TestModel$MAI1()]");
Method m4 = getMethods(TestModel.C4.class, withName("m4")).iterator().next();
assertEquals(m4.getName(), "m4");
@@ -138,4 +148,10 @@ public void describeTo(Description description) {
}
};
}
+
+ public static String toStringSorted(Set> 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/util/ConfigurationBuilderTest.java b/src/test/java/org/reflections/util/ConfigurationBuilderTest.java
new file mode 100644
index 00000000..46a169ad
--- /dev/null
+++ b/src/test/java/org/reflections/util/ConfigurationBuilderTest.java
@@ -0,0 +1,22 @@
+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/another-project.jar b/src/test/resources/another-project.jar
new file mode 100644
index 00000000..6b91fc1b
Binary files /dev/null and b/src/test/resources/another-project.jar differ