net.jawr.web.minification.CSSMinifier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jawr-core Show documentation
Show all versions of jawr-core Show documentation
Javascript/CSS bundling and compressing tool for java web apps.
By using jawr resources are automatically bundled together and optionally minified and gzipped.
Jawr provides tag libraries to reference a generated bundle either by id or by using the name of any of its members.
The newest version!
/**
* Copyright 2007-2016 Jordi Hernández Sellés, Ibrahim Chaehoi
*
* 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 net.jawr.web.minification;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.jawr.web.resource.bundle.factory.util.RegexUtil;
/**
* Minifies CSS files by removing expendable whitespace and comments.
*
* @author Jordi Hernández Sellés
* @author ibrahim Chaehoi
*/
public class CSSMinifier {
// This regex captures comments
private static final String COMMENT_REGEX = "(/\\*(?!(!))[^*]*\\*+([^/][^*]*\\*+)*/)";
// This regex captures licence (ex : /*! My licence content */
private static final String LICENCE_REGEX = "(/\\*(?=(!))[^*]*\\*+([^/][^*]*\\*+)*/)";
// Captures CSS strings
private static final String QUOTED_CONTENT_REGEX = "([\"']).*?\\1";
// A placeholder string to replace and restore CSS strings
private static final String STRING_PLACEHOLDER = "______'JAWR_STRING'______";
// A placeholder string to replace and restore licence comments
private static final String LICENCE_PLACEHOLDER = "______'LICENCE'______";
// Captured CSS rules (requires replacing CSS strings with a placeholder, or
// quoted braces will fool it.
private static final String RULES_REGEX = "([^\\{\\}]*)(\\{[^\\{\\}]*})";
// Captures newlines and tabs
private static final String NEW_LINE_TABS_REGEX = "\\r|\\n|\\t|\\f";
/**
* There is a special case when the space should not be removed when
* preceeded by and keyword. Ex:
*
* @media only screen and (max-width:767px){ }
*/
private static final String SPACES_REGEX = "(?ims)(\\s*\\{\\s*)|(\\s+\\-\\s+)|(\\s+\\+\\s+)|(\\s+\\*\\s+)|(\\s+\\/\\s+)|(\\s*\\}\\s*)|((? licences = new ArrayList<>();
final Matcher licenceMatcher = LICENCE_PATTERN.matcher(compressed);
compressed = new MatcherProcessorCallback() {
@Override
String matchCallback(final Matcher matcher) {
final String match = matcher.group();
String replacement = "";
if (keepLicence) {
licences.add(match);
replacement = LICENCE_PLACEHOLDER;
}
return replacement;
}
}.processWithMatcher(licenceMatcher);
// Temporarily replace the strings with a placeholder
final List strings = new ArrayList<>();
final Matcher stringMatcher = QUOTED_CONTENT_PATTERN.matcher(compressed);
compressed = new MatcherProcessorCallback() {
@Override
String matchCallback(final Matcher matcher) {
final String match = matcher.group();
strings.add(match);
return STRING_PLACEHOLDER;
}
}.processWithMatcher(stringMatcher);
// Grab all rules and replace whitespace in selectors
final Matcher rulesmatcher = RULES_PATTERN.matcher(compressed);
compressed = new MatcherProcessorCallback() {
@Override
String matchCallback(final Matcher matcher) {
final String match = matcher.group(1);
final String spaced = NEW_LINES_TAB_PATTERN.matcher(match).replaceAll(SPACE).trim();
return spaced + matcher.group(2);
}
}.processWithMatcher(rulesmatcher);
// Replace all linefeeds and tabs
compressed = NEW_LINES_TAB_PATTERN.matcher(compressed).replaceAll(" ");
// Match all empty space we can minify
final Matcher matcher = SPACES_PATTERN.matcher(compressed);
compressed = new MatcherProcessorCallback() {
@Override
String matchCallback(final Matcher matcher) {
String replacement = SPACE;
final String match = matcher.group();
if (match.contains(PLUS_OPERATOR)) {
replacement = PLUS_OPERATOR_REPLACEMENT;
} else if (match.contains(MINUS_OPERATOR)) {
replacement = MINUS_OPERATOR_REPLACEMENT;
} else if (match.contains(MULTIPLICATION_OPERATOR)) {
replacement = MULTIPLICATION_OPERATOR_REPLACEMENT;
} else if (match.contains(DIVISION_OPERATOR)) {
replacement = DIVISION_OPERATOR_REPLACEMENT;
} else if (match.contains(PLUS_OPERATOR)) {
replacement = PLUS_OPERATOR_REPLACEMENT;
} else if (match.contains(BRACKET_OPEN)) {
replacement = BRACKET_OPEN;
} else if (match.contains(BRACKET_CLOSE)) {
replacement = BRACKET_CLOSE;
} else if (match.contains(PAREN_OPEN)) {
replacement = PAREN_OPEN;
} else if (match.contains(COLON)) {
replacement = COLON;
} else if (match.contains(SEMICOLON)) {
replacement = SEMICOLON;
} else if (match.contains(PAREN_CLOSE)) {
replacement = PAREN_CLOSE;
}
return replacement;
}
}.processWithMatcher(matcher);
// Restore all Strings
final Matcher restoreStringMatcher = STRING_PLACEHOLDER_PATTERN.matcher(compressed);
final Iterator itStr = strings.iterator();
compressed = new MatcherProcessorCallback() {
@Override
String matchCallback(final Matcher matcher) {
final String replacement = itStr.next();
return replacement;
}
}.processWithMatcher(restoreStringMatcher);
// Restore all licences
final Matcher restoreLicenceMatcher = LICENCE_PLACEHOLDER_PATTERN.matcher(compressed);
final Iterator itLicence = licences.iterator();
compressed = new MatcherProcessorCallback() {
@Override
String matchCallback(final Matcher matcher) {
final String replacement = itLicence.next();
return replacement;
}
}.processWithMatcher(restoreLicenceMatcher);
return new StringBuffer(compressed);
}
}