org.wicketstuff.mergedresources.annotations.ContributionScanner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wicketstuff-merged-resources Show documentation
Show all versions of wicketstuff-merged-resources Show documentation
Resource merging for Apache Wicket (http://wicket.apache.org),
see http://talk-on-tech.blogspot.com/2008/08/wicket-interface-speed-up-merging.html
/**
* 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());
}
}
}