/* * 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 }