/*
* Copyright (c) 2002-2003 by OpenSymphony
* All rights reserved.
*/
package com.opensymphony.xwork2.util.finder;
import org.apache.commons.lang.StringUtils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
/**
* @author David Blevins
* @version $Rev$ $Date$
*/
public class ResourceFinder {
private static final Logger LOG = LoggerFactory.getLogger(ResourceFinder.class);
private final URL[] urls;
private final String path;
private final ClassLoaderInterface classLoaderInterface;
private final List<String> resourcesNotLoaded = new ArrayList<String>();
public ResourceFinder(URL... urls) {
this(null, new ClassLoaderInterfaceDelegate(Thread.currentThread().getContextClassLoader()), urls);
}
public ResourceFinder(String path) {
this(path, new ClassLoaderInterfaceDelegate(Thread.currentThread().getContextClassLoader()), null);
}
public ResourceFinder(String path, URL... urls) {
this(path, new ClassLoaderInterfaceDelegate(Thread.currentThread().getContextClassLoader()), urls);
}
public ResourceFinder(String path, ClassLoaderInterface classLoaderInterface) {
this(path, classLoaderInterface, null);
}
public ResourceFinder(String path, ClassLoaderInterface classLoaderInterface, URL... urls) {
if (path == null){
path = "";
} else if (path.length() > 0 && !path.endsWith("/")) {
path += "/";
}
this.path = path;
this.classLoaderInterface = classLoaderInterface == null ? new ClassLoaderInterfaceDelegate(Thread.currentThread().getContextClassLoader()) : classLoaderInterface ;
for (int i = 0; urls != null && i < urls.length; i++) {
URL url = urls[i];
if (url == null || isDirectory(url) || "jar".equals(url.getProtocol())) {
continue;
}
try {
urls[i] = new URL("jar", "", -1, url.toString() + "!/");
} catch (MalformedURLException e) {
}
}
this.urls = (urls == null || urls.length == 0)? null : urls;
}
private static boolean isDirectory(URL url) {
String file = url.getFile();
return (file.length() > 0 && file.charAt(file.length() - 1) == '/');
}
/**
* Returns a list of resources that could not be loaded in the last invoked findAvailable* or
* mapAvailable* methods.
* <p/>
* The list will only contain entries of resources that match the requirements
* of the last invoked findAvailable* or mapAvailable* methods, but were unable to be
* loaded and included in their results.
* <p/>
* The list returned is unmodifiable and the results of this method will change
* after each invocation of a findAvailable* or mapAvailable* methods.
* <p/>
* This method is not thread safe.
*/
public List<String> getResourcesNotLoaded() {
return Collections.unmodifiableList(resourcesNotLoaded);
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
//
// Find
//
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
public URL find(String uri) throws IOException {
String fullUri = path + uri;
return getResource(fullUri);
}
public List<URL> findAll(String uri) throws IOException {
String fullUri = path + uri;
Enumeration<URL> resources = getResources(fullUri);
List<URL> list = new ArrayList<URL>();
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
list.add(url);
}
return list;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
//
// Find String
//
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/**
* Reads the contents of the URL as a {@link String}'s and returns it.
*
* @param uri
* @return a stringified content of a resource
* @throws IOException if a resource pointed out by the uri param could not be find
* @see ClassLoader#getResource(String)
*/
public String findString(String uri) throws IOException {
String fullUri = path + uri;
URL resource = getResource(fullUri);
if (resource == null) {
throw new IOException("Could not find a resource in : " + fullUri);
}
return readContents(resource);
}
/**
* Reads the contents of the found URLs as a list of {@link String}'s and returns them.
*
* @param uri
* @return a list of the content of each resource URL found
* @throws IOException if any of the found URLs are unable to be read.
*/
public List<String> findAllStrings(String uri) throws IOException {
String fulluri = path + uri;
List<String> strings = new ArrayList<String>();
Enumeration<URL> resources = getResources(fulluri);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
String string = readContents(url);
strings.add(string);
}
return strings;
}
/**
* Reads the contents of the found URLs as a Strings and returns them.
* Individual URLs that cannot be read are skipped and added to the
* list of 'resourcesNotLoaded'
*
* @param uri
* @return a list of the content of each resource URL found
* @throws IOException if classLoader.getResources throws an exception
*/
public List<String> findAvailableStrings(String uri) throws IOException {
resourcesNotLoaded.clear();
String fulluri = path + uri;
List<String> strings = new ArrayList<String>();
Enumeration<URL> resources = getResources(fulluri);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
try {
String string = readContents(url);
strings.add(string);
} catch (IOException notAvailable) {
resourcesNotLoaded.add(url.toExternalForm());
}
}
return strings;
}
/**
* Reads the contents of all non-directory URLs immediately under the specified
* location and returns them in a map keyed by the file name.
* <p/>
* Any URLs that cannot be read will cause an exception to be thrown.
* <p/>
* Example classpath:
* <p/>
* META-INF/serializables/one
* META-INF/serializables/two
* META-INF/serializables/three
* META-INF/serializables/four/foo.txt
* <p/>
* ResourceFinder finder = new ResourceFinder("META-INF/");
* Map map = finder.mapAvailableStrings("serializables");
* map.contains("one"); // true
* map.contains("two"); // true
* map.contains("three"); // true
* map.contains("four"); // false
*
* @param uri
* @return a list of the content of each resource URL found
* @throws IOException if any of the urls cannot be read
*/
public Map<String, String> mapAllStrings(String uri) throws IOException {
Map<String, String> strings = new HashMap<String, String>();
Map<String, URL> resourcesMap = getResourcesMap(uri);
for (Map.Entry<String, URL> entry : resourcesMap.entrySet()) {
String name = entry.getKey();
URL url = entry.getValue();
String value = readContents(url);
strings.