/* * 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.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; import org.apache.wicket.Application; import org.apache.wicket.core.util.file.WebApplicationPath; import org.apache.wicket.util.file.IResourceFinder; import org.apache.wicket.util.file.Path; 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.util.string.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Locates Wicket resources. * * <p> * Contains the logic to locate a resource based on a path, a variation, a style (see * {@link org.apache.wicket.Session}), a locale and an extension string. The full filename will be * built like: <path>_<variation>_<style>_<locale>.<extension>. * <p> * Resource matches will be attempted in the following order: * <ol> * <li><path>_<variation>_<style>_<locale>.<extension></li> * <li><path>_<style>_<locale>.<extension></li> * <li><path>_<locale>.<extension></li> * <li><path>_<style>.<extension></li> * <li><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> * <p> * Resources will be actually loaded by the {@link IResourceFinder}s defined in the resource * settings. By default there are finders that look in the classpath and in the classpath in * META-INF/resources. You can add more by adding {@link WebApplicationPath}s or {@link Path}s to * {@link org.apache.wicket.settings.ResourceSettings#getResourceFinders()}. * * @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<>(0); /** If null, the application registered finder will be used */ private List<IResourceFinder> finders; /** * Constructor */ public ResourceStreamLocator() { this((List<IResourceFinder>)null); } /** * Constructor * * @param finders * resource finders. These will be tried in the given order. */ public ResourceStreamLocator(final IResourceFinder... finders) { this(Arrays.asList(finders)); } /** * Constructor * * @param finders * resource finders. These will be tried in the given order. */ public ResourceStreamLocator(final List<IResourceFinder> finders) { this.finders = finders; } /** * * @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) if (finders == null) { finders = Application.get().getResourceSettings().getResourceFinders(); } IResourceStream result; for (IResourceFinder finder : finders) { log.debug("Attempting to locate resource '{}' using finder'{}'", path, finder); result = finder.find(clazz, path); if (result != null) { return result; } } 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, then 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. IResourceNameIterator 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; } /** * * @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 IResourceNameIterator newResourceNameIterator(final String path, final Locale locale, final String style, final String variation, final String extension, final boolean strict) { final Iterable<String> extensions; final String realPath; if ((extension == null) && (path != null) && (path.indexOf('.') != -1)) { // extract the path and extension realPath = Strings.beforeLast(path, '.'); String realExtension = Strings.afterLast(path, '.'); if (realExtension.indexOf(',') > -1) { // multiple extensions are not allowed in the path parameter // it could be an attack, so ignore it and pretend there are no resources return new EmptyResourceNameIterator(); } // add a minimized file to the resource lookup if necessary if (Application.exists() && Application.get().getResourceSettings().getUseMinifiedResources()) { extensions = Arrays.asList("min." + realExtension, realExtension); } else { extensions = Collections.singleton(realExtension); } } else { realPath = path; if (extension == null) { extensions = NO_EXTENSIONS; } else { String[] commaSeparated = Strings.split(extension, ','); List<String> nonMinifiedExtensions = Arrays.asList(commaSeparated); // add a minimized file to the resource lookup if necessary if (Application.exists() && Application.get().getResourceSettings().getUseMinifiedResources()) { ArrayList<String> minifiedExtensions = new ArrayList<>(); for (String nonMinifiedExtension : nonMinifiedExtensions) { minifiedExtensions.add("min." + nonMinifiedExtension); minifiedExtensions.add(nonMinifiedExtension); } extensions = minifiedExtensions; } else { extensions = nonMinifiedExtensions; } } } return newResourceNameIterator(realPath, locale, style, variation, extensions, strict); } // TODO Wicket 7 Add this method to IResourceStreamLocator interface. public IResourceNameIterator newResourceNameIterator(final String path, final Locale locale, final String style, final String variation, final Iterable<String> extensions, final boolean strict) { return new ResourceNameIterator(path, style, variation, locale, extensions, strict); } }