| /* |
| * 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; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.wicket.css.ICssCompressor; |
| import org.apache.wicket.javascript.IJavaScriptCompressor; |
| import org.apache.wicket.markup.head.CssHeaderItem; |
| import org.apache.wicket.markup.head.CssReferenceHeaderItem; |
| import org.apache.wicket.markup.head.HeaderItem; |
| import org.apache.wicket.markup.head.IReferenceHeaderItem; |
| import org.apache.wicket.markup.head.JavaScriptHeaderItem; |
| import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem; |
| import org.apache.wicket.request.resource.CssResourceReference; |
| import org.apache.wicket.request.resource.JavaScriptResourceReference; |
| import org.apache.wicket.request.resource.ResourceReference; |
| import org.apache.wicket.request.resource.ResourceReferenceRegistry; |
| import org.apache.wicket.resource.bundles.ConcatResourceBundleReference; |
| import org.apache.wicket.util.lang.Args; |
| |
| /** |
| * Contains all resource bundles that are registered in the application. Resource bundles provide a |
| * way to combine multiple resources into one, reducing the number of requests needed to load a |
| * page. The code using the resources does not need to know about the registered resources, making |
| * it possible to create resource bundles for 3rd party libraries. When a single resource from a |
| * resource bundle is requested, the bundle is rendered instead. All other resources from the bundle |
| * are marked as rendered. A specific resource can only be part of one bundle. |
| * |
| * @author papegaaij |
| */ |
| public class ResourceBundles |
| { |
| private final ResourceReferenceRegistry registry; |
| |
| private final Map<HeaderItem, HeaderItem> providedResourcesToBundles; |
| |
| /** |
| * Construct. |
| * |
| * @param registry |
| * the registry that keeps all referenced resources |
| */ |
| public ResourceBundles(final ResourceReferenceRegistry registry) |
| { |
| this.registry = Args.notNull(registry, "registry"); |
| this.providedResourcesToBundles = new HashMap<HeaderItem, HeaderItem>(); |
| } |
| |
| /** |
| * Adds a javascript bundle that is automatically generated by concatenating the given package |
| * resources. If the given resources depend on each other, you should make sure that the |
| * resources are provided in the order they need to be concatenated. If the resources depend on |
| * other resources, that are not part of the bundle, the bundle will inherit these dependencies. |
| * |
| * This method is equivalent to {@link #addBundle(HeaderItem)} with a |
| * {@link JavaScriptHeaderItem} for a {@link ConcatResourceBundleReference}. |
| * |
| * @param scope |
| * The {@linkplain ResourceReference#getScope() scope} of the bundle |
| * @param name |
| * The name of the resource. This will show up as the filename in the markup. |
| * @param references |
| * The resources this bundle will consist of. |
| * @return the newly created bundle |
| */ |
| public JavaScriptReferenceHeaderItem addJavaScriptBundle(Class<?> scope, String name, |
| JavaScriptResourceReference... references) |
| { |
| return addJavaScriptBundle(scope, name, false, references); |
| } |
| |
| /** |
| * Adds a javascript bundle that is automatically generated by concatenating the given package |
| * resources. If the given resources depend on each other, you should make sure that the |
| * resources are provided in the order they need to be concatenated. If the resources depend on |
| * other resources, that are not part of the bundle, the bundle will inherit these dependencies. |
| * |
| * This method is equivalent to {@link #addBundle(HeaderItem)} with a |
| * {@link JavaScriptHeaderItem} for a {@link ConcatResourceBundleReference}. |
| * |
| * @param scope |
| * The {@linkplain ResourceReference#getScope() scope} of the bundle |
| * @param defer |
| * specifies that the execution of a script should be deferred (delayed) until after |
| * the page has been loaded. |
| * @param name |
| * The name of the resource. This will show up as the filename in the markup. |
| * @param references |
| * The resources this bundle will consist of. |
| * @return the newly created bundle |
| */ |
| public JavaScriptReferenceHeaderItem addJavaScriptBundle(Class<?> scope, String name, boolean defer, |
| JavaScriptResourceReference... references) |
| { |
| List<JavaScriptReferenceHeaderItem> items = new ArrayList<>(); |
| |
| for (JavaScriptResourceReference curReference : references) |
| { |
| items.add(JavaScriptHeaderItem.forReference(curReference)); |
| } |
| |
| ConcatResourceBundleReference<JavaScriptReferenceHeaderItem> bundleReference = |
| newBundleResourceReference(scope, name, items); |
| |
| if (Application.exists()) |
| { |
| IJavaScriptCompressor javaScriptCompressor = Application.get().getResourceSettings().getJavaScriptCompressor(); |
| bundleReference.setCompressor(javaScriptCompressor); |
| } |
| |
| JavaScriptReferenceHeaderItem item = JavaScriptHeaderItem.forReference(bundleReference); |
| item.setDefer(defer); |
| return addBundle(item); |
| } |
| |
| /** |
| * Adds a css bundle that is automatically generated by concatenating the given package |
| * resources. If the given resources depend on each other, you should make sure that the |
| * resources are provided in the order they need to be concatenated. If the resources depend on |
| * other resources, that are not part of the bundle, the bundle will inherit these dependencies. |
| * |
| * This method is equivalent to {@link #addBundle(HeaderItem)} with a {@link CssHeaderItem} for |
| * a {@link ConcatResourceBundleReference}. |
| * |
| * @param scope |
| * The {@linkplain ResourceReference#getScope() scope} of the bundle |
| * @param name |
| * The name of the resource. This will show up as the filename in the markup. |
| * @param references |
| * The resources this bundle will consist of. |
| * @return the newly created bundle |
| */ |
| public CssReferenceHeaderItem addCssBundle(Class<?> scope, String name, |
| CssResourceReference... references) |
| { |
| List<CssReferenceHeaderItem> items = new ArrayList<CssReferenceHeaderItem>(); |
| for (CssResourceReference curReference : references) |
| { |
| items.add(CssHeaderItem.forReference(curReference)); |
| } |
| ConcatResourceBundleReference<CssReferenceHeaderItem> bundleReference = |
| newBundleResourceReference(scope, name, items); |
| if (Application.exists()) |
| { |
| ICssCompressor cssCompressor = Application.get().getResourceSettings().getCssCompressor(); |
| bundleReference.setCompressor(cssCompressor); |
| } |
| return addBundle(CssHeaderItem.forReference(bundleReference)); |
| } |
| |
| /** |
| * Creates a ResourceReference that will point to the bundle with the items |
| * |
| * @param scope |
| * The {@linkplain ResourceReference#getScope() scope} of the bundle |
| * @param name |
| * The name of the resource. This will show up as the filename in the markup. |
| * @param items |
| * The HeaderItems for the resource references for the bundle |
| * @param <T> |
| * The type of the header items |
| * @return A ResourceReference that will point to the bundle with the items |
| */ |
| protected <T extends HeaderItem & IReferenceHeaderItem> ConcatResourceBundleReference<T> newBundleResourceReference(Class<?> scope, String name, List<T> items) |
| { |
| return new ConcatResourceBundleReference<T>(scope, name, items); |
| } |
| |
| /** |
| * Adds a bundle to the registry. |
| * |
| * @param bundle |
| * The bundle to register |
| * @return the bundle |
| * @throws IllegalArgumentException |
| * if any of the provided resources of the given bundle is already provided by a |
| * different bundle. |
| */ |
| public <T extends HeaderItem> T addBundle(T bundle) |
| { |
| for (HeaderItem curProvidedResource : bundle.getProvidedResources()) |
| { |
| if (providedResourcesToBundles.containsKey(curProvidedResource)) |
| { |
| throw new IllegalArgumentException( |
| "Only one bundle can provide a certain resource. " + |
| providedResourcesToBundles.get(curProvidedResource) + |
| " already provides the resource " + curProvidedResource); |
| } |
| providedResourcesToBundles.put(curProvidedResource, bundle); |
| } |
| if (bundle instanceof IReferenceHeaderItem) |
| { |
| ResourceReference reference = ((IReferenceHeaderItem) bundle).getReference(); |
| if (reference.canBeRegistered()) |
| { |
| registry.registerResourceReference(reference); |
| } |
| } |
| return bundle; |
| } |
| |
| /** |
| * Finds a bundle that provides the given item. |
| * |
| * @param item |
| * The item that should be provided by the bundle. |
| * @return The bundle that provides the given item or null if no bundle is found. |
| */ |
| public HeaderItem findBundle(HeaderItem item) |
| { |
| return providedResourcesToBundles.get(item); |
| } |
| } |