//----------------------------BEGIN LICENSE----------------------------
/*
* Willow : the Open Source WorkFlow Project
* Distributable under GNU LGPL license by gun.org
*
* Copyright (C) 2004-2010 huihoo.org
* Copyright (C) 2004-2010 ZosaTapo <dertyang@hotmail.com>
*
* ====================================================================
* Project Homepage : http://www.huihoo.org/willow
* Source Forge : http://sourceforge.net/projects/huihoo
* Mailing list : willow@lists.sourceforge.net
*/
//----------------------------END LICENSE-----------------------------
package org.huihoo.workflow.rules.compiler;
import java.io.CharArrayWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.Hashtable;
import org.huihoo.workflow.rules.Constants;
import org.huihoo.workflow.rules.ScriptException;
import org.huihoo.workflow.rules.config.ScriptCompilationContext;
import org.huihoo.workflow.rules.util.StringManager;
/**
* @author reic
*
* To change the template for this generated type comment go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
public class ScriptReader
{
private static StringManager sm = StringManager.getManager(Constants.RESOURCE_BUNDLE_BASE);
private Mark current = null;
private ScriptCompilationContext context;
private String encoding = null;
protected ScriptReader(String file, ScriptCompilationContext ctx, String encoding) throws ScriptException, FileNotFoundException
{
this.context = ctx;
this.encoding = encoding;
if (this.encoding == null)
{
this.encoding = "ISO-8859-1";
}
loadFile(file, this.encoding);
}
protected ScriptReader(String content, ScriptCompilationContext ctx) throws ScriptException, FileNotFoundException
{
this.context = ctx;
loadContent(content);
}
public static ScriptReader createGodjReader(String file, ScriptCompilationContext ctx, String encoding)
throws ScriptException, FileNotFoundException
{
return new ScriptReader(file, ctx, encoding);
}
public static ScriptReader createGodjReader(String content, ScriptCompilationContext ctx)
throws ScriptException, FileNotFoundException
{
return new ScriptReader(content, ctx);
}
public boolean hasMoreInput() throws ScriptException
{
if (current.cursor >= current.stream.length)
{
return false;
}
return true;
}
public int nextChar() throws ScriptException
{
if (!hasMoreInput())
return -1;
int ch = current.stream[current.cursor];
current.cursor++;
if (ch == '\n')
{
current.line++;
current.col = 0;
}
else
{
current.col++;
}
return ch;
}
/**
* Gets Content until the next potential RULE element. Because all elements
* begin with a '<' we can just move until we see the next one.
*/
String nextContent()
{
int cur_cursor = current.cursor;
int len = current.stream.length;
char ch;
if (peekChar() == '\n')
{
current.line++;
current.col = 0;
}
else
{
current.col++;
}
// pure obsfuscated genius!
while ((++current.cursor < len) && ((ch = current.stream[current.cursor]) != '<'))
{
if (ch == '\n')
{
current.line++;
current.col = 0;
}
else
{
current.col++;
}
}
return new String(current.stream, cur_cursor, current.cursor - cur_cursor);
}
char[] getChars(Mark start, Mark stop) throws ScriptException
{
Mark oldstart = mark();
reset(start);
CharArrayWriter caw = new CharArrayWriter();
while (!stop.equals(mark()))
caw.write(nextChar());
caw.close();
reset(oldstart);
return caw.toCharArray();
}
public int peekChar()
{
return current.stream[current.cursor];
}
public Mark mark()
{
return new Mark(current);
}
public void reset(Mark mark)
{
current = new Mark(mark);
}
public boolean matchesIgnoreCase(String string) throws ScriptException
{
Mark mark = mark();
int ch = 0;
int i = 0;
do
{
ch = nextChar();
if (Character.toLowerCase((char) ch) != string.charAt(i++))
{
reset(mark);
return false;
}
}
while (i < string.length());
reset(mark);
return true;
}
public boolean matches(String string) throws ScriptException
{
Mark mark = mark();
int ch = 0;
int i = 0;
do
{
ch = nextChar();
if (((char) ch) != string.charAt(i++))
{
reset(mark);
return false;
}
}
while (i < string.length());
reset(mark);
return true;
}
public void advance(int n) throws ScriptException
{
while (--n >= 0)
nextChar();
}
public int skipSpaces() throws ScriptException
{
int i = 0;
while (isSpace())
{
i++;
nextChar();
}
return i;
}
/**
* Skip until the given string is matched in the stream.
* When returned, the context is positioned past the end of the match.
* @param s The String to match.
* @return A non-null <code>Mark</code> instance if found,
* <strong>null</strong> otherwise.
*/
public Mark skipUntil(String limit) throws ScriptException
{
Mark ret = null;
int limlen = limit.length();
int ch;
skip : for (ret = mark(), ch = nextChar(); ch != -1; ret = mark(), ch = nextChar())
{
if (ch == limit.charAt(0))
{
for (int i = 1; i < limlen; i++)
{
if (Character.toLowerCase((char) nextChar()) != limit.charAt(i))
continue skip;
}
return ret;
}
}
return null;
}
final boolean isSpace()
{
return peekChar() <= ' ';
}
/**
* Parse a space delimited token.
* If quoted the token will consume all characters up to a matching quote,
* otherwise, it consumes up to the first delimiter character.
* @param quoted If <strong>true</strong> accept quoted strings.
*/
public String parseToken(boolean quoted) throws ScriptException
{
StringBuffer stringBuffer = new StringBuffer();
skipSpaces();
stringBuffer.setLength(0);
int ch = peekChar();
if (quoted)
{
if (ch == '"' || ch == '\'')
{
char endQuote = ch == '"' ? '"' : '\'';
// Consume the open quote:
ch = nextChar();
for (ch = nextChar(); ch != -1 && ch != endQuote; ch = nextChar())
{
if (ch == '\\')
ch = nextChar();
stringBuffer.append((char) ch);
}
// Check end of quote, skip closing quote:
if (ch == -1)
{
throw new ScriptException(mark(), sm.getString("script.error.quotes.unterminated"));
}
}
else
{
throw new ScriptException(mark(), sm.getString("script.error.attr.quoted"));
}
}
else
{
if (!isDelimiter()) // Read value until delimiter is found:
do
{
ch = nextChar();
// Take care of the quoting here.
if (ch == '\\')
{
if (peekChar() == '"' || peekChar() == '\'' || peekChar() == '>' || peekChar() == '%')
ch = nextChar();
}
stringBuffer.append((char) ch);
}
while (!isDelimiter());
}
return stringBuffer.toString();
}
/**
* Parse an attribute/value pair, and store it in provided hash table.
* The attribute/value pair is defined by:
* <pre>
* av := spaces token spaces '=' spaces token spaces
* </pre>
* Where <em>token</em> is defined by <code>parseToken</code> and
* <em>spaces</em> is defined by <code>skipSpaces</code>.
* The name is always considered case insensitive, hence stored in its
* lower case version.
* @param into The Hashtable instance to save the result to.
*/
private void parseAttributeValue(Hashtable into) throws ScriptException
{
// Get the attribute name:
skipSpaces();
String name = parseToken(false);
// Check for an equal sign:
skipSpaces();
if (peekChar() != '=')
{
throw new ScriptException(mark(), sm.getString("script.error.attr.novalue", new Object[] { name }));
}
char ch = (char) nextChar();
// Get the attribute value:
skipSpaces();
String value = parseToken(true);
skipSpaces();
// Add the binding to the provided hashtable:
into.put(name, value);
return;
}
/**
* Parse some tag attributes.
* The stream is assumed to be positioned right after the tag name. The
* syntax recognized is:
* <pre>
* tag-attrs := empty | attr-list (">" | "-->" | %>)
* attr-list := empty | av spaces attr-list
* empty := spaces
* </pre>
* Where <em>av</em> is defined by <code>parseAttributeValue</code>.
* @return A Hashtable mapping String instances (variable names) into
* String instances (variable values).
*/
public Hashtable parseTagAttributes() throws ScriptException
{
Hashtable values = new Hashtable(11);
while (true)
{
skipSpaces();
int ch = peekChar();
if (ch == '>')
{
return values;
}
if (ch == '-')
{
Mark mark = mark();
nextChar();
// Close NCSA like attributes "->"
try
{
if (nextChar() == '-' && nextChar() == '>')
return values;
}
finally
{
reset(mark);
}
}
else if (ch == '%')
{
Mark mark = mark();
nextChar();
// Close variable like attributes "%>"
try
{
if (nextChar() == '>')
return values;
}
finally
{
reset(mark);
}
}
else if (ch == '/')
{
Mark mark = mark();
nextChar();
// XMLesque Close tags
try
{
if (nextChar() == '>')
return values;
}
finally
{
reset(mark);
}
}
if (ch == -1)
break;
// Parse as an attribute=value:
parseAttributeValue(values);
}
// Reached EOF:
throw new ScriptException(mark(), sm.getString("script.error.tag.attr.unterminated"));
}
/**
* Parse utils - Is current character a token delimiter ?
* Delimiters are currently defined to be =, >, <, ", and ' or any
* any space character as defined by <code>isSpace</code>.
* @return A boolean.
*/
private boolean isDelimiter() throws ScriptException
{
if (!isSpace())
{
int ch = peekChar();
// Look for a single-char work delimiter:
if (ch == '=' || ch == '>' || ch == '"' || ch == '\'' || ch == '/')
return true;
// Look for an end-of-comment or end-of-tag:
if (ch == '-')
{
Mark mark = mark();
if (((ch = nextChar()) == '>') || ((ch == '-') && (nextChar() == '>')))
{
reset(mark);
return true;
}
else
{
reset(mark);
return false;
}
}
return false;
}
else
{
return true;
}
}
private void loadFile(String file, String encoding) throws ScriptException, FileNotFoundException
{
loadFile(new File(file), encoding);
}
private void loadFile(File file, String encoding) throws ScriptException, FileNotFoundException
{
// Default encoding if needed:
if (encoding == null)
{
encoding = this.encoding;
}
InputStreamReader reader = null;
try
{
reader = new InputStreamReader(new FileInputStream(file), encoding);
CharArrayWriter caw = new CharArrayWriter();
char buf[] = new char[1024];
for (int i = 0;(i = reader.read(buf)) != -1;)
caw.write(buf, 0, i);
caw.close();
current = new Mark(this, caw.toCharArray(), file.getPath());
}
catch (FileNotFoundException fnfe)
{
throw fnfe;
}
catch (Throwable ex)
{
throw new ScriptException(sm.getString("script.error.file.cannot.read", new Object[] { file }));
}
finally
{
if (reader != null)
{
try
{
reader.close();
}
catch (Exception any)
{
}
}
}
}
private void loadContent(String content)
{
StringReader reader = new StringReader(content);
try
{
CharArrayWriter caw = new CharArrayWriter();
char buf[] = new char[1024];
for (int i = 0;(i = reader.read(buf)) != -1;)
caw.write(buf, 0, i);
caw.close();
current = new Mark(this, caw.toCharArray(), null);
}
catch (IOException ex)
{
}
finally
{
try
{
reader.close();
}
catch (Exception any)
{
}
}
}
}