package net.xoetrope.optional.laf.synth;
import java.io.InputStream;
import java.awt.Color;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Hashtable;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.optional.filter.FilePreprocessor;
import net.xoetrope.optional.laf.ImageConverter;
import net.xoetrope.xui.XProject;
import net.xoetrope.xui.XProjectManager;
import net.xoetrope.xui.build.BuildProperties;
import net.xoetrope.xui.style.XStyle;
import net.xoetrope.xui.style.XStyleManager;
/**
* Process a synth file to replace colors etc... with the styles
* <p>Copyright (c) Xoetrope Ltd., 2001-2005<br>
* License: see license.txt
* $Revision: 1.13 $
*/
public class SynthPreprocessor implements FilePreprocessor
{
private static Hashtable methodNames = new Hashtable();
private static String[] weightTags = { "PLAIN", "BOLD" };
private static String[] italicTags = { "PLAIN", "ITALIC" };
private XStyleManager styleManager;
private SvgPreprocessor svgPreprocessor;
private ImageConverter imageConverter;
private XProject currentProject;
private String transcoderClassName;
/** Creates a new instance of SynthPreprocessor */
public SynthPreprocessor()
{
currentProject = XProjectManager.getCurrentProject();
String tc = currentProject.getStartupParam( "TranscoderClass" );
if ( tc != null )
transcoderClassName = tc;
else
transcoderClassName = "net.xoetrope.optional.laf.synth.batik.PngTranscoderWrapper";
/**
* @todo Use constants for the IDs
*/
methodNames.put( "getFontFace", new Integer( 0 ));
methodNames.put( "getFontSize", new Integer( 1 ));
methodNames.put( "getColorBackground", new Integer( 2 ));
methodNames.put( "getColorForeground", new Integer( 3 ));
methodNames.put( "getFontWeight", new Integer( 4 ));
methodNames.put( "getFontItalic", new Integer( 5 ));
methodNames.put( "svgToPng", new Integer( 6 ));
methodNames.put( "svgToTiff", new Integer( 7 ));
methodNames.put( "getColorResourceBackground", new Integer( 8 ));
methodNames.put( "getColorResourceForeground", new Integer( 9 ));
}
/**
* Read the file from the input stream, do any substitutions required and
* return the processed file as a string.
* @param is the input stream
* @throws java.io.IOException problems reading the input stream
* @return the processed file
*/
public String process( BufferedInputStream is ) throws IOException
{
styleManager = currentProject.getStyleManager();
byte[] b = new byte[ 1000 ];
if ( is != null ) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int i = is.read( b );
while ( i != -1 )
{
baos.write( b, 0, i );
b = new byte[ 1000 ];
i = is.read( b );
}
return findAndReplace( methodNames, baos.toString());
// StringBuffer sb = new StringBuffer( baos.toString());
// substituteStyleAttribute( "${getFontFace(", XStyle.FONT_FACE, sb );
// substituteStyleAttribute( "${getFontSize(", XStyle.FONT_SIZE, sb );
// substituteColorStyleAttribute( "${getColorBackground(", XStyle.COLOR_BACK, sb );
// substituteColorStyleAttribute( "${getColorForeground(", XStyle.COLOR_FORE, sb );
// substituteIndexedStyleAttribute( "${getFontWeight(", XStyle.FONT_WEIGHT, sb, weightTags );
// substituteIndexedStyleAttribute( "${getFontItalic(", XStyle.FONT_ITALIC, sb, italicTags );
// substitutePngForSvg( "${svgToPng(", sb );
//
// return sb.toString();
}
return null;
}
/**
* Search and replace a function references embedded in the synth file
* @param methodNames the table of methods to find and replace
* @param originalText the original config file text
* @return the processed text
*/
protected String findAndReplace( Hashtable methodNames, String originalText )
{
/**
* @todo Use something like a StringTokenizer to split the string and allow for recursion
*/
String resultString = "";
int startPos = 0;
int espressionStart = 0;
int argsStart = 0;
int argsEnd = 0;
while ( ( espressionStart = originalText.indexOf( "${", startPos )) > 0 ) {
argsStart = originalText.indexOf( "(", espressionStart );
String key = originalText.substring( espressionStart + 2, argsStart );
argsEnd = originalText.indexOf( ")}", argsStart );
if ( argsEnd > 0 ) {
resultString += originalText.substring( startPos, espressionStart );
startPos = argsEnd + 2;
String result = "";
String args = originalText.substring( argsStart + 1, argsEnd );
args = findAndReplace( methodNames, args );
switch ( ((Integer)methodNames.get( key )).intValue () ) {
case 0:
result = getStyleAttribute( args, XStyle.FONT_FACE );
break;
case 1:
result = getStyleAttribute( args, XStyle.FONT_SIZE );
break;
case 2:
result = getColorStyleAttribute( args, XStyle.COLOR_BACK, false );
break;
case 3:
result = getColorStyleAttribute( args, XStyle.COLOR_FORE, false );
break;
case 4:
result = getIndexedStyleAttribute( args, XStyle.FONT_WEIGHT, weightTags );
break;
case 5:
result = getIndexedStyleAttribute( args, XStyle.FONT_ITALIC, italicTags );
break;
case 6:
result = getPngForSvg( args );
break;
case 7:
result = getTiffForSvg( args );
break;
case 8:
result = getColorStyleAttribute( args, XStyle.COLOR_BACK, true );
break;
case 9:
result = getColorStyleAttribute( args, XStyle.COLOR_FORE, true );
break;
}
resultString += result;
}
}
if ( argsEnd == 0 )
return originalText;
if ( argsEnd < originalText.length() )
resultString += originalText.substring( argsEnd + 2 );
return resultString;
}
/**
* Replace the tagged field
* @param key the style name
* @param attrib the attribute to lookup in the style
* @return the style value
*/
protected String getStyleAttribute( String key, int attrib )
{
return styleManager.getStyle( key.trim()).getStyleAsString( attrib );
}
/**
* Replace the tagged field
* @param key the argument value to replace
* @param attrib the attribute to lookup in the style
* @param asColorResource output the colour as a ColorUIResource
* @return the color style value
*/
protected String getColorStyleAttribute( String key, int attrib, boolean asColorResource )
{
Color attribValue = styleManager.getStyle( key.trim()).getStyleAsColor( attrib );
int r = attribValue.getRed();
int g = attribValue.getGreen();
int b = attribValue.getBlue();
String val = "";
if ( !asColorResource ) {
if ( r < 11 )
val = "0";
val += Integer.toHexString( r );
if ( g < 11 )
val += "0";
val += Integer.toHexString( g );
if ( b < 11 )
val += "0";
val += Integer.toHexString( b );
}
else {
val = "<int>";
val += Integer.toString( r );
val += "</int>";
val += "<int>";
val += Integer.toString( g );
val += "</int>";
val += "<int>";
val += Integer.toString( b );
val += "</int>";
}
return val;
}
/**
* Replace the tagged field
* @param key the style name
* @param attrib the attribute to lookup in the style
* @param values the values array into which the attribute indexes
* @return the style value
*/
protected String getIndexedStyleAttribute( String key, int attrib, String[] values )
{
int attribValue = styleManager.getStyle( key.trim()).getStyleAsInt( attrib );
return values[ Math.min( attribValue, values.length ) ];
}
/**
* Replace the tagged field and create a PNG file in place of the original SVG.
* The key can optionally include the width and height.
* @param key the style name
* @return the png file name
*/
protected String getPngForSvg( String key )
{
int width = 50;
int height = 50;
int pos;
if ( ( pos = key.indexOf( ',')) > 0 ) {
int pos2 = key.indexOf( ',', pos + 1 );
width = Integer.parseInt( key.substring( pos + 1, pos2 ));
height = Integer.parseInt( key.substring( pos2 + 1 ));
key = key.substring( 0, pos );
}
return convertSvg( key.trim(), width, height, true );
}
/**
* Replace the tagged field and create a TIFF file in place of the original SVG.
* The key can optionally include the width and height.
* @param key the style name
* @return the png file name
*/
protected String getTiffForSvg( String key )
{
int width = 50;
int height = 50;
int pos;
if ( ( pos = key.indexOf( ',')) > 0 ) {
int pos2 = key.indexOf( ',', pos + 1 );
width = Integer.parseInt( key.substring( pos + 1, pos2 ));
height = Integer.parseInt( key.substring( pos2 + 1 ));
key = key.substring( 0, pos );
}
return convertSvg( key.trim(), width, height, false );
}
/**
* Converts an SVG file to a PNG or a TIFF file
* @param name the file name
* @param width the target image width
* @param height the targetimage height
* @param ext the converted file extension
* @param isPng true for a png file, false for a tiff file
* @return the png file name
*/
protected String convertSvg( String name, int width, int height, boolean isPng )
{
XProject currentProject = XProjectManager.getCurrentProject();
int pos = name.indexOf( ".svg" );
try {
URL sourceUrl = currentProject.getUrl( name );
if ( BuildProperties.DEBUG ) {
if ( sourceUrl == null ) {
DebugLogger.logError( "Missing synth resource:" + name );
return "synth/blank.png";
}
}
String srcUrlStr = sourceUrl.toString();
if ( imageConverter == null ) {
// If batik is not available don't try reading the SVG
Class clazz = null;
if ( !isPng )
clazz = Class.forName( "net.xoetrope.optional.laf.synth.batik.TiffTranscoderWrapper" );
else
clazz = Class.forName( transcoderClassName.trim() );
if ( clazz == null )
return name;
imageConverter = (ImageConverter)clazz.newInstance();
}
String resName = name.substring( 0, pos ) + imageConverter.getOutputExt();
int svgDirPos = resName.indexOf( "svg/" );
if ( svgDirPos >= 0 )
resName = resName.substring( 0, svgDirPos ) + resName.substring( svgDirPos + 4 );
String outUrlStr = srcUrlStr.substring( 0, srcUrlStr.indexOf( name )) + resName;
try {
File sourceFile = new File( sourceUrl.toURI());
File targetFile = new File( new URL( outUrlStr ).toURI());
if ( targetFile.lastModified() > sourceFile.lastModified()) {
DebugLogger.trace( "File up-to-date, no need to convert: " + name );
return resName;
}
}
catch ( MalformedURLException mfue ) {}
if ( outUrlStr.indexOf( "file:" ) == 0 )
outUrlStr = outUrlStr.substring( 6 );
BufferedInputStream bis = currentProject.getBufferedInputStream( srcUrlStr );
if ( bis != null ) {
if ( svgPreprocessor == null )
svgPreprocessor = (SvgPreprocessor)SvgPreprocessor.class.newInstance();
String str = svgPreprocessor.process( bis );
InputStream is = new ByteArrayInputStream( str.getBytes());
OutputStream ostream = new FileOutputStream( outUrlStr );
imageConverter.convert( name, is, ostream, width, height );
return resName;
}
}
catch ( NoClassDefFoundError e )
{
e.printStackTrace();
}
catch ( Throwable e )
{
if ( BuildProperties.DEBUG )
DebugLogger.logError( "Cannot convert: " + name );
else
e.printStackTrace();
return "synth/blank.png";
}
return name;
}
}