/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2009 Jaspersoft Corporation. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.jasperreports.engine.util;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* Provides methods for resource resolution via class loaders or URL stream handlers.
*
* @author Lucian Chirita (lucianc@users.sourceforge.net)
* @version $Id: JRResourcesUtil.java 3717 2010-04-09 10:01:33Z teodord $
*/
public final class JRResourcesUtil
{
private static FileResolver globalFileResolver;
private static ThreadLocalStack localFileResolverStack = new ThreadLocalStack();
private static URLStreamHandlerFactory globalURLHandlerFactory;
private static ThreadLocalStack localURLHandlerFactoryStack = new ThreadLocalStack();
private static ClassLoader globalClassLoader;
private static ThreadLocalStack localClassLoaderStack = new ThreadLocalStack();
/**
* Tries to parse a <code>String</code> as an URL.
*
* @param spec the <code>String</code> to parse
* @param urlHandlerFactory an URL stream handler factory to use
* @return an URL if the parsing is successful
* @see #getURLHandler(String, URLStreamHandlerFactory)
* @see #getURLHandlerFactory(URLStreamHandlerFactory)
*/
public static URL createURL(String spec, URLStreamHandlerFactory urlHandlerFactory)
{
URLStreamHandler handler = getURLHandler(spec, urlHandlerFactory);
URL url;
try
{
if (handler == null)
{
url = new URL(spec);
}
else
{
url = new URL(null, spec, handler);
}
}
catch (MalformedURLException e)
{
url = null;
}
return url;
}
/**
* Returns an URL stream handler for an URL specified as a <code>String</code>.
*
* @param spec the <code>String</code> to parse as an URL
* @param urlHandlerFact an URL stream handler factory
* @return an URL stream handler if one was found for the protocol of the URL
* @see #getURLHandlerFactory(URLStreamHandlerFactory)
*/
public static URLStreamHandler getURLHandler(String spec, URLStreamHandlerFactory urlHandlerFact)
{
URLStreamHandlerFactory urlHandlerFactory = getURLHandlerFactory(urlHandlerFact);
URLStreamHandler handler = null;
if (urlHandlerFactory != null)
{
String protocol = getURLProtocol(spec);
if (protocol != null)
{
handler = urlHandlerFactory.createURLStreamHandler(protocol);
}
}
return handler;
}
private static String getURLProtocol(String urlSpec)
{
String protocol = null;
String spec = urlSpec.trim();
int colon = spec.indexOf(':');
if (colon > 0)
{
String proto = spec.substring(0, colon);
if (protocolValid(proto))
{
protocol = proto;
}
}
return protocol;
}
private static boolean protocolValid(String protocol)
{
int length = protocol.length();
if (length < 1)
{
return false;
}
if (!Character.isLetter(protocol.charAt(0)))
{
return false;
}
for (int i = 1; i < length; ++i)
{
char c = protocol.charAt(i);
if (!(Character.isLetterOrDigit(c) || c == '+' || c == '-' || c == '.'))
{
return false;
}
}
return true;
}
/**
* Returns a file resolver.
* <p/>
* The first not null value from the following is returned:
* <ul>
* <li>the value of the parameter</li>
* <li>the thread local file resolver</li>
* <li>the global file resolver</li>
* </ul>
*
* @param fileRes a file resolver that will be returned if not null
* @return a file resolver
* @see #setGlobalFileResolver(FileResolver)
* @see #setThreadFileResolver(FileResolver)
*/
public static FileResolver getFileResolver(FileResolver fileRes)
{
FileResolver fileResolver = fileRes;
if (fileResolver == null)
{
fileResolver = getThreadFileResolver();
if (fileResolver == null)
{
fileResolver = globalFileResolver;
}
}
return fileResolver;
}
/**
* Returns the global file resolver.
*
* @return the global file resolver
* @see #setGlobalFileResolver(FileResolver)
*/
public static FileResolver getGlobalFileResolver()
{
return globalFileResolver;
}
/**
* Returns the thread local file resolver.
*
* @return the thread local file resolver.
* @see #setThreadFileResolver(FileResolver)
*/
public static FileResolver getThreadFileResolver()
{
return (FileResolver) localFileResolverStack.top();
}
/**
* Sets the thread local file resolver.
*
* @param fileResolver a file resolver.
* @see #getFileResolver(FileResolver)
* @see #resetThreadFileResolver()
*/
public static void setThreadFileResolver(FileResolver fileResolver)
{
localFileResolverStack.push(fileResolver);
}
/**
* Resets the the thread local file resolver to its previous value.
*/
public static void resetThreadFileResolver()
{
localFileResolverStack.pop();
}
/**
* Sets a global file resolver to be used for file resolution.
*
* @param fileResolver the file resolver
* @see #getFileResolver(FileResolver)
*/
public static void setGlobalFileResolver(FileResolver fileResolver)
{
globalFileResolver = fileResolver;
}
/**
* Attempts to find a file using a file resolver.
*
* @param location file name
* @param fileRes a file resolver
* @return the file, if found
*/
public static File resolveFile(String location, FileResolver fileRes)
{
FileResolver fileResolver = getFileResolver(fileRes);
if (fileResolver != null)
{
return fileResolver.resolveFile(location);
}
File file = new File(location);
if (file.exists() && file.isFile())
{
return file;
}
return null;
}
/**
* Returns an URL steam handler factory.
* <p/>
* The first not null value from the following is returned:
* <ul>
* <li>the value of the parameter</li>
* <li>the thread local URL stream handler factory</li>
* <li>the global URL stream handler factory</li>
* </ul>
*
* @param urlHandlerFact an URL steam handler factory that will be returned if not null
* @return an URL steam handler factory
* @see #setGlobalURLHandlerFactory(URLStreamHandlerFactory)
* @see #setThreadURLHandlerFactory(URLStreamHandlerFactory)
*/
public static URLStreamHandlerFactory getURLHandlerFactory(URLStreamHandlerFactory urlHandlerFact)
{
URLStreamHandlerFactory urlHandlerFactory = urlHandlerFact;
if (urlHandlerFactory == null)
{
urlHandlerFactory = getThreadURLStreamHandlerFactory();
if (urlHandlerFactory == null)
{
urlHandlerFactory = globalURLHandlerFactory;
}
}
return urlHandlerFactory;
}
/**
* Returns the global URL stream handler factory.
*
* @return the global URL stream handler factory
* @see #setGlobalURLHandlerFactory(URLStreamHandlerFactory)
*/
public static URLStreamHandlerFactory getGlobalURLStreamHandlerFactory()
{
return globalURLHandlerFactory;
}
/**
* Returns the thread local URL stream handler factory.
*
* @return the thread local URL stream handler factory.
* @see #setThreadURLHandlerFactory(URLStreamHandlerFactory)
*/
public static URLStreamHandlerFactory getThreadURLStreamHandlerFactory()
{
return (URLStreamHandlerFactory) localURLHandlerFactoryStack.top();
}
/**
* Sets the thread local URL stream handler factory.
*
* @param urlHandlerFactory an URL stream handler factory.
* @see #getURLHandlerFactory(URLStreamHandlerFactory)
* @see #resetThreadURLHandlerFactory()
*/
public static void setThreadURLHandlerFactory(URLStreamHandlerFactory urlHandlerFactory)
{
localURLHandlerFactoryStack.push(urlHandlerFactory);
}
/**
* Resets the the thread local URL stream handler factory to its previous value.
*/
public static void resetThreadURLHandlerFactory()
{
localURLHandlerFactoryStack.pop();
}
/**
* Sets a global URL stream handler facotry to be used for resource resolution.
*
* @param urlHandlerFactory the URL stream handler factory
* @see #getURLHandlerFactory(URLStreamHandlerFactory)
*/
public static void setGlobalURLHandlerFactory(URLStreamHandlerFactory urlHandlerFactory)
{
globalURLHandlerFactory = urlHandlerFactory;
}
/**
* Returns a class loader.
* <p/>
* The first not null value from the following is returned:
* <ul>
* <li>the value of the parameter</li>
* <li>the thread local class loader</li>
* <li>the global class loader</li>
* </ul>
*
* @param clsLoader a class loader that will be returned if not null
* @return a class loader.
* @see #setGlobalClassLoader(ClassLoader)
* @see #setThreadClassLoader(ClassLoader)
*/
public static ClassLoader getClassLoader(ClassLoader clsLoader)
{
ClassLoader classLoader = clsLoader;
if (classLoader == null)
{
classLoader = getThreadClassLoader();
if (classLoader == null)
{
classLoader = globalClassLoader;
}
}
return classLoader;
}
/**
* Returns the global class loader.
*
* @return the global class loader.
* @see #setGlobalClassLoader(ClassLoader)
*/
public static ClassLoader getGlobalClassLoader()
{
return globalClassLoader;
}
/**
* Returns the thread local class loader.
*
* @return the thread local class loader.
* @see #setThreadClassLoader(ClassLoader)
*/
public static ClassLoader getThreadClassLoader()
{
return (ClassLoader) localClassLoaderStack.top();
}
/**
* Sets the thread local class loader.
*
* @param classLoader a class loader
* @see #getClassLoader(ClassLoader)
* @see #resetThreadURLHandlerFactory()
*/
public static void setThreadClassLoader(ClassLoader classLoader)
{
localClassLoaderStack.push(classLoader);
}
/**
* Resets the the thread local class loader to its previous value.
*/
public static void resetClassLoader()
{
localClassLoaderStack.pop();
}
/**
* Sets a global class loader to be used for resource resolution.
*
* @param classLoader the class loader
* @see #getClassLoader(ClassLoader)
*/
public static void setGlobalClassLoader(ClassLoader classLoader)
{
globalClassLoader = classLoader;
}
/**
* Attempts to find a resource using a class loader.
* <p/>
* The following sources are tried:
* <ul>
* <li>the class loader returned by {@link #getClassLoader(ClassLoader) <code>getClassLoader(classLoader)</code>}</li>
* <li>the context class loader</li>
* <li><code>clazz.getClassLoader()</code></li>
* <li><code>clazz.getResource()</code></li>
* </ul>
*
* @param location the resource name
* @param clsLoader a class loader
* @param clazz a class
* @return the resource URL if found
*/
public static URL findClassLoaderResource(String location, ClassLoader clsLoader, Class clazz)
{
ClassLoader classLoader = getClassLoader(clsLoader);
URL url = null;
if (classLoader != null)
{
url = classLoader.getResource(location);
}
if (url == null)
{
classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader != null)
{
url = classLoader.getResource(location);
}
if (url == null)
{
classLoader = clazz.getClassLoader();
if (classLoader == null)
{
url = clazz.getResource("/" + location);
}
else
{
url = classLoader.getResource(location);
}
}
}
return url;
}
/**
* Loads a resource bundle for a given base name and locale.
*
* <p>
* This methods calls {@link #loadResourceBundle(String, Locale, ClassLoader)} with a null classloader.
* </p>
*
* @param baseName the base name
* @param locale the locale
* @return the resource bundle for the given base name and locale
*/
public static ResourceBundle loadResourceBundle(String baseName, Locale locale)
{
return loadResourceBundle(baseName, locale, null);
}
/**
* Loads a resource bundle for a given base name and locale.
*
* <p>
* The method attempts to load the resource bundle using the following classloaders
* (and stops at the first successful attempt):
* <ul>
* <li>the class loader returned by {@link #getClassLoader(ClassLoader) <code>getClassLoader(classLoader)</code>}</li>
* <li>the context class loader</li>
* <li><code>JRClassLoader.class.getClassLoader()</code></li>
* </ul>
* </p>
*
* @param baseName the base name
* @param locale the locale
* @param clsLoader
* @return the resource bundle for the given base name and locale
* @see ResourceBundle#getBundle(String, Locale, ClassLoader)
*/
public static ResourceBundle loadResourceBundle(String baseName, Locale locale, ClassLoader clsLoader)
{
ResourceBundle resourceBundle = null;
ClassLoader classLoader = getClassLoader(clsLoader);
if (classLoader != null)
{
try
{
resourceBundle = ResourceBundle.getBundle(baseName, locale, classLoader);
}
catch (MissingResourceException e)
{
}
}
if (resourceBundle == null)
{
classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader != null)
{
try
{
resourceBundle = ResourceBundle.getBundle(baseName, locale, classLoader);
}
catch (MissingResourceException e)
{
}
}
}
if (resourceBundle == null)
{
classLoader = JRClassLoader.class.getClassLoader();
if (classLoader == null)
{
resourceBundle = ResourceBundle.getBundle(baseName, locale);
}
else
{
resourceBundle = ResourceBundle.getBundle(baseName, locale, classLoader);
}
}
return resourceBundle;
}
private JRResourcesUtil()
{
}
}