/* * Copyright 2012 Jason Miller * * 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 jj.css; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.inject.Inject; import javax.inject.Singleton; import jj.application.AppLocation; import jj.application.Application; import jj.http.server.ServableResource; import jj.http.server.uri.URIMatch; import jj.resource.ServableLoader; /** * <p> * processes a string in css format to replace internal URL references * with versioned URLs * * @author jason * */ @Singleton class CssReferenceVersionProcessor { private static final Pattern IMPORT = Pattern.compile("@import\\s+(['\"])(.+?)\\1"); private static final Pattern URL = Pattern.compile("url\\((['\"])?(.+?)\\1?\\)"); private static final Pattern ABSOLUTE = Pattern.compile("^(?:https?:)?//"); private final Application application; private final ServableLoader servableLoader; @Inject CssReferenceVersionProcessor(Application application, ServableLoader servableLoader) { this.application = application; this.servableLoader = servableLoader; } String fixUris(final String css, final StylesheetResource resource) { return fixUrls(fixImports(css, resource), resource); } private String fixUrls(final String css, final StylesheetResource resource) { return doReplacement(css, resource, URL, "url($1", "$1)"); } private String fixImports(final String css, final StylesheetResource resource) { return doReplacement(css, resource, IMPORT, "@import $1", "$1"); } private String doReplacement( String css, StylesheetResource resource, Pattern pattern, String prefix, String suffix ) { // yuck. the API was never updated StringBuffer sb = new StringBuffer(); Matcher matcher = pattern.matcher(css); while (matcher.find()) { String replacement = matcher.group(2); if (!ABSOLUTE.matcher(replacement).find()) { String name; if (replacement.startsWith("/")) { name = replacement.substring(1); } else { name = application.resolvePath(AppLocation.Public, "") // Public! .relativize(resource.path().resolveSibling(replacement)) .normalize() .toString(); } URIMatch uriMatch = new URIMatch("/" + name); ServableResource dependency = servableLoader.loadResource(uriMatch); if (dependency != null) { dependency.addDependent(resource); if (!uriMatch.versioned) { // we only want to replace uris that weren't already versioned replacement = dependency.serverPath(); } else { // replace with the absolute path replacement = "/" + name; } } matcher.appendReplacement(sb, prefix + replacement + suffix); } } matcher.appendTail(sb); return sb.toString(); } }