/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (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.apache.org/licenses/LICENSE-2.0
*
* 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.apache.wicket.core.util.resource.locator;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import org.apache.wicket.Application;
import org.apache.wicket.util.file.IResourceFinder;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceUtils;
import org.apache.wicket.util.resource.ResourceUtils.PathLocale;
import org.apache.wicket.core.util.resource.UrlResourceStream;
import org.apache.wicket.util.string.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Locate Wicket resource.
* <p>
* Contains the logic to locate a resource based on a path, a style (see
* {@link org.apache.wicket.Session}), a locale and an extension string. The full filename will be
* built like: <path>_<style>_<locale>.<extension>.
* <p>
* Resource matches will be attempted in the following order:
* <ol>
* <li>1. <path>_<style>_<locale>.<extension></li>
* <li>2. <path>_<locale>.<extension></li>
* <li>3. <path>_<style>.<extension></li>
* <li>4. <path>.<extension></li>
* </ol>
* <p>
* Locales may contain a language, a country and a region or variant. Combinations of these
* components will be attempted in the following order:
* <ol>
* <li>locale.toString() see javadoc for Locale for more details</li>
* <li><language>_<country></li>
* <li><language></li>
* </ol>
*
* @author Juergen Donnerstag
* @author Jonathan Locke
*/
public class ResourceStreamLocator implements IResourceStreamLocator
{
/** Logging */
private static final Logger log = LoggerFactory.getLogger(ResourceStreamLocator.class);
private static final Iterable<String> NO_EXTENSIONS = new ArrayList<String>(0);
/** If null, the application registered finder will be used */
private IResourceFinder finder;
/**
* Constructor
*/
public ResourceStreamLocator()
{
}
/**
* Constructor
*
* @param finder
* resource finder
*/
public ResourceStreamLocator(final IResourceFinder finder)
{
this.finder = finder;
}
/**
*
* @see org.apache.wicket.core.util.resource.locator.IResourceStreamLocator#locate(java.lang.Class,
* java.lang.String)
*/
@Override
public IResourceStream locate(final Class<?> clazz, final String path)
{
// First try with the resource finder registered with the application
// (allows for markup reloading)
IResourceStream stream = locateByResourceFinder(clazz, path);
if (stream != null)
{
return stream;
}
// Then search the resource on the classpath
stream = locateByClassLoader(clazz, path);
if (stream != null)
{
return stream;
}
return null;
}
/**
*
* @see org.apache.wicket.core.util.resource.locator.IResourceStreamLocator#locate(java.lang.Class,
* java.lang.String, java.lang.String, java.lang.String, java.util.Locale,
* java.lang.String, boolean)
*/
@Override
public IResourceStream locate(final Class<?> clazz, String path, final String style,
final String variation, Locale locale, final String extension, final boolean strict)
{
// If path contains a locale, than it'll replace the locale provided to this method
PathLocale data = ResourceUtils.getLocaleFromFilename(path);
if ((data != null) && (data.locale != null))
{
path = data.path;
locale = data.locale;
}
// Try the various combinations of style, locale and extension to find the resource.
ResourceNameIterator iter = newResourceNameIterator(path, locale, style, variation,
extension, strict);
while (iter.hasNext())
{
String newPath = iter.next();
IResourceStream stream = locate(clazz, newPath);
if (stream != null)
{
stream.setLocale(iter.getLocale());
stream.setStyle(iter.getStyle());
stream.setVariation(iter.getVariation());
return stream;
}
}
return null;
}
/**
* Search the the resource my means of the various classloaders available
*
* @param clazz
* @param path
* @return resource stream
*/
protected IResourceStream locateByClassLoader(final Class<?> clazz, final String path)
{
IResourceStream resourceStream = null;
if (clazz != null)
{
resourceStream = getResourceStream(clazz.getClassLoader(), path);
if (resourceStream != null)
{
return resourceStream;
}
}
// use context classloader when no specific classloader is set
// (package resources for instance)
resourceStream = getResourceStream(Thread.currentThread().getContextClassLoader(), path);
if (resourceStream != null)
{
return resourceStream;
}
// use Wicket classloader when no specific classloader is set
resourceStream = getResourceStream(getClass().getClassLoader(), path);
if (resourceStream != null)
{
return resourceStream;
}
return null;
}
/**
* Get the resource
*
* @param classLoader
* @param path
* @return resource stream
*/
private IResourceStream getResourceStream(final ClassLoader classLoader, final String path)
{
if (classLoader == null)
{
return null;
}
if (log.isDebugEnabled())
{
log.debug("Attempting to locate resource '" + path + "' using classloader " +
classLoader);
}
// Try loading path using classloader
URL url = classLoader.getResource(path);
if (url == null)
{
// maybe it is in the Servlet 3.0 like directory
url = classLoader.getResource("META-INF/resources/" + path);
}
if (url != null)
{
return new UrlResourceStream(url);
}
return null;
}
/**
* Search the resource by means of the application registered resource finder
*
* @param clazz
* @param path
* @return resource stream
*/
protected IResourceStream locateByResourceFinder(final Class<?> clazz, final String path)
{
if (finder == null)
{
finder = Application.get().getResourceSettings().getResourceFinder();
}
// Log attempt
if (log.isDebugEnabled())
{
log.debug("Attempting to locate resource '" + path + "' on path " + finder);
}
// Try to find file resource on the path supplied
return finder.find(clazz, path);
}
/**
*
* @see org.apache.wicket.core.util.resource.locator.IResourceStreamLocator#newResourceNameIterator(java.lang.String,
* java.util.Locale, java.lang.String, java.lang.String, java.lang.String, boolean)
*/
@Override
public ResourceNameIterator newResourceNameIterator(final String path, final Locale locale,
final String style, final String variation, final String extension, final boolean strict)
{
final Iterable<String> extensions = extension == null ? NO_EXTENSIONS : Arrays.asList(extension);
final String realPath;
final String realExtension;
if ((extension == null) && (path != null) && (path.indexOf('.') != -1))
{
realPath = Strings.beforeLast(path, '.');
// for extensions with separator take the first extension
realExtension = Strings.afterLast(path, '.');
if (realExtension.indexOf(',') > -1)
{
// multiple extensions are not allowed in the path parameter
return new EmptyResourceNameIterator();
}
}
else
{
realPath = path;
realExtension = extension;
}
return new ResourceNameIterator(path, style, variation, locale, extensions, strict);
}
}