YUI CSS Compressor : CSS « Development Class « Java






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

1.This interface defines constants for CSS.