Navigation Menu

Skip to content

Commit

Permalink
Refactored Scanner.onJarEntry() and onResourceFile() to scanJarFile()…
Browse files Browse the repository at this point in the history
… and scanDirectory().

Advantages discussed sofar:

1. scanJarFile(JarFile) and scanDirectory(File) leaves no room for ambiguity. Questions like "are manifest files, directory resources filtered out?" won't be asked because it's up to subclasses.

2. scanDirectory() can potentially be more efficient when searching for a particular package for example. You wouldn't even need to recurse into c/g/common if you are only interested in c/g/foo.

3. scanJarFile() can use JarFile.entries() and loop over. It's an existing API that we don't need to provide an alternative way to traverse through Jar file entries.

4. scanDirectory() can use either Files.fileTreeTraverser() or java.nio.file.Files.newDirectoryStream(). So we shouldn't expect too much boilerplate duplication.

5. If a client doesn't like to silently ignore unreadable sub directory, it doesn't have to.

Disadvantages include inconvenience for the subclass compared to implementing the bare-bone onJarEntry() and onResourceFile().
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=91324193
  • Loading branch information
benyu authored and cgdecker committed Apr 16, 2015
1 parent c33a718 commit 45d5463
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 45 deletions.
32 changes: 32 additions & 0 deletions guava-tests/test/com/google/common/reflect/ClassPathTest.java
Expand Up @@ -21,6 +21,7 @@
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Closer;
import com.google.common.io.Files;
import com.google.common.io.Resources;
import com.google.common.reflect.ClassPath.ClassInfo;
import com.google.common.reflect.ClassPath.ResourceInfo;
Expand All @@ -37,10 +38,16 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
Expand Down Expand Up @@ -314,6 +321,12 @@ public void testNulls() throws IOException {
.testAllPublicInstanceMethods(ClassPath.from(getClass().getClassLoader()));
}

public void testResourceScanner() throws IOException {
ResourceScanner scanner = new ResourceScanner();
scanner.scan(ClassLoader.getSystemClassLoader());
assertThat(scanner.resources).contains("com/google/common/reflect/ClassPathTest.class");
}

private static ClassPath.ClassInfo findClass(
Iterable<ClassPath.ClassInfo> classes, Class<?> cls) {
for (ClassPath.ClassInfo classInfo : classes) {
Expand Down Expand Up @@ -376,4 +389,23 @@ private static Manifest manifest(String content) throws IOException {
private static File fullpath(String path) {
return new File(new File(path).toURI());
}

private static class ResourceScanner extends ClassPath.Scanner {
final Set<String> resources = new HashSet<String>();

@Override protected void scanDirectory(ClassLoader loader, File root) throws IOException {
URI base = root.toURI();
for (File entry : Files.fileTreeTraverser().preOrderTraversal(root)) {
String resourceName = new File(base.relativize(entry.toURI()).getPath()).getPath();
resources.add(resourceName);
}
}

@Override protected void scanJarFile(ClassLoader loader, JarFile file) throws IOException {
Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) {
resources.add(entries.nextElement().getName());
}
}
}
}
85 changes: 40 additions & 45 deletions guava/src/com/google/common/reflect/ClassPath.java
Expand Up @@ -285,7 +285,8 @@ public Class<?> load() {

/**
* Abstract class that scans through the class path represented by a {@link ClassLoader} and calls
* {@link #onResourceFile} and {@link #onJarEntry} for each resource found on the class path.
* {@link #scanDirectory} and {@link #scanJarFile} for directories and jar files on the class path
* respectively.
*/
abstract static class Scanner {

Expand All @@ -299,13 +300,12 @@ public final void scan(ClassLoader classloader) throws IOException {
}
}

/** Called when a resource file is scanned. */
protected abstract void onResourceFile(ClassLoader loader, String packagePath, File f)
/** Called when a directory is scanned for resource files. */
protected abstract void scanDirectory(ClassLoader loader, File directory)
throws IOException;

/** Called when a resource entry in a jar file is scanned. */
protected abstract void onJarEntry(ClassLoader loader, JarFile file, JarEntry entry)
throws IOException;
/** Called when a jar file is scanned for resource entries. */
protected abstract void scanJarFile(ClassLoader loader, JarFile file) throws IOException;

@VisibleForTesting final void scan(File file, ClassLoader classloader) throws IOException {
if (scannedUris.add(file.getCanonicalFile())) {
Expand All @@ -318,34 +318,12 @@ private void scanFrom(File file, ClassLoader classloader) throws IOException {
return;
}
if (file.isDirectory()) {
scanDirectory(file, classloader);
scanDirectory(classloader, file);
} else {
scanJar(file, classloader);
}
}

private void scanDirectory(File directory, ClassLoader classloader) throws IOException {
scanDirectory(directory, classloader, "");
}

private void scanDirectory(
File directory, ClassLoader classloader, String packagePrefix) throws IOException {
File[] files = directory.listFiles();
if (files == null) {
logger.warning("Cannot read directory " + directory);
// IO error, just skip the directory
return;
}
for (File f : files) {
String name = f.getName();
if (f.isDirectory()) {
scanDirectory(f, classloader, packagePrefix + name + "/");
} else {
onResourceFile(classloader, packagePrefix, f);
}
}
}

private void scanJar(File file, ClassLoader classloader) throws IOException {
JarFile jarFile;
try {
Expand All @@ -358,14 +336,7 @@ private void scanJar(File file, ClassLoader classloader) throws IOException {
for (File path : getClassPathFromManifest(file, jarFile.getManifest())) {
scan(path, classloader);
}
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.isDirectory() || entry.getName().equals(JarFile.MANIFEST_NAME)) {
continue;
}
onJarEntry(classloader, jarFile, entry);
}
scanJarFile(classloader, jarFile);
} finally {
try {
jarFile.close();
Expand Down Expand Up @@ -452,17 +423,41 @@ ImmutableSet<ResourceInfo> getResources() {
return builder.build();
}

@Override protected void onResourceFile(
ClassLoader classloader, String packagePath, File file) {
String resourceName = packagePath + file.getName();
if (!resourceName.equals(JarFile.MANIFEST_NAME)) {
resources.get(classloader).add(resourceName);
@Override protected void scanJarFile(ClassLoader classloader, JarFile file) {
Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.isDirectory() || entry.getName().equals(JarFile.MANIFEST_NAME)) {
continue;
}
resources.get(classloader).add(entry.getName());
}
}

@Override protected void onJarEntry(ClassLoader classloader, JarFile file, JarEntry entry) {
String resourceName = entry.getName();
resources.get(classloader).add(resourceName);
@Override protected void scanDirectory(ClassLoader classloader, File directory)
throws IOException {
scanDirectory(directory, classloader, "");
}

private void scanDirectory(
File directory, ClassLoader classloader, String packagePrefix) throws IOException {
File[] files = directory.listFiles();
if (files == null) {
logger.warning("Cannot read directory " + directory);
// IO error, just skip the directory
return;
}
for (File f : files) {
String name = f.getName();
if (f.isDirectory()) {
scanDirectory(f, classloader, packagePrefix + name + "/");
} else {
String resourceName = packagePrefix + name;
if (!resourceName.equals(JarFile.MANIFEST_NAME)) {
resources.get(classloader).add(resourceName);
}
}
}
}
}

Expand Down

0 comments on commit 45d5463

Please sign in to comment.