/* * Created on Jul 5, 2004 * * Copyright Ian Kaplan 2004, Bear Products International * * You may use this code for any purpose, without restriction, * including in proprietary code for which you charge a fee. * In using this code you acknowledge that you understand its * function completely and accept all risk in its use. * * @author Ian Kaplan, www.bearcave.com, iank@bearcave.com */ package xmlexpr; import java.io.IOException; import java.io.StringWriter; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.XMLSerializer; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; /** * ParseExpToXML *
The syntax for assignments and expressions in the input string is:
statement: assignment | addExp
assignment: ident "=" addExp
addExp: term | term addOp addExp
term: unaryExpr | unaryExpr mulOp term
unaryExpr: factor | minus factor
factor: ident | number | "(" addExp ")"
* @author Ian Kaplan, iank@bearcave.com, Jul 5, 2004
*
*/
public class ParseExpToXML
{
private Document mDoc = null;
private Scanner mScan = null;
private String mNameSpace = null;
private String mPrefix = null;
/**
Create an DOM Element object. If mNameSpace is set then create the
Element with a name space, otherwise, create an unqualified Element.
*/
private Element createElement( String xmlTag )
{
Element elem = null;
if (mNameSpace == null) {
elem = mDoc.createElement( xmlTag );
}
else {
if (mPrefix != null) {
xmlTag = mPrefix + ":" + xmlTag;
}
elem = mDoc.createElementNS( mNameSpace, xmlTag );
}
return elem;
} // createElement
/*
This function is passed a DOM document and returns the
XML (in String form) for this document. The XML is produced
with indentation (for readability in debugging).
*/
private String documentToString(Document doc)
{
StringWriter writer = new StringWriter();
OutputFormat out = new OutputFormat();
out.setOmitXMLDeclaration(true);
out.setIndenting( true );
out.setIndent( 4 );
out.setLineSeparator(System.getProperty("line.separator"));
out.setLineWidth(Integer.MAX_VALUE);
XMLSerializer serializer = new XMLSerializer(writer, out);
try {
Element rootElement = doc.getDocumentElement();
serializer.serialize(rootElement);
}
catch (IOException e) {
System.out.println("ParseExpToXML::documentToString: IOException = " + e );
}
return writer.toString();
} // documentToString
/**
factor: ident | number | "(" expression ")"
* @throws ExpParseException
*/
private Node factor() throws ExpParseException
{
final String msg = "identifier, number or ( exp ) expected";
Node fact = null;
Token f = mScan.getToken();
String errMsg = null;
if (f != null) {
if (f.getType() == TokenType.IDENT || f.getType() == TokenType.INT) {
if (f.getType() == TokenType.INT) {
fact = createElement( TokenType.INT.getString() );
}
else {
fact = createElement( TokenType.IDENT.getString() );
}
Text txt = mDoc.createTextNode( f.getString() );
fact.appendChild( txt );
}
else if (f.getType() == TokenType.LPAREN) {
fact = addExp();
f = mScan.getToken();
if (f == null || f.getType() != TokenType.RPAREN) {
errMsg = "\")\" expected";
}
else {
Node top = createElement( TokenType.PAREN.getString() );
top.appendChild( fact );
fact = top;
}
}
else {
errMsg = msg;
}
}
else {
errMsg = msg;
}
if (errMsg != null) {
throw new ExpParseException( errMsg );
}
return fact;
} // factor
/**
unaryExpr: factor | minus factor
*/
private Node unaryExpr() throws ExpParseException
{
boolean unaryMinus = false;
Token t = mScan.getToken();
if (t.getType() == TokenType.MINUS) {
unaryMinus = true;
}
else {
mScan.pushToken( t );
}
Node top = factor();
if (unaryMinus) {
Node minus = createElement( TokenType.UMINUS.getString() );
minus.appendChild( top );
top = minus;
}
return top;
} // unaryExpr
/**
term: unaryExpr | unaryExpr addOp term
* @throws ExpParseException
*/
private Node term() throws ExpParseException
{
Node exp = null;
Node t = unaryExpr(); // either unaryExp or the LHS of the sub-expression
Token op = mScan.getToken();
if (op.getType() == TokenType.TIMES ||
op.getType() == TokenType.DIV ||
op.getType() == TokenType.MOD) {
Node rhs = term();
Node mulOp = createElement( op.getType().getString() );
mulOp.appendChild( t );
mulOp.appendChild( rhs );
exp = mulOp;
}
else {
exp = t;
if (op.getType() != TokenType.EOL) {
mScan.pushToken( op );
}
}
return exp;
} // term
/**
addExp: term | term addOp addExp
* @throws ExpParseException
*/
private Node addExp() throws ExpParseException
{
Node exp = null;
Node t = term();
Token op = mScan.getToken();
if (op.getType() == TokenType.MINUS || op.getType() == TokenType.PLUS) {
Node rhs = addExp();
Node addOp = createElement( op.getType().getString() );
addOp.appendChild( t );
addOp.appendChild( rhs );
exp = addOp;
}
else {
exp = t;
if (op.getType() != TokenType.EOL) {
mScan.pushToken( op );
}
}
return exp;
} // addExp
/**
statement: assign | expression
assign: ident "=" expression;
* @throws ExpParseException
*/
private Node statement() throws ExpParseException
{
String errMsg = null;
Node stmt = null;
Node lhs = addExp();
Token t = mScan.getToken();
if (mScan.isEOL()) {
stmt = lhs;
}
else if (t.getType() == TokenType.EQUAL) {
if (lhs.getLocalName().equals(TokenType.IDENT.getString())) {
Node rhs = addExp();
Node equals = (Node)createElement( t.getType().getString() );
equals.appendChild( lhs );
equals.appendChild( rhs );
stmt = equals;
t = mScan.getToken();
if (t.getType() != TokenType.EOL) {
errMsg = "Syntax error: badly formed expression";
}
}
else {
errMsg = "LHS identifier expected";
}
}
else {
errMsg = "\"=\" expected";
}
if (errMsg != null) {
throw new ExpParseException( errMsg );
}
return stmt;
} // statement
public String parse(String exp,
String topTag,
String prefix,
String nameSpace,
String schemaLoc ) throws ExpParseException
{
mDoc = DocumentFactory.newDocument( topTag,
prefix,
nameSpace,
schemaLoc );
mPrefix = prefix;
mNameSpace = nameSpace;
mScan = new Scanner( exp );
Node root = (Node)mDoc.getDocumentElement();
Node child = statement();
if (child != null) {
root.appendChild( child );
}
String xml = documentToString( mDoc );
return xml;
} // parse
}