/* * Copyright (C) 2014 Civilian Framework. * * Licensed under the Civilian License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.civilian-framework.org/license.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.civilian.util; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import javax.servlet.ServletContext; /** * ResourceLoader provides a common interface for loading * resources or ordinary files. */ public abstract class ResourceLoader { /** * Holds a Builder object to construct resource loaders. */ public static final Builder builder = new Builder(); /** * Allows to construct resource loaders. */ public static class Builder { /** * Creates a resource loader who uses * ClassLoader.getResource to load resources. */ public ResourceLoader forClassLoader(ClassLoader classLoader) { return new ClassLoaderResLoader(classLoader); } /** * Creates a resource loader who uses * the system ClassLoader to load resources. */ public ResourceLoader forSystemClassLoader() { return forClassLoader(ClassLoader.getSystemClassLoader()); } /** * Creates a resource loader who uses * the ClassLoader of this class to load resources. */ public ResourceLoader forClassLoader() { return forClassLoader(getClass().getClassLoader()); } /** * Creates a resource loader who uses * Class.getResource to load resources. */ public ResourceLoader forClass(Class<?> c) { return new ClassResLoader(c); } /** * Creates a resource loader who uses * ServletContext.getResource to load resources. */ public ResourceLoader forSerlvetContext(ServletContext context) { return new ServletContextResLoader(context); } /** * Creates a resource loader who * loads resources from the current directory. */ public ResourceLoader forCurrentDirectory() { return forDirectory(new File(".")); } /** * Creates a resource loader who * loads resources from the given directory. */ public ResourceLoader forDirectory(File file) { File directory = file.isDirectory() ? file : file.getParentFile(); return new DirectoryResLoader(directory); } /** * Creates a resource loader who * loads resources from the given directory. */ public ResourceLoader forFile(File file) { return new FileResLoader(file); } /** * Creates a resource loader who * returns the string content if the resource name * matches the specified name. */ public ResourceLoader forString(String name, String content) { return new StringResLoader(name, content); } /** * Creates a chain of resource loaders. If a loader * cannot load a resource, the next loader in the chain * is asked. */ public ResourceLoader chain(ResourceLoader... loaders) { return new ChainedLoader(loaders); } /** * Returns a ResourceLoader which constantly returns null. */ public ResourceLoader empty() { return new EmptyResLoader(); } } /** * Returns a resource loader which internally uses this * ResourceLoader but throws an IllegalArgumentException * if the resource cannot be found. */ public ResourceLoader required() { return new RequiredResLoader(this); } /** * Returns the URL of a resource with the specified name or null * if the resource could not be found. * @param name the resource name */ public abstract URL getResourceUrl(String name); /** * Returns URL of all resources with the specified name */ public abstract Enumeration<URL> getResourceUrls(String name) throws IOException; /** * Returns an InputStream for a resource with the specified name or null * if the resource could not be found. * @param name the resource name */ public abstract InputStream getResourceAsStream(String name); /** * Returns a Reader for a resource with the specified name or null * if the resource could not be found. * @param name the resource name * @param charset the encoding of the resource or null * if the system encoding should be used */ public Reader getResourceAsReader(String name, String charset) throws UnsupportedEncodingException { InputStream in = getResourceAsStream(name); if (in == null) return null; else if (charset == null) return new InputStreamReader(in); else return new InputStreamReader(in, charset); } } /** * A ResourceLoader based on a ClassLoader. */ class ClassLoaderResLoader extends ResourceLoader { public ClassLoaderResLoader(ClassLoader classLoader) { classLoader_ = Check.notNull(classLoader, "classLoader"); } @Override public URL getResourceUrl(String name) { return classLoader_.getResource(name); } @Override public Enumeration<URL> getResourceUrls(String name) throws IOException { return classLoader_.getResources(name); } @Override public InputStream getResourceAsStream(String name) { return classLoader_.getResourceAsStream(name); } private ClassLoader classLoader_; } /** * A ResourceLoader based on a Class. */ class ClassResLoader extends ResourceLoader { public ClassResLoader(Class<?> c) { class_ = Check.notNull(c, "class"); } @Override public URL getResourceUrl(String name) { return class_.getResource(name); } @Override public Enumeration<URL> getResourceUrls(String name) throws IOException { return Iterators.asEnumeration(Iterators.forValue(getResourceUrl(name))); } @Override public InputStream getResourceAsStream(String name) { return class_.getResourceAsStream(name); } private Class<?> class_; } /** * A ResourceLoader based on a ServletContext. */ class ServletContextResLoader extends ResourceLoader { public ServletContextResLoader(ServletContext servletContext) { servletContext_ = Check.notNull(servletContext, "servletContext"); } @Override public URL getResourceUrl(String name) { try { return servletContext_.getResource(name); } catch (MalformedURLException e) { throw new IllegalStateException("cannot create resource URL for '" + name + "'", e); } } @Override public Enumeration<URL> getResourceUrls(String name) throws IOException { final Iterator<String> paths = servletContext_.getResourcePaths(name).iterator(); return new Enumeration<URL>() { @Override public boolean hasMoreElements() { return paths.hasNext(); } @Override public URL nextElement() { return getResourceUrl(paths.next()); } }; } @Override public InputStream getResourceAsStream(String name) { return servletContext_.getResourceAsStream(name); } private ServletContext servletContext_; } /** * A ResourceLoader based on a directory. */ class DirectoryResLoader extends ResourceLoader { public DirectoryResLoader(File directory) { directory_ = Check.notNull(directory, "directory"); } @Override public URL getResourceUrl(String name) { try { File file = getResourceFile(name); return file == null ? null : file.toURI().toURL(); } catch (MalformedURLException e) { throw new IllegalStateException("cannot create resource URL for '" + name + "'", e); } } @Override public Enumeration<URL> getResourceUrls(String name) throws IOException { URL url = getResourceUrl(name); return Iterators.asEnumeration(Iterators.forValue(url)); } @Override public InputStream getResourceAsStream(String name) { try { File file = getResourceFile(name); if (file != null) return new FileInputStream(file); } catch (FileNotFoundException e) { // ignore silently } return null; } private File getResourceFile(String name) { File file = new File(directory_, name); return file.exists() ? file : null; } private File directory_; } /** * A ResourceLoader based on a single file. */ class FileResLoader extends ResourceLoader { public FileResLoader(File file) { file_ = Check.notNull(file, "file"); } @Override public URL getResourceUrl(String name) { try { File file = getResourceFile(name); return file == null ? null : file.toURI().toURL(); } catch (MalformedURLException e) { throw new IllegalStateException("cannot create resource URL for '" + name + "'", e); } } @Override public Enumeration<URL> getResourceUrls(String name) throws IOException { URL url = getResourceUrl(name); return Iterators.asEnumeration(Iterators.forValue(url)); } @Override public InputStream getResourceAsStream(String name) { try { File file = getResourceFile(name); if (file != null) return new FileInputStream(file); } catch (FileNotFoundException e) { // ignore silently } return null; } private File getResourceFile(String name) { return file_.exists() && file_.getName().equals(name) ? file_ : null; } private File file_; } class ChainedLoader extends ResourceLoader { public ChainedLoader(ResourceLoader[] loaders) { if (loaders == null) throw new IllegalArgumentException("loaders null"); loaders_ = loaders; } @Override public URL getResourceUrl(String name) { for (int i=0; i<loaders_.length; i++) { URL url = loaders_[i].getResourceUrl(name); if (url != null) return url; } return null; } @Override public Enumeration<URL> getResourceUrls(String name) throws IOException { ArrayList<URL> urls = new ArrayList<>(); for (int i=0; i<loaders_.length; i++) Iterators.addAll(urls, loaders_[i].getResourceUrls(name)); return Iterators.asEnumeration(urls.iterator()); } @Override public InputStream getResourceAsStream(String name) { for (int i=0; i<loaders_.length; i++) { InputStream in = loaders_[i].getResourceAsStream(name); if (in != null) return in; } return null; } private ResourceLoader[] loaders_; } class StringResLoader extends ResourceLoader { public StringResLoader(String name, String content) { name_ = name; content_ = content; } @Override public URL getResourceUrl(String name) { throw new UnsupportedOperationException(); } @Override public Enumeration<URL> getResourceUrls(String name) throws IOException { throw new UnsupportedOperationException(); } @Override public InputStream getResourceAsStream(String name) { return name_.equals(name) ? new ByteArrayInputStream(content_.getBytes()) : null; } @Override public Reader getResourceAsReader(String name, String charset) { return name_.equals(name) ? new StringReader(content_) : null; } private String name_; private String content_; } class RequiredResLoader extends ResourceLoader { public RequiredResLoader(ResourceLoader loader) { loader_ = loader; } @Override public URL getResourceUrl(String name) { return check(loader_.getResourceUrl(name), name); } @Override public Enumeration<URL> getResourceUrls(String name) throws IOException { return check(loader_.getResourceUrls(name), name); } @Override public InputStream getResourceAsStream(String name) { return check(loader_.getResourceAsStream(name), name); } @Override public Reader getResourceAsReader(String name, String charset) throws UnsupportedEncodingException { return check(loader_.getResourceAsReader(name, charset), name); } private <T> T check(T object, String name) { if (object == null) throw new IllegalArgumentException("resource '" + name + "' not found"); return object; } private ResourceLoader loader_; } class EmptyResLoader extends ResourceLoader { @Override public URL getResourceUrl(String name) { return null; } @Override public Enumeration<URL> getResourceUrls(String name) throws IOException { return Iterators.asEnumeration(Iterators.<URL>empty()); } @Override public InputStream getResourceAsStream(String name) { return null; } }