package org.yajul.scanner;
import org.yajul.collections.CollectionUtil;
import org.yajul.util.ResourceUtil;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Classpath scanner adapted from Seam. Finds a 'tag' resource in the classpath and then scans through
* all the directories and files inside the JAR or directory where the tag resource was found. This can be used to
* iterate through all the classes in a particular class path element to find all classes that implement a particular
* interface, for example.
* <br>
* User: josh
* Date: Mar 6, 2008
* Time: 6:09:45 PM
*
* @author Josh Davis - Adapted to Yajul.
* @author Thomas Heute
* @author Gavin King
* @author Norman Richards
*/
public abstract class AbstractScanner {
private static final Logger log = Logger.getLogger(AbstractScanner.class.getName());
private final String resourceName;
private final ClassLoader classLoader;
private final Set<String> paths = CollectionUtil.newHashSet();
private final boolean useParentDirectory;
private boolean scanned = false;
/**
* Scans everything in the classpath where the specified resource is located.
*
* @param resourceName resource name used to find a directory or archive to scan
*/
public AbstractScanner(String resourceName) {
this(resourceName, Thread.currentThread().getContextClassLoader());
}
public AbstractScanner(String resourceName, ClassLoader classLoader) {
this(resourceName, classLoader, false);
}
public AbstractScanner(String resourceName, ClassLoader classLoader, boolean useParentDirectory) {
this.resourceName = resourceName;
this.classLoader = classLoader;
this.useParentDirectory = useParentDirectory;
}
public ClassLoader getClassLoader() {
return classLoader;
}
public void scan() {
if (scanned)
return;
if (resourceName == null) {
for (URL url : getURLsFromClassLoader()) {
String urlPath = url.getFile();
if (urlPath.endsWith("/")) {
urlPath = urlPath.substring(0, urlPath.length() - 1);
}
addPath(urlPath);
}
} else {
try {
Enumeration<URL> urlEnum = classLoader.getResources(resourceName);
while (urlEnum.hasMoreElements()) {
URL url = urlEnum.nextElement();
String urlPath = ResourceUtil.getPath(url);
if (ResourceUtil.isFileURL(urlPath)) {
urlPath = ResourceUtil.getFilePathFromURL(urlPath);
}
if (urlPath.indexOf('!') > 0) {
urlPath = urlPath.substring(0, urlPath.indexOf('!'));
} else {
File dirOrArchive = new File(urlPath);
// When the tag resource is in the META-INF directory
// we will want to search the parent directory.
if (useParentDirectory &&
resourceName.lastIndexOf('/') > 0) {
//for META-INF/someresource.xyz
dirOrArchive = dirOrArchive.getParentFile();
}
urlPath = dirOrArchive.getParent();
}
addPath(urlPath);
}
} catch (IOException ioe) {
log.log(Level.WARNING, "could not read: " + resourceName, ioe);
return;
}
}
for (String urlPath : paths) {
try {
if (log.isLoggable(Level.FINE))
log.log(Level.FINE, "scanning: " + urlPath);
File file = new File(urlPath);
if (file.isDirectory()) {
handleDirectory(file, null);
} else {
handleArchive(file);
}
} catch (IOException ioe) {
log.log(Level.WARNING, "could not read entries", ioe);
}
}
scanned = true;
}
private void addPath(String urlPath) {
if (log.isLoggable(Level.FINER))
log.log(Level.FINER, "addPath('" + urlPath + "')");
paths.add(urlPath);
}
protected URL[] getURLsFromClassLoader() {
return ((URLClassLoader) classLoader).getURLs();
}
private void handleArchive(File file) throws IOException {
if (log.isLoggable(Level.FINER))
log.log(Level.FINER, "archive: " + file);
ZipFile zip = new ZipFile(file);
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
String name = entry.getName();
if (log.isLoggable(Level.FINER))
log.log(Level.FINER, "found: " + name);
handleItem(name);
}
}
private void handleDirectory(File file, String path) {
if (log.isLoggable(Level.FINER))
log.log(Level.FINER, "directory: " + file);
if (file == null)
throw new IllegalArgumentException("Argument 'file' cannot be null!");
final File[] files = file.listFiles();
if (files == null)
return;
for (File child : files) {
String newPath = path == null ?
child.getName() : path + '/' + child.getName();
if (child.isDirectory()) {
handleDirectory(child, newPath);
} else {
handleItem(newPath);
}
}
}
protected InputStream getResourceAsStream(String name) {
InputStream stream = classLoader.getResourceAsStream(name);
if (stream == null)
log.log(Level.WARNING, "Resource '" + name + "' not found.");
return stream;
}
protected abstract void handleItem(String name);
public Set<String> getPaths() {
return paths;
}
}