diff --git a/.travis.yml b/.travis.yml index 3ac47107..d6d0f984 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,5 @@ language: java jdk: - openjdk8 + - openjdk9 - openjdk11 diff --git a/pom.xml b/pom.xml index ed78ec3b..af6edbea 100644 --- a/pom.xml +++ b/pom.xml @@ -62,8 +62,7 @@ org.javassist javassist ${javassist.version} - false - + false @@ -103,6 +102,14 @@ true + + org.jboss + jboss-vfs + 3.2.12.Final + provided + true + + junit junit @@ -112,19 +119,26 @@ + - build + release + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + org.apache.maven.plugins maven-source-plugin + 3.1.0 attach-sources - jar + jar-no-fork @@ -132,6 +146,7 @@ org.apache.maven.plugins maven-javadoc-plugin + 3.1.1 attach-javadocs @@ -140,40 +155,11 @@ - - none - - - - maven-enforcer-plugin - - - - enforce - - - - - gpg.keyname - gpg.keyname must be supplied using -D. - create a key using gpg --gen-key or use existing one. - - - - gpg.passphrase - gpg.passphrase must be supplied using -D. - create a key using gpg --gen-key or use existing one. - - - - true - - - org.apache.maven.plugins maven-gpg-plugin + 1.6 sign-artifacts @@ -184,6 +170,17 @@ + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.8 + true + + ossrh + https://oss.sonatype.org/ + true + + diff --git a/src/main/java/org/reflections/ReflectionUtils.java b/src/main/java/org/reflections/ReflectionUtils.java index bd82e2ca..fe1b8322 100644 --- a/src/main/java/org/reflections/ReflectionUtils.java +++ b/src/main/java/org/reflections/ReflectionUtils.java @@ -131,14 +131,18 @@ public static Set getFields(Class type, Predicate... pr } /** 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 HashSet<>(); + public static Set getAllAnnotations(T type, Predicate... predicates) { + Set result = new LinkedHashSet<>(); + List keys = new ArrayList(); if (type instanceof Class) { - for (Class t : getAllSuperTypes((Class) type)) { - result.addAll(getAnnotations(t, predicates)); + 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()); + } } - } else { - result.addAll(getAnnotations(type, predicates)); } return result; } @@ -313,10 +317,9 @@ public static Class forName(String typeName, ClassLoader... classLoaders) { } } - if (Reflections.log != null) { + if (Reflections.log != null && Reflections.log.isTraceEnabled()) { for (ReflectionsException reflectionsException : reflectionsExceptions) { - Reflections.log.warn("could not get type for name " + typeName + " from any class loader", - reflectionsException); + Reflections.log.trace("could not get type for name " + typeName + " from any class loader", reflectionsException); } } diff --git a/src/main/java/org/reflections/Reflections.java b/src/main/java/org/reflections/Reflections.java index 724b67e6..6697ca03 100644 --- a/src/main/java/org/reflections/Reflections.java +++ b/src/main/java/org/reflections/Reflections.java @@ -37,6 +37,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; @@ -126,7 +127,7 @@ public class Reflections { */ public Reflections(final Configuration configuration) { this.configuration = configuration; - store = new Store(); + store = new Store(configuration); if (configuration.getScanners() != null && !configuration.getScanners().isEmpty()) { //inject to scanners @@ -184,18 +185,20 @@ public Reflections(final Object... params) { protected Reflections() { configuration = new ConfigurationBuilder(); - store = new Store(); + store = new Store(configuration); } // 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"); + if (log != null) { + log.warn("given scan urls are empty. set urls in the configuration"); + } return; } - if (log != null && log.isDebugEnabled()) { - log.debug("going to scan these urls: {}", configuration.getUrls()); + if (log != null && log.isTraceEnabled()) { + log.trace("going to scan these urls: {}", configuration.getUrls()); } long time = System.currentTimeMillis(); @@ -207,8 +210,8 @@ protected void scan() { try { if (executorService != null) { futures.add(executorService.submit(() -> { - if (log != null) { - log.debug("[{}] scanning {}", Thread.currentThread().toString(), url); + if (log != null && log.isTraceEnabled()) { + log.trace("[{}] scanning {}", Thread.currentThread().toString(), url); } scan(url); })); @@ -223,10 +226,13 @@ protected void scan() { } } - //todo use CompletionService if (executorService != null) { for (Future future : futures) { - try { future.get(); } catch (Exception e) { throw new RuntimeException(e); } + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } } } @@ -270,9 +276,9 @@ protected void scan(URL url) { classObject = scanner.scan(file, classObject, store); } } catch (Exception e) { - if (log != null) { + if (log != null && log.isTraceEnabled()) { // SLF4J will filter out Throwables from the format string arguments. - log.debug("could not scan file {} in url {} with scanner {}", file.getRelativePath(), url.toExternalForm(), scanner.getClass().getSimpleName(), e); + log.trace("could not scan file {} in url {} with scanner {}", file.getRelativePath(), url.toExternalForm(), scanner.getClass().getSimpleName(), e); } } } @@ -332,7 +338,6 @@ public static Reflections collect(final String packagePrefix, final Predicate type) { for (Class supertype : ReflectionUtils.getSuperTypes(type)) { if (store.put(SubTypesScanner.class, supertype.getName(), key)) { - if (log != null) log.debug("expanded subtype {} -> {}", supertype.getName(), key); + if (log != null && log.isTraceEnabled()) { + log.trace("expanded subtype {} -> {}", supertype.getName(), key); + } expandSupertypes(store, supertype.getName(), supertype); } } @@ -650,10 +657,7 @@ public File save(final String filename) { * so that it could be found later much faster using the load method */ public File save(final String filename, final Serializer serializer) { - File file = serializer.save(this, filename); - if (log != null) - log.info("Reflections successfully saved in " + file.getAbsolutePath() + " using " + serializer.getClass().getSimpleName()); - return file; + return serializer.save(this, filename); } private ClassLoader[] loaders() { return configuration.getClassLoaders(); } diff --git a/src/main/java/org/reflections/Store.java b/src/main/java/org/reflections/Store.java index 4e4ecf6c..35edefa4 100644 --- a/src/main/java/org/reflections/Store.java +++ b/src/main/java/org/reflections/Store.java @@ -1,5 +1,7 @@ package org.reflections; +import org.reflections.scanners.Scanner; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -23,8 +25,12 @@ public class Store { private final ConcurrentHashMap>> storeMap; - protected Store() { + 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 */ @@ -115,7 +121,7 @@ public boolean put(Class scannerClass, String key, String value) { public boolean put(String index, String key, String value) { return storeMap.computeIfAbsent(index, s -> new ConcurrentHashMap<>()) - .computeIfAbsent(key, s -> new ArrayList<>()) + .computeIfAbsent(key, s -> Collections.synchronizedList(new ArrayList<>())) .add(value); } diff --git a/src/main/java/org/reflections/scanners/MethodParameterNamesScanner.java b/src/main/java/org/reflections/scanners/MethodParameterNamesScanner.java index 01d07fc0..9823e3f9 100644 --- a/src/main/java/org/reflections/scanners/MethodParameterNamesScanner.java +++ b/src/main/java/org/reflections/scanners/MethodParameterNamesScanner.java @@ -7,10 +7,8 @@ import org.reflections.adapters.MetadataAdapter; import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; - -import static org.reflections.util.Utils.join; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** scans methods/constructors and indexes parameter names */ @SuppressWarnings("unchecked") @@ -25,12 +23,16 @@ public void scan(Object cls, Store store) { if (acceptResult(key)) { CodeAttribute codeAttribute = ((MethodInfo) method).getCodeAttribute(); LocalVariableAttribute table = codeAttribute != null ? (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag) : null; - int length = table != null ? table.tableLength() : 0; - int i = Modifier.isStatic(((MethodInfo) method).getAccessFlags()) ? 0 : 1; //skip this - if (i < length) { - List 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> 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 annotationType() { return Meta.class; } + }; + for (Class type: reflections.getTypesAnnotatedWith(meta)) { + Set allAnnotations = ReflectionUtils.getAllAnnotations(type); + List> 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