[richfaces-svn-commits] JBoss Rich Faces SVN: r4655 - in branches/3.1.x/framework/impl/src: main/java/org/ajax4jsf/css and 3 other directories.

richfaces-svn-commits at lists.jboss.org richfaces-svn-commits at lists.jboss.org
Mon Dec 10 09:48:48 EST 2007


Author: andrei_exadel
Date: 2007-12-10 09:48:48 -0500 (Mon, 10 Dec 2007)
New Revision: 4655

Added:
   branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/css/
   branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/css/CssCompressor.java
   branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/resource/CountingOutputWriter.java
   branches/3.1.x/framework/impl/src/test/java/org/ajax4jsf/css/
   branches/3.1.x/framework/impl/src/test/java/org/ajax4jsf/css/CssCompressorTest.java
Modified:
   branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/resource/TemplateCSSRenderer.java
Log:
RF-1349

Added: branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/css/CssCompressor.java
===================================================================
--- branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/css/CssCompressor.java	                        (rev 0)
+++ branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/css/CssCompressor.java	2007-12-10 14:48:48 UTC (rev 4655)
@@ -0,0 +1,154 @@
+/*
+ * YUI Compressor
+ * Author: Julien Lecomte <jlecomte at 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.
+ */
+
+package org.ajax4jsf.css;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class CssCompressor {
+
+    private StringBuffer srcsb = null;
+
+    public CssCompressor(StringBuffer buffer) throws IOException {
+        srcsb = buffer;
+    }
+
+    public int compress(Writer out, int linebreakpos)
+            throws IOException {
+    	
+        Pattern p;
+        Matcher m;
+        String css;
+        StringBuffer sb;
+        int startIndex, endIndex;
+        int bytesLength;
+
+        // Remove all comment blocks...
+        sb = new StringBuffer(srcsb.toString());
+        while ((startIndex = sb.indexOf("/*")) >= 0) {
+            endIndex = sb.indexOf("*/", startIndex + 2);
+            if (endIndex >= startIndex + 2)
+                sb.delete(startIndex, endIndex + 2);
+        }
+
+        css = sb.toString();
+
+        // Normalize all whitespace strings to single spaces. Easier to work with that way.
+        css = css.replaceAll("\\s+", " ");
+
+        // 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();
+        }
+
+        // Trim the final string (for any leading or trailing white spaces)
+        css = css.trim();
+        bytesLength = css.length() * 2;
+        // Write the output...
+        out.write(css);
+        return bytesLength;
+    }
+}

Added: branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/resource/CountingOutputWriter.java
===================================================================
--- branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/resource/CountingOutputWriter.java	                        (rev 0)
+++ branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/resource/CountingOutputWriter.java	2007-12-10 14:48:48 UTC (rev 4655)
@@ -0,0 +1,110 @@
+/*
+ * CountingOutputWriter.java		Date created: 21.11.2007
+ * Last modified by: $Author$
+ * $Revision$	$Date$
+ */
+
+package org.ajax4jsf.resource;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Class provides custom writer implementation with counting of bytes written
+ * Is using for replacement of css component writer
+ * @author Andrey Markavtsov
+ */
+public class CountingOutputWriter extends Writer {
+	
+	/** count of written bytes */
+	private int written = 0;
+	
+	/** Size of char type */
+	private static final int sizeOfChar = 2;
+	
+	/** Size of int type */
+	private static final int sizeOfInt = 4;
+	
+	/** Buffer to store bytes written */
+	private StringBuffer buffer; 
+			
+	/**
+	 * Default constructor 
+	 */
+	public CountingOutputWriter() {
+		super();
+		this.buffer = new StringBuffer();
+	}
+
+	/* (non-Javadoc)
+	 * @see java.io.Writer#close()
+	 */
+	public void close() throws IOException {
+		;
+	}
+
+	/* (non-Javadoc)
+	 * @see java.io.Writer#flush()
+	 */
+	public void flush() throws IOException {
+		;
+	}
+
+    /** Methods appends chars written to buffer
+	 *  @param cbuf - chars to be written
+	 *  @param off  - offset
+	 *  @param len  - length of bytes 
+	 */ 
+	public void write(char[] cbuf, int off, int len) throws IOException {
+		buffer.append(cbuf, off, len);
+		written += len * sizeOfChar;
+	}
+
+	/** Methods appends chars written to buffer
+	 *  @param cbuf - chars to be written
+	 */ 
+	public void write(char[] cbuf) throws IOException {
+		buffer.append(cbuf);
+		written += cbuf.length * sizeOfChar;
+	}
+
+    /** Methods appends int written to buffer
+	 *  @param c - int to be written
+	 */ 
+	public void write(int c) throws IOException {
+		buffer.append(c);
+		written += sizeOfInt;
+	}
+
+	 /** Methods appends string written to buffer
+	 *  @param str  - string to be written
+	 *  @param off  - offset
+	 *  @param len  - length of bytes 
+	 */ 
+	public void write(String str, int off, int len) throws IOException {
+		buffer.append(str, off, len);
+		written += len * sizeOfChar;
+	}
+
+	/** Methods appends string written to buffer
+	 *  @param str - string to be written
+     */
+	public void write(String str) throws IOException {
+		buffer.append(str);
+		written += str.length() * sizeOfChar;
+	}
+
+	/** Methods gets written bytes count
+	 *  @return written count of bytes 
+	 */
+	public int getWritten() {
+		return written;
+	}
+	
+	/** Methods gets content of written bytes
+	 *  @return buffer  
+	 */
+	public StringBuffer getContent () {
+		return buffer;
+	}
+}
\ No newline at end of file

