|
|
@@ -6,6 +6,15 @@
|
|
|
*/
|
|
|
package net.ranides.assira.reflection.util;
|
|
|
|
|
|
+import net.ranides.assira.collection.iterators.EnumerationUtils;
|
|
|
+import net.ranides.assira.collection.iterators.ForwardSpliterator;
|
|
|
+import net.ranides.assira.collection.iterators.IteratorUtils;
|
|
|
+import net.ranides.assira.generic.ValueUtils;
|
|
|
+import net.ranides.assira.io.ZipReader;
|
|
|
+import net.ranides.assira.io.uri.URIUtils;
|
|
|
+import net.ranides.assira.text.StringUtils;
|
|
|
+import net.ranides.assira.trace.ExceptionUtils;
|
|
|
+
|
|
|
import java.io.File;
|
|
|
import java.io.FileInputStream;
|
|
|
import java.io.IOException;
|
|
|
@@ -13,74 +22,74 @@ import java.net.URL;
|
|
|
import java.nio.charset.Charset;
|
|
|
import java.nio.file.Files;
|
|
|
import java.nio.file.Path;
|
|
|
-import java.util.Collections;
|
|
|
-import java.util.Enumeration;
|
|
|
-import java.util.Set;
|
|
|
-import java.util.Spliterator;
|
|
|
+import java.util.*;
|
|
|
import java.util.function.Consumer;
|
|
|
import java.util.function.Function;
|
|
|
import java.util.jar.JarEntry;
|
|
|
import java.util.jar.JarInputStream;
|
|
|
-import java.util.stream.Collectors;
|
|
|
import java.util.stream.Stream;
|
|
|
import java.util.stream.StreamSupport;
|
|
|
-import net.ranides.assira.collection.iterators.ForwardSpliterator;
|
|
|
-import net.ranides.assira.io.ZipReader;
|
|
|
-import net.ranides.assira.text.StringUtils;
|
|
|
-import net.ranides.assira.io.uri.URIUtils;
|
|
|
-import net.ranides.assira.trace.ExceptionUtils;
|
|
|
|
|
|
|
|
|
public final class PackageScanner {
|
|
|
|
|
|
+ private String root;
|
|
|
+
|
|
|
+ private ClassLoader loader;
|
|
|
+
|
|
|
private PackageScanner() {
|
|
|
/* utility class */
|
|
|
}
|
|
|
-
|
|
|
- public static Set<Class<?>> getTypes(String root) throws IOException {
|
|
|
- return getTypes(root, ClassUtils.loader());
|
|
|
- }
|
|
|
-
|
|
|
- public static Set<Class<?>> getTypes(String root, ClassLoader loader) throws IOException {
|
|
|
- return getTypeStream(root, loader).collect(Collectors.toSet());
|
|
|
- }
|
|
|
-
|
|
|
- public static Stream<Class<?>> getTypeStream(String root) throws IOException {
|
|
|
- return getTypeStream(root, ClassUtils.loader());
|
|
|
+
|
|
|
+ public static PackageScanner scanner() {
|
|
|
+ return new PackageScanner();
|
|
|
}
|
|
|
-
|
|
|
- public static Stream<Class<?>> getTypeStream(String root, ClassLoader loader) throws IOException {
|
|
|
- return getNameStream(root, loader).map(toclass(loader));
|
|
|
+
|
|
|
+ public PackageScanner root(String root) {
|
|
|
+ this.root = root;
|
|
|
+ return this;
|
|
|
}
|
|
|
-
|
|
|
- public static Set<String> getNames(String root) throws IOException {
|
|
|
- return getNames(root, ClassUtils.loader());
|
|
|
+
|
|
|
+ public PackageScanner loader(ClassLoader loader) {
|
|
|
+ this.loader = loader;
|
|
|
+ return this;
|
|
|
}
|
|
|
-
|
|
|
- public static Set<String> getNames(String root, ClassLoader loader) throws IOException {
|
|
|
- return getNameStream(root, loader).collect(Collectors.toSet());
|
|
|
+
|
|
|
+ public Stream<Class<?>> types() throws IOException {
|
|
|
+ return names().map(toclass(cloader())).filter(Objects::nonNull);
|
|
|
}
|
|
|
-
|
|
|
- public static Stream<String> getNameStream(String root) throws IOException {
|
|
|
- return getNameStream(root, ClassUtils.loader());
|
|
|
+
|
|
|
+ public Stream<String> names() throws IOException {
|
|
|
+ if(root == null) {
|
|
|
+ return StreamSupport.stream(new PackageSpliterator(), false);
|
|
|
+ } else {
|
|
|
+ return StreamSupport.stream(new PackageSpliterator(root, cloader()), false);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- public static Stream<String> getNameStream(String root, ClassLoader loader) throws IOException {
|
|
|
- return StreamSupport.stream(new PackageSpliterator(root, loader), false);
|
|
|
+
|
|
|
+ private ClassLoader cloader() {
|
|
|
+ return ValueUtils.or(loader, ClassUtils.loader());
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private static final class PackageSpliterator extends ForwardSpliterator<String> {
|
|
|
|
|
|
private final String nroot;
|
|
|
private final String croot;
|
|
|
- private final Enumeration<URL> resources;
|
|
|
+ private final Iterator<String> resources;
|
|
|
|
|
|
private Spliterator<String> current;
|
|
|
+
|
|
|
+ public PackageSpliterator() {
|
|
|
+ this.nroot = "";
|
|
|
+ this.croot = "";
|
|
|
+ this.resources = Collections.singleton(PackageScanner.class.getProtectionDomain().getCodeSource().getLocation().getPath()+"!").iterator();
|
|
|
+ this.current = Collections.<String>emptyList().spliterator();
|
|
|
+ }
|
|
|
|
|
|
public PackageSpliterator(String root, ClassLoader loader) throws IOException {
|
|
|
- this.nroot = root + ".";
|
|
|
+ this.nroot = (root==null)? ":" : (root + ".");
|
|
|
this.croot = StringUtils.replace(root, '.', '/');
|
|
|
- this.resources = loader.getResources(croot);
|
|
|
+ this.resources = EnumerationUtils.map(loader.getResources(croot), URL::getPath);
|
|
|
this.current = Collections.<String>emptyList().spliterator();
|
|
|
}
|
|
|
|
|
|
@@ -90,11 +99,10 @@ public final class PackageScanner {
|
|
|
}
|
|
|
|
|
|
private boolean tryAdvanceResource(Consumer<? super String> action) {
|
|
|
- if(!resources.hasMoreElements()) {
|
|
|
+ if(!resources.hasNext()) {
|
|
|
return false;
|
|
|
}
|
|
|
- String str = resources.nextElement().getPath();
|
|
|
- String uri = URIUtils.decodeURL(str, Charset.defaultCharset());
|
|
|
+ String uri = URIUtils.decodeURL(resources.next(), Charset.defaultCharset());
|
|
|
File file = new File(uri);
|
|
|
current = file.isDirectory() ? forDirectory(file.toPath()) : forJAR(uri);
|
|
|
return current.tryAdvance(action);
|
|
|
@@ -103,6 +111,9 @@ public final class PackageScanner {
|
|
|
private Spliterator<String> forJAR(String uri) {
|
|
|
try {
|
|
|
String jar = StringUtils.between(uri, ":", "!");
|
|
|
+ if(jar.isEmpty()) {
|
|
|
+ jar = uri;
|
|
|
+ }
|
|
|
return new JARSpliterator(ZipReader.openJAR(new FileInputStream(jar)));
|
|
|
} catch (IOException cause) {
|
|
|
throw ExceptionUtils.rethrow(cause);
|
|
|
@@ -113,8 +124,8 @@ public final class PackageScanner {
|
|
|
try {
|
|
|
return Files
|
|
|
.walk(file)
|
|
|
- .filter(PackageScanner::isclass)
|
|
|
- .map(s -> nroot + tocname(file.relativize(s)))
|
|
|
+ .filter(PackageScanner::isClass)
|
|
|
+ .map(s -> nroot + toClassName(file.relativize(s)))
|
|
|
.spliterator();
|
|
|
} catch(IOException cause) {
|
|
|
throw ExceptionUtils.rethrow(cause);
|
|
|
@@ -136,7 +147,7 @@ public final class PackageScanner {
|
|
|
while(null != (entry = istream.getNextJarEntry())) {
|
|
|
String ename = entry.getName();
|
|
|
if( ename.endsWith(".class") && ename.startsWith(croot) ) {
|
|
|
- action.accept(tocname(ename));
|
|
|
+ action.accept(toClassName(ename));
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
@@ -151,7 +162,7 @@ public final class PackageScanner {
|
|
|
|
|
|
}
|
|
|
|
|
|
- private static boolean isclass(Path path) {
|
|
|
+ private static boolean isClass(Path path) {
|
|
|
return path.getFileName().toString().endsWith(".class");
|
|
|
}
|
|
|
|
|
|
@@ -159,17 +170,17 @@ public final class PackageScanner {
|
|
|
return name -> {
|
|
|
try {
|
|
|
return Class.forName(name, false, loader);
|
|
|
- } catch(ClassNotFoundException cause) {
|
|
|
- throw ExceptionUtils.rethrow(cause);
|
|
|
+ } catch(ClassNotFoundException | NoClassDefFoundError cause) {
|
|
|
+ return null;
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- private static String tocname(Path path) {
|
|
|
- return tocname(path.toString());
|
|
|
+ private static String toClassName(Path path) {
|
|
|
+ return toClassName(path.toString());
|
|
|
}
|
|
|
|
|
|
- private static String tocname(String path) {
|
|
|
+ private static String toClassName(String path) {
|
|
|
String fname = StringUtils.removeSuffix(path, ".class");
|
|
|
return StringUtils.replace(StringUtils.replace(fname, '/', '.'), '\\', '.');
|
|
|
}
|