├── src ├── test │ ├── resources │ │ ├── dir+with spaces │ │ │ └── empty.file │ │ ├── another-project.jar │ │ ├── jarWithManifest.jar │ │ ├── jarWithBootLibJar.jar │ │ └── META-INF │ │ │ └── reflections │ │ │ ├── inner │ │ │ └── resource2-reflections.xml │ │ │ └── resource1-reflections.xml │ └── java │ │ └── org │ │ └── reflections8 │ │ ├── repeatable │ │ ├── SingleName.java │ │ ├── Name.java │ │ ├── Names.java │ │ ├── RepeatableAnnotationTest.java │ │ └── MultiName.java │ │ ├── util │ │ ├── UtilsTest.java │ │ ├── ConfigurationBuilderTest.java │ │ ├── ClasspathHelperTest.java │ │ └── FilterBuilderTest.java │ │ ├── ReflectionsParallelTest.java │ │ ├── ReflectionsThreadSafenessTest.java │ │ ├── ReflectionsExpandSupertypesTest.java │ │ ├── TestModel.java │ │ ├── JavaCodeSerializerTest.java │ │ ├── ReflectionsCollectTest.java │ │ ├── MyTestModelStore.java │ │ ├── VfsTest.java │ │ ├── FilterBuilderTest.java │ │ └── ReflectionUtilsTest.java └── main │ └── java │ └── org │ └── reflections8 │ ├── util │ ├── AlwaysTruePredicate.java │ ├── SetMultimap.java │ ├── Joiner.java │ ├── AbstractIterator.java │ ├── ReflectionsIterables.java │ ├── HashSetMultimap.java │ ├── SynchronizedHashSetMultimap.java │ ├── FilterBuilder.java │ └── Utils.java │ ├── ReflectionsException.java │ ├── serializers │ ├── Serializer.java │ ├── JsonSerializer.java │ └── XmlSerializer.java │ ├── scanners │ ├── MethodAnnotationsScanner.java │ ├── Scanner.java │ ├── ResourcesScanner.java │ ├── TypeAnnotationsScanner.java │ ├── TypesScanner.java │ ├── FieldAnnotationsScanner.java │ ├── SubTypesScanner.java │ ├── MethodParameterScanner.java │ ├── MethodParameterNamesScanner.java │ ├── TypeElementsScanner.java │ ├── AbstractScanner.java │ └── MemberUsageScanner.java │ ├── vfs │ ├── JbossFile.java │ ├── JrtFile.java │ ├── ZipFile.java │ ├── ZipDir.java │ ├── SystemFile.java │ ├── JrtDir.java │ ├── SystemDir.java │ ├── JarInputFile.java │ ├── JbossDir.java │ ├── JarInputDir.java │ ├── UrlTypeVFS.java │ └── Vfs.java │ ├── adapters │ ├── MetadataAdapter.java │ ├── JavaReflectionAdapter.java │ └── JavassistAdapter.java │ ├── Configuration.java │ └── Store.java ├── .travis.yml ├── .gitignore ├── Changelog.md ├── COPYING.txt ├── README.md ├── pom.xml └── Licence-2.0.txt /src/test/resources/dir+with spaces/empty.file: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk8 5 | - openjdk10 6 | - openjdk11 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | /.settings/ 4 | /target/ 5 | .idea/ 6 | *.ipr 7 | *.iml 8 | TODO 9 | -------------------------------------------------------------------------------- /src/test/resources/another-project.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aschoerk/reflections8/HEAD/src/test/resources/another-project.jar -------------------------------------------------------------------------------- /src/test/resources/jarWithManifest.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aschoerk/reflections8/HEAD/src/test/resources/jarWithManifest.jar -------------------------------------------------------------------------------- /src/test/resources/jarWithBootLibJar.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aschoerk/reflections8/HEAD/src/test/resources/jarWithBootLibJar.jar -------------------------------------------------------------------------------- /src/test/java/org/reflections8/repeatable/SingleName.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.repeatable; 2 | 3 | @Name(name = "foo") 4 | public class SingleName { 5 | } 6 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/repeatable/Name.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.repeatable; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Repeatable(Names.class) 6 | @Retention(RetentionPolicy.RUNTIME) 7 | @Target({ElementType.TYPE}) 8 | public @interface Name { 9 | String name(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/util/AlwaysTruePredicate.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import java.util.function.Predicate; 4 | 5 | public class AlwaysTruePredicate implements Predicate { 6 | 7 | @Override 8 | public boolean test(T t) { 9 | return true; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/repeatable/Names.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.repeatable; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.TYPE}) 10 | public @interface Names { 11 | Name[] value() default {}; 12 | } 13 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | **Changelog By Versions in Mavencentral** 2 | 3 | |Version|Changes| 4 | |-------|-------| 5 | |0.11.4|merged some pull requests from master, fixed dom4j vulnerability| 6 | |0.11.3|Fixed SubTypeScan introduced by of multimap-simulation| 7 | |0.11.2|Fixes for jdk11 by Hylke van der Schaaf| 8 | |0.11.1|renamed package to org.reflections8, to avoid collisions| 9 | |0.10.5|Some Findbugs fixes| 10 | |0.10.4|Initial Version independent on Guava| 11 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/reflections/inner/resource2-reflections.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | org.reflections8.TestModel$AF1 7 | 8 | org.reflections8.TestModel$C4.f1 9 | org.reflections8.TestModel$C4.f2 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/ReflectionsException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * User: ophir 3 | * Date: Mar 28, 2009 4 | * Time: 12:52:22 AM 5 | */ 6 | package org.reflections8; 7 | 8 | public class ReflectionsException extends RuntimeException { 9 | 10 | public ReflectionsException(String message) { 11 | super(message); 12 | } 13 | 14 | public ReflectionsException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | public ReflectionsException(Throwable cause) { 19 | super(cause); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /COPYING.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/util/SetMultimap.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | /** 8 | * Helper class used to avoid guava 9 | * 10 | * @author aschoerk 11 | */ 12 | public interface SetMultimap extends Map> { 13 | boolean putSingle(T key, V value); 14 | 15 | void putAllSingles(SetMultimap m); 16 | 17 | boolean removeSingle(Object key, V value); 18 | 19 | Collection flatValues(); 20 | 21 | Set flatValuesAsSet(); 22 | 23 | Map> asMap(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/serializers/Serializer.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.serializers; 2 | 3 | import java.io.File; 4 | import java.io.InputStream; 5 | 6 | import org.reflections8.Reflections; 7 | 8 | /** Serilizer of a {@link org.reflections8.Reflections} instance */ 9 | public interface Serializer { 10 | /** reads the input stream into a new Reflections instance, populating it's store */ 11 | Reflections read(InputStream inputStream); 12 | 13 | /** saves a Reflections instance into the given filename */ 14 | File save(Reflections reflections, String filename); 15 | 16 | /** returns a string serialization of the given Reflections instance */ 17 | String toString(Reflections reflections); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/MethodAnnotationsScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.util.List; 4 | 5 | @SuppressWarnings({"unchecked"}) 6 | /** scans for method's annotations */ 7 | public class MethodAnnotationsScanner extends AbstractScanner { 8 | public void scan(final Object cls) { 9 | for (Object method : getMetadataAdapter().getMethods(cls)) { 10 | for (String methodAnnotation : (List) getMetadataAdapter().getMethodAnnotationNames(method)) { 11 | if (acceptResult(methodAnnotation)) { 12 | getStore().putSingle(methodAnnotation, getMetadataAdapter().getMethodFullKey(cls, method)); 13 | } 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/util/UtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.lang.reflect.Method; 6 | 7 | import org.junit.Test; 8 | import org.reflections8.TestModel.C8; 9 | 10 | public class UtilsTest { 11 | 12 | @Test 13 | public void testGetMemberFromDescriptorStringClassLoaderArray() { 14 | Method[] declaredMethods = C8.class.getDeclaredMethods(); 15 | for (Method method : declaredMethods) { 16 | String expectedString = method.toString(); 17 | String actualString = Utils.getMemberFromDescriptor(expectedString).toString(); 18 | assertEquals(expectedString, actualString); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/Scanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.util.Optional; 4 | import java.util.function.Predicate; 5 | 6 | import org.reflections8.Configuration; 7 | import org.reflections8.util.SetMultimap; 8 | import org.reflections8.vfs.Vfs; 9 | 10 | /** 11 | * 12 | */ 13 | public interface Scanner { 14 | 15 | void setConfiguration(Configuration configuration); 16 | 17 | SetMultimap getStore(); 18 | 19 | void setStore(SetMultimap store); 20 | 21 | Scanner filterResultsBy(Predicate filter); 22 | 23 | boolean acceptsInput(String file); 24 | 25 | Object scan(Vfs.File file, Optional classObject); 26 | 27 | boolean acceptResult(String fqn); 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/reflections/resource1-reflections.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | org.reflections8.TestModel$AM1 7 | 8 | org.reflections8.TestModel$C4.<init>(java.lang.String) 9 | org.reflections8.TestModel$C4.m3() 10 | org.reflections8.TestModel$C4.m1(int[][], java.lang.String[][]) 11 | org.reflections8.TestModel$C4.m1(int, java.lang.String[]) 12 | org.reflections8.TestModel$C4.m1() 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/ResourcesScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.util.Optional; 4 | 5 | import org.reflections8.vfs.Vfs; 6 | 7 | /** collects all resources that are not classes in a collection 8 | *

key: value - {web.xml: WEB-INF/web.xml} */ 9 | public class ResourcesScanner extends AbstractScanner { 10 | public boolean acceptsInput(String file) { 11 | return !file.endsWith(".class"); //not a class 12 | } 13 | 14 | @Override public Object scan(Vfs.File file, Optional classObject) { 15 | getStore().putSingle(file.getName(), file.getRelativePath()); 16 | return classObject.get(); 17 | } 18 | 19 | public void scan(Object cls) { 20 | throw new UnsupportedOperationException(); //shouldn't get here 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/util/Joiner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import java.util.StringJoiner; 4 | 5 | public class Joiner { 6 | 7 | StringJoiner j; 8 | 9 | public Joiner(String separator) { 10 | this.j = new StringJoiner(separator); 11 | } 12 | 13 | public static Joiner on(String separator) { 14 | return new Joiner(separator); 15 | } 16 | 17 | public String join(Object first, Object ... rest) { 18 | j.add(first.toString()); 19 | for (Object o: rest) { 20 | j.add(o.toString()); 21 | } 22 | return j.toString(); 23 | } 24 | 25 | public String join(Iterable parts) { 26 | for (Object o: parts) { 27 | j.add(o.toString()); 28 | } 29 | return j.toString(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/TypeAnnotationsScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.lang.annotation.Inherited; 4 | import java.util.List; 5 | 6 | /** scans for class's annotations, where @Retention(RetentionPolicy.RUNTIME) */ 7 | @SuppressWarnings({"unchecked"}) 8 | public class TypeAnnotationsScanner extends AbstractScanner { 9 | public void scan(final Object cls) { 10 | final String className = getMetadataAdapter().getClassName(cls); 11 | 12 | for (String annotationType : (List) getMetadataAdapter().getClassAnnotationNames(cls)) { 13 | 14 | if (acceptResult(annotationType) || 15 | annotationType.equals(Inherited.class.getName())) { //as an exception, accept Inherited as well 16 | getStore().putSingle(annotationType, className); 17 | } 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/TypesScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.util.Optional; 4 | 5 | import org.reflections8.vfs.Vfs; 6 | 7 | /** scans classes and stores fqn as key and full path as value. 8 | *

Deprecated. use {@link org.reflections8.scanners.TypeElementsScanner} */ 9 | @Deprecated 10 | public class TypesScanner extends AbstractScanner { 11 | 12 | @Override 13 | public Object scan(Vfs.File file, Optional classObject) { 14 | Object tmpClassObject = super.scan(file, classObject); 15 | String className = getMetadataAdapter().getClassName(tmpClassObject); 16 | getStore().putSingle(className, className); 17 | return tmpClassObject; 18 | } 19 | 20 | @Override 21 | public void scan(Object cls) { 22 | throw new UnsupportedOperationException("should not get here"); 23 | } 24 | } -------------------------------------------------------------------------------- /src/test/java/org/reflections8/repeatable/RepeatableAnnotationTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.repeatable; 2 | 3 | import org.junit.Test; 4 | import org.reflections8.Reflections; 5 | 6 | import java.util.Set; 7 | 8 | import static org.junit.Assert.assertFalse; 9 | import static org.junit.Assert.assertTrue; 10 | 11 | public class RepeatableAnnotationTest { 12 | 13 | @Test 14 | public void test() { 15 | Reflections ref = new Reflections(RepeatableAnnotationTest.class.getPackage().getName()); 16 | Set> clazzes = ref.getTypesAnnotatedWith(Name.class); 17 | assertTrue(clazzes.contains(SingleName.class)); 18 | assertFalse(clazzes.contains(MultiName.class)); 19 | 20 | clazzes = ref.getTypesAnnotatedWith(Names.class); 21 | assertFalse(clazzes.contains(SingleName.class)); 22 | assertTrue(clazzes.contains(MultiName.class)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/util/AbstractIterator.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import java.util.Iterator; 4 | import java.util.NoSuchElementException; 5 | 6 | public abstract class AbstractIterator implements Iterator { 7 | 8 | private T tmp = null; 9 | 10 | private boolean endOfData = false; 11 | 12 | protected T endOfData() { 13 | endOfData = true; 14 | return null; 15 | } 16 | 17 | protected abstract T computeNext(); 18 | 19 | public T next() { 20 | if (endOfData || !hasNext()) { 21 | throw new NoSuchElementException("nothing left"); 22 | } 23 | T res = tmp; 24 | tmp = null; 25 | return res; 26 | } 27 | 28 | @Override 29 | public boolean hasNext() { 30 | if (tmp != null) 31 | return true; 32 | tmp = computeNext(); 33 | return !endOfData; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/FieldAnnotationsScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.util.List; 4 | 5 | /** scans for field's annotations */ 6 | @SuppressWarnings({"unchecked"}) 7 | public class FieldAnnotationsScanner extends AbstractScanner { 8 | public void scan(final Object cls) { 9 | final String className = getMetadataAdapter().getClassName(cls); 10 | List fields = getMetadataAdapter().getFields(cls); 11 | for (final Object field : fields) { 12 | List fieldAnnotations = getMetadataAdapter().getFieldAnnotationNames(field); 13 | for (String fieldAnnotation : fieldAnnotations) { 14 | 15 | if (acceptResult(fieldAnnotation)) { 16 | String fieldName = getMetadataAdapter().getFieldName(field); 17 | getStore().putSingle(fieldAnnotation, String.format("%s.%s", className, fieldName)); 18 | } 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/JbossFile.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import org.jboss.vfs.VirtualFile; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | public class JbossFile implements Vfs.File { 9 | 10 | private final JbossDir root; 11 | private final VirtualFile virtualFile; 12 | 13 | public JbossFile(final JbossDir root, VirtualFile virtualFile) { 14 | this.root = root; 15 | this.virtualFile = virtualFile; 16 | } 17 | 18 | @Override 19 | public String getName() { 20 | return virtualFile.getName(); 21 | } 22 | 23 | @Override 24 | public String getRelativePath() { 25 | String filepath = virtualFile.getPathName(); 26 | if(filepath.startsWith(root.getPath())) { 27 | return filepath.substring(root.getPath().length() + 1); 28 | } 29 | 30 | return null; 31 | } 32 | 33 | @Override 34 | public InputStream openInputStream() throws IOException { 35 | return virtualFile.openStream(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/JrtFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license 3 | * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template 4 | */ 5 | package org.reflections8.vfs; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.nio.file.FileSystem; 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import org.reflections8.vfs.Vfs.File; 13 | 14 | /** 15 | * 16 | * @author lnavrat 17 | */ 18 | public class JrtFile implements File { 19 | 20 | private final Path path; 21 | 22 | public JrtFile(Path path) { 23 | this.path = path; 24 | } 25 | 26 | @Override 27 | public String getName() { 28 | return path.getFileName().toString(); 29 | } 30 | 31 | @Override 32 | public String getRelativePath() { 33 | return path.toString(); 34 | } 35 | 36 | @Override 37 | public InputStream openInputStream() throws IOException { 38 | return Files.newInputStream(path); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/ZipFile.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.zip.ZipEntry; 6 | 7 | /** an implementation of {@link org.reflections.vfs.Vfs.File} for {@link java.util.zip.ZipEntry} */ 8 | public class ZipFile implements Vfs.File { 9 | private final ZipDir root; 10 | private final ZipEntry entry; 11 | 12 | public ZipFile(final ZipDir root, ZipEntry entry) { 13 | this.root = root; 14 | this.entry = entry; 15 | } 16 | 17 | public String getName() { 18 | String name = entry.getName(); 19 | return name.substring(name.lastIndexOf("/") + 1); 20 | } 21 | 22 | public String getRelativePath() { 23 | return entry.getName(); 24 | } 25 | 26 | public InputStream openInputStream() throws IOException { 27 | return root.jarFile.getInputStream(entry); 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return root.getPath() + "!" + java.io.File.separatorChar + entry.toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/ZipDir.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import org.reflections8.Reflections; 4 | 5 | import java.io.IOException; 6 | import java.util.jar.JarFile; 7 | 8 | /** an implementation of {@link org.reflections.vfs.Vfs.Dir} for {@link java.util.zip.ZipFile} */ 9 | public class ZipDir implements Vfs.Dir { 10 | final java.util.zip.ZipFile jarFile; 11 | 12 | public ZipDir(JarFile jarFile) { 13 | this.jarFile = jarFile; 14 | } 15 | 16 | public String getPath() { 17 | return jarFile != null ? jarFile.getName().replace("\\", "/") : "/NO-SUCH-DIRECTORY/"; 18 | } 19 | 20 | public Iterable getFiles() { 21 | return () -> jarFile.stream() 22 | .filter(entry -> !entry.isDirectory()) 23 | .map(entry -> (Vfs.File) new ZipFile(ZipDir.this, entry)) 24 | .iterator(); 25 | } 26 | 27 | public void close() { 28 | try { jarFile.close(); } catch (IOException e) { 29 | if (Reflections.log.isPresent()) { 30 | Reflections.log.get().warn("Could not close JarFile", e); 31 | } 32 | } 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return jarFile.getName(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/SystemFile.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import java.io.InputStream; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | 7 | /** an implementation of {@link org.reflections.vfs.Vfs.File} for a directory {@link java.io.File} */ 8 | public class SystemFile implements Vfs.File { 9 | private final SystemDir root; 10 | private final java.io.File file; 11 | 12 | public SystemFile(final SystemDir root, java.io.File file) { 13 | this.root = root; 14 | this.file = file; 15 | } 16 | 17 | public String getName() { 18 | return file.getName(); 19 | } 20 | 21 | public String getRelativePath() { 22 | String filepath = file.getPath().replace("\\", "/"); 23 | if (filepath.startsWith(root.getPath())) { 24 | return filepath.substring(root.getPath().length() + 1); 25 | } 26 | 27 | return null; //should not get here 28 | } 29 | 30 | public InputStream openInputStream() { 31 | try { 32 | return new FileInputStream(file); 33 | } catch (FileNotFoundException e) { 34 | throw new RuntimeException(e); 35 | } 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return file.toString(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/JrtDir.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import java.io.IOException; 4 | import java.net.URISyntaxException; 5 | import java.net.URL; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.util.Collections; 9 | import org.reflections8.ReflectionsException; 10 | import org.reflections8.vfs.Vfs.Dir; 11 | 12 | public class JrtDir implements Dir { 13 | 14 | private final Path path; 15 | 16 | public JrtDir(URL url) throws URISyntaxException { 17 | Path p = Path.of(url.toURI()); 18 | if (!Files.exists(p)) { 19 | path = p.resolve("/modules").resolve(p.getRoot().relativize(p)); 20 | } else { 21 | path = p; 22 | } 23 | } 24 | 25 | @Override 26 | public String getPath() { 27 | return path.toString(); 28 | } 29 | 30 | @Override 31 | public Iterable getFiles() { 32 | if (path == null) { 33 | return Collections.emptyList(); 34 | } 35 | return () -> { 36 | try { 37 | return Files.walk(path) 38 | .filter(Files::isRegularFile) 39 | .map(currentPath -> (Vfs.File) new JrtFile(currentPath)) 40 | .iterator(); 41 | } catch (IOException e) { 42 | throw new ReflectionsException("could not get files for " + path, e); 43 | } 44 | }; 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/adapters/MetadataAdapter.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.adapters; 2 | 3 | import java.util.List; 4 | 5 | import org.reflections8.vfs.Vfs; 6 | 7 | /** 8 | * 9 | */ 10 | public interface MetadataAdapter { 11 | 12 | // 13 | String getClassName(final C cls); 14 | 15 | String getSuperclassName(final C cls); 16 | 17 | List getInterfacesNames(final C cls); 18 | 19 | // 20 | List getFields(final C cls); 21 | 22 | List getMethods(final C cls); 23 | 24 | String getMethodName(final M method); 25 | 26 | List getParameterNames(final M method); 27 | 28 | List getClassAnnotationNames(final C aClass); 29 | 30 | List getFieldAnnotationNames(final F field); 31 | 32 | List getMethodAnnotationNames(final M method); 33 | 34 | List getParameterAnnotationNames(final M method, final int parameterIndex); 35 | 36 | String getReturnTypeName(final M method); 37 | 38 | String getFieldName(final F field); 39 | 40 | C getOrCreateClassObject(Vfs.File file) throws Exception; 41 | 42 | String getMethodModifier(M method); 43 | 44 | String getMethodKey(C cls, M method); 45 | 46 | String getMethodFullKey(C cls, M method); 47 | 48 | boolean isPublic(Object o); 49 | 50 | boolean acceptsInput(String file); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/SystemDir.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import org.reflections8.ReflectionsException; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.nio.file.Files; 8 | import java.util.Collections; 9 | 10 | /* 11 | * An implementation of {@link org.reflections.vfs.Vfs.Dir} for directory {@link java.io.File}. 12 | */ 13 | public class SystemDir implements Vfs.Dir { 14 | private final File file; 15 | 16 | public SystemDir(File file) { 17 | if (file != null && (!file.isDirectory() || !file.canRead())) { 18 | throw new RuntimeException("cannot use dir " + file); 19 | } 20 | this.file = file; 21 | } 22 | 23 | public String getPath() { 24 | return file != null ? file.getPath().replace("\\", "/") : "/NO-SUCH-DIRECTORY/"; 25 | } 26 | 27 | public Iterable getFiles() { 28 | if (file == null || !file.exists()) return Collections.emptyList(); 29 | return () -> { 30 | try { 31 | return Files.walk(file.toPath()) 32 | .filter(Files::isRegularFile) 33 | .map(path -> (Vfs.File) new SystemFile(SystemDir.this, path.toFile())) 34 | .iterator(); 35 | } catch (IOException e) { 36 | throw new ReflectionsException("could not get files for " + file, e); 37 | } 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/JarInputFile.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.zip.ZipEntry; 6 | 7 | /** 8 | * 9 | */ 10 | public class JarInputFile implements Vfs.File { 11 | private final ZipEntry entry; 12 | private final JarInputDir jarInputDir; 13 | private final long fromIndex; 14 | private final long endIndex; 15 | 16 | public JarInputFile(ZipEntry entry, JarInputDir jarInputDir, long cursor, long nextCursor) { 17 | this.entry = entry; 18 | this.jarInputDir = jarInputDir; 19 | fromIndex = cursor; 20 | endIndex = nextCursor; 21 | } 22 | 23 | public String getName() { 24 | String name = entry.getName(); 25 | return name.substring(name.lastIndexOf("/") + 1); 26 | } 27 | 28 | public String getRelativePath() { 29 | return entry.getName(); 30 | } 31 | 32 | public InputStream openInputStream() { 33 | return new InputStream() { 34 | @Override 35 | public int read() throws IOException { 36 | if (jarInputDir.cursor >= fromIndex && jarInputDir.cursor <= endIndex) { 37 | int read = jarInputDir.jarInputStream.read(); 38 | jarInputDir.cursor++; 39 | return read; 40 | } else { 41 | return -1; 42 | } 43 | } 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/SubTypesScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.util.List; 4 | 5 | import org.reflections8.util.FilterBuilder; 6 | 7 | /** scans for superclass and interfaces of a class, allowing a reverse lookup for subtypes */ 8 | public class SubTypesScanner extends AbstractScanner { 9 | 10 | /** created new SubTypesScanner. will exclude direct Object subtypes */ 11 | public SubTypesScanner() { 12 | this(true); //exclude direct Object subtypes by default 13 | } 14 | 15 | /** created new SubTypesScanner. 16 | * @param excludeObjectClass if false, include direct {@link Object} subtypes in results. */ 17 | public SubTypesScanner(boolean excludeObjectClass) { 18 | if (excludeObjectClass) { 19 | filterResultsBy(new FilterBuilder().exclude(Object.class.getName())); //exclude direct Object subtypes 20 | } 21 | } 22 | 23 | @SuppressWarnings({"unchecked"}) 24 | public void scan(final Object cls) { 25 | String className = getMetadataAdapter().getClassName(cls); 26 | String superclass = getMetadataAdapter().getSuperclassName(cls); 27 | 28 | if (acceptResult(superclass)) { 29 | getStore().putSingle(superclass, className); 30 | } 31 | 32 | for (String anInterface : (List) getMetadataAdapter().getInterfacesNames(cls)) { 33 | if (acceptResult(anInterface)) { 34 | getStore().putSingle(anInterface, className); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/ReflectionsParallelTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import static java.util.Arrays.asList; 4 | 5 | import org.junit.BeforeClass; 6 | import org.reflections8.scanners.FieldAnnotationsScanner; 7 | import org.reflections8.scanners.MemberUsageScanner; 8 | import org.reflections8.scanners.MethodAnnotationsScanner; 9 | import org.reflections8.scanners.MethodParameterNamesScanner; 10 | import org.reflections8.scanners.MethodParameterScanner; 11 | import org.reflections8.scanners.SubTypesScanner; 12 | import org.reflections8.scanners.TypeAnnotationsScanner; 13 | import org.reflections8.util.ClasspathHelper; 14 | import org.reflections8.util.ConfigurationBuilder; 15 | 16 | /** */ 17 | public class ReflectionsParallelTest extends ReflectionsTest { 18 | 19 | @BeforeClass 20 | public static void init() { 21 | reflections8 = new Reflections(new ConfigurationBuilder() 22 | .setUrls(asList(ClasspathHelper.forClass(TestModel.class))) 23 | .filterInputsBy(TestModelFilter) 24 | .setScanners( 25 | new SubTypesScanner(false), 26 | new TypeAnnotationsScanner(), 27 | new FieldAnnotationsScanner(), 28 | new MethodAnnotationsScanner(), 29 | new MethodParameterScanner(), 30 | new MethodParameterNamesScanner(), 31 | new MemberUsageScanner()) 32 | .useParallelExecutor()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/MethodParameterScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.util.List; 4 | 5 | import org.reflections8.adapters.MetadataAdapter; 6 | 7 | /** scans methods/constructors and indexes parameters, return type and parameter annotations */ 8 | @SuppressWarnings("unchecked") 9 | public class MethodParameterScanner extends AbstractScanner { 10 | 11 | @Override 12 | public void scan(Object cls) { 13 | final MetadataAdapter md = getMetadataAdapter(); 14 | 15 | for (Object method : md.getMethods(cls)) { 16 | 17 | String signature = md.getParameterNames(method).toString(); 18 | if (acceptResult(signature)) { 19 | getStore().putSingle(signature, md.getMethodFullKey(cls, method)); 20 | } 21 | 22 | String returnTypeName = md.getReturnTypeName(method); 23 | if (acceptResult(returnTypeName)) { 24 | getStore().putSingle(returnTypeName, md.getMethodFullKey(cls, method)); 25 | } 26 | 27 | List parameterNames = md.getParameterNames(method); 28 | for (int i = 0; i < parameterNames.size(); i++) { 29 | for (Object paramAnnotation : md.getParameterAnnotationNames(method, i)) { 30 | if (acceptResult((String) paramAnnotation)) { 31 | getStore().putSingle((String) paramAnnotation, md.getMethodFullKey(cls, method)); 32 | } 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/MethodParameterNamesScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.lang.reflect.Modifier; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import org.reflections8.adapters.MetadataAdapter; 8 | import org.reflections8.util.Joiner; 9 | 10 | import javassist.bytecode.LocalVariableAttribute; 11 | import javassist.bytecode.MethodInfo; 12 | 13 | /** scans methods/constructors and indexes parameter names */ 14 | @SuppressWarnings("unchecked") 15 | public class MethodParameterNamesScanner extends AbstractScanner { 16 | 17 | @Override 18 | public void scan(Object cls) { 19 | final MetadataAdapter md = getMetadataAdapter(); 20 | 21 | for (Object method : md.getMethods(cls)) { 22 | String key = md.getMethodFullKey(cls, method); 23 | if (acceptResult(key)) { 24 | LocalVariableAttribute table = (LocalVariableAttribute) ((MethodInfo) method).getCodeAttribute().getAttribute(LocalVariableAttribute.tag); 25 | int length = table.tableLength(); 26 | int i = Modifier.isStatic(((MethodInfo) method).getAccessFlags()) ? 0 : 1; //skip this 27 | if (i < length) { 28 | List names = new ArrayList(length - i); 29 | while (i < length) names.add(((MethodInfo) method).getConstPool().getUtf8Info(table.nameIndex(i++))); 30 | getStore().putSingle(key, Joiner.on(", ").join(names)); 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/Configuration.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import java.net.URL; 4 | import java.util.Optional; 5 | import java.util.Set; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.function.Predicate; 8 | 9 | import org.reflections8.adapters.MetadataAdapter; 10 | import org.reflections8.scanners.Scanner; 11 | import org.reflections8.serializers.Serializer; 12 | 13 | /** 14 | * Configuration is used to create a configured instance of {@link Reflections} 15 | *

it is preferred to use {@link org.reflections8.util.ConfigurationBuilder} 16 | */ 17 | public interface Configuration { 18 | /** the scanner instances used for scanning different metadata */ 19 | Set getScanners(); 20 | 21 | /** the urls to be scanned */ 22 | Set getUrls(); 23 | 24 | /** the metadata adapter used to fetch metadata from classes */ 25 | @SuppressWarnings({"RawUseOfParameterizedType"}) 26 | MetadataAdapter getMetadataAdapter(); 27 | 28 | /** get the fully qualified name filter used to filter types to be scanned */ 29 | Optional> getInputsFilter(); 30 | 31 | /** executor service used to scan files. if null, scanning is done in a simple for loop */ 32 | Optional getExecutorService(); 33 | 34 | /** the default serializer to use when saving Reflection */ 35 | Serializer getSerializer(); 36 | 37 | /** get class loaders, might be used for resolving methods/fields */ 38 | Optional getClassLoaders(); 39 | 40 | /** if true (default), expand super types after scanning, for super types that were not scanned. 41 | *

see {@link org.reflections8.Reflections#expandSuperTypes()}*/ 42 | boolean shouldExpandSuperTypes(); 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/ReflectionsThreadSafenessTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import static java.util.Collections.singletonList; 4 | import static java.util.concurrent.TimeUnit.SECONDS; 5 | import static org.junit.Assert.assertEquals; 6 | 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.concurrent.Callable; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.Future; 13 | 14 | import org.junit.Test; 15 | import org.reflections8.scanners.SubTypesScanner; 16 | import org.reflections8.util.ClasspathHelper; 17 | import org.reflections8.util.ConfigurationBuilder; 18 | 19 | public class ReflectionsThreadSafenessTest { 20 | 21 | /** 22 | * https://github.com/ronmamo/reflections/issues/81 23 | */ 24 | @Test 25 | public void reflections_scan_is_thread_safe() throws Exception { 26 | 27 | Callable>> callable = new Callable>>() { 28 | @Override 29 | public Set> call() throws Exception { 30 | final Reflections reflections8 = new Reflections(new ConfigurationBuilder() 31 | .setUrls(singletonList(ClasspathHelper.forClass(Map.class))) 32 | .setScanners(new SubTypesScanner(false))); 33 | 34 | return reflections8.getSubTypesOf(Map.class); 35 | } 36 | }; 37 | 38 | final ExecutorService pool = Executors.newFixedThreadPool(2); 39 | 40 | final Future first = pool.submit(callable); 41 | final Future second = pool.submit(callable); 42 | 43 | assertEquals(first.get(5, SECONDS), second.get(5, SECONDS)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/ReflectionsExpandSupertypesTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import java.util.Set; 4 | 5 | import org.junit.Test; 6 | import org.reflections8.util.ClasspathHelper; 7 | import org.reflections8.util.ConfigurationBuilder; 8 | import org.reflections8.util.FilterBuilder; 9 | 10 | import junit.framework.Assert; 11 | 12 | public class ReflectionsExpandSupertypesTest { 13 | 14 | private final static String packagePrefix = 15 | "org.reflections8.ReflectionsExpandSupertypesTest\\$TestModel\\$ScannedScope\\$.*"; 16 | private FilterBuilder inputsFilter = new FilterBuilder().include(packagePrefix); 17 | 18 | public interface TestModel { 19 | interface A { 20 | } // outside of scanned scope 21 | 22 | interface B extends A { 23 | } // outside of scanned scope, but immediate supertype 24 | 25 | interface ScannedScope { 26 | interface C extends B { 27 | } 28 | 29 | interface D extends B { 30 | } 31 | } 32 | } 33 | 34 | @Test 35 | public void testExpandSupertypes() throws Exception { 36 | Reflections refExpand = new Reflections(new ConfigurationBuilder(). 37 | setUrls(ClasspathHelper.forClass(TestModel.ScannedScope.C.class)). 38 | filterInputsBy(inputsFilter)); 39 | Assert.assertTrue(refExpand.getConfiguration().shouldExpandSuperTypes()); 40 | Set> subTypesOf = refExpand.getSubTypesOf(TestModel.A.class); 41 | Assert.assertTrue("expanded", subTypesOf.contains(TestModel.B.class)); 42 | Assert.assertTrue("transitivity", subTypesOf.containsAll(refExpand.getSubTypesOf(TestModel.B.class))); 43 | } 44 | 45 | @Test 46 | public void testNotExpandSupertypes() throws Exception { 47 | Reflections refDontExpand = new Reflections(new ConfigurationBuilder(). 48 | setUrls(ClasspathHelper.forClass(TestModel.ScannedScope.C.class)). 49 | filterInputsBy(inputsFilter). 50 | setExpandSuperTypes(false)); 51 | Assert.assertFalse(refDontExpand.getConfiguration().shouldExpandSuperTypes()); 52 | Set> subTypesOf1 = refDontExpand.getSubTypesOf(TestModel.A.class); 53 | Assert.assertFalse(subTypesOf1.contains(TestModel.B.class)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/JbossDir.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import org.jboss.vfs.VirtualFile; 4 | import org.jboss.vfs.VirtualJarInputStream; 5 | 6 | import java.lang.reflect.Field; 7 | 8 | import java.net.URL; 9 | import java.util.Iterator; 10 | import java.util.Stack; 11 | import java.util.jar.JarFile; 12 | 13 | public class JbossDir implements Vfs.Dir { 14 | 15 | private final VirtualFile virtualFile; 16 | 17 | private JbossDir(VirtualFile virtualFile) { 18 | this.virtualFile = virtualFile; 19 | } 20 | 21 | public static Vfs.Dir createDir(URL url) throws Exception { 22 | Object content = url.openConnection().getContent(); 23 | if (content instanceof VirtualJarInputStream) { 24 | Field root = content.getClass().getDeclaredField("root"); 25 | root.setAccessible(true); 26 | content = root.get(content); 27 | } 28 | VirtualFile virtualFile = (VirtualFile) content; 29 | if (virtualFile.isFile()) { 30 | return new ZipDir(new JarFile(virtualFile.getPhysicalFile())); 31 | } 32 | return new JbossDir(virtualFile); 33 | } 34 | 35 | 36 | @Override 37 | public String getPath() { 38 | return virtualFile.getPathName(); 39 | } 40 | 41 | @Override 42 | public Iterable getFiles() { 43 | return () -> new Iterator() { 44 | final Stack stack = new Stack<>(); 45 | Vfs.File entry = null; 46 | 47 | { 48 | stack.addAll(virtualFile.getChildren()); 49 | } 50 | 51 | @Override 52 | public boolean hasNext() { 53 | return entry != null || (entry = computeNext()) != null; 54 | } 55 | 56 | @Override 57 | public Vfs.File next() { 58 | Vfs.File next = entry; 59 | entry = null; 60 | return next; 61 | } 62 | 63 | private Vfs.File computeNext() { 64 | while (!stack.isEmpty()) { 65 | final VirtualFile file = stack.pop(); 66 | if (file.isDirectory()) { 67 | stack.addAll(file.getChildren()); 68 | } else { 69 | return new JbossFile(JbossDir.this, file); 70 | } 71 | } 72 | return null; 73 | } 74 | }; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/TypeElementsScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import org.reflections8.util.Joiner; 4 | 5 | /** scans fields and methods and stores fqn as key and elements as values */ 6 | @SuppressWarnings({"unchecked"}) 7 | public class TypeElementsScanner extends AbstractScanner { 8 | private boolean includeFields = true; 9 | private boolean includeMethods = true; 10 | private boolean includeAnnotations = true; 11 | private boolean publicOnly = true; 12 | 13 | public void scan(Object cls) { 14 | String className = getMetadataAdapter().getClassName(cls); 15 | if (!acceptResult(className)) return; 16 | 17 | getStore().putSingle(className, ""); 18 | 19 | if (includeFields) { 20 | for (Object field : getMetadataAdapter().getFields(cls)) { 21 | String fieldName = getMetadataAdapter().getFieldName(field); 22 | getStore().putSingle(className, fieldName); 23 | } 24 | } 25 | 26 | if (includeMethods) { 27 | for (Object method : getMetadataAdapter().getMethods(cls)) { 28 | if (!publicOnly || getMetadataAdapter().isPublic(method)) { 29 | String methodKey = getMetadataAdapter().getMethodName(method) + "(" + 30 | Joiner.on(", ").join(getMetadataAdapter().getParameterNames(method)) + ")"; 31 | getStore().putSingle(className, methodKey); 32 | } 33 | } 34 | } 35 | 36 | if (includeAnnotations) { 37 | for (Object annotation : getMetadataAdapter().getClassAnnotationNames(cls)) { 38 | getStore().putSingle(className, "@" + annotation); 39 | } 40 | } 41 | } 42 | 43 | // 44 | public TypeElementsScanner includeFields() { return includeFields(true); } 45 | public TypeElementsScanner includeFields(boolean include) { includeFields = include; return this; } 46 | public TypeElementsScanner includeMethods() { return includeMethods(true); } 47 | public TypeElementsScanner includeMethods(boolean include) { includeMethods = include; return this; } 48 | public TypeElementsScanner includeAnnotations() { return includeAnnotations(true); } 49 | public TypeElementsScanner includeAnnotations(boolean include) { includeAnnotations = include; return this; } 50 | public TypeElementsScanner publicOnly(boolean only) { publicOnly = only; return this; } 51 | public TypeElementsScanner publicOnly() { return publicOnly(true); } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/repeatable/MultiName.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.repeatable; 2 | 3 | /** 4 | * Classfile /Users/honwhywang/github/reflections/target/test-classes/org/reflections/repeatable/MultiName.class 5 | * Last modified 2019-3-13; size 471 bytes 6 | * MD5 checksum fb5f9f4fa77213e01322598ad6727f98 7 | * Compiled from "MultiName.java" 8 | * public class org.reflections.repeatable.MultiName 9 | * minor version: 0 10 | * major version: 52 11 | * flags: ACC_PUBLIC, ACC_SUPER 12 | * Constant pool: 13 | * #1 = Methodref #3.#20 // java/lang/Object."":()V 14 | * #2 = Class #21 // org/reflections/repeatable/MultiName 15 | * #3 = Class #22 // java/lang/Object 16 | * #4 = Utf8 17 | * #5 = Utf8 ()V 18 | * #6 = Utf8 Code 19 | * #7 = Utf8 LineNumberTable 20 | * #8 = Utf8 LocalVariableTable 21 | * #9 = Utf8 this 22 | * #10 = Utf8 Lorg/reflections/repeatable/MultiName; 23 | * #11 = Utf8 SourceFile 24 | * #12 = Utf8 MultiName.java 25 | * #13 = Utf8 RuntimeVisibleAnnotations 26 | * #14 = Utf8 Lorg/reflections/repeatable/Names; 27 | * #15 = Utf8 value 28 | * #16 = Utf8 Lorg/reflections/repeatable/Name; 29 | * #17 = Utf8 name 30 | * #18 = Utf8 foo 31 | * #19 = Utf8 bar 32 | * #20 = NameAndType #4:#5 // "":()V 33 | * #21 = Utf8 org/reflections/repeatable/MultiName 34 | * #22 = Utf8 java/lang/Object 35 | * { 36 | * public org.reflections.repeatable.MultiName(); 37 | * descriptor: ()V 38 | * flags: ACC_PUBLIC 39 | * Code: 40 | * stack=1, locals=1, args_size=1 41 | * 0: aload_0 42 | * 1: invokespecial #1 // Method java/lang/Object."":()V 43 | * 4: return 44 | * LineNumberTable: 45 | * line 5: 0 46 | * LocalVariableTable: 47 | * Start Length Slot Name Signature 48 | * 0 5 0 this Lorg/reflections/repeatable/MultiName; 49 | * } 50 | * SourceFile: "MultiName.java" 51 | * RuntimeVisibleAnnotations: 52 | * 0: #14(#15=[@#16(#17=s#18),@#16(#17=s#19)]) 53 | * 54 | * two @name was compiled to one @NAMEs Array with two elements 55 | */ 56 | @Name(name = "foo") 57 | @Name(name = "bar") 58 | public class MultiName { 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/util/ReflectionsIterables.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashSet; 5 | import java.util.Iterator; 6 | import java.util.List; 7 | import java.util.NoSuchElementException; 8 | import java.util.Set; 9 | import java.util.function.Function; 10 | 11 | /** 12 | * @author aschoerk 13 | */ 14 | public class ReflectionsIterables { 15 | 16 | public static boolean isEmpty(Iterable iterable) { 17 | return iterable == null || !iterable.iterator().hasNext(); 18 | } 19 | 20 | public static T getOnlyElement(Iterable iterable) { 21 | if (iterable == null) 22 | throw new NoSuchElementException(); 23 | final Iterator iterator = iterable.iterator(); 24 | if (!iterator.hasNext()) 25 | throw new NoSuchElementException(); 26 | T res = iterator.next(); 27 | if (iterator.hasNext()) 28 | throw new IllegalArgumentException(); 29 | return res; 30 | } 31 | 32 | public static Iterable concat(Iterable a, 33 | Iterable b) { 34 | List res = new ArrayList<>(); 35 | for (T aEl: a) { 36 | res.add(aEl); 37 | } 38 | for (T bEl: b) { 39 | res.add(bEl); 40 | } 41 | return res; 42 | } 43 | 44 | public static Set makeSetOf(Iterable a) { 45 | HashSet res = new HashSet<>(); 46 | 47 | for (T aEl: a) { 48 | res.add(aEl); 49 | } 50 | return res; 51 | } 52 | 53 | 54 | public static boolean contains(Iterable iterable, Object element) { 55 | for (Object el: iterable) { 56 | if (el.equals(element)) 57 | return true; 58 | } 59 | return false; 60 | } 61 | 62 | public static Iterable transform(Iterable fromIterable, 63 | Function function) { 64 | final ArrayList res = new ArrayList<>(); 65 | if (fromIterable != null) { 66 | for (F el : fromIterable) { 67 | res.add(function.apply(el)); 68 | } 69 | } 70 | return res; 71 | } 72 | 73 | public static Set transformToSet(Iterable fromIterable, 74 | Function function) { 75 | Set res = new HashSet<>(); 76 | if (fromIterable != null) { 77 | for (F el : fromIterable) { 78 | res.add(function.apply(el)); 79 | } 80 | } 81 | return res; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/JarInputDir.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import org.reflections8.Reflections; 4 | import org.reflections8.ReflectionsException; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.net.URL; 9 | import java.util.Iterator; 10 | import java.util.jar.JarInputStream; 11 | import java.util.zip.ZipEntry; 12 | 13 | public class JarInputDir implements Vfs.Dir { 14 | private final URL url; 15 | JarInputStream jarInputStream; 16 | long cursor = 0; 17 | long nextCursor = 0; 18 | 19 | public JarInputDir(URL url) { 20 | this.url = url; 21 | } 22 | 23 | public String getPath() { 24 | return url.getPath(); 25 | } 26 | 27 | public Iterable getFiles() { 28 | return () -> new Iterator() { 29 | { 30 | try { jarInputStream = new JarInputStream(url.openConnection().getInputStream()); } 31 | catch (Exception e) { throw new ReflectionsException("Could not open url connection", e); } 32 | } 33 | 34 | Vfs.File entry = null; 35 | 36 | @Override 37 | public boolean hasNext() { 38 | return entry != null || (entry = computeNext()) != null; 39 | } 40 | 41 | @Override 42 | public Vfs.File next() { 43 | Vfs.File next = entry; 44 | entry = null; 45 | return next; 46 | } 47 | 48 | private Vfs.File computeNext() { 49 | while (true) { 50 | try { 51 | ZipEntry entry = jarInputStream.getNextJarEntry(); 52 | if (entry == null) { 53 | return null; 54 | } 55 | 56 | long size = entry.getSize(); 57 | if (size < 0) size = 0xffffffffl + size; //JDK-6916399 58 | nextCursor += size; 59 | if (!entry.isDirectory()) { 60 | return new JarInputFile(entry, JarInputDir.this, cursor, nextCursor); 61 | } 62 | } catch (IOException e) { 63 | throw new ReflectionsException("could not get next zip entry", e); 64 | } 65 | } 66 | } 67 | }; 68 | } 69 | 70 | public void close() { 71 | try { if (jarInputStream != null) ((InputStream) jarInputStream).close(); } 72 | catch (IOException e) { 73 | if (Reflections.log.isPresent()) { 74 | Reflections.log.get().warn("Could not close InputStream", e); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/TestModel.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 4 | 5 | import java.lang.annotation.Inherited; 6 | import java.lang.annotation.Retention; 7 | 8 | /** 9 | * 10 | */ 11 | @SuppressWarnings({"ALL"}) 12 | public interface TestModel { 13 | public @Retention(RUNTIME) @Inherited @interface MAI1 {} 14 | public @Retention(RUNTIME) @MAI1 @interface AI1 {} 15 | public @AI1 interface I1 {} 16 | public @Retention(RUNTIME) @Inherited @interface AI2 {} 17 | public @AI2 interface I2 extends I1 {} 18 | 19 | public @Retention(RUNTIME) @Inherited @interface AC1 {} 20 | public @Retention(RUNTIME) @interface AC1n {} 21 | public @AC1 @AC1n class C1 implements I2 {} 22 | public @Retention(RUNTIME) @interface AC2 { 23 | public abstract String value(); 24 | } 25 | 26 | public @AC2("grr...") class C2 extends C1 {} 27 | public @AC2("ugh?!") class C3 extends C1 {} 28 | 29 | public @Retention(RUNTIME) @interface AM1 { 30 | public abstract String value(); 31 | } 32 | public @Retention(RUNTIME) @interface AF1 { 33 | public abstract String value(); 34 | } 35 | public class C4 { 36 | @AF1("1") private String f1; 37 | @AF1("2") protected String f2; 38 | protected String f3; 39 | 40 | public C4() { } 41 | @AM1("1") public C4(@AM1("1") String f1) { this.f1 = f1; } 42 | 43 | @AM1("1") protected void m1() {} 44 | @AM1("1") public void m1(int integer, String... strings) {} 45 | @AM1("1") public void m1(int[][] integer, String[][] strings) {} 46 | @AM1("2") public String m3() {return null;} 47 | public String m4(@AM1("2") String string) {return null;} 48 | public C3 c2toC3(C2 c2) {return null;} 49 | public int add(int i1, int i2) { return i1+i2; } 50 | } 51 | 52 | public class C5 extends C3 {} 53 | public @AC2("ugh?!") interface I3 {} 54 | public class C6 implements I3 {} 55 | 56 | public @Retention(RUNTIME) @AC2("ugh?!") @interface AC3 { } 57 | public @AC3 class C7 {} 58 | 59 | public interface Usage { 60 | public static class C1 { 61 | C2 c2 = new C2(); 62 | public C1() { } 63 | public C1(C2 c2) { this.c2 = c2; } 64 | public void method() { c2.method(); } 65 | public void method(String string) { c2.method(); } 66 | } 67 | public static class C2 { 68 | C1 c1 = new C1(); 69 | public void method() { 70 | c1 = new C1(); 71 | c1 = new C1(this); 72 | c1.method(); 73 | c1.method(""); 74 | } 75 | } 76 | } 77 | public class C8 { 78 | public void print() {Runnable run = () -> {};} 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/AbstractScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.util.Optional; 4 | import java.util.function.Predicate; 5 | 6 | import org.reflections8.Configuration; 7 | import org.reflections8.ReflectionsException; 8 | import org.reflections8.adapters.MetadataAdapter; 9 | import org.reflections8.util.AlwaysTruePredicate; 10 | import org.reflections8.util.SetMultimap; 11 | import org.reflections8.vfs.Vfs; 12 | 13 | /** 14 | * 15 | */ 16 | @SuppressWarnings({"RawUseOfParameterizedType", "unchecked"}) 17 | public abstract class AbstractScanner implements Scanner { 18 | 19 | private Configuration configuration; 20 | private SetMultimap store; 21 | private Predicate resultFilter = new AlwaysTruePredicate(); //accept all by default 22 | 23 | public boolean acceptsInput(String file) { 24 | return getMetadataAdapter().acceptsInput(file); 25 | } 26 | 27 | public Object scan(Vfs.File file, Optional classObject) { 28 | if (!classObject.isPresent()) { 29 | try { 30 | classObject = Optional.of(configuration.getMetadataAdapter().getOrCreateClassObject(file)); 31 | } catch (Exception e) { 32 | throw new ReflectionsException("could not create class object from file " + file.getRelativePath(), e); 33 | } 34 | } 35 | scan(classObject.get()); 36 | return classObject.get(); 37 | } 38 | 39 | public abstract void scan(Object cls); 40 | 41 | // 42 | public Configuration getConfiguration() { 43 | return configuration; 44 | } 45 | 46 | public void setConfiguration(final Configuration configuration) { 47 | this.configuration = configuration; 48 | } 49 | 50 | public SetMultimap getStore() { 51 | return store; 52 | } 53 | 54 | public void setStore(final SetMultimap store) { 55 | this.store = store; 56 | } 57 | 58 | public Predicate getResultFilter() { 59 | return resultFilter; 60 | } 61 | 62 | public void setResultFilter(Predicate resultFilter) { 63 | this.resultFilter = resultFilter; 64 | } 65 | 66 | public Scanner filterResultsBy(Predicate filter) { 67 | this.setResultFilter(filter); return this; 68 | } 69 | 70 | // 71 | public boolean acceptResult(final String fqn) { 72 | return fqn != null && resultFilter.test(fqn); 73 | } 74 | 75 | protected MetadataAdapter getMetadataAdapter() { 76 | return configuration.getMetadataAdapter(); 77 | } 78 | 79 | // 80 | @Override public boolean equals(Object o) { 81 | return this == o || o != null && getClass() == o.getClass(); 82 | } 83 | 84 | @Override public int hashCode() { 85 | return getClass().hashCode(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/JavaCodeSerializerTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import static java.util.Arrays.asList; 4 | import static org.junit.Assert.assertEquals; 5 | import static org.reflections8.TestModel.AC2; 6 | import static org.reflections8.TestModel.C1; 7 | import static org.reflections8.TestModel.C2; 8 | import static org.reflections8.TestModel.C4; 9 | 10 | import java.util.function.Predicate; 11 | 12 | import org.junit.BeforeClass; 13 | import org.junit.Test; 14 | import org.reflections8.scanners.TypeElementsScanner; 15 | import org.reflections8.serializers.JavaCodeSerializer; 16 | import org.reflections8.util.ClasspathHelper; 17 | import org.reflections8.util.ConfigurationBuilder; 18 | import org.reflections8.util.FilterBuilder; 19 | 20 | /** */ 21 | public class JavaCodeSerializerTest { 22 | 23 | @BeforeClass 24 | public static void generateAndSave() { 25 | Predicate filter = new FilterBuilder().include("org.reflections8.TestModel\\$.*"); 26 | 27 | Reflections reflections8 = new Reflections(new ConfigurationBuilder() 28 | .filterInputsBy(filter) 29 | .setScanners(new TypeElementsScanner().includeFields().publicOnly(false)) 30 | .setUrls(asList(ClasspathHelper.forClass(TestModel.class)))); 31 | 32 | //save 33 | String filename = ReflectionsTest.getUserDir() + "/src/test/java/org.reflections8.MyTestModelStore"; 34 | reflections8.save(filename, new JavaCodeSerializer()); 35 | } 36 | 37 | @Test 38 | public void resolve() throws NoSuchMethodException, NoSuchFieldException { 39 | //class 40 | assertEquals(C1.class, 41 | JavaCodeSerializer.resolveClass(MyTestModelStore.org.reflections8.TestModel$C1.class)); 42 | 43 | //method 44 | assertEquals(C4.class.getDeclaredMethod("m1"), 45 | JavaCodeSerializer.resolveMethod(MyTestModelStore.org.reflections8.TestModel$C4.methods.m1.class)); 46 | 47 | //overloaded method with parameters 48 | assertEquals(C4.class.getDeclaredMethod("m1", int.class, String[].class), 49 | JavaCodeSerializer.resolveMethod(MyTestModelStore.org.reflections8.TestModel$C4.methods.m1_int__java_lang_String$$.class)); 50 | 51 | //overloaded method with parameters and multi dimensional array 52 | assertEquals(C4.class.getDeclaredMethod("m1", int[][].class, String[][].class), 53 | JavaCodeSerializer.resolveMethod(MyTestModelStore.org.reflections8.TestModel$C4.methods.m1_int$$$$__java_lang_String$$$$.class)); 54 | 55 | //field 56 | assertEquals(C4.class.getDeclaredField("f1"), 57 | JavaCodeSerializer.resolveField(MyTestModelStore.org.reflections8.TestModel$C4.fields.f1.class)); 58 | 59 | //annotation 60 | assertEquals(C2.class.getAnnotation(AC2.class), 61 | JavaCodeSerializer.resolveAnnotation(MyTestModelStore.org.reflections8.TestModel$C2.annotations.org_reflections8_TestModel$AC2.class)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/util/ConfigurationBuilderTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.util.Arrays; 7 | import java.util.Optional; 8 | 9 | public class ConfigurationBuilderTest { 10 | @Test 11 | public void shouldAddNewClassloaderToEmptyConfiguration() { 12 | ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); 13 | 14 | Assert.assertEquals("Should be empty", configurationBuilder.getClassLoaders(), Optional.empty()); 15 | Assert.assertFalse("Should not present", configurationBuilder.getClassLoaders().isPresent()); 16 | 17 | configurationBuilder.addClassLoader(this.getClass().getClassLoader()); 18 | 19 | Assert.assertTrue("Should be present", configurationBuilder.getClassLoaders().isPresent()); 20 | Assert.assertEquals("Should be one", 1, configurationBuilder.getClassLoaders().get().length); 21 | Assert.assertArrayEquals("Should be equals", Optional.of(new ClassLoader[]{this.getClass().getClassLoader()}).get(), configurationBuilder.getClassLoaders().get()); 22 | } 23 | 24 | @Test 25 | public void shouldAddNewTwoClassloaderToEmptyConfiguration() { 26 | ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); 27 | 28 | Assert.assertEquals("Should be empty", configurationBuilder.getClassLoaders(), Optional.empty()); 29 | Assert.assertFalse("Should not present", configurationBuilder.getClassLoaders().isPresent()); 30 | 31 | configurationBuilder.addClassLoader(this.getClass().getClassLoader()); 32 | configurationBuilder.addClassLoader(this.getClass().getClassLoader().getParent()); 33 | 34 | Assert.assertTrue("Should be present", configurationBuilder.getClassLoaders().isPresent()); 35 | Assert.assertEquals("Should be two", 2, configurationBuilder.getClassLoaders().get().length); 36 | Assert.assertArrayEquals("Should be equals", Optional.of(new ClassLoader[]{this.getClass().getClassLoader(), this.getClass().getClassLoader().getParent()}).get(), configurationBuilder.getClassLoaders().get()); 37 | } 38 | 39 | @Test 40 | public void shouldAddNewTwoClassloaderAsListToEmptyConfiguration() { 41 | ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); 42 | 43 | Assert.assertEquals("Should be empty", configurationBuilder.getClassLoaders(), Optional.empty()); 44 | Assert.assertFalse("Should not present", configurationBuilder.getClassLoaders().isPresent()); 45 | 46 | configurationBuilder.addClassLoaders(Arrays.asList(this.getClass().getClassLoader(), this.getClass().getClassLoader().getParent())); 47 | 48 | Assert.assertTrue("Should be present", configurationBuilder.getClassLoaders().isPresent()); 49 | Assert.assertEquals("Should be two", 2, configurationBuilder.getClassLoaders().get().length); 50 | Assert.assertArrayEquals("Should be equals", Optional.of(new ClassLoader[]{this.getClass().getClassLoader(), this.getClass().getClassLoader().getParent()}).get(), configurationBuilder.getClassLoaders().get()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/util/HashSetMultimap.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.HashMap; 6 | import java.util.HashSet; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.function.Supplier; 10 | 11 | /** 12 | * Helper class used to avoid guava 13 | * 14 | * @author aschoerk 15 | */ 16 | public class HashSetMultimap extends HashMap> implements SetMultimap { 17 | private static final long serialVersionUID = 140511307437539771L; 18 | private transient final Supplier> setSupplier; 19 | 20 | public HashSetMultimap() { 21 | setSupplier = () -> new HashSet<>(); 22 | } 23 | 24 | public HashSetMultimap(Supplier> setSupplier) { 25 | this.setSupplier = setSupplier; 26 | } 27 | 28 | @Override 29 | public boolean putSingle(T key, V value) { 30 | Set vs = super.get(key); 31 | if (vs != null) { 32 | return vs.add(value); 33 | } else { 34 | Set setValue = setSupplier.get(); 35 | setValue.add(value); 36 | super.put(key, setValue); 37 | return true; 38 | } 39 | } 40 | 41 | @Override 42 | public void putAllSingles(SetMultimap m) { 43 | for (T key: m.keySet()) { 44 | Set val = m.get(key); 45 | Set vs = super.get(key); 46 | if(vs != null) { 47 | if (val != null) { 48 | vs.addAll(val); 49 | } 50 | } 51 | else { 52 | if (val == null) { 53 | super.put(key, null); 54 | } else { 55 | Set setValue = setSupplier.get(); 56 | if(val != null) 57 | setValue.addAll(m.get(key)); 58 | super.put(key, setValue); 59 | } 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | public boolean removeSingle(Object key, V value) { 66 | Set vs = super.get(key); 67 | if (vs == null) 68 | return false; 69 | else { 70 | boolean res = vs.remove(value); 71 | if (vs.isEmpty()) { 72 | super.remove(key); 73 | } 74 | return res; 75 | } 76 | } 77 | 78 | @Override 79 | public Collection flatValues() { 80 | ArrayList res = new ArrayList<>(); 81 | for (Set s: values()) { 82 | res.addAll(s); 83 | } 84 | return res; 85 | } 86 | 87 | @Override 88 | public Set flatValuesAsSet() { 89 | HashSet res = new HashSet<>(); 90 | for (Set s: values()) { 91 | res.addAll(s); 92 | } 93 | return res; 94 | } 95 | 96 | @Override 97 | public Map> asMap() { 98 | Map> result = new HashMap<>(); 99 | for (Entry> e: entrySet()) { 100 | if (e.getValue() != null && !e.getValue().isEmpty()) 101 | result.put(e.getKey(), e.getValue()); 102 | } 103 | return result; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/ReflectionsCollectTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import static java.util.Arrays.asList; 4 | import static org.junit.Assert.assertThat; 5 | import static org.reflections8.util.Utils.index; 6 | 7 | import java.util.Set; 8 | import java.util.function.Predicate; 9 | import java.util.regex.Pattern; 10 | 11 | import org.junit.BeforeClass; 12 | import org.junit.Test; 13 | import org.reflections8.scanners.MemberUsageScanner; 14 | import org.reflections8.scanners.MethodAnnotationsScanner; 15 | import org.reflections8.scanners.MethodParameterNamesScanner; 16 | import org.reflections8.scanners.MethodParameterScanner; 17 | import org.reflections8.scanners.ResourcesScanner; 18 | import org.reflections8.scanners.SubTypesScanner; 19 | import org.reflections8.scanners.TypeAnnotationsScanner; 20 | import org.reflections8.serializers.JsonSerializer; 21 | import org.reflections8.util.ClasspathHelper; 22 | import org.reflections8.util.ConfigurationBuilder; 23 | import org.reflections8.util.FilterBuilder; 24 | 25 | /** */ 26 | public class ReflectionsCollectTest extends ReflectionsTest { 27 | 28 | @BeforeClass 29 | public static void init() { 30 | Reflections ref = new Reflections(new ConfigurationBuilder() 31 | .addUrls(ClasspathHelper.forClass(TestModel.class)) 32 | .filterInputsBy(TestModelFilter) 33 | .setScanners( 34 | new SubTypesScanner(false), 35 | new TypeAnnotationsScanner(), 36 | new MethodAnnotationsScanner(), 37 | new MethodParameterNamesScanner(), 38 | new MemberUsageScanner())); 39 | 40 | ref.save(getUserDir() + "/target/test-classes" + "/META-INF/reflections/testModel-reflections.xml"); 41 | 42 | ref = new Reflections(new ConfigurationBuilder() 43 | .setUrls(asList(ClasspathHelper.forClass(TestModel.class))) 44 | .filterInputsBy(TestModelFilter) 45 | .setScanners( 46 | new MethodParameterScanner())); 47 | 48 | final JsonSerializer serializer = new JsonSerializer(); 49 | ref.save(getUserDir() + "/target/test-classes" + "/META-INF/reflections/testModel-reflections.json", serializer); 50 | 51 | reflections8 = Reflections 52 | .collect() 53 | .merge(Reflections.collect("META-INF/reflections", 54 | new FilterBuilder().include(".*-reflections.json"), 55 | serializer)); 56 | } 57 | 58 | @Test 59 | public void testResourcesScanner() { 60 | Predicate filter = new FilterBuilder().include(".*\\.xml").include(".*\\.json"); 61 | Reflections localReflections8 = new Reflections(new ConfigurationBuilder() 62 | .filterInputsBy(filter) 63 | .setScanners(new ResourcesScanner()) 64 | .setUrls(asList(ClasspathHelper.forClass(TestModel.class)))); 65 | 66 | Set resolved = localReflections8.getResources(Pattern.compile(".*resource1-reflections\\.xml")); 67 | assertThat(resolved, are("META-INF/reflections/resource1-reflections.xml")); 68 | 69 | Set resources = localReflections8.getStore().get(index(ResourcesScanner.class)).keySet(); 70 | assertThat(resources, are("resource1-reflections.xml", "resource2-reflections.xml", 71 | "testModel-reflections.xml", "testModel-reflections.json")); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/serializers/JsonSerializer.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.serializers; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.lang.reflect.Type; 8 | import java.nio.charset.Charset; 9 | import java.nio.file.Files; 10 | import java.util.HashSet; 11 | import java.util.Map; 12 | import java.util.Set; 13 | import java.util.function.Supplier; 14 | 15 | import org.reflections8.Reflections; 16 | import org.reflections8.util.HashSetMultimap; 17 | import org.reflections8.util.SetMultimap; 18 | import org.reflections8.util.Utils; 19 | 20 | import com.google.gson.Gson; 21 | import com.google.gson.GsonBuilder; 22 | import com.google.gson.JsonArray; 23 | import com.google.gson.JsonDeserializationContext; 24 | import com.google.gson.JsonDeserializer; 25 | import com.google.gson.JsonElement; 26 | import com.google.gson.JsonObject; 27 | import com.google.gson.JsonParseException; 28 | import com.google.gson.JsonSerializationContext; 29 | 30 | /** serialization of Reflections to json 31 | * 32 | *

an example of produced json: 33 | *

34 |  * {"store":{"storeMap":
35 |  *    {"org.reflections8.scanners.TypeAnnotationsScanner":{
36 |  *       "org.reflections8.TestModel$AC1":["org.reflections8.TestModel$C1"],
37 |  *       "org.reflections8.TestModel$AC2":["org.reflections8.TestModel$I3",
38 |  * ...
39 |  * 
40 | * */ 41 | public class JsonSerializer implements Serializer { 42 | private Gson gson; 43 | 44 | public Reflections read(InputStream inputStream) { 45 | return getGson().fromJson(new InputStreamReader(inputStream, Charset.forName("UTF-8")), Reflections.class); 46 | } 47 | 48 | public File save(Reflections reflections, String filename) { 49 | try { 50 | File file = Utils.prepareFile(filename); 51 | Files.write(file.toPath(),toString(reflections).getBytes(Charset.defaultCharset())); 52 | return file; 53 | } catch (IOException e) { 54 | throw new RuntimeException(e); 55 | } 56 | } 57 | 58 | public String toString(Reflections reflections) { 59 | return getGson().toJson(reflections); 60 | } 61 | 62 | private Gson getGson() { 63 | if (gson == null) { 64 | gson = new GsonBuilder() 65 | .registerTypeAdapter(SetMultimap.class, new com.google.gson.JsonSerializer() { 66 | public JsonElement serialize(SetMultimap multimap, Type type, JsonSerializationContext jsonSerializationContext) { 67 | return jsonSerializationContext.serialize(multimap.asMap()); 68 | } 69 | }) 70 | .registerTypeAdapter(SetMultimap.class, new JsonDeserializer() { 71 | public SetMultimap deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { 72 | final SetMultimap map = new HashSetMultimap(new Supplier>() { 73 | public Set get() { 74 | return new HashSet(); 75 | } 76 | }); 77 | for (Map.Entry entry : ((JsonObject) jsonElement).entrySet()) { 78 | for (JsonElement element : (JsonArray) entry.getValue()) { 79 | map.putSingle(entry.getKey(), element.getAsString()); 80 | } 81 | } 82 | return map; 83 | } 84 | }) 85 | .setPrettyPrinting() 86 | .create(); 87 | 88 | } 89 | return gson; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/util/ClasspathHelperTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | import static org.junit.Assert.assertNull; 6 | 7 | import java.net.MalformedURLException; 8 | import java.net.URL; 9 | import java.net.URLClassLoader; 10 | import java.util.Arrays; 11 | import java.util.Collection; 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | import org.junit.Assert; 16 | import org.junit.Test; 17 | import org.reflections8.util.ClasspathHelper; 18 | 19 | /** 20 | * Test ClasspathHelper utility class 21 | */ 22 | public final class ClasspathHelperTest { 23 | @Test 24 | public void testForClassLoaderShouldntReorderUrls() throws MalformedURLException { 25 | // testing same URL set with different order to not fall into the case when HashSet orders elements in the same order as we do 26 | final URL[] urls1 = {new URL("file", "foo", 1111, "foo"), new URL("file", "bar", 1111, "bar"),new URL("file", "baz", 1111, "baz")}; 27 | final List urlsList2 = Arrays.asList(urls1); 28 | Collections.reverse(urlsList2); 29 | final URL[] urls2 = urlsList2.toArray(new URL[urlsList2.size()]); 30 | 31 | final URLClassLoader urlClassLoader1 = new URLClassLoader(urls1, null); 32 | final URLClassLoader urlClassLoader2 = new URLClassLoader(urls2, null); 33 | final Collection resultUrls1 = ClasspathHelper.forClassLoader(urlClassLoader1); 34 | final Collection resultUrls2 = ClasspathHelper.forClassLoader(urlClassLoader2); 35 | 36 | Assert.assertArrayEquals("URLs returned from forClassLoader should be in the same order as source URLs", urls1, resultUrls1.toArray()); 37 | Assert.assertArrayEquals("URLs returned from forClassLoader should be in the same order as source URLs", urls2, resultUrls2.toArray()); 38 | } 39 | 40 | 41 | @Test 42 | public void cleanPathWillRemoveFileProtocol() throws Exception { 43 | URL url = new URL("file:/D:/repo/reflections/"); 44 | String cleanPath = ClasspathHelper.cleanPath(url); 45 | assertEquals("Should've removed file from path", "/D:/repo/reflections/", cleanPath); 46 | } 47 | 48 | @Test 49 | public void cleanPathWillRemoveJarAndFileProtocolsAndBang() throws Exception { 50 | URL url = new URL("jar:file:/D:/repo/reflections/jarWithManifest.jar!/"); 51 | String cleanPath = ClasspathHelper.cleanPath(url); 52 | assertEquals("Should've removed jar, file and ! from path", "/D:/repo/reflections/jarWithManifest.jar/", cleanPath); 53 | } 54 | 55 | @Test 56 | public void tryToGetValidUrlWillReturnNullWhenCannotFindFile() { 57 | URL validUrl = ClasspathHelper.tryToGetValidUrl("does", "not", "exist.jar"); 58 | assertNull(validUrl); 59 | } 60 | 61 | @Test 62 | public void tryToGetValidUrlWillReturnUrlBasedOnFilename() { 63 | URL validUrl = ClasspathHelper.tryToGetValidUrl(null, null, "src/test/resources/jarWithManifest.jar"); 64 | assertNotNull(validUrl); 65 | } 66 | 67 | @Test 68 | public void tryToGetValidUrlWillReturnUrlBasedOnPathAndFilename() { 69 | URL validUrl = ClasspathHelper.tryToGetValidUrl(null, "src/test/resources", "jarWithManifest.jar"); 70 | assertNotNull(validUrl); 71 | } 72 | 73 | @Test 74 | public void tryToGetValidUrlWillReturnUrlBasedOnWorkingDirAndFilename() { 75 | URL validUrl = ClasspathHelper.tryToGetValidUrl("src/test/resources", null, "jarWithManifest.jar"); 76 | assertNotNull(validUrl); 77 | } 78 | 79 | @Test 80 | public void tryToGetValidUrlWillReturnUrlBasedOnFilenameAsUrl() { 81 | // handle Windows path, shouldn't affect *nix since replace won't find anything 82 | String workingDir = System.getProperty("user.dir").replace("\\", "/"); 83 | // Windows won't include starting / so add it if needed 84 | workingDir = workingDir.startsWith("/") ? workingDir : "/" + workingDir; 85 | URL validUrl = ClasspathHelper.tryToGetValidUrl(null, null, 86 | "file:" + workingDir + "/src/test/resources/jarWithManifest.jar"); 87 | assertNotNull(validUrl); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/UrlTypeVFS.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import org.reflections8.Reflections; 4 | import org.reflections8.ReflectionsException; 5 | import org.reflections8.vfs.Vfs.Dir; 6 | import org.reflections8.vfs.Vfs.UrlType; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.net.MalformedURLException; 11 | import java.net.URL; 12 | import java.util.function.Predicate; 13 | import java.util.jar.JarFile; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | /** 18 | * UrlType to be used by Reflections library. 19 | * This class handles the vfszip and vfsfile protocol of JBOSS files. 20 | *

21 | *

to use it, register it in Vfs via {@link org.reflections.vfs.Vfs#addDefaultURLTypes(org.reflections.vfs.Vfs.UrlType)} or {@link org.reflections.vfs.Vfs#setDefaultURLTypes(java.util.List)}. 22 | * @author Sergio Pola 23 | * 24 | */ 25 | public class UrlTypeVFS implements UrlType { 26 | public final static String[] REPLACE_EXTENSION = new String[]{".ear/", ".jar/", ".war/", ".sar/", ".har/", ".par/"}; 27 | 28 | final String VFSZIP = "vfszip"; 29 | final String VFSFILE = "vfsfile"; 30 | 31 | public boolean matches(URL url) { 32 | return VFSZIP.equals(url.getProtocol()) || VFSFILE.equals(url.getProtocol()); 33 | } 34 | 35 | public Dir createDir(final URL url) { 36 | try { 37 | URL adaptedUrl = adaptURL(url); 38 | return new ZipDir(new JarFile(adaptedUrl.getFile())); 39 | } catch (Exception e) { 40 | try { 41 | return new ZipDir(new JarFile(url.getFile())); 42 | } catch (IOException e1) { 43 | if (Reflections.log.isPresent()) { 44 | Reflections.log.get().warn("Could not get URL", e); 45 | } 46 | } 47 | } 48 | return null; 49 | } 50 | 51 | public URL adaptURL(URL url) throws MalformedURLException { 52 | if (VFSZIP.equals(url.getProtocol())) { 53 | return replaceZipSeparators(url.getPath(), file -> file.exists() && file.isFile()); 54 | } else if (VFSFILE.equals(url.getProtocol())) { 55 | return new URL(url.toString().replace(VFSFILE, "file")); 56 | } else { 57 | return url; 58 | } 59 | } 60 | 61 | URL replaceZipSeparators(String path, Predicate acceptFile) 62 | throws MalformedURLException { 63 | int pos = 0; 64 | while (pos != -1) { 65 | pos = findFirstMatchOfDeployableExtention(path, pos); 66 | 67 | if (pos > 0) { 68 | File file = new File(path.substring(0, pos - 1)); 69 | if (acceptFile.test(file)) { return replaceZipSeparatorStartingFrom(path, pos); } 70 | } 71 | } 72 | 73 | throw new ReflectionsException("Unable to identify the real zip file in path '" + path + "'."); 74 | } 75 | 76 | int findFirstMatchOfDeployableExtention(String path, int pos) { 77 | Pattern p = Pattern.compile("\\.[ejprw]ar/"); 78 | Matcher m = p.matcher(path); 79 | if (m.find(pos)) { 80 | return m.end(); 81 | } else { 82 | return -1; 83 | } 84 | } 85 | 86 | URL replaceZipSeparatorStartingFrom(String path, int pos) 87 | throws MalformedURLException { 88 | String zipFile = path.substring(0, pos - 1); 89 | String zipPath = path.substring(pos); 90 | 91 | int numSubs = 1; 92 | for (String ext : REPLACE_EXTENSION) { 93 | while (zipPath.contains(ext)) { 94 | zipPath = zipPath.replace(ext, ext.substring(0, 4) + "!"); 95 | numSubs++; 96 | } 97 | } 98 | 99 | String prefix = ""; 100 | for (int i = 0; i < numSubs; i++) { 101 | prefix += "zip:"; 102 | } 103 | 104 | if (zipPath.trim().length() == 0) { 105 | return new URL(prefix + "/" + zipFile); 106 | } else { 107 | return new URL(prefix + "/" + zipFile + "!" + zipPath); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/MyTestModelStore.java: -------------------------------------------------------------------------------- 1 | //generated using Reflections JavaCodeSerializer [Wed Jul 27 14:27:42 CEST 2022] 2 | package org.reflections8; 3 | 4 | public interface MyTestModelStore { 5 | 6 | public interface org { 7 | public interface reflections8 { 8 | public interface TestModel$AC1 { 9 | public interface annotations { 10 | public interface java_lang_annotation_Inherited {} 11 | public interface java_lang_annotation_Retention {} 12 | } 13 | } 14 | public interface TestModel$AC1n { 15 | public interface annotations { 16 | public interface java_lang_annotation_Retention {} 17 | } 18 | } 19 | public interface TestModel$AC2 { 20 | public interface methods { 21 | public interface value {} 22 | } 23 | public interface annotations { 24 | public interface java_lang_annotation_Retention {} 25 | } 26 | } 27 | public interface TestModel$AC3 { 28 | public interface annotations { 29 | public interface org_reflections8_TestModel$AC2 {} 30 | public interface java_lang_annotation_Retention {} 31 | } 32 | } 33 | public interface TestModel$AF1 { 34 | public interface methods { 35 | public interface value {} 36 | } 37 | public interface annotations { 38 | public interface java_lang_annotation_Retention {} 39 | } 40 | } 41 | public interface TestModel$AI1 { 42 | public interface annotations { 43 | public interface org_reflections8_TestModel$MAI1 {} 44 | public interface java_lang_annotation_Retention {} 45 | } 46 | } 47 | public interface TestModel$AI2 { 48 | public interface annotations { 49 | public interface java_lang_annotation_Inherited {} 50 | public interface java_lang_annotation_Retention {} 51 | } 52 | } 53 | public interface TestModel$AM1 { 54 | public interface methods { 55 | public interface value {} 56 | } 57 | public interface annotations { 58 | public interface java_lang_annotation_Retention {} 59 | } 60 | } 61 | public interface TestModel$C1 { 62 | public interface annotations { 63 | public interface org_reflections8_TestModel$AC1 {} 64 | public interface org_reflections8_TestModel$AC1n {} 65 | } 66 | } 67 | public interface TestModel$C2 { 68 | public interface annotations { 69 | public interface org_reflections8_TestModel$AC2 {} 70 | } 71 | } 72 | public interface TestModel$C3 { 73 | public interface annotations { 74 | public interface org_reflections8_TestModel$AC2 {} 75 | } 76 | } 77 | public interface TestModel$C4 { 78 | public interface fields { 79 | public interface f1 {} 80 | public interface f2 {} 81 | public interface f3 {} 82 | } 83 | public interface methods { 84 | public interface add {} 85 | public interface m1_int__java_lang_String$$ {} 86 | public interface m1 {} 87 | public interface m1_int$$$$__java_lang_String$$$$ {} 88 | public interface m3 {} 89 | public interface m4 {} 90 | public interface c2toC3 {} 91 | } 92 | } 93 | public interface TestModel$C5 { 94 | } 95 | public interface TestModel$C6 { 96 | } 97 | public interface TestModel$C7 { 98 | public interface annotations { 99 | public interface org_reflections8_TestModel$AC3 {} 100 | } 101 | } 102 | public interface TestModel$C8 { 103 | public interface methods { 104 | public interface lambda$print$0 {} 105 | public interface print {} 106 | } 107 | } 108 | public interface TestModel$I1 { 109 | public interface annotations { 110 | public interface org_reflections8_TestModel$AI1 {} 111 | } 112 | } 113 | public interface TestModel$I2 { 114 | public interface annotations { 115 | public interface org_reflections8_TestModel$AI2 {} 116 | } 117 | } 118 | public interface TestModel$I3 { 119 | public interface annotations { 120 | public interface org_reflections8_TestModel$AC2 {} 121 | } 122 | } 123 | public interface TestModel$MAI1 { 124 | public interface annotations { 125 | public interface java_lang_annotation_Inherited {} 126 | public interface java_lang_annotation_Retention {} 127 | } 128 | } 129 | public interface TestModel$Usage { 130 | } 131 | public interface TestModel$Usage$C1 { 132 | public interface fields { 133 | public interface c2 {} 134 | } 135 | public interface methods { 136 | public interface method {} 137 | public interface method_java_lang_String {} 138 | } 139 | } 140 | public interface TestModel$Usage$C2 { 141 | public interface fields { 142 | public interface c1 {} 143 | } 144 | public interface methods { 145 | public interface method {} 146 | } 147 | } 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/serializers/XmlSerializer.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.serializers; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.StringWriter; 8 | import java.lang.reflect.Constructor; 9 | 10 | import org.dom4j.Document; 11 | import org.dom4j.DocumentException; 12 | import org.dom4j.DocumentFactory; 13 | import org.dom4j.Element; 14 | import org.dom4j.io.OutputFormat; 15 | import org.dom4j.io.SAXReader; 16 | import org.dom4j.io.XMLWriter; 17 | import org.reflections8.Reflections; 18 | import org.reflections8.ReflectionsException; 19 | import org.reflections8.Store; 20 | import org.reflections8.util.ConfigurationBuilder; 21 | import org.reflections8.util.Utils; 22 | 23 | /** serialization of Reflections to xml 24 | * 25 | *

an example of produced xml: 26 | *

 27 |  * <?xml version="1.0" encoding="UTF-8"?>
 28 |  *
 29 |  * <Reflections>
 30 |  *  <SubTypesScanner>
 31 |  *      <entry>
 32 |  *          <key>com.google.inject.Module</key>
 33 |  *          <values>
 34 |  *              <value>fully.qualified.name.1</value>
 35 |  *              <value>fully.qualified.name.2</value>
 36 |  * ...
 37 |  * 
38 | * */ 39 | public class XmlSerializer implements Serializer { 40 | 41 | public Reflections read(InputStream inputStream) { 42 | Reflections reflections8; 43 | try { 44 | Constructor constructor = Reflections.class.getDeclaredConstructor(); 45 | constructor.setAccessible(true); 46 | reflections8 = constructor.newInstance(); 47 | } catch (Exception e) { 48 | reflections8 = new Reflections(new ConfigurationBuilder()); 49 | } 50 | 51 | try { 52 | Document document = new SAXReader().read(inputStream); 53 | for (Object e1 : document.getRootElement().elements()) { 54 | Element index = (Element) e1; 55 | for (Object e2 : index.elements()) { 56 | Element entry = (Element) e2; 57 | Element key = entry.element("key"); 58 | Element values = entry.element("values"); 59 | for (Object o3 : values.elements()) { 60 | Element value = (Element) o3; 61 | reflections8.getStore().getOrCreate(index.getName()).putSingle(key.getText(), value.getText()); 62 | } 63 | } 64 | } 65 | } catch (DocumentException e) { 66 | throw new ReflectionsException("could not read.", e); 67 | } catch (Throwable e) { 68 | throw new RuntimeException("Could not read. Make sure relevant dependencies exist on classpath.", e); 69 | } 70 | 71 | return reflections8; 72 | } 73 | 74 | public File save(final Reflections reflections, final String filename) { 75 | File file = Utils.prepareFile(filename); 76 | 77 | 78 | try { 79 | Document document = createDocument(reflections); 80 | XMLWriter xmlWriter = new XMLWriter(new FileOutputStream(file), OutputFormat.createPrettyPrint()); 81 | xmlWriter.write(document); 82 | xmlWriter.close(); 83 | } catch (IOException e) { 84 | throw new ReflectionsException("could not save to file " + filename, e); 85 | } catch (Throwable e) { 86 | throw new RuntimeException("Could not save to file " + filename + ". Make sure relevant dependencies exist on classpath.", e); 87 | } 88 | 89 | return file; 90 | } 91 | 92 | public String toString(final Reflections reflections) { 93 | Document document = createDocument(reflections); 94 | 95 | try { 96 | StringWriter writer = new StringWriter(); 97 | XMLWriter xmlWriter = new XMLWriter(writer, OutputFormat.createPrettyPrint()); 98 | xmlWriter.write(document); 99 | xmlWriter.close(); 100 | return writer.toString(); 101 | } catch (IOException e) { 102 | throw new RuntimeException(); 103 | } 104 | } 105 | 106 | private Document createDocument(final Reflections reflections8) { 107 | Store map = reflections8.getStore(); 108 | 109 | Document document = DocumentFactory.getInstance().createDocument(); 110 | Element root = document.addElement("Reflections"); 111 | for (String indexName : map.keySet()) { 112 | Element indexElement = root.addElement(indexName); 113 | for (String key : map.get(indexName).keySet()) { 114 | Element entryElement = indexElement.addElement("entry"); 115 | entryElement.addElement("key").setText(key); 116 | Element valuesElement = entryElement.addElement("values"); 117 | for (String value : map.get(indexName).get(key)) { 118 | valuesElement.addElement("value").setText(value); 119 | } 120 | } 121 | } 122 | return document; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/Store.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.Collections; 6 | import java.util.HashMap; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Set; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | import java.util.function.Supplier; 13 | 14 | import org.reflections8.util.HashSetMultimap; 15 | import org.reflections8.util.SetMultimap; 16 | import org.reflections8.util.SynchronizedHashSetMultimap; 17 | 18 | /** 19 | * stores metadata information in multimaps 20 | *

use the different query methods (getXXX) to query the metadata 21 | *

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

use {@link org.reflections8.Reflections#getStore()} to access this store 23 | */ 24 | public class Store { 25 | 26 | private transient boolean concurrent; 27 | private final Map> storeMap; 28 | 29 | //used via reflection 30 | @SuppressWarnings("UnusedDeclaration") 31 | protected Store() { 32 | storeMap = new HashMap>(); 33 | concurrent = false; 34 | } 35 | 36 | public Store(Configuration configuration) { 37 | storeMap = new HashMap>(); 38 | concurrent = configuration.getExecutorService().isPresent(); 39 | } 40 | 41 | /** return all indices */ 42 | public Set keySet() { 43 | return storeMap.keySet(); 44 | } 45 | 46 | /** get or create the multimap object for the given {@code index} */ 47 | public SetMultimap getOrCreate(String index) { 48 | SetMultimap mmap = storeMap.get(index); 49 | if (mmap == null) { 50 | SetMultimap multimap = 51 | new HashSetMultimap( 52 | new Supplier>() { 53 | public Set get() { 54 | return Collections.newSetFromMap(new ConcurrentHashMap()); 55 | } 56 | }); 57 | mmap = concurrent ? new SynchronizedHashSetMultimap(multimap) : multimap; 58 | storeMap.put(index,mmap); 59 | } 60 | return mmap; 61 | } 62 | 63 | /** get the multimap object for the given {@code index}, otherwise throws a {@link org.reflections8.ReflectionsException} */ 64 | public SetMultimap get(String index) { 65 | SetMultimap mmap = storeMap.get(index); 66 | if (mmap == null) { 67 | throw new ReflectionsException("Scanner " + index + " was not configured"); 68 | } 69 | return mmap; 70 | } 71 | 72 | /** get the values stored for the given {@code index} and {@code keys} */ 73 | public Iterable get(String index, String... keys) { 74 | return get(index, Arrays.asList(keys)); 75 | } 76 | 77 | /** get the values stored for the given {@code index} and {@code keys} */ 78 | public Iterable get(String index, Iterable keys) { 79 | SetMultimap mmap = get(index); 80 | IterableChain result = new IterableChain(); 81 | for (String key : keys) { 82 | result.addAll(mmap.get(key)); 83 | } 84 | return result; 85 | } 86 | 87 | /** recursively get the values stored for the given {@code index} and {@code keys}, including keys */ 88 | private Iterable getAllIncluding(String index, Iterable keys, IterableChain result) { 89 | result.addAll(keys); 90 | for (String key : keys) { 91 | Iterable values = get(index, key); 92 | if (values.iterator().hasNext()) { 93 | getAllIncluding(index, values, result); 94 | } 95 | } 96 | return result; 97 | } 98 | 99 | /** recursively get the values stored for the given {@code index} and {@code keys}, not including keys */ 100 | public Iterable getAll(String index, String key) { 101 | return getAllIncluding(index, get(index, key), new IterableChain()); 102 | } 103 | 104 | /** recursively get the values stored for the given {@code index} and {@code keys}, not including keys */ 105 | public Iterable getAll(String index, Iterable keys) { 106 | return getAllIncluding(index, get(index, keys), new IterableChain()); 107 | } 108 | 109 | private static class IterableChain implements Iterable { 110 | private final List> chain = new ArrayList(); 111 | 112 | private void addAll(Iterable iterable) { chain.add(iterable); } 113 | 114 | public Iterator iterator() { 115 | List result = new ArrayList<>(); 116 | chain.forEach(iterable -> { if (iterable != null) iterable.forEach(element -> result.add(element));}); 117 | return result.iterator(); 118 | } 119 | 120 | // public Iterator iterator() { return Iterables.concat(chain).iterator(); } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/scanners/MemberUsageScanner.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.scanners; 2 | 3 | import java.util.Optional; 4 | 5 | import org.reflections8.ReflectionsException; 6 | import org.reflections8.util.ClasspathHelper; 7 | import org.reflections8.util.Joiner; 8 | 9 | import javassist.CannotCompileException; 10 | import javassist.ClassPool; 11 | import javassist.CtBehavior; 12 | import javassist.CtClass; 13 | import javassist.LoaderClassPath; 14 | import javassist.NotFoundException; 15 | import javassist.bytecode.MethodInfo; 16 | import javassist.expr.ConstructorCall; 17 | import javassist.expr.ExprEditor; 18 | import javassist.expr.FieldAccess; 19 | import javassist.expr.MethodCall; 20 | import javassist.expr.NewExpr; 21 | 22 | /** scans methods/constructors/fields usage 23 | *

depends on {@link org.reflections8.adapters.JavassistAdapter} configured */ 24 | @SuppressWarnings("unchecked") 25 | public class MemberUsageScanner extends AbstractScanner { 26 | private ClassPool classPool; 27 | 28 | @Override 29 | public void scan(Object cls) { 30 | try { 31 | CtClass ctClass = getClassPool().get(getMetadataAdapter().getClassName(cls)); 32 | for (CtBehavior member : ctClass.getDeclaredConstructors()) { 33 | scanMember(member); 34 | } 35 | for (CtBehavior member : ctClass.getDeclaredMethods()) { 36 | scanMember(member); 37 | } 38 | ctClass.detach(); 39 | } catch (Exception e) { 40 | throw new ReflectionsException("Could not scan method usage for " + getMetadataAdapter().getClassName(cls), e); 41 | } 42 | } 43 | 44 | void scanMember(CtBehavior member) throws CannotCompileException { 45 | //key contains this$/val$ means local field/parameter closure 46 | final String key = member.getDeclaringClass().getName() + "." + member.getMethodInfo().getName() + 47 | "(" + parameterNames(member.getMethodInfo()) + ")"; //+ " #" + member.getMethodInfo().getLineNumber(0) 48 | member.instrument(new ExprEditor() { 49 | @Override 50 | public void edit(NewExpr e) throws CannotCompileException { 51 | try { 52 | put(e.getConstructor().getDeclaringClass().getName() + "." + "" + 53 | "(" + parameterNames(e.getConstructor().getMethodInfo()) + ")", e.getLineNumber(), key); 54 | } catch (NotFoundException e1) { 55 | throw new ReflectionsException("Could not find new instance usage in " + key, e1); 56 | } 57 | } 58 | 59 | @Override 60 | public void edit(MethodCall m) throws CannotCompileException { 61 | try { 62 | put(m.getMethod().getDeclaringClass().getName() + "." + m.getMethodName() + 63 | "(" + parameterNames(m.getMethod().getMethodInfo()) + ")", m.getLineNumber(), key); 64 | } catch (NotFoundException e) { 65 | throw new ReflectionsException("Could not find member " + m.getClassName() + " in " + key, e); 66 | } 67 | } 68 | 69 | @Override 70 | public void edit(ConstructorCall c) throws CannotCompileException { 71 | try { 72 | put(c.getConstructor().getDeclaringClass().getName() + "." + "" + 73 | "(" + parameterNames(c.getConstructor().getMethodInfo()) + ")", c.getLineNumber(), key); 74 | } catch (NotFoundException e) { 75 | throw new ReflectionsException("Could not find member " + c.getClassName() + " in " + key, e); 76 | } 77 | } 78 | 79 | @Override 80 | public void edit(FieldAccess f) throws CannotCompileException { 81 | try { 82 | put(f.getField().getDeclaringClass().getName() + "." + f.getFieldName(), f.getLineNumber(), key); 83 | } catch (NotFoundException e) { 84 | throw new ReflectionsException("Could not find member " + f.getFieldName() + " in " + key, e); 85 | } 86 | } 87 | }); 88 | } 89 | 90 | private void put(String key, int lineNumber, String value) { 91 | if (acceptResult(key)) { 92 | getStore().putSingle(key, value + " #" + lineNumber); 93 | } 94 | } 95 | 96 | String parameterNames(MethodInfo info) { 97 | return Joiner.on(", ").join(getMetadataAdapter().getParameterNames(info)); 98 | } 99 | 100 | private ClassPool getClassPool() { 101 | if (classPool == null) { 102 | synchronized (this) { 103 | classPool = new ClassPool(); 104 | Optional classLoaders = getConfiguration().getClassLoaders(); 105 | if (!classLoaders.isPresent()) { 106 | classLoaders = ClasspathHelper.classLoaders(); 107 | } 108 | for (ClassLoader classLoader : classLoaders.get()) { 109 | classPool.appendClassPath(new LoaderClassPath(classLoader)); 110 | } 111 | } 112 | } 113 | return classPool; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/adapters/JavaReflectionAdapter.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.adapters; 2 | 3 | import static org.reflections8.ReflectionUtils.forName; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.Member; 9 | import java.lang.reflect.Method; 10 | import java.lang.reflect.Modifier; 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | import org.reflections8.util.Joiner; 17 | import org.reflections8.util.Utils; 18 | import org.reflections8.vfs.Vfs; 19 | 20 | /** */ 21 | public class JavaReflectionAdapter implements MetadataAdapter { 22 | 23 | public List getFields(Class cls) { 24 | return Arrays.asList(cls.getDeclaredFields()); 25 | } 26 | 27 | public List getMethods(Class cls) { 28 | List methods = new ArrayList(); 29 | methods.addAll(Arrays.asList(cls.getDeclaredMethods())); 30 | methods.addAll(Arrays.asList(cls.getDeclaredConstructors())); 31 | return methods; 32 | } 33 | 34 | public String getMethodName(Member method) { 35 | return method instanceof Method ? method.getName() : 36 | method instanceof Constructor ? "" : null; 37 | } 38 | 39 | public List getParameterNames(final Member member) { 40 | List result = new ArrayList(); 41 | 42 | Class[] parameterTypes = member instanceof Method ? ((Method) member).getParameterTypes() : 43 | member instanceof Constructor ? ((Constructor) member).getParameterTypes() : null; 44 | 45 | if (parameterTypes != null) { 46 | for (Class paramType : parameterTypes) { 47 | String name = getName(paramType); 48 | result.add(name); 49 | } 50 | } 51 | 52 | return result; 53 | } 54 | 55 | public List getClassAnnotationNames(Class aClass) { 56 | return getAnnotationNames(aClass.getDeclaredAnnotations()); 57 | } 58 | 59 | public List getFieldAnnotationNames(Field field) { 60 | return getAnnotationNames(field.getDeclaredAnnotations()); 61 | } 62 | 63 | public List getMethodAnnotationNames(Member method) { 64 | Annotation[] annotations = 65 | method instanceof Method ? ((Method) method).getDeclaredAnnotations() : 66 | method instanceof Constructor ? ((Constructor) method).getDeclaredAnnotations() : null; 67 | return getAnnotationNames(annotations); 68 | } 69 | 70 | public List getParameterAnnotationNames(Member method, int parameterIndex) { 71 | Annotation[][] annotations = 72 | method instanceof Method ? ((Method) method).getParameterAnnotations() : 73 | method instanceof Constructor ? ((Constructor) method).getParameterAnnotations() : null; 74 | 75 | return getAnnotationNames(annotations != null ? annotations[parameterIndex] : null); 76 | } 77 | 78 | public String getReturnTypeName(Member method) { 79 | return ((Method) method).getReturnType().getName(); 80 | } 81 | 82 | public String getFieldName(Field field) { 83 | return field.getName(); 84 | } 85 | 86 | public Class getOrCreateClassObject(Vfs.File file) throws Exception { 87 | return getOrCreateClassObject(file, null); 88 | } 89 | 90 | public Class getOrCreateClassObject(Vfs.File file, ClassLoader... loaders) throws Exception { 91 | String name = file.getRelativePath().replace("/", ".").replace(".class", ""); 92 | return forName(name, loaders); 93 | } 94 | 95 | public String getMethodModifier(Member method) { 96 | return Modifier.toString(method.getModifiers()); 97 | } 98 | 99 | public String getMethodKey(Class cls, Member method) { 100 | return getMethodName(method) + "(" + Joiner.on(", ").join(getParameterNames(method)) + ")"; 101 | } 102 | 103 | public String getMethodFullKey(Class cls, Member method) { 104 | return getClassName(cls) + "." + getMethodKey(cls, method); 105 | } 106 | 107 | public boolean isPublic(Object o) { 108 | if (o == null) 109 | return false; 110 | Integer mod = 111 | o instanceof Class ? ((Class) o).getModifiers() : 112 | o instanceof Member ? ((Member) o).getModifiers() : null; 113 | 114 | return mod != null && Modifier.isPublic(mod); 115 | } 116 | 117 | public String getClassName(Class cls) { 118 | return cls.getName(); 119 | } 120 | 121 | public String getSuperclassName(Class cls) { 122 | Class superclass = cls.getSuperclass(); 123 | return superclass != null ? superclass.getName() : ""; 124 | } 125 | 126 | public List getInterfacesNames(Class cls) { 127 | Class[] classes = cls.getInterfaces(); 128 | List names = new ArrayList(classes != null ? classes.length : 0); 129 | if (classes != null) for (Class cls1 : classes) names.add(cls1.getName()); 130 | return names; 131 | } 132 | 133 | public boolean acceptsInput(String file) { 134 | return file.endsWith(".class"); 135 | } 136 | 137 | // 138 | private List getAnnotationNames(Annotation[] annotations) { 139 | if (annotations == null) 140 | return Collections.emptyList(); 141 | List names = new ArrayList(annotations.length); 142 | for (Annotation annotation : annotations) { 143 | names.add(annotation.annotationType().getName()); 144 | } 145 | return names; 146 | } 147 | 148 | public static String getName(Class type) { 149 | if (type.isArray()) { 150 | try { 151 | Class cl = type; 152 | int dim = 0; while (cl.isArray()) { dim++; cl = cl.getComponentType(); } 153 | return cl.getName() + Utils.repeat("[]", dim); 154 | } catch (Throwable e) { 155 | // 156 | } 157 | } 158 | return type.getName(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/VfsTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.DataInputStream; 5 | import java.io.File; 6 | import java.net.MalformedURLException; 7 | import java.net.URL; 8 | import java.nio.file.Path; 9 | import static java.text.MessageFormat.format; 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | import java.util.stream.StreamSupport; 15 | import javassist.bytecode.ClassFile; 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertFalse; 18 | import static org.junit.Assert.assertNotEquals; 19 | import static org.junit.Assert.assertNotNull; 20 | import static org.junit.Assert.assertTrue; 21 | import static org.junit.Assert.fail; 22 | import org.junit.Test; 23 | import org.reflections8.util.ClasspathHelper; 24 | import org.reflections8.vfs.SystemDir; 25 | import org.reflections8.vfs.Vfs; 26 | import org.slf4j.Logger; 27 | 28 | public class VfsTest { 29 | 30 | @Test 31 | public void testJarFile() throws Exception { 32 | URL url = new URL(ClasspathHelper.forClass(Logger.class).toExternalForm().replace("jar:", "")); 33 | assertTrue(url.toString().startsWith("file:")); 34 | assertTrue(url.toString().contains(".jar")); 35 | 36 | assertTrue(Vfs.DefaultUrlTypes.jarFile.matches(url)); 37 | assertFalse(Vfs.DefaultUrlTypes.jarUrl.matches(url)); 38 | assertFalse(Vfs.DefaultUrlTypes.directory.matches(url)); 39 | 40 | Vfs.Dir dir = Vfs.DefaultUrlTypes.jarFile.createDir(url); 41 | testVfsDir(dir); 42 | } 43 | 44 | @Test 45 | public void testJarUrl() throws Exception { 46 | URL url = ClasspathHelper.forClass(Logger.class); 47 | assertTrue(url.toString().startsWith("jar:file:")); 48 | assertTrue(url.toString().contains(".jar!")); 49 | 50 | assertFalse(Vfs.DefaultUrlTypes.jarFile.matches(url)); 51 | assertTrue(Vfs.DefaultUrlTypes.jarUrl.matches(url)); 52 | assertFalse(Vfs.DefaultUrlTypes.directory.matches(url)); 53 | 54 | Vfs.Dir dir = Vfs.DefaultUrlTypes.jarUrl.createDir(url); 55 | testVfsDir(dir); 56 | } 57 | 58 | @Test 59 | public void testDirectory() throws Exception { 60 | URL url = ClasspathHelper.forClass(getClass()); 61 | assertTrue(url.toString().startsWith("file:")); 62 | assertFalse(url.toString().contains(".jar")); 63 | 64 | assertFalse(Vfs.DefaultUrlTypes.jarFile.matches(url)); 65 | assertFalse(Vfs.DefaultUrlTypes.jarUrl.matches(url)); 66 | assertTrue(Vfs.DefaultUrlTypes.directory.matches(url)); 67 | 68 | Vfs.Dir dir = Vfs.DefaultUrlTypes.directory.createDir(url); 69 | testVfsDir(dir); 70 | } 71 | 72 | @Test 73 | public void testJarInputStream() throws Exception { 74 | URL url = ClasspathHelper.forClass(Logger.class); 75 | assertTrue(Vfs.DefaultUrlTypes.jarInputStream.matches(url)); 76 | try { 77 | testVfsDir(Vfs.DefaultUrlTypes.jarInputStream.createDir(url)); 78 | fail(); 79 | } catch (ReflectionsException e) { 80 | // expected 81 | } 82 | 83 | url = new URL(ClasspathHelper.forClass(Logger.class).toExternalForm().replace("jar:", "").replace(".jar!", ".jar")); 84 | assertTrue(Vfs.DefaultUrlTypes.jarInputStream.matches(url)); 85 | testVfsDir(Vfs.DefaultUrlTypes.jarInputStream.createDir(url)); 86 | 87 | url = ClasspathHelper.forClass(getClass()); 88 | assertFalse(Vfs.DefaultUrlTypes.jarInputStream.matches(url)); 89 | try { 90 | testVfsDir(Vfs.DefaultUrlTypes.jarInputStream.createDir(url)); 91 | fail(); 92 | } catch (AssertionError e) { 93 | // expected 94 | } 95 | } 96 | 97 | @Test 98 | public void dirWithSpaces() { 99 | Collection urls = ClasspathHelper.forPackage("dir+with spaces"); 100 | assertFalse(urls.isEmpty()); 101 | for (URL url : urls) { 102 | Vfs.Dir dir = Vfs.fromURL(url); 103 | assertNotNull(dir); 104 | assertNotNull(dir.getFiles().iterator().next()); 105 | } 106 | } 107 | 108 | @Test 109 | public void vfsFromDirWithJarInName() throws MalformedURLException { 110 | String tmpFolder = System.getProperty("java.io.tmpdir"); 111 | tmpFolder = tmpFolder.endsWith(File.separator) ? tmpFolder : tmpFolder + File.separator; 112 | String dirWithJarInName = tmpFolder + "tony.jarvis"; 113 | File newDir = new File(dirWithJarInName); 114 | newDir.mkdir(); 115 | 116 | try { 117 | Vfs.Dir dir = Vfs.fromURL(new URL(format("file:{0}", dirWithJarInName))); 118 | 119 | assertEquals(dirWithJarInName.replace("\\", "/"), dir.getPath()); 120 | assertEquals(SystemDir.class, dir.getClass()); 121 | } finally { 122 | newDir.delete(); 123 | } 124 | } 125 | 126 | @Test 127 | public void vfsFromDirWithJarInJar() throws Exception { 128 | URL resource = ClasspathHelper.contextClassLoader().getResource("jarWithBootLibJar.jar"); 129 | URL innerJarUrl = new URL("jar:" + resource.toExternalForm() + "!/BOOT-INF/lib/jarWithManifest.jar"); 130 | 131 | assertFalse(Vfs.DefaultUrlTypes.jarUrl.matches(innerJarUrl)); 132 | Vfs.Dir jarUrlDir = Vfs.DefaultUrlTypes.jarUrl.createDir(innerJarUrl); 133 | assertNotEquals(innerJarUrl.getPath(), jarUrlDir.getPath()); 134 | 135 | assertTrue(Vfs.DefaultUrlTypes.jarInputStream.matches(innerJarUrl)); 136 | Vfs.Dir jarInputStreamDir = Vfs.DefaultUrlTypes.jarInputStream.createDir(innerJarUrl); 137 | assertEquals(innerJarUrl.getPath(), jarInputStreamDir.getPath()); 138 | 139 | List files = StreamSupport.stream(jarInputStreamDir.getFiles().spliterator(), false).collect(Collectors.toList()); 140 | assertEquals(1, files.size()); 141 | Vfs.File file1 = files.get(0); 142 | assertEquals("empty.class", file1.getName()); 143 | assertEquals("pack/empty.class", file1.getRelativePath()); 144 | 145 | for (Vfs.File file : jarInputStreamDir.getFiles()) { 146 | try (DataInputStream dis = new DataInputStream(new BufferedInputStream(file.openInputStream()))) { 147 | ClassFile classFile = new ClassFile(dis); 148 | assertEquals("org.reflections.empty", classFile.getName()); 149 | } catch (Exception e) { 150 | throw new RuntimeException(e); 151 | } 152 | } 153 | } 154 | 155 | private void testVfsDir(Vfs.Dir dir) { 156 | List files = new ArrayList<>(); 157 | for (Vfs.File file : dir.getFiles()) { 158 | files.add(file); 159 | } 160 | assertFalse(files.isEmpty()); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This fork is changed to use java8 instead of guava. 2 | 3 | ```xml 4 | 5 | net.oneandone.reflections8 6 | reflections8 7 | 0.11.5 8 | 9 | ``` 10 | 11 | To avoid mixup of classes between "pre java8"-reflections and reflections8 all is moved from org.reflections to org.reflections8 12 | [![Build Status](https://travis-ci.org/aschoerk/reflections8.svg?branch=master)](https://travis-ci.org/aschoerk/reflections8) 13 | 14 | 15 | 16 | ---- 17 | 18 | ## Java runtime metadata analysis, in the spirit of [Scannotations](http://bill.burkecentral.com/2008/01/14/scanning-java-annotations-at-runtime/) 19 | 20 | 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. 21 | 22 | Using Reflections you can query your metadata such as: 23 | * get all subtypes of some type 24 | * get all types/members annotated with some annotation 25 | * get all resources matching a regular expression 26 | * get all methods with specific signature including parameters, parameter annotations and return type 27 | 28 | [![Build Status](https://travis-ci.org/ronmamo/reflections.svg?branch=master)](https://travis-ci.org/ronmamo/reflections) 29 | 30 | ### Intro 31 | Add Reflections to your project. for maven projects just add this dependency: 32 | ```xml 33 | 34 | net.oneandone.reflections8 35 | reflections8 36 | 0.11.5 37 | 38 | ``` 39 | 40 | A typical use of Reflections would be: 41 | ```java 42 | Reflections reflections = new Reflections("my.project"); 43 | 44 | Set> subTypes = reflections.getSubTypesOf(SomeType.class); 45 | 46 | Set> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class); 47 | ``` 48 | 49 | ### Usage 50 | Basically, to use Reflections first instantiate it with urls and scanners 51 | 52 | ```java 53 | //scan urls that contain 'my.package', include inputs starting with 'my.package', use the default scanners 54 | Reflections reflections = new Reflections("my.package"); 55 | 56 | //or using ConfigurationBuilder 57 | new Reflections(new ConfigurationBuilder() 58 | .setUrls(ClasspathHelper.forPackage("my.project.prefix")) 59 | .setScanners(new SubTypesScanner(), 60 | new TypeAnnotationsScanner().filterResultsBy(optionalFilter), ...), 61 | .filterInputsBy(new FilterBuilder().includePackage("my.project.prefix")) 62 | ...); 63 | ``` 64 | Then use the convenient query methods: (depending on the scanners configured) 65 | 66 | ```java 67 | //SubTypesScanner 68 | Set> modules = 69 | reflections.getSubTypesOf(com.google.inject.Module.class); 70 | ``` 71 | ```java 72 | //TypeAnnotationsScanner 73 | Set> singletons = 74 | reflections.getTypesAnnotatedWith(javax.inject.Singleton.class); 75 | ``` 76 | ```java 77 | //ResourcesScanner 78 | Set properties = 79 | reflections.getResources(Pattern.compile(".*\\.properties")); 80 | ``` 81 | ```java 82 | //MethodAnnotationsScanner 83 | Set resources = 84 | reflections.getMethodsAnnotatedWith(javax.ws.rs.Path.class); 85 | Set injectables = 86 | reflections.getConstructorsAnnotatedWith(javax.inject.Inject.class); 87 | ``` 88 | ```java 89 | //FieldAnnotationsScanner 90 | Set ids = 91 | reflections.getFieldsAnnotatedWith(javax.persistence.Id.class); 92 | ``` 93 | ```java 94 | //MethodParameterScanner 95 | Set someMethods = 96 | reflections.getMethodsMatchParams(long.class, int.class); 97 | Set voidMethods = 98 | reflections.getMethodsReturn(void.class); 99 | Set pathParamMethods = 100 | reflections.getMethodsWithAnyParamAnnotated(PathParam.class); 101 | ``` 102 | ```java 103 | //MethodParameterNamesScanner 104 | List parameterNames = 105 | reflections.getMethodParamNames(Method.class) 106 | ``` 107 | ```java 108 | //MemberUsageScanner 109 | Set usages = 110 | reflections.getMethodUsages(Method.class) 111 | ``` 112 | 113 | * If no scanners are configured, the default will be used - `SubTypesScanner` and `TypeAnnotationsScanner`. 114 | * Classloader can also be configured, which will be used for resolving runtime classes from names. 115 | * 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. 116 | 117 | *Checkout the [javadoc](http://ronmamo.github.io/reflections/index.html?org/reflections/Reflections.html) for more info.* 118 | 119 | *Also, browse the [tests directory](https://github.com/ronmamo/reflections/tree/master/src/test/java/org/reflections) to see some more examples.* 120 | 121 | ### ReflectionUtils 122 | 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) 123 | 124 | for example: 125 | 126 | ```java 127 | import static org.reflections8.ReflectionUtils.*; 128 | 129 | Set getters = getAllMethods(someClass, 130 | withModifier(Modifier.PUBLIC), withPrefix("get"), withParametersCount(0)); 131 | 132 | //or 133 | Set listMethodsFromCollectionToBoolean = 134 | getAllMethods(List.class, 135 | withParametersAssignableTo(Collection.class), withReturnType(boolean.class)); 136 | 137 | Set fields = getAllFields(SomeClass.class, withAnnotation(annotation), withTypeAssignableTo(type)); 138 | ``` 139 | 140 | *See more in the [ReflectionUtils javadoc](http://ronmamo.github.io/reflections/index.html?org/reflections/ReflectionUtils.html)* 141 | 142 | ### Integrating into your build lifecycle 143 | 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. 144 | With simple Maven/Gradle/SBT/whatever configuration you can save all scanned metadata into xml/json files just after compile time. 145 | Later on, when your project is bootstrapping you can let Reflections collect all those resources and re-create that metadata for you, 146 | making it available at runtime without re-scanning the classpath - thus reducing the bootstrapping time. 147 | 148 | *For Maven, see example using gmavenplus in the [reflections-maven](https://github.com/ronmamo/reflections-maven/) repository* 149 | 150 | ### Other use cases 151 | *See the [UseCases](https://github.com/ronmamo/reflections/blob/gh-pages/UseCases.md) wiki page* 152 | 153 | ### Contribute 154 | Pull requests are welcomed!! 155 | 156 | The license is [WTFPL](http://www.wtfpl.net/), just do what the fuck you want to. 157 | 158 | This library is published as an act of giving and generosity, from developers to developers. 159 | 160 | 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) 161 | [![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) 162 | 163 | _Cheers_ 164 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/util/SynchronizedHashSetMultimap.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | import java.util.Set; 6 | import java.util.function.BiConsumer; 7 | import java.util.function.BiFunction; 8 | import java.util.function.Function; 9 | 10 | /** 11 | * Helper class used to avoid guava 12 | * 13 | * @author aschoerk 14 | */ 15 | public class SynchronizedHashSetMultimap extends HashSetMultimap implements SetMultimap { 16 | 17 | private static final long serialVersionUID = -907195260984577390L; 18 | 19 | private final Object mutex; 20 | private final SetMultimap delegate; 21 | 22 | public SynchronizedHashSetMultimap(final SetMultimap delegate) { 23 | this.delegate = delegate; 24 | this.mutex = this; 25 | } 26 | 27 | @Override 28 | public boolean putSingle(final Object key, final Object value) { 29 | synchronized (mutex) { 30 | return delegate.putSingle(key, value); 31 | } 32 | } 33 | 34 | 35 | @Override 36 | public void putAllSingles(final SetMultimap m) { 37 | synchronized (mutex) { 38 | delegate.putAllSingles(m); 39 | } 40 | } 41 | 42 | public boolean removeSingle(final Object key, final Object value) { 43 | synchronized (mutex) { 44 | return delegate.removeSingle(key, value); 45 | } 46 | } 47 | 48 | @Override 49 | public Collection flatValues() { 50 | synchronized (mutex) { 51 | return delegate.flatValues(); 52 | } 53 | } 54 | 55 | @Override 56 | public Set flatValuesAsSet() { 57 | synchronized (mutex) { 58 | return delegate.flatValuesAsSet(); 59 | } 60 | } 61 | 62 | @Override 63 | public Map asMap() { 64 | synchronized (mutex) { 65 | return delegate.asMap(); 66 | } 67 | } 68 | 69 | /** 70 | * Returns the number of key-value mappings in this map. 71 | * 72 | * @return the number of key-value mappings in this map 73 | */ 74 | @Override 75 | public int size() { 76 | synchronized (mutex) { 77 | return delegate.size(); 78 | } 79 | } 80 | 81 | /** 82 | * Returns true if this map contains no key-value mappings. 83 | * 84 | * @return true if this map contains no key-value mappings 85 | */ 86 | @Override 87 | public boolean isEmpty() { 88 | synchronized (mutex) { 89 | return delegate.isEmpty(); 90 | } 91 | } 92 | 93 | 94 | @Override 95 | public Set get(final Object key) { 96 | synchronized (mutex) { 97 | return (Set) delegate.get(key); 98 | } 99 | } 100 | 101 | 102 | @Override 103 | public boolean containsKey(final Object key) { 104 | synchronized (mutex) { 105 | return delegate.containsKey(key); 106 | } 107 | } 108 | 109 | 110 | public Set put(final K key, final Set value) { 111 | synchronized (mutex) { 112 | return (Set)delegate.put(key, value); 113 | } 114 | } 115 | 116 | 117 | public void putAll(final Map m) { 118 | synchronized (mutex) { 119 | delegate.putAll(m); 120 | } 121 | } 122 | 123 | 124 | 125 | @Override 126 | public Set remove(final Object key) { 127 | synchronized (mutex) { 128 | return (Set) delegate.remove(key); 129 | } 130 | } 131 | 132 | 133 | @Override 134 | public void clear() { 135 | synchronized (mutex) { 136 | delegate.clear(); 137 | } 138 | } 139 | 140 | 141 | @Override 142 | public boolean containsValue(final Object value) { 143 | synchronized (mutex) { 144 | return delegate.containsValue(value); 145 | } 146 | } 147 | 148 | 149 | @Override 150 | public Set keySet() { 151 | synchronized (mutex) { 152 | return delegate.keySet(); 153 | } 154 | } 155 | 156 | 157 | @Override 158 | public Collection values() { 159 | synchronized (mutex) { 160 | return delegate.values(); 161 | } 162 | } 163 | 164 | 165 | @Override 166 | public Set>> entrySet() { 167 | synchronized (mutex) { 168 | return delegate.entrySet(); 169 | } 170 | } 171 | 172 | public Set getOrDefault(final Object key, final Set defaultValue) { 173 | synchronized (mutex) { 174 | return (Set)delegate.getOrDefault(key, defaultValue); 175 | } 176 | } 177 | 178 | public Set putIfAbsent(final Object key, final Set value) { 179 | synchronized (mutex) { 180 | return (Set)delegate.putIfAbsent(key, value); 181 | } 182 | } 183 | 184 | @Override 185 | public boolean remove(final Object key, final Object value) { 186 | synchronized (mutex) { 187 | return delegate.remove(key, value); 188 | } 189 | } 190 | 191 | public boolean replace(final Object key, final Set oldValue, final Set newValue) { 192 | synchronized (mutex) { 193 | return delegate.replace(key, oldValue, newValue); 194 | } 195 | } 196 | 197 | public Set replace(final Object key, final Set value) { 198 | synchronized (mutex) { 199 | return (Set)delegate.replace(key, value); 200 | } 201 | } 202 | 203 | public Set computeIfAbsent(final Object key, final Function mappingFunction) { 204 | synchronized (mutex) { 205 | return (Set)delegate.computeIfAbsent(key, mappingFunction); 206 | } 207 | } 208 | 209 | public Set computeIfPresent(final Object key, final BiFunction remappingFunction) { 210 | synchronized (mutex) { 211 | return (Set)delegate.computeIfPresent(key, remappingFunction); 212 | } 213 | } 214 | 215 | public Set compute(final Object key, final BiFunction remappingFunction) { 216 | synchronized (mutex) { 217 | return (Set) delegate.compute(key, remappingFunction); 218 | } 219 | } 220 | 221 | public Set merge(final Object key, final Set value, final BiFunction remappingFunction) { 222 | synchronized (mutex) { 223 | return (Set)delegate.merge(key, value, remappingFunction); 224 | } 225 | } 226 | 227 | public void forEach(final BiConsumer action) { 228 | synchronized (mutex) { 229 | delegate.forEach(action); 230 | } 231 | } 232 | 233 | public void replaceAll(final BiFunction function) { 234 | synchronized (mutex) { 235 | delegate.replaceAll(function); 236 | } 237 | } 238 | 239 | 240 | @Override 241 | public boolean equals(final Object o) { 242 | synchronized (mutex) { 243 | return delegate.equals(o); 244 | } 245 | } 246 | 247 | 248 | @Override 249 | public int hashCode() { 250 | synchronized (mutex) { 251 | return delegate.hashCode(); 252 | } 253 | } 254 | 255 | @Override 256 | public String toString() { 257 | synchronized (mutex) { 258 | return delegate.toString(); 259 | } 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/FilterBuilderTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import org.junit.Test; 7 | import org.reflections8.util.FilterBuilder; 8 | 9 | /** 10 | * Test filtering 11 | */ 12 | public class FilterBuilderTest { 13 | 14 | @Test 15 | public void test_include() { 16 | FilterBuilder filter = new FilterBuilder().include("org\\.reflections8.*"); 17 | assertTrue(filter.test("org.reflections8.Reflections")); 18 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 19 | assertFalse(filter.test("org.foobar.Reflections")); 20 | } 21 | 22 | @Test 23 | public void test_includePackage() { 24 | FilterBuilder filter = new FilterBuilder().includePackage("org.reflections8"); 25 | assertTrue(filter.test("org.reflections8.Reflections")); 26 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 27 | assertFalse(filter.test("org.foobar.Reflections")); 28 | } 29 | 30 | @Test 31 | public void test_includePackageMultiple() { 32 | FilterBuilder filter = new FilterBuilder().includePackage("org.reflections8", "org.foo"); 33 | assertTrue(filter.test("org.reflections8.Reflections")); 34 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 35 | assertTrue(filter.test("org.foo.Reflections")); 36 | assertTrue(filter.test("org.foo.bar.Reflections")); 37 | assertFalse(filter.test("org.bar.Reflections")); 38 | } 39 | 40 | @Test 41 | public void test_includePackagebyClass() { 42 | FilterBuilder filter = new FilterBuilder().includePackage(Reflections.class); 43 | assertTrue(filter.test("org.reflections8.Reflections")); 44 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 45 | assertFalse(filter.test("org.foobar.Reflections")); 46 | } 47 | 48 | //----------------------------------------------------------------------- 49 | @Test 50 | public void test_exclude() { 51 | FilterBuilder filter = new FilterBuilder().exclude("org\\.reflections8.*"); 52 | assertFalse(filter.test("org.reflections8.Reflections")); 53 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 54 | assertTrue(filter.test("org.foobar.Reflections")); 55 | } 56 | 57 | @Test 58 | public void test_excludePackage() { 59 | FilterBuilder filter = new FilterBuilder().excludePackage("org.reflections8"); 60 | assertFalse(filter.test("org.reflections8.Reflections")); 61 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 62 | assertTrue(filter.test("org.foobar.Reflections")); 63 | } 64 | 65 | @Test 66 | public void test_excludePackageByClass() { 67 | FilterBuilder filter = new FilterBuilder().excludePackage(Reflections.class); 68 | assertFalse(filter.test("org.reflections8.Reflections")); 69 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 70 | assertTrue(filter.test("org.foobar.Reflections")); 71 | } 72 | 73 | //----------------------------------------------------------------------- 74 | @Test 75 | public void test_parse_include() { 76 | FilterBuilder filter = FilterBuilder.parse("+org.reflections8.*"); 77 | assertTrue(filter.test("org.reflections8.Reflections")); 78 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 79 | assertFalse(filter.test("org.foobar.Reflections")); 80 | assertTrue(filter.test("org.reflections8plus.Reflections")); 81 | } 82 | 83 | @Test 84 | public void test_parse_include_notRegex() { 85 | FilterBuilder filter = FilterBuilder.parse("+org.reflections8"); 86 | assertFalse(filter.test("org.reflections8.Reflections")); 87 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 88 | assertFalse(filter.test("org.foobar.Reflections")); 89 | assertFalse(filter.test("org.reflectionsplus.Reflections")); 90 | } 91 | 92 | @Test 93 | public void test_parse_exclude() { 94 | FilterBuilder filter = FilterBuilder.parse("-org.reflections8.*"); 95 | assertFalse(filter.test("org.reflections8.Reflections")); 96 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 97 | assertTrue(filter.test("org.foobar.Reflections")); 98 | assertFalse(filter.test("org.reflections8plus.Reflections")); 99 | } 100 | 101 | @Test 102 | public void test_parse_exclude_notRegex() { 103 | FilterBuilder filter = FilterBuilder.parse("-org.reflections8"); 104 | assertTrue(filter.test("org.reflections8.Reflections")); 105 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 106 | assertTrue(filter.test("org.foobar.Reflections")); 107 | assertTrue(filter.test("org.reflections8plus.Reflections")); 108 | } 109 | 110 | @Test 111 | public void test_parse_include_exclude() { 112 | FilterBuilder filter = FilterBuilder.parse("+org.reflections8.*, -org.reflections8.foo.*"); 113 | assertTrue(filter.test("org.reflections8.Reflections")); 114 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 115 | assertFalse(filter.test("org.foobar.Reflections")); 116 | } 117 | 118 | //----------------------------------------------------------------------- 119 | @Test 120 | public void test_parsePackages_include() { 121 | FilterBuilder filter = FilterBuilder.parsePackages("+org.reflections8"); 122 | assertTrue(filter.test("org.reflections8.Reflections")); 123 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 124 | assertFalse(filter.test("org.foobar.Reflections")); 125 | assertFalse(filter.test("org.reflectionsplus.Reflections")); 126 | } 127 | 128 | @Test 129 | public void test_parsePackages_include_trailingDot() { 130 | FilterBuilder filter = FilterBuilder.parsePackages("+org.reflections8."); 131 | assertTrue(filter.test("org.reflections8.Reflections")); 132 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 133 | assertFalse(filter.test("org.foobar.Reflections")); 134 | assertFalse(filter.test("org.reflectionsplus.Reflections")); 135 | } 136 | 137 | @Test 138 | public void test_parsePackages_exclude() { 139 | FilterBuilder filter = FilterBuilder.parsePackages("-org.reflections8"); 140 | assertFalse(filter.test("org.reflections8.Reflections")); 141 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 142 | assertTrue(filter.test("org.foobar.Reflections")); 143 | assertTrue(filter.test("org.reflectionsplus.Reflections")); 144 | } 145 | 146 | @Test 147 | public void test_parsePackages_exclude_trailingDot() { 148 | FilterBuilder filter = FilterBuilder.parsePackages("-org.reflections8."); 149 | assertFalse(filter.test("org.reflections8.Reflections")); 150 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 151 | assertTrue(filter.test("org.foobar.Reflections")); 152 | assertTrue(filter.test("org.reflectionsplus.Reflections")); 153 | } 154 | 155 | @Test 156 | public void test_parsePackages_include_exclude() { 157 | FilterBuilder filter = FilterBuilder.parsePackages("+org.reflections8, -org.reflections8.foo"); 158 | assertTrue(filter.test("org.reflections8.Reflections")); 159 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 160 | assertFalse(filter.test("org.foobar.Reflections")); 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/util/FilterBuilderTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import static org.junit.Assert.assertFalse; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import org.junit.Test; 7 | import org.reflections8.Reflections; 8 | import org.reflections8.util.FilterBuilder; 9 | 10 | /** 11 | * Test filtering 12 | */ 13 | public class FilterBuilderTest { 14 | 15 | @Test 16 | public void test_include() { 17 | FilterBuilder filter = new FilterBuilder().include("org\\.reflections8.*"); 18 | assertTrue(filter.test("org.reflections8.Reflections")); 19 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 20 | assertFalse(filter.test("org.foobar.Reflections")); 21 | } 22 | 23 | @Test 24 | public void test_includePackage() { 25 | FilterBuilder filter = new FilterBuilder().includePackage("org.reflections8"); 26 | assertTrue(filter.test("org.reflections8.Reflections")); 27 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 28 | assertFalse(filter.test("org.foobar.Reflections")); 29 | } 30 | 31 | @Test 32 | public void test_includePackageMultiple() { 33 | FilterBuilder filter = new FilterBuilder().includePackage("org.reflections8", "org.foo"); 34 | assertTrue(filter.test("org.reflections8.Reflections")); 35 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 36 | assertTrue(filter.test("org.foo.Reflections")); 37 | assertTrue(filter.test("org.foo.bar.Reflections")); 38 | assertFalse(filter.test("org.bar.Reflections")); 39 | } 40 | 41 | @Test 42 | public void test_includePackagebyClass() { 43 | FilterBuilder filter = new FilterBuilder().includePackage(Reflections.class); 44 | assertTrue(filter.test("org.reflections8.Reflections")); 45 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 46 | assertFalse(filter.test("org.foobar.Reflections")); 47 | } 48 | 49 | //----------------------------------------------------------------------- 50 | @Test 51 | public void test_exclude() { 52 | FilterBuilder filter = new FilterBuilder().exclude("org\\.reflections8.*"); 53 | assertFalse(filter.test("org.reflections8.Reflections")); 54 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 55 | assertTrue(filter.test("org.foobar.Reflections")); 56 | } 57 | 58 | @Test 59 | public void test_excludePackage() { 60 | FilterBuilder filter = new FilterBuilder().excludePackage("org.reflections8"); 61 | assertFalse(filter.test("org.reflections8.Reflections")); 62 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 63 | assertTrue(filter.test("org.foobar.Reflections")); 64 | } 65 | 66 | @Test 67 | public void test_excludePackageByClass() { 68 | FilterBuilder filter = new FilterBuilder().excludePackage(Reflections.class); 69 | assertFalse(filter.test("org.reflections8.Reflections")); 70 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 71 | assertTrue(filter.test("org.foobar.Reflections")); 72 | } 73 | 74 | //----------------------------------------------------------------------- 75 | @Test 76 | public void test_parse_include() { 77 | FilterBuilder filter = FilterBuilder.parse("+org.reflections8.*"); 78 | assertTrue(filter.test("org.reflections8.Reflections")); 79 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 80 | assertFalse(filter.test("org.foobar.Reflections")); 81 | assertTrue(filter.test("org.reflections8plus.Reflections")); 82 | } 83 | 84 | @Test 85 | public void test_parse_include_notRegex() { 86 | FilterBuilder filter = FilterBuilder.parse("+org.reflections8"); 87 | assertFalse(filter.test("org.reflections8.Reflections")); 88 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 89 | assertFalse(filter.test("org.foobar.Reflections")); 90 | assertFalse(filter.test("org.reflections8plus.Reflections")); 91 | } 92 | 93 | @Test 94 | public void test_parse_exclude() { 95 | FilterBuilder filter = FilterBuilder.parse("-org.reflections8.*"); 96 | assertFalse(filter.test("org.reflections8.Reflections")); 97 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 98 | assertTrue(filter.test("org.foobar.Reflections")); 99 | assertFalse(filter.test("org.reflections8plus.Reflections")); 100 | } 101 | 102 | @Test 103 | public void test_parse_exclude_notRegex() { 104 | FilterBuilder filter = FilterBuilder.parse("-org.reflections8"); 105 | assertTrue(filter.test("org.reflections8.Reflections")); 106 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 107 | assertTrue(filter.test("org.foobar.Reflections")); 108 | assertTrue(filter.test("org.reflections8plus.Reflections")); 109 | } 110 | 111 | @Test 112 | public void test_parse_include_exclude() { 113 | FilterBuilder filter = FilterBuilder.parse("+org.reflections8.*, -org.reflections8.foo.*"); 114 | assertTrue(filter.test("org.reflections8.Reflections")); 115 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 116 | assertFalse(filter.test("org.foobar.Reflections")); 117 | } 118 | 119 | //----------------------------------------------------------------------- 120 | @Test 121 | public void test_parsePackages_include() { 122 | FilterBuilder filter = FilterBuilder.parsePackages("+org.reflections8"); 123 | assertTrue(filter.test("org.reflections8.Reflections")); 124 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 125 | assertFalse(filter.test("org.foobar.Reflections")); 126 | assertFalse(filter.test("org.reflections8plus.Reflections")); 127 | } 128 | 129 | @Test 130 | public void test_parsePackages_include_trailingDot() { 131 | FilterBuilder filter = FilterBuilder.parsePackages("+org.reflections8."); 132 | assertTrue(filter.test("org.reflections8.Reflections")); 133 | assertTrue(filter.test("org.reflections8.foo.Reflections")); 134 | assertFalse(filter.test("org.foobar.Reflections")); 135 | assertFalse(filter.test("org.reflections8plus.Reflections")); 136 | } 137 | 138 | @Test 139 | public void test_parsePackages_exclude() { 140 | FilterBuilder filter = FilterBuilder.parsePackages("-org.reflections8"); 141 | assertFalse(filter.test("org.reflections8.Reflections")); 142 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 143 | assertTrue(filter.test("org.foobar.Reflections")); 144 | assertTrue(filter.test("org.reflections8plus.Reflections")); 145 | } 146 | 147 | @Test 148 | public void test_parsePackages_exclude_trailingDot() { 149 | FilterBuilder filter = FilterBuilder.parsePackages("-org.reflections8."); 150 | assertFalse(filter.test("org.reflections8.Reflections")); 151 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 152 | assertTrue(filter.test("org.foobar.Reflections")); 153 | assertTrue(filter.test("org.reflections8plus.Reflections")); 154 | } 155 | 156 | @Test 157 | public void test_parsePackages_include_exclude() { 158 | FilterBuilder filter = FilterBuilder.parsePackages("+org.reflections8, -org.reflections8.foo"); 159 | assertTrue(filter.test("org.reflections8.Reflections")); 160 | assertFalse(filter.test("org.reflections8.foo.Reflections")); 161 | assertFalse(filter.test("org.foobar.Reflections")); 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/util/FilterBuilder.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.StringJoiner; 6 | import java.util.function.Predicate; 7 | import java.util.regex.Pattern; 8 | 9 | import org.reflections8.ReflectionsException; 10 | 11 | /** 12 | * Builds include/exclude filters for Reflections. 13 | *

14 | * For example: 15 | *

 16 |  * Predicate<String> filter1 = FilterBuilder.parsePackages("-java, "-javax");
 17 |  * Predicate<String> filter2 = new FilterBuilder().include(".*").exclude("java.*");
 18 |  * 
19 | */ 20 | public class FilterBuilder implements Predicate { 21 | private final List> chain; 22 | 23 | public FilterBuilder() {chain = new ArrayList();} 24 | private FilterBuilder(final Iterable> filters) {chain = new ArrayList(); filters.forEach(el -> chain.add(el));} 25 | 26 | /** include a regular expression */ 27 | public FilterBuilder include(final String regex) {return add(new Include(regex));} 28 | 29 | /** exclude a regular expression*/ 30 | public FilterBuilder exclude(final String regex) {add(new Exclude(regex)); return this;} 31 | 32 | /** add a Predicate to the chain of predicates*/ 33 | public FilterBuilder add(Predicate filter) {chain.add(filter); return this;} 34 | 35 | /** include a package of a given class */ 36 | public FilterBuilder includePackage(final Class aClass) {return add(new Include(packageNameRegex(aClass)));} 37 | 38 | /** exclude a package of a given class */ 39 | public FilterBuilder excludePackage(final Class aClass) {return add(new Exclude(packageNameRegex(aClass)));} 40 | 41 | /** include packages of given prefixes */ 42 | public FilterBuilder includePackage(final String... prefixes) { 43 | for (String prefix : prefixes) { 44 | add(new Include(prefix(prefix))); 45 | } 46 | return this; 47 | } 48 | 49 | /** exclude a package of a given prefix */ 50 | public FilterBuilder excludePackage(final String prefix) {return add(new Exclude(prefix(prefix)));} 51 | 52 | private static String packageNameRegex(Class aClass) {return prefix(aClass.getPackage().getName() + ".");} 53 | 54 | public static String prefix(String qualifiedName) {return qualifiedName.replace(".","\\.") + ".*";} 55 | 56 | @Override public String toString() { 57 | StringJoiner j = new StringJoiner(", "); 58 | chain.forEach(s -> j.add(s.toString())); 59 | return j.toString(); 60 | } 61 | 62 | public boolean test(String regex) { 63 | boolean accept = chain == null || chain.isEmpty() || chain.get(0) instanceof Exclude; 64 | 65 | if (chain != null) { 66 | for (Predicate filter : chain) { 67 | if (accept && filter instanceof Include) {continue;} //skip if this filter won't change 68 | if (!accept && filter instanceof Exclude) {continue;} 69 | accept = filter.test(regex); 70 | if (!accept && filter instanceof Exclude) {break;} //break on first exclusion 71 | } 72 | } 73 | return accept; 74 | } 75 | 76 | public abstract static class Matcher implements Predicate { 77 | final Pattern pattern; 78 | public Matcher(final String regex) {pattern = Pattern.compile(regex);} 79 | public abstract boolean test(String regex); 80 | @Override public String toString() {return pattern.pattern();} 81 | } 82 | 83 | public static class Include extends Matcher { 84 | public Include(final String patternString) {super(patternString);} 85 | @Override public boolean test(final String regex) {return pattern.matcher(regex).matches();} 86 | @Override public String toString() {return "+" + super.toString();} 87 | } 88 | 89 | public static class Exclude extends Matcher { 90 | public Exclude(final String patternString) {super(patternString);} 91 | @Override public boolean test(final String regex) {return !pattern.matcher(regex).matches();} 92 | @Override public String toString() {return "-" + super.toString();} 93 | } 94 | 95 | /** 96 | * Parses a string representation of an include/exclude filter. 97 | *

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

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

105 | * See also the more useful {@link FilterBuilder#parsePackages(String)} method. 106 | */ 107 | public static FilterBuilder parse(String includeExcludeString) { 108 | List> filters = new ArrayList>(); 109 | 110 | if (!Utils.isEmpty(includeExcludeString)) { 111 | for (String string : includeExcludeString.split(",")) { 112 | String trimmed = string.trim(); 113 | char prefix = trimmed.charAt(0); 114 | String pattern = trimmed.substring(1); 115 | 116 | Predicate filter; 117 | switch (prefix) { 118 | case '+': 119 | filter = new Include(pattern); 120 | break; 121 | case '-': 122 | filter = new Exclude(pattern); 123 | break; 124 | default: 125 | throw new ReflectionsException("includeExclude should start with either + or -"); 126 | } 127 | 128 | filters.add(filter); 129 | } 130 | 131 | return new FilterBuilder(filters); 132 | } else { 133 | return new FilterBuilder(); 134 | } 135 | } 136 | 137 | /** 138 | * Parses a string representation of an include/exclude filter. 139 | *

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

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

146 | * The input strings "-java" and "-java." are equivalent. 147 | */ 148 | public static FilterBuilder parsePackages(String includeExcludeString) { 149 | List> filters = new ArrayList>(); 150 | 151 | if (!Utils.isEmpty(includeExcludeString)) { 152 | for (String string : includeExcludeString.split(",")) { 153 | String trimmed = string.trim(); 154 | char prefix = trimmed.charAt(0); 155 | String pattern = trimmed.substring(1); 156 | if (pattern.endsWith(".") == false) { 157 | pattern += "."; 158 | } 159 | pattern = prefix(pattern); 160 | 161 | Predicate filter; 162 | switch (prefix) { 163 | case '+': 164 | filter = new Include(pattern); 165 | break; 166 | case '-': 167 | filter = new Exclude(pattern); 168 | break; 169 | default: 170 | throw new ReflectionsException("includeExclude should start with either + or -"); 171 | } 172 | 173 | filters.add(filter); 174 | } 175 | 176 | return new FilterBuilder(filters); 177 | } else { 178 | return new FilterBuilder(); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/test/java/org/reflections8/ReflectionUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.reflections8; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertThat; 6 | import static org.junit.Assert.assertTrue; 7 | import static org.reflections8.ReflectionUtils.getAllAnnotations; 8 | import static org.reflections8.ReflectionUtils.getAllConstructors; 9 | import static org.reflections8.ReflectionUtils.getAllFields; 10 | import static org.reflections8.ReflectionUtils.getAllMethods; 11 | import static org.reflections8.ReflectionUtils.getAllSuperTypes; 12 | import static org.reflections8.ReflectionUtils.getAnnotations; 13 | import static org.reflections8.ReflectionUtils.getMethods; 14 | import static org.reflections8.ReflectionUtils.withAnnotation; 15 | import static org.reflections8.ReflectionUtils.withAnyParameterAnnotation; 16 | import static org.reflections8.ReflectionUtils.withModifier; 17 | import static org.reflections8.ReflectionUtils.withName; 18 | import static org.reflections8.ReflectionUtils.withParameters; 19 | import static org.reflections8.ReflectionUtils.withParametersAssignableFrom; 20 | import static org.reflections8.ReflectionUtils.withParametersAssignableTo; 21 | import static org.reflections8.ReflectionUtils.withParametersCount; 22 | import static org.reflections8.ReflectionUtils.withPattern; 23 | import static org.reflections8.ReflectionUtils.withReturnType; 24 | import static org.reflections8.ReflectionUtils.withReturnTypeAssignableTo; 25 | import static org.reflections8.ReflectionUtils.withTypeAssignableTo; 26 | import static org.reflections8.ReflectionsTest.are; 27 | 28 | import java.lang.annotation.Annotation; 29 | import java.lang.reflect.Field; 30 | import java.lang.reflect.Member; 31 | import java.lang.reflect.Method; 32 | import java.lang.reflect.Modifier; 33 | import java.util.Arrays; 34 | import java.util.Collection; 35 | import java.util.Collections; 36 | import java.util.HashSet; 37 | import java.util.Set; 38 | import java.util.function.Function; 39 | 40 | import org.hamcrest.BaseMatcher; 41 | import org.hamcrest.Description; 42 | import org.junit.Test; 43 | import org.reflections8.scanners.FieldAnnotationsScanner; 44 | import org.reflections8.util.ReflectionsIterables; 45 | 46 | /** 47 | * @author mamo 48 | */ 49 | @SuppressWarnings("unchecked") 50 | public class ReflectionUtilsTest { 51 | 52 | @Test 53 | public void getAllTest() { 54 | assertThat(getAllSuperTypes(TestModel.C3.class, withAnnotation(TestModel.AI1.class)), are(TestModel.I1.class)); 55 | 56 | Set allMethods = getAllMethods(TestModel.C4.class, withModifier(Modifier.PUBLIC), withReturnType(void.class)); 57 | Set allMethods1 = getAllMethods(TestModel.C4.class, withPattern("public.*.void .*")); 58 | 59 | assertTrue(allMethods.containsAll(allMethods1) && allMethods1.containsAll(allMethods)); 60 | assertThat(allMethods1, names("m1")); 61 | 62 | assertThat(getAllMethods(TestModel.C4.class, withAnyParameterAnnotation(TestModel.AM1.class)), names("m4")); 63 | 64 | assertThat(getAllFields(TestModel.C4.class, withAnnotation(TestModel.AF1.class)), names("f1", "f2")); 65 | 66 | assertThat(getAllFields(TestModel.C4.class, withAnnotation(new TestModel.AF1() { 67 | public String value() {return "2";} 68 | public Class annotationType() {return TestModel.AF1.class;}})), 69 | names("f2")); 70 | 71 | assertThat(getAllFields(TestModel.C4.class, withTypeAssignableTo(String.class)), names("f1", "f2", "f3")); 72 | 73 | assertThat(getAllConstructors(TestModel.C4.class, withParametersCount(0)), names(TestModel.C4.class.getName())); 74 | 75 | assertEquals(getAllAnnotations(TestModel.C3.class).size(), 5); 76 | 77 | Method m4 = getMethods(TestModel.C4.class, withName("m4")).iterator().next(); 78 | assertEquals(m4.getName(), "m4"); 79 | assertTrue(getAnnotations(m4).isEmpty()); 80 | } 81 | 82 | @Test public void withParameter() throws Exception { 83 | Class target = Collections.class; 84 | Object arg1 = Arrays.asList(1, 2, 3); 85 | 86 | Set allMethods = new HashSet(); 87 | for (Class type : getAllSuperTypes(arg1.getClass())) { 88 | allMethods.addAll(getAllMethods(target, withModifier(Modifier.STATIC), withParameters(type))); 89 | } 90 | 91 | Set allMethods1 = getAllMethods(target, withModifier(Modifier.STATIC), withParametersAssignableTo(arg1.getClass())); 92 | 93 | assertEquals(allMethods, allMethods1); 94 | 95 | for (Method method : allMethods) { //effectively invokable 96 | //noinspection UnusedDeclaration 97 | Object invoke = method.invoke(null, arg1); 98 | } 99 | } 100 | 101 | @Test 102 | public void withParametersAssignableFromTest() throws Exception { 103 | //Check for null safe 104 | getAllMethods(Collections.class, withModifier(Modifier.STATIC), withParametersAssignableFrom()); 105 | 106 | Class target = Collections.class; 107 | Object arg1 = Arrays.asList(1, 2, 3); 108 | 109 | Set allMethods = new HashSet(); 110 | for (Class type : getAllSuperTypes(arg1.getClass())) { 111 | allMethods.addAll(getAllMethods(target, withModifier(Modifier.STATIC), withParameters(type))); 112 | } 113 | 114 | Set allMethods1 = getAllMethods(target, withModifier(Modifier.STATIC), withParametersAssignableFrom(Iterable.class), withParametersAssignableTo(arg1.getClass())); 115 | 116 | assertEquals(allMethods, allMethods1); 117 | 118 | for (Method method : allMethods) { //effectively invokable 119 | //noinspection UnusedDeclaration 120 | Object invoke = method.invoke(null, arg1); 121 | } 122 | } 123 | 124 | @Test public void withReturn() throws Exception { 125 | Set returnMember = getAllMethods(Class.class, withReturnTypeAssignableTo(Member.class)); 126 | Set returnsAssignableToMember = getAllMethods(Class.class, withReturnType(Method.class)); 127 | 128 | assertTrue(returnMember.containsAll(returnsAssignableToMember)); 129 | assertFalse(returnsAssignableToMember.containsAll(returnMember)); 130 | 131 | returnsAssignableToMember = getAllMethods(Class.class, withReturnType(Field.class)); 132 | assertTrue(returnMember.containsAll(returnsAssignableToMember)); 133 | assertFalse(returnsAssignableToMember.containsAll(returnMember)); 134 | } 135 | 136 | @Test 137 | public void getAllAndReflections() { 138 | Reflections reflections8 = new Reflections(TestModel.class, new FieldAnnotationsScanner()); 139 | 140 | Set af1 = reflections8.getFieldsAnnotatedWith(TestModel.AF1.class); 141 | Set allFields = ReflectionUtils.getAll(af1, withModifier(Modifier.PROTECTED)); 142 | assertTrue(allFields.size() == 1); 143 | assertThat(allFields, names("f2")); 144 | } 145 | 146 | private Set names(Set o) { 147 | return ReflectionsIterables.transformToSet(o, new Function() { 148 | public String apply(Member input) { 149 | return input.getName(); 150 | } 151 | }); 152 | } 153 | 154 | private BaseMatcher> names(final String... namesArray) { 155 | return new BaseMatcher>() { 156 | 157 | public boolean matches(Object o) { 158 | Collection transform = names((Set) o); 159 | final Collection names = Arrays.asList(namesArray); 160 | return transform.containsAll(names) && names.containsAll(transform); 161 | } 162 | 163 | public void describeTo(Description description) { 164 | } 165 | }; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/adapters/JavassistAdapter.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.adapters; 2 | 3 | import static javassist.bytecode.AccessFlag.isPrivate; 4 | import static javassist.bytecode.AccessFlag.isProtected; 5 | 6 | import java.io.BufferedInputStream; 7 | import java.io.DataInputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.util.ArrayList; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.StringJoiner; 14 | 15 | import org.reflections8.ReflectionsException; 16 | import org.reflections8.vfs.Vfs; 17 | 18 | import javassist.bytecode.AccessFlag; 19 | import javassist.bytecode.AnnotationsAttribute; 20 | import javassist.bytecode.ClassFile; 21 | import javassist.bytecode.Descriptor; 22 | import javassist.bytecode.FieldInfo; 23 | import javassist.bytecode.MethodInfo; 24 | import javassist.bytecode.ParameterAnnotationsAttribute; 25 | import javassist.bytecode.annotation.Annotation; 26 | 27 | /** 28 | * 29 | */ 30 | public class JavassistAdapter implements MetadataAdapter { 31 | 32 | /**setting this to false will result in returning only visible annotations from the relevant methods here (only {@link java.lang.annotation.RetentionPolicy#RUNTIME})*/ 33 | public static final boolean includeInvisibleTag = true; 34 | 35 | public List getFields(final ClassFile cls) { 36 | //noinspection unchecked 37 | return cls.getFields(); 38 | } 39 | 40 | public List getMethods(final ClassFile cls) { 41 | //noinspection unchecked 42 | return cls.getMethods(); 43 | } 44 | 45 | public String getMethodName(final MethodInfo method) { 46 | return method.getName(); 47 | } 48 | 49 | public List getParameterNames(final MethodInfo method) { 50 | String descriptor = method.getDescriptor(); 51 | descriptor = descriptor.substring(descriptor.indexOf("(") + 1, descriptor.lastIndexOf(")")); 52 | return splitDescriptorToTypeNames(descriptor); 53 | } 54 | 55 | public List getClassAnnotationNames(final ClassFile aClass) { 56 | return getAnnotationNames((AnnotationsAttribute) aClass.getAttribute(AnnotationsAttribute.visibleTag), 57 | includeInvisibleTag ? (AnnotationsAttribute) aClass.getAttribute(AnnotationsAttribute.invisibleTag) : null); 58 | } 59 | 60 | public List getFieldAnnotationNames(final FieldInfo field) { 61 | return getAnnotationNames((AnnotationsAttribute) field.getAttribute(AnnotationsAttribute.visibleTag), 62 | includeInvisibleTag ? (AnnotationsAttribute) field.getAttribute(AnnotationsAttribute.invisibleTag) : null); 63 | } 64 | 65 | public List getMethodAnnotationNames(final MethodInfo method) { 66 | return getAnnotationNames((AnnotationsAttribute) method.getAttribute(AnnotationsAttribute.visibleTag), 67 | includeInvisibleTag ? (AnnotationsAttribute) method.getAttribute(AnnotationsAttribute.invisibleTag) : null); 68 | } 69 | 70 | public List getParameterAnnotationNames(final MethodInfo method, final int parameterIndex) { 71 | List result = new ArrayList<>(); 72 | 73 | List parameterAnnotationsAttributes = Arrays.asList( 74 | (ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.visibleTag), 75 | (ParameterAnnotationsAttribute) method.getAttribute(ParameterAnnotationsAttribute.invisibleTag)); 76 | 77 | if (parameterAnnotationsAttributes != null) { 78 | for (ParameterAnnotationsAttribute parameterAnnotationsAttribute : parameterAnnotationsAttributes) { 79 | if (parameterAnnotationsAttribute != null) { 80 | Annotation[][] annotations = parameterAnnotationsAttribute.getAnnotations(); 81 | if (parameterIndex < annotations.length) { 82 | Annotation[] annotation = annotations[parameterIndex]; 83 | result.addAll(getAnnotationNames(annotation)); 84 | } 85 | } 86 | } 87 | } 88 | 89 | return result; 90 | } 91 | 92 | public String getReturnTypeName(final MethodInfo method) { 93 | String descriptor = method.getDescriptor(); 94 | descriptor = descriptor.substring(descriptor.lastIndexOf(")") + 1); 95 | return splitDescriptorToTypeNames(descriptor).get(0); 96 | } 97 | 98 | public String getFieldName(final FieldInfo field) { 99 | return field.getName(); 100 | } 101 | 102 | public ClassFile getOrCreateClassObject(final Vfs.File file) { 103 | try { 104 | try (InputStream inputStream = file.openInputStream()) { 105 | DataInputStream dis = new DataInputStream(new BufferedInputStream(inputStream)); 106 | return new ClassFile(dis); 107 | } 108 | } catch (IOException e) { 109 | throw new ReflectionsException("could not create class file from " + file.getName(), e); 110 | } 111 | } 112 | 113 | public String getMethodModifier(MethodInfo method) { 114 | int accessFlags = method.getAccessFlags(); 115 | return isPrivate(accessFlags) ? "private" : 116 | isProtected(accessFlags) ? "protected" : 117 | isPublic(accessFlags) ? "public" : ""; 118 | } 119 | 120 | public String getMethodKey(ClassFile cls, MethodInfo method) { 121 | StringJoiner j = new StringJoiner(", ","(",")"); 122 | getParameterNames(method).forEach(name -> j.add(name)); 123 | return getMethodName(method) + j.toString(); 124 | } 125 | 126 | public String getMethodFullKey(ClassFile cls, MethodInfo method) { 127 | return getClassName(cls) + "." + getMethodKey(cls, method); 128 | } 129 | 130 | public boolean isPublic(Object o) { 131 | Integer accessFlags = 132 | o instanceof ClassFile ? ((ClassFile) o).getAccessFlags() : 133 | o instanceof FieldInfo ? ((FieldInfo) o).getAccessFlags() : 134 | o instanceof MethodInfo ? ((MethodInfo) o).getAccessFlags() : null; 135 | 136 | return accessFlags != null && AccessFlag.isPublic(accessFlags); 137 | } 138 | 139 | // 140 | public String getClassName(final ClassFile cls) { 141 | return cls.getName(); 142 | } 143 | 144 | public String getSuperclassName(final ClassFile cls) { 145 | return cls.getSuperclass(); 146 | } 147 | 148 | public List getInterfacesNames(final ClassFile cls) { 149 | return Arrays.asList(cls.getInterfaces()); 150 | } 151 | 152 | public boolean acceptsInput(String file) { 153 | return file.endsWith(".class"); 154 | } 155 | 156 | // 157 | private List getAnnotationNames(final AnnotationsAttribute... annotationsAttributes) { 158 | List result = new ArrayList<>(); 159 | 160 | if (annotationsAttributes != null) { 161 | for (AnnotationsAttribute annotationsAttribute : annotationsAttributes) { 162 | if (annotationsAttribute != null) { 163 | for (Annotation annotation : annotationsAttribute.getAnnotations()) { 164 | result.add(annotation.getTypeName()); 165 | } 166 | } 167 | } 168 | } 169 | 170 | return result; 171 | } 172 | 173 | private List getAnnotationNames(final Annotation[] annotations) { 174 | List result = new ArrayList<>(); 175 | 176 | for (Annotation annotation : annotations) { 177 | result.add(annotation.getTypeName()); 178 | } 179 | 180 | return result; 181 | } 182 | 183 | private List splitDescriptorToTypeNames(final String descriptors) { 184 | List result = new ArrayList<>(); 185 | 186 | if (descriptors != null && descriptors.length() != 0) { 187 | 188 | List indices = new ArrayList<>(); 189 | Descriptor.Iterator iterator = new Descriptor.Iterator(descriptors); 190 | while (iterator.hasNext()) { 191 | indices.add(iterator.next()); 192 | } 193 | indices.add(descriptors.length()); 194 | 195 | for (int i = 0; i < indices.size() - 1; i++) { 196 | String s1 = Descriptor.toString(descriptors.substring(indices.get(i), indices.get(i + 1))); 197 | result.add(s1); 198 | } 199 | 200 | } 201 | 202 | return result; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | net.oneandone.reflections8 5 | reflections8 6 | 0.11.8-SNAPSHOT 7 | 8 | Reflections8 9 | Reflections8 - a Java runtime metadata analysis without guava on Java 8 10 | http://github.com/aschoerk/reflections8 11 | 12 | 13 | 14 | WTFPL 15 | http://www.wtfpl.net/ 16 | 17 | 18 | The New BSD License 19 | http://www.opensource.org/licenses/bsd-license.html 20 | 21 | 22 | 23 | 24 | https://github.com/aschoerk/reflections8 25 | scm:git:ssh://git@github.com/aschoerk/reflections8.git 26 | scm:git:ssh://git@github.com/aschoerk/reflections8.git 27 | HEAD 28 | 29 | 30 | 31 | 32 | https://github.com/aschoerk/reflections8/issues 33 | GitHub Issues 34 | 35 | 36 | 37 | 38 | 39 | Andreas Schörk 40 | andreas.schoerk@1and1.com 41 | 1and1 42 | http://www.1and1.com 43 | 44 | 45 | 46 | 47 | 48 | sonatype-nexus-snapshots 49 | https://oss.sonatype.org/content/repositories/snapshots/ 50 | 51 | 52 | sonatype-nexus-staging 53 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 54 | 55 | 56 | 57 | 58 | 3.22.0-GA 59 | 1.8 60 | -Xdoclint:none 61 | 2.5.3 62 | UTF-8 63 | UTF-8 64 | 65 | 66 | 67 | 68 | 69 | org.javassist 70 | javassist 71 | ${javassist.version} 72 | false 73 | 74 | 75 | 76 | 77 | org.slf4j 78 | slf4j-api 79 | 1.7.24 80 | true 81 | 82 | 83 | 84 | org.dom4j 85 | dom4j 86 | 2.1.1 87 | true 88 | 89 | 90 | 91 | com.google.code.gson 92 | gson 93 | 2.8.0 94 | true 95 | 96 | 97 | 98 | javax.servlet 99 | servlet-api 100 | 2.5 101 | provided 102 | true 103 | 104 | 105 | 106 | 107 | org.slf4j 108 | slf4j-simple 109 | 1.7.24 110 | true 111 | 112 | 113 | 114 | junit 115 | junit 116 | 4.12 117 | test 118 | 119 | 120 | org.jboss 121 | jboss-vfs 122 | 3.2.17.Final 123 | provided 124 | true 125 | 126 | 127 | 128 | 129 | 130 | release-sign-artifacts 131 | 132 | 133 | performRelease 134 | true 135 | 136 | 137 | 138 | 139 | 140 | maven-jar-plugin 141 | 142 | 143 | ${project.build.outputDirectory}/META-INF/MANIFEST.MF 144 | 145 | 146 | 147 | 148 | org.apache.maven.plugins 149 | maven-source-plugin 150 | 151 | 152 | attach-sources 153 | 154 | jar 155 | 156 | 157 | 158 | 159 | 160 | org.apache.maven.plugins 161 | maven-javadoc-plugin 162 | 163 | 164 | attach-javadocs 165 | 166 | jar 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | org.apache.felix 176 | maven-bundle-plugin 177 | 178 | 179 | bundle-manifest 180 | process-classes 181 | 182 | manifest 183 | 184 | 185 | 186 | 187 | 188 | 189 | org.jboss.vfs.*;resolution:=optional, 190 | * 191 | 192 | 193 | org.reflections8.* 194 | 195 | org.reflections8 196 | 197 | 198 | 199 | 200 | org.apache.maven.plugins 201 | maven-gpg-plugin 202 | 203 | 204 | sign-artifacts 205 | verify 206 | 207 | sign 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | org.apache.maven.plugins 221 | maven-compiler-plugin 222 | 3.8.0 223 | 224 | ${jdk.version} 225 | ${jdk.version} 226 | 227 | 228 | 229 | org.apache.maven.plugins 230 | maven-release-plugin 231 | ${maven-release-plugin.version} 232 | true 233 | 234 | 235 | org.apache.maven.scm 236 | maven-scm-provider-gitexe 237 | 1.9.2 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/util/Utils.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.util; 2 | 3 | import static java.util.Optional.of; 4 | import static org.reflections8.ReflectionUtils.forName; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.lang.reflect.Constructor; 10 | import java.lang.reflect.Field; 11 | import java.lang.reflect.Member; 12 | import java.lang.reflect.Method; 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.HashSet; 16 | import java.util.List; 17 | import java.util.Optional; 18 | import java.util.Set; 19 | 20 | import org.reflections8.Reflections; 21 | import org.reflections8.ReflectionsException; 22 | import org.reflections8.scanners.Scanner; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | /** 27 | * a garbage can of convenient methods 28 | */ 29 | public abstract class Utils { 30 | 31 | public static String repeat(String string, int times) { 32 | StringBuilder sb = new StringBuilder(); 33 | 34 | for (int i = 0; i < times; i++) { 35 | sb.append(string); 36 | } 37 | 38 | return sb.toString(); 39 | } 40 | 41 | /** 42 | * isEmpty compatible with Java 5 43 | */ 44 | public static boolean isEmpty(String s) { 45 | return s == null || s.length() == 0; 46 | } 47 | 48 | public static boolean isEmpty(Object[] objects) { 49 | return objects == null || objects.length == 0; 50 | } 51 | 52 | public static File prepareFile(String filename) { 53 | File file = new File(filename); 54 | File parent = file.getAbsoluteFile().getParentFile(); 55 | if (!parent.exists()) { 56 | // noinspection ResultOfMethodCallIgnored 57 | parent.mkdirs(); 58 | } 59 | return file; 60 | } 61 | 62 | public static Member getMemberFromDescriptor(String descriptor, ClassLoader... classLoaders) 63 | throws ReflectionsException { 64 | return getMemberFromDescriptor(descriptor, 65 | Optional.of(classLoaders != null ? classLoaders : new ClassLoader[] {})); 66 | } 67 | 68 | public static Member getMemberFromDescriptor(String descriptor, Optional classLoaders) { 69 | int p0 = descriptor.lastIndexOf('('); 70 | String memberKey = p0 != -1 ? descriptor.substring(0, p0) : descriptor; 71 | String methodParameters = p0 != -1 ? descriptor.substring(p0 + 1, descriptor.lastIndexOf(')')) : ""; 72 | 73 | int p1 = Math.max(memberKey.lastIndexOf('.'), memberKey.lastIndexOf('$')); 74 | String className = memberKey.substring(memberKey.lastIndexOf(' ') + 1, p1); 75 | String memberName = memberKey.substring(p1 + 1); 76 | int endsWithLambda = memberKey.lastIndexOf(".lambda"); 77 | int endsWithAccess = memberKey.lastIndexOf(".access"); 78 | if (endsWithLambda > 0) { 79 | memberName = memberKey.substring(endsWithLambda + 1); 80 | className = memberKey.substring(memberKey.lastIndexOf(' ') + 1, endsWithLambda); 81 | } 82 | if (endsWithAccess > 0) { 83 | memberName = memberKey.substring(endsWithAccess + 1); 84 | className = memberKey.substring(memberKey.lastIndexOf(' ') + 1, endsWithAccess); 85 | } 86 | 87 | Class[] parameterTypes = getParameterTypes(classLoaders, methodParameters); 88 | Class aClass = forName(className, classLoaders); 89 | return getMember(descriptor, className, memberName, parameterTypes, aClass); 90 | } 91 | 92 | private static Class[] getParameterTypes(Optional classLoaders, String methodParameters) { 93 | Class[] parameterTypes = null; 94 | if (!isEmpty(methodParameters)) { 95 | String[] parameterNames = methodParameters.split(","); 96 | List> result = new ArrayList<>(parameterNames.length); 97 | for (String name : parameterNames) { 98 | result.add(forName(name.trim(), classLoaders)); 99 | } 100 | parameterTypes = result.toArray(new Class[result.size()]); 101 | } 102 | return parameterTypes; 103 | } 104 | 105 | private static Member getMember(String descriptor, String className, String memberName, Class[] parameterTypes, 106 | Class aClass) { 107 | while (aClass != null) { 108 | try { 109 | if (!descriptor.contains("(")) { 110 | return aClass.isInterface() ? aClass.getField(memberName) : aClass.getDeclaredField(memberName); 111 | } else if (isConstructor(descriptor)) { 112 | return aClass.isInterface() ? aClass.getConstructor(parameterTypes) 113 | : aClass.getDeclaredConstructor(parameterTypes); 114 | } else { 115 | return aClass.isInterface() ? aClass.getMethod(memberName, parameterTypes) 116 | : aClass.getDeclaredMethod(memberName, parameterTypes); 117 | } 118 | } catch (Exception e) { 119 | aClass = aClass.getSuperclass(); 120 | } 121 | } 122 | throw new ReflectionsException("Can't resolve member named " + memberName + " for class " + className); 123 | } 124 | 125 | public static Set getMethodsFromDescriptors(Iterable annotatedWith, 126 | Optional classLoaders) { 127 | Set result = new HashSet<>(); 128 | for (String annotated : annotatedWith) { 129 | if (!isConstructor(annotated)) { 130 | Method member = (Method) getMemberFromDescriptor(annotated, classLoaders); 131 | if (member != null) 132 | result.add(member); 133 | } 134 | } 135 | return result; 136 | 137 | } 138 | 139 | public static Set getMethodsFromDescriptors(Iterable annotatedWith, ClassLoader... classLoaders) { 140 | return getMethodsFromDescriptors(annotatedWith, 141 | Optional.of(classLoaders != null ? classLoaders : new ClassLoader[] {})); 142 | } 143 | 144 | public static Set getConstructorsFromDescriptors(Iterable annotatedWith, 145 | Optional classLoaders) { 146 | Set result = new HashSet<>(); 147 | for (String annotated : annotatedWith) { 148 | if (isConstructor(annotated)) { 149 | Constructor member = (Constructor) getMemberFromDescriptor(annotated, classLoaders); 150 | if (member != null) 151 | result.add(member); 152 | } 153 | } 154 | return result; 155 | } 156 | 157 | public static Set getConstructorsFromDescriptors(Iterable annotatedWith, 158 | ClassLoader... classLoaders) { 159 | return getConstructorsFromDescriptors(annotatedWith, 160 | Optional.of(classLoaders != null ? classLoaders : new ClassLoader[] {})); 161 | } 162 | 163 | public static Set getMembersFromDescriptors(Iterable values, Optional classLoaders) { 164 | Set result = new HashSet<>(); 165 | for (String value : values) { 166 | try { 167 | result.add(Utils.getMemberFromDescriptor(value, classLoaders)); 168 | } catch (ReflectionsException e) { 169 | throw new ReflectionsException("Can't resolve member named " + value, e); 170 | } 171 | } 172 | return result; 173 | } 174 | 175 | public static Set getMembersFromDescriptors(Iterable values, ClassLoader... classLoaders) { 176 | return getMembersFromDescriptors(values, of(classLoaders != null ? classLoaders : new ClassLoader[] {})); 177 | } 178 | 179 | public static Field getFieldFromString(String field, ClassLoader... classLoaders) { 180 | return getFieldFromString(field, Optional.of(classLoaders != null ? classLoaders : new ClassLoader[] {})); 181 | } 182 | 183 | public static Field getFieldFromString(String field, Optional classLoaders) { 184 | String className = field.substring(0, field.lastIndexOf('.')); 185 | String fieldName = field.substring(field.lastIndexOf('.') + 1); 186 | 187 | try { 188 | return forName(className, classLoaders).getDeclaredField(fieldName); 189 | } catch (NoSuchFieldException e) { 190 | throw new ReflectionsException("Can't resolve field named " + fieldName, e); 191 | } 192 | } 193 | 194 | public static void close(InputStream closeable) { 195 | try { 196 | if (closeable != null) 197 | closeable.close(); 198 | } catch (IOException e) { 199 | if (Reflections.log.isPresent()) { 200 | Reflections.log.get().warn("Could not close InputStream", e); 201 | } 202 | } 203 | } 204 | 205 | public static Optional findLogger(Class aClass) { 206 | try { 207 | // This is to check whether an optional SLF4J binding is available. While SLF4J 208 | // recommends that libraries 209 | // "should not declare a dependency on any SLF4J binding but only depend on 210 | // slf4j-api", doing so forces 211 | // users of the library to either add a binding to the classpath (even if just 212 | // slf4j-nop) or to set the 213 | // "slf4j.suppressInitError" system property in order to avoid the warning, 214 | // which both is inconvenient. 215 | Class.forName("org.slf4j.impl.StaticLoggerBinder"); 216 | return Optional.of(LoggerFactory.getLogger(aClass)); 217 | } catch (Throwable e) { 218 | return Optional.empty(); 219 | } 220 | } 221 | 222 | public static boolean isConstructor(String fqn) { 223 | return fqn.contains("init>"); 224 | } 225 | 226 | public static String name(Class type) { 227 | if (!type.isArray()) { 228 | return type.getName(); 229 | } else { 230 | int dim = 0; 231 | while (type.isArray()) { 232 | dim++; 233 | type = type.getComponentType(); 234 | } 235 | return type.getName() + repeat("[]", dim); 236 | } 237 | } 238 | 239 | public static List names(Iterable> types) { 240 | List result = new ArrayList(); 241 | for (Class type : types) 242 | result.add(name(type)); 243 | return result; 244 | } 245 | 246 | public static List names(Class... types) { 247 | return names(Arrays.asList(types)); 248 | } 249 | 250 | public static String name(Constructor constructor) { 251 | return constructor.getName() + "." + "" + "(" 252 | + Joiner.on(", ").join(names(constructor.getParameterTypes())) + ")"; 253 | } 254 | 255 | public static String name(Method method) { 256 | return method.getDeclaringClass().getName() + "." + method.getName() + "(" 257 | + Joiner.on(", ").join(names(method.getParameterTypes())) + ")"; 258 | } 259 | 260 | public static String name(Field field) { 261 | return field.getDeclaringClass().getName() + "." + field.getName(); 262 | } 263 | 264 | public static String index(Class scannerClass) { 265 | return scannerClass.getSimpleName(); 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /Licence-2.0.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/main/java/org/reflections8/vfs/Vfs.java: -------------------------------------------------------------------------------- 1 | package org.reflections8.vfs; 2 | 3 | import org.reflections8.Reflections; 4 | import org.reflections8.ReflectionsException; 5 | import org.reflections8.util.ClasspathHelper; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.UnsupportedEncodingException; 10 | import java.net.JarURLConnection; 11 | import java.net.URISyntaxException; 12 | import java.net.URL; 13 | import java.net.URLConnection; 14 | import java.net.URLDecoder; 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.Collection; 18 | import java.util.List; 19 | import java.util.function.Predicate; 20 | import java.util.jar.JarFile; 21 | import java.util.stream.Stream; 22 | import java.util.stream.StreamSupport; 23 | 24 | /** 25 | * a simple virtual file system bridge 26 | *

use the {@link org.reflections.vfs.Vfs#fromURL(java.net.URL)} to get a {@link org.reflections.vfs.Vfs.Dir}, 27 | * then use {@link org.reflections.vfs.Vfs.Dir#getFiles()} to iterate over the {@link org.reflections.vfs.Vfs.File} 28 | *

for example: 29 | *

 30 |  *      Vfs.Dir dir = Vfs.fromURL(url);
 31 |  *      Iterable files = dir.getFiles();
 32 |  *      for (Vfs.File file : files) {
 33 |  *          InputStream is = file.openInputStream();
 34 |  *      }
 35 |  * 
36 | *

{@link org.reflections.vfs.Vfs#fromURL(java.net.URL)} uses static {@link org.reflections.vfs.Vfs.DefaultUrlTypes} to resolve URLs. 37 | * It contains VfsTypes for handling for common resources such as local jar file, local directory, jar url, jar input stream and more. 38 | *

It can be plugged in with other {@link org.reflections.vfs.Vfs.UrlType} using {@link org.reflections.vfs.Vfs#addDefaultURLTypes(org.reflections.vfs.Vfs.UrlType)} or {@link org.reflections.vfs.Vfs#setDefaultURLTypes(java.util.List)}. 39 | *

for example: 40 | *

 41 |  *      Vfs.addDefaultURLTypes(new Vfs.UrlType() {
 42 |  *          public boolean matches(URL url)         {
 43 |  *              return url.getProtocol().equals("http");
 44 |  *          }
 45 |  *          public Vfs.Dir createDir(final URL url) {
 46 |  *              return new HttpDir(url); //implement this type... (check out a naive implementation on VfsTest)
 47 |  *          }
 48 |  *      });
 49 |  *
 50 |  *      Vfs.Dir dir = Vfs.fromURL(new URL("http://mirrors.ibiblio.org/pub/mirrors/maven2/org/slf4j/slf4j-api/1.5.6/slf4j-api-1.5.6.jar"));
 51 |  * 
52 | *

use {@link org.reflections.vfs.Vfs#findFiles(java.util.Collection, java.util.function.Predicate)} to get an 53 | * iteration of files matching given name predicate over given list of urls 54 | */ 55 | public abstract class Vfs { 56 | private static List defaultUrlTypes = new ArrayList<>(Arrays.asList(DefaultUrlTypes.values())); 57 | 58 | /** an abstract vfs dir */ 59 | public interface Dir { 60 | String getPath(); 61 | Iterable getFiles(); 62 | default void close() {} 63 | } 64 | 65 | /** an abstract vfs file */ 66 | public interface File { 67 | String getName(); 68 | String getRelativePath(); 69 | InputStream openInputStream() throws IOException; 70 | } 71 | 72 | /** a matcher and factory for a url */ 73 | public interface UrlType { 74 | boolean matches(URL url) throws Exception; 75 | Dir createDir(URL url) throws Exception; 76 | } 77 | 78 | /** the default url types that will be used when issuing {@link org.reflections.vfs.Vfs#fromURL(java.net.URL)} */ 79 | public static List getDefaultUrlTypes() { 80 | return defaultUrlTypes; 81 | } 82 | 83 | /** sets the static default url types. can be used to statically plug in urlTypes */ 84 | public static void setDefaultURLTypes(final List urlTypes) { 85 | defaultUrlTypes = urlTypes; 86 | } 87 | 88 | /** add a static default url types to the beginning of the default url types list. can be used to statically plug in urlTypes */ 89 | public static void addDefaultURLTypes(UrlType urlType) { 90 | defaultUrlTypes.add(0, urlType); 91 | } 92 | 93 | /** tries to create a Dir from the given url, using the defaultUrlTypes */ 94 | public static Dir fromURL(final URL url) { 95 | return fromURL(url, defaultUrlTypes); 96 | } 97 | 98 | /** tries to create a Dir from the given url, using the given urlTypes*/ 99 | public static Dir fromURL(final URL url, final List urlTypes) { 100 | for (UrlType type : urlTypes) { 101 | try { 102 | if (type.matches(url)) { 103 | Dir dir = type.createDir(url); 104 | if (dir != null) return dir; 105 | } 106 | } catch (Throwable e) { 107 | if (Reflections.log.isPresent()) { 108 | Reflections.log.get().warn("could not create Dir using " + type + " from url " + url.toExternalForm() + ". skipping.", e); 109 | } 110 | } 111 | } 112 | 113 | throw new ReflectionsException("could not create Vfs.Dir from url, no matching UrlType was found [" + url.toExternalForm() + "]\n" + 114 | "either use fromURL(final URL url, final List urlTypes) or " + 115 | "use the static setDefaultURLTypes(final List urlTypes) or addDefaultURLTypes(UrlType urlType) " + 116 | "with your specialized UrlType."); 117 | } 118 | 119 | /** tries to create a Dir from the given url, using the given urlTypes*/ 120 | public static Dir fromURL(final URL url, final UrlType... urlTypes) { 121 | return fromURL(url, Arrays.asList(urlTypes)); 122 | } 123 | 124 | /** return an iterable of all {@link org.reflections.vfs.Vfs.File} in given urls, starting with given packagePrefix and matching nameFilter */ 125 | public static Iterable findFiles(final Collection inUrls, final String packagePrefix, final Predicate nameFilter) { 126 | Predicate fileNamePredicate = file -> { 127 | String path = file.getRelativePath(); 128 | if (path.startsWith(packagePrefix)) { 129 | String filename = path.substring(path.indexOf(packagePrefix) + packagePrefix.length()); 130 | return !filename.isEmpty() && nameFilter.test(filename.substring(1)); 131 | } else { 132 | return false; 133 | } 134 | }; 135 | return findFiles(inUrls, fileNamePredicate); 136 | } 137 | 138 | /** return an iterable of all {@link org.reflections.vfs.Vfs.File} in given urls, matching filePredicate */ 139 | public static Iterable findFiles(final Collection urls, final Predicate filePredicate) { 140 | return () -> urls.stream() 141 | .flatMap(url -> { 142 | try { 143 | return StreamSupport.stream(fromURL(url).getFiles().spliterator(), false); 144 | } catch (Throwable e) { 145 | if (Reflections.log.isPresent()) { 146 | Reflections.log.get().error("could not findFiles for url. continuing. [" + url + "]", e); 147 | } 148 | return Stream.of(); 149 | } 150 | }).filter(filePredicate).iterator(); 151 | } 152 | 153 | /**try to get {@link java.io.File} from url*/ 154 | public static java.io.File getFile(URL url) { 155 | java.io.File file; 156 | String path; 157 | 158 | try { 159 | path = url.toURI().getSchemeSpecificPart(); 160 | if ((file = new java.io.File(path)).exists()) return file; 161 | } catch (URISyntaxException ignored) { 162 | } 163 | 164 | try { 165 | path = URLDecoder.decode(url.getPath(), "UTF-8"); 166 | if (path.contains(".jar!")) path = path.substring(0, path.lastIndexOf(".jar!") + ".jar".length()); 167 | if ((file = new java.io.File(path)).exists()) return file; 168 | 169 | } catch (UnsupportedEncodingException ignored) { 170 | } 171 | 172 | try { 173 | path = url.toExternalForm(); 174 | if (path.startsWith("jar:")) path = path.substring("jar:".length()); 175 | if (path.startsWith("wsjar:")) path = path.substring("wsjar:".length()); 176 | if (path.startsWith("file:")) path = path.substring("file:".length()); 177 | if (path.contains(".jar!")) path = path.substring(0, path.indexOf(".jar!") + ".jar".length()); 178 | if (path.contains(".war!")) path = path.substring(0, path.indexOf(".war!") + ".war".length()); 179 | if ((file = new java.io.File(path)).exists()) return file; 180 | 181 | path = path.replace("%20", " "); 182 | if ((file = new java.io.File(path)).exists()) return file; 183 | 184 | } catch (Exception ignored) { 185 | } 186 | 187 | return null; 188 | } 189 | 190 | private static boolean hasJarFileInPath(URL url) { 191 | return url.toExternalForm().matches(".*\\.jar(!.*|$)"); 192 | } 193 | 194 | private static boolean hasInnerJarFileInPath(URL url) { 195 | return url.toExternalForm().matches(".+\\.jar!/.+"); 196 | } 197 | 198 | /** default url types used by {@link org.reflections.vfs.Vfs#fromURL(java.net.URL)} 199 | *

200 | *

jarFile - creates a {@link org.reflections.vfs.ZipDir} over jar file 201 | *

jarUrl - creates a {@link org.reflections.vfs.ZipDir} over a jar url, using Java's {@link JarURLConnection} 202 | *

directory - creates a {@link org.reflections.vfs.SystemDir} over a file system directory 203 | *

jboss vfs - for protocols vfs, using jboss vfs (should be provided in classpath) 204 | *

jboss vfsfile - creates a {@link UrlTypeVFS} for protocols vfszip and vfsfile. 205 | *

bundle - for bundle protocol, using eclipse FileLocator (should be provided in classpath) 206 | *

jarInputStream - creates a {@link JarInputDir} over jar files (contains ".jar!/" in it's name), using Java's JarInputStream 207 | * */ 208 | public enum DefaultUrlTypes implements UrlType { 209 | jarFile { 210 | public boolean matches(URL url) { 211 | return url.getProtocol().equals("file") && hasJarFileInPath(url); 212 | } 213 | 214 | public Dir createDir(final URL url) throws Exception { 215 | return new ZipDir(new JarFile(getFile(url))); 216 | } 217 | }, 218 | 219 | jarUrl { 220 | public boolean matches(URL url) { 221 | return ("jar".equals(url.getProtocol()) || "zip".equals(url.getProtocol()) || "wsjar".equals(url.getProtocol())) && !hasInnerJarFileInPath(url); 222 | } 223 | 224 | public Dir createDir(URL url) throws Exception { 225 | try { 226 | URLConnection urlConnection = url.openConnection(); 227 | if (urlConnection instanceof JarURLConnection) { 228 | urlConnection.setUseCaches(false); 229 | return new ZipDir(((JarURLConnection) urlConnection).getJarFile()); 230 | } 231 | } catch (Throwable e) { /*fallback*/ } 232 | java.io.File file = getFile(url); 233 | if (file != null) { 234 | return new ZipDir(new JarFile(file)); 235 | } 236 | return null; 237 | } 238 | }, 239 | 240 | directory { 241 | public boolean matches(URL url) { 242 | if (url.getProtocol().equals("file") && !hasJarFileInPath(url)) { 243 | java.io.File file = getFile(url); 244 | return file != null && file.isDirectory(); 245 | } else return false; 246 | } 247 | 248 | public Dir createDir(final URL url) throws Exception { 249 | return new SystemDir(getFile(url)); 250 | } 251 | }, 252 | 253 | jboss_vfs { 254 | public boolean matches(URL url) { 255 | return url.getProtocol().equals("vfs"); 256 | } 257 | 258 | public Vfs.Dir createDir(URL url) throws Exception { 259 | return JbossDir.createDir(url); 260 | } 261 | }, 262 | 263 | jboss_vfsfile { 264 | public boolean matches(URL url) throws Exception { 265 | return "vfszip".equals(url.getProtocol()) || "vfsfile".equals(url.getProtocol()); 266 | } 267 | 268 | public Dir createDir(URL url) throws Exception { 269 | return new UrlTypeVFS().createDir(url); 270 | } 271 | }, 272 | 273 | bundle { 274 | public boolean matches(URL url) throws Exception { 275 | return url.getProtocol().startsWith("bundle"); 276 | } 277 | 278 | public Dir createDir(URL url) throws Exception { 279 | return fromURL((URL) ClasspathHelper.contextClassLoader(). 280 | loadClass("org.eclipse.core.runtime.FileLocator").getMethod("resolve", URL.class).invoke(null, url)); 281 | } 282 | }, 283 | 284 | jarInputStream { 285 | public boolean matches(URL url) throws Exception { 286 | return url.toExternalForm().contains(".jar"); 287 | } 288 | 289 | public Dir createDir(final URL url) throws Exception { 290 | return new JarInputDir(url); 291 | } 292 | }, 293 | jrt { 294 | public boolean matches(URL url) throws Exception { 295 | return url.getProtocol().startsWith("jrt"); 296 | } 297 | 298 | public Dir createDir(final URL url) throws Exception { 299 | return new JrtDir(url); 300 | } 301 | } 302 | } 303 | } 304 | --------------------------------------------------------------------------------