YUI CSS Compressor
//package org.zkoss.maven.yuicompressor;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
* YUI Compressor
* Author: Julien Lecomte <jlecomte@yahoo-inc.com>
* Copyright (c) 2007, Yahoo! Inc. All rights reserved.
* Code licensed under the BSD License:
* http://developer.yahoo.net/yui/license.txt
*
* This code is a port of Isaac Schlueter's cssmin utility.
*
*----
* This code is a port of com.yahoo.platform.yui.compressor.CssCompressor
* we modify the version for zk css.dsp compress. (To avoid that $ make error happen.);
*/
public class CssCompressor {
private StringBuffer srcsb = new StringBuffer();
public CssCompressor(Reader in) throws IOException {
// Read the stream...
int c;
while ((c = in.read()) != -1) {
srcsb.append((char) c);
}
}
public String getSrc(){
return srcsb.toString();
}
public void compress(Writer out, int linebreakpos) throws IOException {
Pattern p;
Matcher m;
String css;
StringBuffer sb;
int startIndex, endIndex;
// Remove all comment blocks...
startIndex = 0;
boolean iemac = false;
boolean preserve = false;
sb = new StringBuffer(srcsb.toString());
while ((startIndex = sb.indexOf("/*", startIndex)) >= 0) {
preserve = sb.length() > startIndex + 2 && sb.charAt(startIndex + 2) == '!';
endIndex = sb.indexOf("*/", startIndex + 2);
if (endIndex < 0) {
if (!preserve) {
sb.delete(startIndex, sb.length());
}
} else if (endIndex >= startIndex + 2) {
if (sb.charAt(endIndex - 1) == '\\') {
// Looks like a comment to hide rules from IE Mac.
// Leave this comment, and the following one, alone...
startIndex = endIndex + 2;
iemac = true;
} else if (iemac) {
startIndex = endIndex + 2;
iemac = false;
} else if (!preserve) {
sb.delete(startIndex, endIndex + 2);
} else {
startIndex = endIndex + 2;
}
}
}
css = sb.toString();
// Normalize all whitespace strings to single spaces. Easier to work
// with that way.
css = css.replaceAll("\\s+", " ");
// Make a pseudo class for the Box Model Hack
css = css.replaceAll("\"\\\\\"}\\\\\"\"", "___PSEUDOCLASSBMH___");
// ---------where zk modify it
sb = new StringBuffer();
p = Pattern.compile("\\$\\{([^\\}]+)\\}");
Matcher m1 = p.matcher(css);
while (m1.find()) {
String s1 = m1.group();
s1 = s1.replaceAll("\\$\\{", "__EL__");
s1 = s1.replaceAll(":", "__ELSP__");
s1 = s1.replaceAll("\\}", "__ELEND__");
m1.appendReplacement(sb, s1);
}
m1.appendTail(sb);
css = sb.toString();
// ---------where zk modify it----end
// Remove the spaces before the things that should not have spaces
// before them.
// But, be careful not to turn "p :link {...}" into "p:link{...}"
// Swap out any pseudo-class colons with the token, and then swap back.
sb = new StringBuffer();
p = Pattern.compile("(^|\\})(([^\\{:])+:)+([^\\{]*\\{)");
m = p.matcher(css);
while (m.find()) {
String s = m.group();
s = s.replaceAll(":", "___PSEUDOCLASSCOLON___");
m.appendReplacement(sb, s);
}
m.appendTail(sb);
css = sb.toString();
css = css.replaceAll("\\s+([!{};:>+\\(\\)\\],])", "$1");
css = css.replaceAll("___PSEUDOCLASSCOLON___", ":");
// Remove the spaces after the things that should not have spaces after
// them.
css = css.replaceAll("([!{}:;>+\\(\\[,])\\s+", "$1");
// Add the semicolon where it's missing.
css = css.replaceAll("([^;\\}])}", "$1;}");
// Replace 0(px,em,%) with 0.
css = css.replaceAll("([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", "$1$2");
// Replace 0 0 0 0; with 0.
css = css.replaceAll(":0 0 0 0;", ":0;");
css = css.replaceAll(":0 0 0;", ":0;");
css = css.replaceAll(":0 0;", ":0;");
// Replace background-position:0; with background-position:0 0;
css = css.replaceAll("background-position:0;", "background-position:0 0;");
// Replace 0.6 to .6, but only when preceded by : or a white-space
css = css.replaceAll("(:|\\s)0+\\.(\\d+)", "$1.$2");
// Shorten colors from rgb(51,102,153) to #336699
// This makes it more likely that it'll get further compressed in the
// next step.
p = Pattern.compile("rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)");
m = p.matcher(css);
sb = new StringBuffer();
while (m.find()) {
String[] rgbcolors = m.group(1).split(",");
StringBuffer hexcolor = new StringBuffer("#");
for (int i = 0; i < rgbcolors.length; i++) {
int val = Integer.parseInt(rgbcolors[i]);
if (val < 16) {
hexcolor.append("0");
}
hexcolor.append(Integer.toHexString(val));
}
m.appendReplacement(sb, hexcolor.toString());
}
m.appendTail(sb);
css = sb.toString();
// Shorten colors from #AABBCC to #ABC. Note that we want to make sure
// the color is not preceded by either ", " or =. Indeed, the property
// filter: chroma(color="#FFFFFF");
// would become
// filter: chroma(color="#FFF");
// which makes the filter break in IE.
p = Pattern
.compile("([^\"'=\\s])(\\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])");
m = p.matcher(css);
sb = new StringBuffer();
while (m.find()) {
// Test for AABBCC pattern
if (m.group(3).equalsIgnoreCase(m.group(4)) && m.group(5).equalsIgnoreCase(m.group(6))
&& m.group(7).equalsIgnoreCase(m.group(8))) {
m.appendReplacement(sb, m.group(1) + m.group(2) + "#" + m.group(3) + m.group(5) + m.group(7));
} else {
m.appendReplacement(sb, m.group());
}
}
m.appendTail(sb);
css = sb.toString();
// Remove empty rules.
css = css.replaceAll("[^\\}]+\\{;\\}", "");
if (linebreakpos >= 0) {
// Some source control tools don't like it when files containing
// lines longer
// than, say 8000 characters, are checked in. The linebreak option
// is used in
// that case to split long lines after a specific column.
int i = 0;
int linestartpos = 0;
sb = new StringBuffer(css);
while (i < sb.length()) {
char c = sb.charAt(i++);
if (c == '}' && i - linestartpos > linebreakpos) {
sb.insert(i, '\n');
linestartpos = i;
}
}
css = sb.toString();
}
// Replace the pseudo class for the Box Model Hack
css = css.replaceAll("___PSEUDOCLASSBMH___", "\"\\\\\"}\\\\\"\"");
// Replace multiple semi-colons in a row by a single one
// See SF bug #1980989
css = css.replaceAll(";;+", ";");
// ---------where zk modify it
css = css.replaceAll("__EL__", "\\$\\{");
css = css.replaceAll("__ELSP__", ":");
css = css.replaceAll("__ELEND__", "\\}");
// ---------where zk modify it----end
// Trim the final string (for any leading or trailing white spaces)
css = css.trim();
// Write the output...
out.write(css);
}
}
Related examples in the same category