All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wicketstuff.mergedresources.annotations.ContributionScanner Maven / Gradle / Ivy

Go to download

Resource merging for Apache Wicket (http://wicket.apache.org), see http://talk-on-tech.blogspot.com/2008/08/wicket-interface-speed-up-merging.html

There is a newer version: 4.0.1
Show newest version
/**
 * Copyright 2010 Molindo GmbH
 *
 * Licensed 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.wicketstuff.mergedresources.annotations;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.util.string.Strings;
import org.wicketstuff.config.MatchingResources;
import org.wicketstuff.mergedresources.ResourceSpec;

import edu.umd.cs.findbugs.annotations.SuppressWarnings;

/**
 * Gather page resources to merge, depends on {@link CssContribution} and
 * {@link JsContribution} annotations.
 * 
 * Helper to make using wicketstuff-merged-resources easier.
 */
public class ContributionScanner {

	private static final String DEFAULT_PATH_JS = "all.js";
	private static final String DEFAULT_PATH_CSS = "all.css";

	private final Map> _contributions;

	public ContributionScanner(String packageName) {
		MatchingResources resources = new MatchingResources(getPatternForPackage(packageName));

		_contributions = scan(resources);
	}

	private Map> scan(MatchingResources resources) {
		Map> contributions = new HashMap>();

		for (Class cls : resources.getAnnotatedMatches(JsContribution.class)) {
			JsContribution a = cls.getAnnotation(JsContribution.class);
			addJsContributions(cls, a, contributions);
		}

		for (Class cls : resources.getAnnotatedMatches(CssContribution.class)) {
			CssContribution a = cls.getAnnotation(CssContribution.class);
			addCssContributions(cls, a, contributions);
		}

		for (Class cls : resources.getAnnotatedMatches(CssContributions.class)) {
			CssContributions cssMulti = cls.getAnnotation(CssContributions.class);
			for (CssContribution css : cssMulti.value()) {
				addCssContributions(cls, css, contributions);
			}
		}

		for (Class cls : resources.getAnnotatedMatches(ResourceContribution.class)) {
			ResourceContribution resource = cls.getAnnotation(ResourceContribution.class);
			addResourceContributions(cls, resource, contributions);
		}

		for (Map.Entry> e : contributions.entrySet()) {
			e.setValue(Collections.unmodifiableSortedSet(e.getValue()));
		}

		return Collections.unmodifiableMap(contributions);
	}

	private void addJsContributions(Class scope, JsContribution js,
			Map> contributions) {
		for (String file : js.value()) {
			if (Strings.isEmpty(file)) {
				file = scope.getSimpleName() + ".js";
			}

			String path = Strings.isEmpty(js.path()) ? DEFAULT_PATH_JS : js.path();
			SortedSet specs = contributions.get(path);
			if (specs == null) {
				specs = new TreeSet(WeightedResourceSpecComparator.INSTANCE);
				contributions.put(path, specs);
			}
			if (!specs.add(new WeightedResourceSpec(scope, file, js.order()))) {
				throw new WicketRuntimeException("duplicate resource contribution: " + js + ", scope=" + scope);
			}
		}
	}

	private void addCssContributions(Class scope, CssContribution css,
			Map> contributions) {
		for (String file : css.value()) {
			if (Strings.isEmpty(file)) {
				file = getDefaultCssFile(scope.getSimpleName(), css.media());
			}

			String path = Strings.isEmpty(css.path()) ? getDefaultCssPath(css.media()) : css.path();
			SortedSet specs = contributions.get(path);
			if (specs == null) {
				specs = new TreeSet(WeightedResourceSpecComparator.INSTANCE);
				contributions.put(path, specs);
			}
			if (!specs.add(new WeightedResourceSpec(scope, file, css.order()))) {
				throw new WicketRuntimeException("duplicate resource contribution: " + css + ", scope=" + scope);
			}
		}
	}

	static String getDefaultCssFile(String simpleName, String media) {
		if (!Strings.isEmpty(media) && !"all".equals(media)) {
			return simpleName + "-" + media + ".css";
		}
		return simpleName + ".css";
	}

	static String getDefaultCssPath(String media) {
		if (!Strings.isEmpty(media)) {
			return media + ".css";
		}
		return DEFAULT_PATH_CSS;
	}

	private void addResourceContributions(Class scope, ResourceContribution resource,
			Map> contributions) {
		for (String file : resource.value()) {
			if (Strings.isEmpty(file)) {
				throw new WicketRuntimeException("empty file name not allowed for @ResourceContributions at class "
						+ scope.getName());
			}

			// don't merge resources by default
			String path = Strings.isEmpty(resource.path()) ? file : resource.path();
			SortedSet specs = contributions.get(path);
			if (specs == null) {
				specs = new TreeSet(WeightedResourceSpecComparator.INSTANCE);
				contributions.put(path, specs);
			}
			if (!specs.add(new WeightedResourceSpec(scope, file))) {
				throw new WicketRuntimeException("duplicate resource contribution: " + resource + ", scope=" + scope);
			}
		}
	}

	/**
	 * @return an unmodifiable map of contributions mapped by scope
	 */
	public Map> getContributions() {
		return _contributions;
	}

	/**
	 * Get the Spring search pattern given a package name or part of a package
	 * name
	 * 
	 * @param packageName
	 *            a package name
	 * @return a Spring search pattern for the given package
	 */
	private String getPatternForPackage(String packageName) {
		if (packageName == null) {
			packageName = "";
		}
		packageName = packageName.replace('.', '/');
		if (!packageName.endsWith("/")) {
			packageName += '/';
		}

		return "classpath*:" + packageName + "**/*.class";
	}

	@SuppressWarnings(value = "EQ_DOESNT_OVERRIDE_EQUALS", justification = "super type is sufficient, ignore weight")
	public static final class WeightedResourceSpec extends ResourceSpec {

		private static final long serialVersionUID = 1L;

		private final int _weight;

		public WeightedResourceSpec(Class scope, String file, int weight) {
			super(scope, file);
			_weight = weight;
		}

		public WeightedResourceSpec(Class scope, String file) {
			this(scope, file, 0);
		}

		@Override
		public String toString() {
			return super.toString() + " (weight=" + _weight + ")";
		}
	}

	public enum WeightedResourceSpecComparator implements Comparator {
		INSTANCE;

		@Override
		public int compare(WeightedResourceSpec o1, WeightedResourceSpec o2) {
			if (o1 == null) {
				return o2 == null ? 0 : -1;
			} else if (o2 == null) {
				return 1;
			}
			if (o1.equals(o2)) {
				return 0;
			}

			// from highest to lowest - avoid overflow
			int val = Integer.valueOf(o2._weight).compareTo(o1._weight);
			if (val != 0) {
				return val;
			}
			val = o1.getFile().compareTo(o2.getFile());
			if (val != 0) {
				return val;
			}
			return o1.getScope().getName().compareTo(o2.getScope().getName());
		}

	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy