/*******************************************************************************
* Copyright (c) 2002, 2008 Innoopract Informationssysteme GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Innoopract Informationssysteme GmbH - initial API and implementation
******************************************************************************/
package org.eclipse.rwt.internal.resources;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import org.eclipse.rwt.internal.util.HTML;
import org.eclipse.rwt.resources.IResourceManager;
public final class ResourceUtil {
private static final String ONE_LINE_COMMENT = "//";
private static final String MULTI_LINE_COMMENT_START = "/*";
private static final String MULTI_LINE_COMMENT_END = "*/";
private static final String NEWLINE_MAC = "\r";
private static final String NEWLINE_UNIX = "\n";
private static final String NEWLINE_WIN = "\r\n";
private static final char SINGLE_QUOTE = '\'';
private static final char DOUBLE_QUOTE = '"';
private static final char NO_QUOTE = '-';
private static final char BACKSLASH = '\\';
private static final char LINE_FEED = '\r';
private static final char CARRIAGE_RETURN = '\n';
private static ByteArrayOutputStream jsConcatenationBuffer = null;
static int[] read( final String name,
final String charset,
final boolean compress )
throws IOException
{
int[] result;
if( charset != null ) {
result = readText( name, charset, compress );
} else {
result = readBinary( name );
}
return result;
}
static int[] read( final InputStream is,
final String charset,
final boolean compress )
throws IOException
{
int[] result;
if( charset != null ) {
result = readText( is, charset, compress );
} else {
result = readBinary( is );
}
return result;
}
static void write( final File toWrite, final int[] content )
throws IOException
{
FileOutputStream fos = new FileOutputStream( toWrite );
try {
OutputStream out = new BufferedOutputStream( fos );
try {
for( int i = 0; i < content.length; i++ ) {
out.write( content[ i ] );
if( jsConcatenationBuffer != null
&& toWrite.getName().endsWith( "js" ) )
{
jsConcatenationBuffer.write( content[ i ] );
if( i == content.length - 1 ) {
jsConcatenationBuffer.write( '\n' );
}
}
}
} finally {
out.close();
}
} finally {
fos.close();
}
}
public static void startJsConcatenation() {
jsConcatenationBuffer = new ByteArrayOutputStream();
}
static String getJsConcatenationContentAsString() {
String result = "";
try {
result = jsConcatenationBuffer.toString( HTML.CHARSET_NAME_UTF_8 );
} catch( UnsupportedEncodingException e ) {
// ignore
}
jsConcatenationBuffer = null;
return result;
}
private static int[] readText( final String name,
final String charset,
final boolean compress )
throws IOException
{
// read resource
InputStream is = openStream( name );
int[] result;
try {
result = readText( is, charset, compress );
} finally {
is.close();
}
return result;
}
static int[] readText( final InputStream is,
final String charset,
final boolean compress )
throws UnsupportedEncodingException, IOException
{
StringBuffer buffer = new StringBuffer();
InputStreamReader reader = new InputStreamReader( is, charset );
BufferedReader br = new BufferedReader( reader );
try {
int character = br.read();
while( character != -1 ) {
buffer.append( ( char )character );
character = br.read();
}
} finally {
br.close();
}
// compress (JavaScript-) buffer if requested
if( compress ) {
compress( buffer );
}
// write just read resource to byte array stream
byte[] bytes;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
OutputStreamWriter osw
= new OutputStreamWriter( baos, HTML.CHARSET_NAME_UTF_8 );
try {
osw.write( buffer.toString() );
osw.flush();
} finally {
osw.close();
}
bytes = baos.toByteArray();
} finally {
baos.close();
}
// convert byte[] to int[] and return
int[] result = new int[ bytes.length ];
for( int i = 0; i < result.length; i++ ) {
result[ i ] = ( bytes[ i ] & 0x0ff );
}
return result;
}
private static int[] readBinary( final String name )
throws IOException
{
int[] result;
InputStream is = openStream( name );
try {
result = readBinary( is );
} finally {
is.close();
}
return result;
}
static int[] readBinary( final InputStream is ) throws IOException {
int[] result;
BufferedInputStream bis = new BufferedInputStream( is );
try {
bis.mark( Integer.MAX_VALUE );
result = new int[ getAvailableCount( bis ) ];
for( int i = 0; i < result.length; i++ ) {
result[ i ] = bis.read();
}
} finally {
bis.close();
}
return result;
}
private static int getAvailableCount( final BufferedInputStream bis )
throws IOException
{
int result = 0;
int streamElement = 0;
while( streamElement != -1 ) {
streamElement = bis.read();
if( streamElement != -1 ) {
result++;
}
}
bis.reset();
return result;
}
private static InputStream openStream( final String name )
throws IOException
{
ClassLoader loader = ResourceManagerImpl.class.getClassLoader();
URL resource = loader.getResource( name );
if( resource == null ) {
IResourceManager manager = ResourceManagerImpl.getInstance();
resource = manager.getResource( name );
}
if( resource == null ) {
throw new IOException( "Resource to read not found: " + name );
}
URLConnection con = resource.openConnection();
con.setUseCaches( false );
InputStream result = con.getInputStream();
return result;
}
static void compress( final StringBuffer javaScript ) {
removeOneLineComments( javaScript );
removeMultiLineComments( javaScript );
removeMultipleBlanks( javaScript );
// Remove blanks from arithmetic/logic operations
replace( javaScript, " = ", "=" );
replace( javaScript, " == ", "==" );
replace( javaScript, " === ", "===" );
replace( javaScript, " + ", "+" );
replace( javaScript, " - ", "-" );
replace( javaScript, " * ", "*" );
replace( javaScript, " / ", "/" );
replace( javaScript, " > ", ">" );
replace( javaScript, " < ", "<" );
replace( javaScript, " <= ", "<=" );
replace( javaScript, " >= ", ">=" );
replace( javaScript, " != ", "!=" );
replace( javaScript, " : ", ":" );
replace( javaScript, " && ", "&&" );
replace( javaScript, " || ", "||" );
replace( javaScript, " ? ", "?" );
// Always remove leading single blanks after removing blanks from
// arithmetic/logic operations
removeLeadingBlanks( javaScript );
// Always remove multiple new lines after removing leading blanks
removeMultipleNewLines( javaScript, NEWLINE_WIN );
removeMultipleNewLines( javaScript, NEWLINE_UNIX );
removeMultipleNewLines( javaScript, NEWLINE_MAC );
// Remove blanks from brackets and some punctuation
replace( javaScript, "( ", "(" );
replace( javaScript, " )", ")" );
replace( javaScript, "} ", "}" );
replace( javaScript, " {", "{" );
replace( javaScript, " }", "}" );
replace( javaScript, "{ ", "{" );
replace( javaScript, "[ ", "[" );
replace( javaScript, " ]", "]" );
replace( javaScript, ", ", "," );
replace( javaScript, "; ", ";" );
// Remove some new lines
replace( javaScript, NEWLINE_WIN + "}", "}" );
replace( javaScript, NEWLINE_UNIX + "}", "}" );
replace( javaScript, NEWLINE_MAC + "}", "}" );
replace( javaScript, NEWLINE_WIN + "{", "{" );
replace( javaScript, NEWLINE_UNIX + "{", "{" );
replace( javaScript, NEWLINE_MAC + "{", "{" );
replace( javaScript, "}" + NEWLINE_WIN, "}" );
replace( javaScript, "}" + NEWLINE_UNIX, "}" );
replace( javaScript, "}" + NEWLINE_MAC, "}" );
replace( javaScript, "{" + NEWLINE_WIN, "{" );
replace( javaScript, "{" + NEWLINE_UNIX, "{" );
replace( javaScript, "{" + NEWLINE_MAC, "{" );
replace( javaScript, NEWLINE_WIN + "&&", "&&" );
replace( javaScript, NEWLINE_UNIX + "&&", "&&" );
replace( javaScript, NEWLINE_MAC + "&&", "&&" );
replace( javaScript, NEWLINE_WIN + "||", "||" );
replace( javaScript, NEWLINE_UNIX + "||", "||" );
replace( javaScript, NEWLINE_MAC + "||", "||" );
replace( javaScript, NEWLINE_WIN + "=", "=" );
replace( javaScript, NEWLINE_UNIX + "=", "=" );
replace( javaScript, NEWLINE_MAC + "=", "=" );
replace( javaScript, NEWLINE_WIN + "+", "+" );
replace( javaScript, NEWLINE_UNIX + "+", "+" );
replace( javaScript, NEWLINE_MAC + "+", "+" );
replace( javaScript, NEWLINE_WIN + "-", "-" );
replace( javaScript, NEWLINE_UNIX + "-", "-" );
replace( javaScript, NEWLINE_MAC + "-", "-" );
replace( javaScript, NEWLINE_WIN + ":", ":" );
replace( javaScript, NEWLINE_UNIX + ":", ":" );
replace( javaScript, NEWLINE_MAC + ":", ":" );
replace( javaScript, NEWLINE_WIN + "?", "?" );
replace( javaScript, NEWLINE_UNIX + "?", "?" );
replace( javaScript, NEWLINE_MAC + "?", "?" );
replace( javaScript, "," + NEWLINE_WIN, "," );
replace( javaScript, "," + NEWLINE_UNIX, "," );
replace( javaScript, "," + NEWLINE_MAC, "," );
replace( javaScript, ";" + NEWLINE_WIN, ";" );
replace( javaScript, ";" + NEWLINE_UNIX, ";" );
replace( javaScript, ";" + NEWLINE_MAC, ";" );
replace( javaScript, ":" + NEWLINE_WIN, ":" );
replace( javaScript, ":" + NEWLINE_UNIX, ":" );
replace( javaScript, ":" + NEWLINE_MAC, ":" );
}
static void replace( final StringBuffer javaScript,
final String strToFind,
final String strToReplace )
{
int index = javaScript.indexOf( strToFind, 0 );
while( index != -1 ) {
if( !isInsideString( javaScript, index ) ) {
javaScript.replace( index, index + strToFind.length(), strToReplace );
} else {
index += strToFind.length();
}
index = javaScript.indexOf( strToFind, index + strToReplace.length() );
}
}
static void removeOneLineComments( final StringBuffer javaScript ) {
// strip one-line comments
int commentStart = javaScript.indexOf( ONE_LINE_COMMENT, 0 );
while( commentStart != -1 ) {
int lineEnd = nextNewLine( javaScript, commentStart );
if( !isInsideString( javaScript, commentStart ) ) {
javaScript.delete( commentStart, lineEnd );
} else {
commentStart += 2;
}
commentStart = javaScript.indexOf( ONE_LINE_COMMENT, commentStart );
}
}
static int nextNewLine( final StringBuffer buffer, final int currentPos ) {
int result = buffer.indexOf( NEWLINE_WIN, currentPos );
if( result == -1 ) {
result = buffer.indexOf( NEWLINE_UNIX, currentPos );
}
if( result == -1 ) {
result = buffer.indexOf( NEWLINE_MAC, currentPos );
}
if( result == -1 ) {
result = buffer.length();
}
return result;
}
static void removeMultiLineComments( final StringBuffer javaScript ) {
int index = javaScript.indexOf( MULTI_LINE_COMMENT_START );
while( index != -1 ) {
int end = javaScript.indexOf( MULTI_LINE_COMMENT_END );
if( end != -1 ) {
if( !isInsideString( javaScript, index ) ) {
javaScript.delete( index, end + MULTI_LINE_COMMENT_END.length() );
} else {
index += 2;
}
}
index = javaScript.indexOf( MULTI_LINE_COMMENT_START, index );
}
}
static void removeMultipleBlanks( final StringBuffer javaScript ) {
int index = javaScript.indexOf( " " );
while( index != -1 ) {
if( !isInsideString( javaScript, index ) ) {
javaScript.delete( index, index + 1 );
} else {
index += 1;
}
index = javaScript.indexOf( " ", index );
}
}
static void removeLeadingBlanks( final StringBuffer javaScript ) {
int index = javaScript.indexOf( " " );
while( index != -1 ) {
char prev = '-';
if( index > 0 ) {
prev = javaScript.charAt( index - 1 );
}
if( prev == LINE_FEED || prev == CARRIAGE_RETURN || index == 0 ) {
javaScript.delete( index, index + 1 );
} else {
index += 1;
}
index = javaScript.indexOf( " ", index );
}
}
static void removeMultipleNewLines( final StringBuffer javaScript,
final String newline ) {
String doubleNewLine = newline + newline;
int index = javaScript.indexOf( doubleNewLine );
while( index != -1 ) {
javaScript.delete( index, index + newline.length() );
index = javaScript.indexOf( doubleNewLine, index );
}
}
static boolean isInsideString( final StringBuffer javaScript,
final int position )
{
String line = getLineAtPosition( javaScript, position );
int pos = getPositionInLine( javaScript, position );
char quoteChar = NO_QUOTE;
char prevChar = NO_QUOTE;
for( int i = 0; i < pos; i++ ) {
char ch = line.charAt( i );
if( ch == DOUBLE_QUOTE && quoteChar == NO_QUOTE ) {
quoteChar = DOUBLE_QUOTE;
} else if( ch == DOUBLE_QUOTE
&& quoteChar == DOUBLE_QUOTE
&& prevChar != BACKSLASH )
{
quoteChar = NO_QUOTE;
} else if( ch == SINGLE_QUOTE && quoteChar == NO_QUOTE ) {
quoteChar = SINGLE_QUOTE;
} else if( ch == SINGLE_QUOTE
&& quoteChar == SINGLE_QUOTE
&& prevChar != BACKSLASH )
{
quoteChar = NO_QUOTE;
}
prevChar = ch;
}
return quoteChar != NO_QUOTE;
}
static String getLineAtPosition( final StringBuffer javaScript,
final int position )
{
String line = "";
if( position >= 0 && position < javaScript.length() ) {
int start = position;
int end = position;
char ch = javaScript.charAt( start );
while( ch != LINE_FEED && ch != CARRIAGE_RETURN && start > 0 ) {
start--;
ch = javaScript.charAt( start );
}
ch = javaScript.charAt( end );
while( ch != LINE_FEED
&& ch != CARRIAGE_RETURN
&& end < javaScript.length() )
{
ch = javaScript.charAt( end );
end++;
}
line = javaScript.substring( start, end );
if( line.startsWith( NEWLINE_MAC ) || line.startsWith( NEWLINE_UNIX ) ) {
line = line.substring( 1 );
}
if( line.endsWith( NEWLINE_MAC ) || line.endsWith( NEWLINE_UNIX ) ) {
line = line.substring( 0, line.length() - 1 );
}
}
return line;
}
static int getPositionInLine( final StringBuffer javaScript,
final int position )
{
int pos = 0;
if( position >= 0 && position < javaScript.length() ) {
int start = position;
char ch = javaScript.charAt( start );
while( ch != LINE_FEED && ch != CARRIAGE_RETURN && start > 0 ) {
start--;
pos++;
ch = javaScript.charAt( start );
}
if( ch == LINE_FEED || ch == CARRIAGE_RETURN ) {
pos--;
}
}
return pos;
}
private ResourceUtil() {
// prevent instantiation
}
}