Modified: branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/resource/TemplateCSSRenderer.java
===================================================================
--- branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/resource/TemplateCSSRenderer.java	2007-12-10 14:37:16 UTC (rev 4654)
+++ branches/3.1.x/framework/impl/src/main/java/org/ajax4jsf/resource/TemplateCSSRenderer.java	2007-12-10 14:48:48 UTC (rev 4655)
@@ -23,6 +23,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.Writer;
 import java.nio.ByteBuffer;
 
 import javax.faces.FacesException;
@@ -33,12 +34,11 @@
 import javax.faces.render.RenderKit;
 import javax.faces.render.RenderKitFactory;
 
+import org.ajax4jsf.css.CssCompressor;
 import org.ajax4jsf.renderkit.RendererBase;
 import org.ajax4jsf.renderkit.compiler.HtmlCompiler;
 import org.ajax4jsf.renderkit.compiler.PreparedTemplate;
 import org.ajax4jsf.renderkit.compiler.TemplateContext;
-import org.ajax4jsf.resource.InternetResource;
-import org.ajax4jsf.resource.ResourceContext;
 import org.richfaces.skin.SkinFactory;
 
 /**
@@ -48,6 +48,9 @@
 public class TemplateCSSRenderer extends StyleRenderer {
 	
 	private static final String COMPILED_TEMPLATE_PROPERTY = "compiled-template";
+	
+	/** Parameter allows switch on/off comressing for css */
+	private static final String COMPRESS_STYLE_PARAMETER = "org.ajax4jsf.COMPRESS_STYLE";
 
 	private RendererBase renderer = new RendererBase() {
 
@@ -62,8 +65,13 @@
 	 */
 	public int send(InternetResource base, ResourceContext context) throws IOException {
 		PreparedTemplate template = null;
+		CountingOutputWriter countingOutputWriter = new CountingOutputWriter();
 		template = getTemplate(base, context);
 		FacesContext facesContext = FacesContext.getCurrentInstance();
+		boolean _CompressStyleOn = !"false".equals(facesContext.getExternalContext()
+					   		.getInitParameter(COMPRESS_STYLE_PARAMETER));
+		Writer writer = context.getWriter();
+		int bytesLength;
 		if(null != facesContext) {
 			// Create responseWriter.
 			String defaultRenderKitId = facesContext.getApplication().getDefaultRenderKitId();
@@ -73,19 +81,34 @@
 			RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
 			RenderKit renderKit = renderKitFactory.getRenderKit(facesContext,defaultRenderKitId);
 			// TODO - handle response encoding 
-			ResponseWriter responseWriter = renderKit.createResponseWriter(context.getWriter(),null,"UTF-8");
+				
+			ResponseWriter responseWriter = renderKit.createResponseWriter(countingOutputWriter,null,"UTF-8");
 			facesContext.setResponseWriter(responseWriter);
 			responseWriter.startDocument();
+			
 			// TODO - parameters and mock renderer/component ?
 			// for first time, this template only allow skin or faces variables interaction
 			template.encode(renderer,facesContext,null);
 			responseWriter.endDocument();
 			responseWriter.flush();
 			responseWriter.close();
+			
+			if (_CompressStyleOn) {    
+			    	CssCompressor compressor = new CssCompressor(countingOutputWriter.getContent()); // Compressing css document and printing result in response stream
+			    	bytesLength = compressor.compress(writer, -1);
+			    	writer.flush();
+			    	writer.close();
+			} else {
+			    	writer.write(countingOutputWriter.getContent().toString());  // Write not compressed style content
+			    	bytesLength = countingOutputWriter.getWritten();
+			    	writer.flush();
+			    	writer.close();
+			}
+			
 		} else {
 			throw new FacesException("FacesContext for resource from template "+base.getKey()+" is null");
 		}
-		return 0;
+		return bytesLength;
 	}
 
 	/**

Added: branches/3.1.x/framework/impl/src/test/java/org/ajax4jsf/css/CssCompressorTest.java
===================================================================
--- branches/3.1.x/framework/impl/src/test/java/org/ajax4jsf/css/CssCompressorTest.java	                        (rev 0)
+++ branches/3.1.x/framework/impl/src/test/java/org/ajax4jsf/css/CssCompressorTest.java	2007-12-10 14:48:48 UTC (rev 4655)
@@ -0,0 +1,87 @@
+/*
+ * CssCompressorTest.java		Date created: 21.11.2007
+ * Last modified by: $Author$
+ * $Revision$	$Date$
+ */
+
+package org.ajax4jsf.css;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.ajax4jsf.resource.CountingOutputWriter;
+
+/**
+ * Test case for css compressing process
+ * @author Andrey Markavtsov
+ *
+ */
+public class CssCompressorTest extends TestCase {
+    
+    /** Length of correctly compressed css example */
+    private static final int lengthCompressed = 610;
+    
+    /** css example to be comressed */
+    private static final String cssExample = 
+	    "HTML { \n"+
+	    "MARGIN-BOTTOM: 0.01em; HEIGHT: 100%; BACKGROUND-COLOR: #ffffff;\n" +
+	    "}\n" +
+	    "TD {\n" +
+	    "FONT-SIZE: 10px; COLOR: #000000; FONT-FAMILY: verdana, arial\n" +
+    	    "}\n" +
+    	    "TH {\n" +
+            "FONT-WEIGHT: bold; FONT-SIZE: 10px; COLOR: #336699; FONT-FAMILY: verdana, arial; BACKGROUND-COLOR: #ffffff; text-align:left;\n" +
+            "}\n" +
+    	    ".header {\n" +
+    	    "FONT-WEIGHT: bold; FONT-SIZE: 11px; COLOR: #000000; FONT-FAMILY: verdana, arial\n" +
+    	    "}\n"; 
+    
+    /**
+     * Constructor
+     * @param name
+     */
+    public CssCompressorTest(String name) {
+	super(name);
+	// TODO Auto-generated constructor stub
+    }
+
+    /* (non-Javadoc)
+     * @see junit.framework.TestCase#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+	// TODO Auto-generated method stub
+	super.setUp();
+    }
+
+    /* (non-Javadoc)
+     * @see junit.framework.TestCase#tearDown()
+     */
+    @Override
+    protected void tearDown() throws Exception {
+	// TODO Auto-generated method stub
+	super.tearDown();
+    }
+    
+    
+    /**
+     * Test method
+     * @throws IOException
+     */
+    public void testCssCompressor () throws IOException {
+	
+	StringBuffer cssBuffer = new StringBuffer(cssExample);
+	CssCompressor compressor = new CssCompressor(cssBuffer);
+	CountingOutputWriter writer = new CountingOutputWriter();
+	compressor.compress(writer, -1);
+	
+	// compressed length should equal
+	assertEquals(writer.getWritten(), lengthCompressed);
+	
+   }
+    
+    
+    
+
+}




More information about the richfaces-svn-commits mailing list