Ok, a new version is available. This is an extended WebappLoader class,
with in memory compression.
I would like to make a "general" class, that can use "any"
ResourceLoader, than Chris suggested (when will have more time to make it).
Thank to Chris and Nathan for suggest good programing tips. :-)
package com.ys.velocity;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import org.apache.commons.collections.ExtendedProperties;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.VelocityException;
import org.apache.velocity.runtime.resource.util.StringResource;
import org.apache.velocity.tools.view.servlet.WebappLoader;
/**
* A compressor for templates before cached and parsed by any loader.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Lutischan Ferenc</a>
*/
public class HTMLCompressFilter extends WebappLoader {
private static final String ALPHA =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$\\";
private static final String SPECIAL = "{};=*/+-%()[]<>"+(char) 10;
private static final String END_LINE = "{};=*/+-%([<>"+(char) 10;
/**
* Get an InputStream so that the Runtime can build a
* template with it.
*
* @param templateName name of template to get
* @return InputStream containing the compressed template
* @throws ResourceNotFoundException if template not found
* in the file template path.
*/
public InputStream getResourceStream(String templateName)
throws ResourceNotFoundException {
InputStream in = super.getResourceStream(templateName);
return getCompressedStream(in);
}
/**
* Compress the html stream.
* @param in The input stream of template.
* @return A compressed bytestream.
*/
private InputStream getCompressedStream(InputStream in){
byte [] origBytes = getBytesFromInputStream(in);
byte newBytes[] = compressHTML(origBytes);
rsvc.info("Compressed template. Original length:
"+origBytes.length+ " New length: " + newBytes.length);
return new ByteArrayInputStream(newBytes);
}
/**
* Compress input html byte array.
* @param origBytes input byte array.
* @return compressed byte array.
*/
private byte[] compressHTML(byte[] origBytes) {
byte newBytes[] = null;
newBytes = new byte[origBytes.length];
int i = 0;
int j = 0;
int end = 0;
for (i = 0; i < origBytes.length; i++) {
byte curByte = origBytes[i];
switch (curByte) {
case 32: case 9:// space
if (j > 0 && newBytes[j-1] != 32) { // only one space
if (origBytes[i+1] == '<') {
if (!checkTag(i, origBytes, "/div>")
&& !checkTag(i, origBytes, "/body>")
&& !checkTag(i, origBytes, "/tr>")
&& !checkTag(i, origBytes, "option>")
&& !checkTag(i, origBytes, "/html>")) {
newBytes[j] = 32;
j++;
}
} else {
newBytes[j] = 32;
j++;
}
}
break;
case 10: case 13: // skip lf, cr
break;
case '<': // tag begining
if ( checkTag(i, origBytes, "pre") ) {
end = getTagEnd(i, origBytes, "/pre>");
System.arraycopy(origBytes, i, newBytes, j,
end-i+1);
j+=end-i+1;
i = end;
} else if ( checkTag(i, origBytes, "textarea") ) {
end = getTagEnd(i, origBytes, "/textarea>");
System.arraycopy(origBytes, i, newBytes, j,
end-i+1);
j+=end-i+1;
i = end;
} else if (checkTag(i, origBytes, "script")){
end = getTagEnd(i, origBytes, "/script>");
byte newJs[] = getCompressedJs(origBytes, i, end);
System.arraycopy(newJs, 0, newBytes, j,
newJs.length);
j+=newJs.length;
i = end;
} else if (checkTag(i, origBytes, "style")) {
end = getTagEnd(i, origBytes, "/style>");
byte newCSS[] = getCompressedCSS(origBytes, i, end);
System.arraycopy(newCSS, 0, newBytes, j,
newCSS.length);
j+=newCSS.length;
i = end;
} else if (checkTag(i, origBytes, "<!--")) { // comment
i = skipComment(i, origBytes);
} else {
newBytes[j] = '<';
j++;
}
break;
default:
newBytes[j] = origBytes[i];
j++;
break;
}
}
byte result[] = new byte[j];
System.arraycopy(newBytes, 0, result, 0, j);
return result;
}
/**
* Check from given position, in the given byte array, the given tag.
* @param i from position
* @param origBytes in the byte array
* @param tag the tag (e.g. "/div>")
* @return true if found, false if not found.
*/
private boolean checkTag(int i, byte[] origBytes, String tag) {
String upperTag = tag.toUpperCase();
if (i + upperTag.length() >= origBytes.length) {
return false;
}
String curTag = new String(origBytes, i+1, upperTag.length());
return upperTag.equals(curTag.toUpperCase());
}
/**
* Skip html comment from given position to end ("-->");
* @param i from position
* @param origBytes in the array
* @return the position after end of comment.
*/
private int skipComment(int i, byte[] origBytes) {
int j = i;
while (j < origBytes.length) {
if (origBytes[j] == '-' && checkTag(j, origBytes, "->")) {
j = j+3;
break;
}
j++;
}
return j;
}
/**
* Return the given tag end position in the byte array.
* @param i from position
* @param origBytes In the byte array
* @param tag the end tag e.g. ("/span>")
* @return the position of the end of tag.
*/
private int getTagEnd(int i, byte[] origBytes, String tag) {
int j = i;
while (j < origBytes.length) {
if (origBytes[j] == '<' && checkTag(j, origBytes, tag)) {
j = j+tag.length();
break;
}
j++;
}
return j;
}
/**
* return the compressed javascript-byte array.
* @param origBytes orig byte array
* @param start from
* @param end to end
* @return The compressed javascript byte array
*/
private byte[] getCompressedJs(byte[] origBytes, int start, int end) {
boolean isHTMLComment = false;
byte temp[] = new byte[end-start+1];
int j= 0;
for (int k = start; k<=end; k++) {
byte curByte = origBytes[k];
switch (curByte) {
case 32: case 9:// space, tab
if (k<end && isAlpha(origBytes[k+1]) &&
!isSpecial(temp[j-1])) { // only one space
temp[j] = 32;
j++;
}
break;
case 10: // lf
if (isHTMLComment || (j>0 && !isEndLine(temp[j-1])) ) {
temp[j] = 10;
j++;
}
break;
case 13: // cr
break;
case '/': // tag begining
// if script is commented with
'<!--', '//' comment is not removed,
// because e.g.: '//--> </script>''
if ( origBytes[k+1] == '/' && !isHTMLComment ) {
while (k<=end && origBytes[k] != '\n') {k++;}
} else
if ( origBytes[k+1] == '*' ) {
k++;
while (k<=end && origBytes[k-1] != '*' &&
origBytes[k] != '/') {k++;}
} else {
temp[j] = origBytes[k];
j++;
}
break;
case '"': // tag begining
while (k <= end) {
temp[j] = origBytes[k];
j++;
k++;
if (origBytes[k] == '"' ) {
temp[j] = origBytes[k];
j++;
break;
}
}
break;
case '\'': // tag begining
while (k <= end) {
temp[j] = origBytes[k];
j++;
k++;
if (origBytes[k] == '\'' ) {
temp[j] = origBytes[k];
j++;
break;
}
}
break;
case '<':
if (origBytes[k+1] == '!' && origBytes[k+2] == '-' &&
origBytes[k+3] == '-') {
isHTMLComment = true;
}
temp[j] = '<';
j++;
break;
default:
temp[j] = origBytes[k];
j++;
break;
}
}
byte result[] = new byte[j];
System.arraycopy(temp, 0, result, 0, j);
return result;
}
/**
* return the compressed css-byte array.
* @param origBytes orig byte array
* @param start from
* @param end to end
* @return The compressed css-byte array
*/
private byte[] getCompressedCSS(byte[] origBytes, int start, int end) {
byte temp[] = new byte[end-start+1];
int j= 0;
for (int k = start; k<=end; k++) {
byte curByte = origBytes[k];
switch (curByte) {
case 32: case 9: case 10: case 13:// space, tab
if (j <8) {
temp[j] = 32;
j++;
}
break;
default:
temp[j] = origBytes[k];
j++;
break;
}
}
byte result[] = new byte[j];
System.arraycopy(temp, 0, result, 0, j);
return result;
}
/**
* Is the given byte is Alpha?
* @param b The byte to check
* @return true if is the given byte is an Alpha
*/
private boolean isAlpha(byte b) {
return ALPHA.indexOf((char) b)!=-1 || b > 126;
}
/**
* Is the given byte is Special?
* @param b The byte to check
* @return true if is the given byte is a Special
*/
private boolean isSpecial(byte b) {
return SPECIAL.indexOf((char) b)!=-1;
}
/**
* Is the given byte is a not useable end line?
* @param b The byte to check
* @return true if is the given byte is a not useable end line
*/
private boolean isEndLine(byte b) {
return END_LINE.indexOf((char) b)!=-1;
}
/**
* Read from given InputStream into a byte array.
* @param in The InputStream
* @return The byte array
*/
private byte[] getBytesFromInputStream(InputStream in) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte buffer[] = new byte[4096];
int length;
try { while ( (length = in.read(buffer)) > 0 ) {
baos.write(buffer, 0, length);
}
} catch (IOException ex) {
rsvc.error(ex);
}
return baos.toByteArray();
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]