expression.xsd 0100700 0000764 0000764 00000010533 10105173726 012431 0 ustar iank iank
A validated DOM Object is built for the XML expression. The recursive methods in this class walk over the DOM tree and evaluate the expression.
The tree walk reflects the structure of the expression. The expression structure is shown below in BNF like syntax.
statement: assignment | addExp assignment: ident "=" addExp addExp: term | addExp addOp addExp term: unaryExpr | term mulOp term unaryExpr: factor | minus factor factor: ident | number | "(" addExp ")"
The XML expression is validated by an XML schema (see expression.xsd).
Expressions may be evaluated by themselves or assigned to a variable. The class supports a basic symbol table to support symbols and their associated values.
* * @author Ian Kaplan, www.bearcave.com, iank@bearcave.com */ public class EvalXML { /** The symbol table consists of a set of String, Integer pairs, where the String object is the key. */ private HashMap mSymTab = new HashMap(); /** Parse the XML into a DOM Document */ private static Document bytesToDocument(DOMParser parser, byte[] xmlBytes ) throws SAXException, IOException { ByteArrayInputStream istream = new ByteArrayInputStream( xmlBytes ); InputSource isource = new InputSource( istream ); parser.parse( isource ); Document xmlDoc = parser.getDocument(); return xmlDoc; } // bytesToDocument /** A DOM NodeList may include a variety of different node types. This is awkward when it comes to expression processing, since most of the time we only care about ELEMENT_NODEs. The toElementNodeList method builds an ArrayList that consists only of ELEMENT_NODES. @param list a NodeList of DOM Node objects @return return a Vector of ELEMENT_TYPE nodes. If there are no element type nodes, the size() of the Vector will zero. */ private ArrayList toElementNodeList( NodeList list ) { ArrayList elemList = new ArrayList(); if (list != null) { int len = list.getLength(); for (int i = 0; i < len; i++) { if (list.item(i).getNodeType() == Node.ELEMENT_NODE) { elemList.add( list.item(i) ); } } } return elemList; } // toElementNodeList /** Given a symbol name, look the symbol up in the symbol table. @return the value of the symbol, or 0 if the symbol is not found. */ private int symbolLookup( String name ) { int symVal = 0; Integer i = (Integer)mSymTab.get( name ); if (i != null) { symVal = i.intValue(); } else { System.out.println("No symbol found for " + name ); } return symVal; } // symbolLookup /** Enter a symbol and its value into the symbol table @param name symbol name @param value symbol value */ private void symbolEnter( String name, int value ) { Integer i = new Integer( value ); mSymTab.put( name, i ); } // symbolEnter /** Get the type name associated with the numeric Node type value. This method is called from error messages. */ private String nodeTypeToString( short type ) { String typeName = "Unknow Node type"; if (type == Node.ATTRIBUTE_NODE) { typeName = "ATTRIBUTE_NODE"; } else if (type == Node.CDATA_SECTION_NODE) { typeName = "CDATA_SECTION_NODE"; } else if (type == Node.COMMENT_NODE) { typeName = "COMMENT_NODE"; } else if (type == Node.DOCUMENT_FRAGMENT_NODE) { typeName = "DOCUMENT_FRAGMENT_NODE"; } else if (type == Node.DOCUMENT_NODE) { typeName = "DOCUMENT_NODE"; } else if (type == Node.DOCUMENT_TYPE_NODE) { typeName = "DOCUMENT_TYPE_NODE"; } else if (type == Node.ELEMENT_NODE) { typeName = "ELEMENT_NODE"; } else if (type == Node.ENTITY_NODE) { typeName = "ENTITY_NODE"; } else if (type == Node.ENTITY_REFERENCE_NODE) { typeName = "ENTITY_REFERENCE_NODE"; } else if (type == Node.NOTATION_NODE) { typeName = "NOTATION_NODE"; } else if (type == Node.PROCESSING_INSTRUCTION_NODE) { typeName = "PROCESSING_INSTRUCTION_NODE"; } else if (type == Node.TEXT_NODE ) { typeName = "TEXT_NODE "; } return typeName; } // nodeTypeToString /** Evaluate identifiers, numbers or parenthesized expressions (via a recursive call to the addExp method).factor: ident | number | paren addExp*/ private int factor( Node root ) { int rslt = 0; String tagName = root.getLocalName(); if (tagName.equals( TokenType.IDENT.toString() ) || tagName.equals( TokenType.INT.toString() )) { NodeList children = root.getChildNodes(); if (children.getLength() == 1) { Node child = children.item( 0 ); if (child.getNodeType() == Node.TEXT_NODE) { Text textNode = (Text)child; String textData = textNode.getData(); if (tagName.equals(TokenType.IDENT.toString())) { rslt = symbolLookup( textData ); } else { try { rslt = Integer.parseInt( textData ); } catch (NumberFormatException e) { System.out.println("factor: bad format for number \"" + textData + "\""); } } } else { System.out.println("factor: unexpected node type = " + nodeTypeToString( child.getNodeType() )); } } else { System.out.println("factor: 1 child expected for " + tagName + ", got " + children.getLength() + " children"); } } // root is not an IDENT or an INT, so it should be an expression else if (tagName.equals( TokenType.PAREN.toString() )) { ArrayList children = toElementNodeList( root.getChildNodes() ); if (children.size() == 1) { Node expr = (Node)children.get( 0 ); rslt = addExp( expr ); } else { System.out.println("factor: extra children of PAREN"); } } else { System.out.println("factor: Unexpected tag = " + tagName ); } return rslt; } // factor /** Process a factor or a unary minus expression (the silly unary plus is not supported).
unaryExp: factor | uminus factor*/ private int unaryExp( Node root ) { int rslt = 0; boolean unaryMinus = false; if (root.getLocalName().equals( TokenType.UMINUS.toString() )) { ArrayList children = toElementNodeList( root.getChildNodes() ); if (children.size() == 1) { unaryMinus = true; root = (Node)children.get( 0 ); } else { System.out.println("unaryExp: more than one child found for UMINUS"); } } rslt = factor( root ); if (unaryMinus) { rslt = -rslt; } return rslt; } // unaryExp /** Process a factor expression
term: factor | term mulOp term*/ private int term( Node root ) { int rslt = 0; if (root.getLocalName().equals( TokenType.TIMES.toString() ) || root.getLocalName().equals( TokenType.DIV.toString() ) || root.getLocalName().equals( TokenType.MOD.toString() )) { ArrayList children = toElementNodeList( root.getChildNodes() ); Node lhs = (Node)children.get( 0 ); Node rhs = (Node)children.get( 1 ); int lhsInt = term( lhs ); int rhsInt = term( rhs ); if (root.getLocalName().equals( TokenType.TIMES.toString() )) { rslt = lhsInt * rhsInt; } else if (root.getLocalName().equals( TokenType.DIV.toString() )) { if (rhsInt != 0) { // don't allow divide by zero rslt = lhsInt / rhsInt; } } else { // root.getLocalName().equals( TokenType.MOD ) if (rhsInt != 0) { // don't allow mod by zero rslt = lhsInt % rhsInt; } } } else { rslt = unaryExp( root ); } return rslt; } // term /**
addExp: term | addExp addOp addExp*/ private int addExp( Node root ) { int rslt = 0; if (root.getLocalName().equals( TokenType.MINUS.toString() ) || root.getLocalName().equals( TokenType.PLUS.toString() )) { ArrayList children = toElementNodeList( root.getChildNodes() ); Node lhs = (Node)children.get( 0 ); Node rhs = (Node)children.get( 1 ); int lhsInt = addExp( lhs ); int rhsInt = addExp( rhs ); if (root.getLocalName().equals( TokenType.MINUS.toString() )) { rslt = lhsInt - rhsInt; } else { rslt = lhsInt + rhsInt; } } else { rslt = term( root ); } return rslt; } // addExp /** "Store" a value in a symbol */ private void store( Node lhs, int value ) { NodeList children = lhs.getChildNodes(); Node child = children.item( 0 ); if (child.getNodeType() == Node.TEXT_NODE) { Text textNode = (Text)child; String symbolName = textNode.getData(); symbolEnter( symbolName, value ); } } // store /** Process and assignment statement or an expression
statement: assignment | addExp*/ private Integer statement( Node root ) { Integer rslt = null; Node expr = root; Node lhs = null; if (root.getLocalName().equals( TokenType.EQUAL.toString() )) { ArrayList children = toElementNodeList( root.getChildNodes() ); int len = children.size(); if (len == 2) { lhs = (Node)children.get(0); expr = (Node)children.get(1); } else { System.out.println("statement: two children expected, got " + len + " instead"); } } else { expr = root; } int expRslt = addExp( expr ); if (lhs != null) { store( lhs, expRslt ); } else { rslt = new Integer( expRslt ); } return rslt; } // statement /** This function is passed an assignment statement or an expression, formatted in XML. If the argument is an assignment statement, the right hand side (RHS) value is assigned to the variable on the left hand side (LHS). In the case of an assignment statement the function will return null.
If the argument is an expression the expression will be evaluated. The function will return the result as an Integer object.
*/ public Integer eval(DOMParser parser, byte[] xmlBytes ) { Integer result = null; Document doc = null; try { doc = bytesToDocument(parser, xmlBytes); Node root = doc.getDocumentElement(); if (root.getLocalName().equals("EXPRESSION")) { ArrayList children = toElementNodeList( root.getChildNodes() ); root = (Node)children.get( 0 ); result = statement( root ); } else { System.out.println("eval: EXPRESSION XML Document expected"); } } catch (SAXException e) { String msg = null; if (e instanceof SAXParseException) { int lineNum = ((SAXParseException)e).getLineNumber(); int columnNumber = ((SAXParseException)e).getColumnNumber(); String exceptionMsg = ((SAXParseException)e).getMessage(); msg = "Error: line = " + lineNum + ", column = " + columnNumber + ": " + exceptionMsg; } else { msg = "TestXerces.bytesToDocument: SAX Exception = " + e; } System.out.println(msg); } catch (IOException e) { System.out.println("TestXerces.bytesToDocument: IOException = " + e); } return result; } // eval } xmlexpr/ExpParseException.java 0100700 0000764 0000764 00000001717 10105175644 015467 0 ustar iank iank /* * Created on Jul 14, 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; /** * ExpParseException * A custom exception class that is thrown when errors are encountered when processing an expression (e.g., a + b * c) into XML. * @author Ian Kaplan, iank@bearcave.com, Jul 14, 2004 * */ public class ExpParseException extends Exception { /** * @param message */ public ExpParseException(String message) { super(message); } /** * @param message * @param cause */ public ExpParseException(String message, Throwable cause) { super(message, cause); } } xmlexpr/ExpToXML.java 0100600 0000764 0000764 00000002537 10105025013 013461 0 ustar iank iank /** \file * * Aug 1, 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; /** * ExpToXML * Aug 1, 2004 * * @author Ian Kaplan, www.bearcave.com, iank@bearcave.com */ public class ExpToXML { static final String TOP_TAG = "EXPRESSION"; static final String NAME_SPACE = "http://www.bearcave.com/expression"; static final String PREFIX = "ex"; public static void main(String[] args) { if (args.length == 1) { ParseExpToXML xmlParse = new ParseExpToXML(); String expr = args[0]; String xml = ""; try { xml = xmlParse.parse( expr, TOP_TAG, PREFIX, NAME_SPACE, "expression.xsd"); } catch (ExpParseException e) { System.out.println("Expression parse error:" + e ); } System.out.println( xml ); } } } xmlexpr/ParseExpToXML.java 0100700 0000764 0000764 00000020464 10105442511 014462 0 ustar iank iank /* * 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 } xmlexpr/ParserFactory.java 0100600 0000764 0000764 00000011461 10105773371 014642 0 ustar iank iank /** \file * * Jul 26, 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 org.apache.xerces.parsers.DOMParser; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.SAXParseException; import org.xml.sax.ErrorHandler; /** * ParserFactory * Jul 26, 2004 * * @author Ian Kaplan, www.bearcave.com, iank@bearcave.com */ public class ParserFactory { /** the namespace feature is true by default */ final static String NAMESPACES_FEATURE_PREFIX = "http://xml.org/sax/features/namespaces"; final static String VALIDATION_FEATURE = "http://xml.org/sax/features/validation"; final static String VALIDATION_SCHEMA_FEATURE = "http://apache.org/xml/features/validation/schema"; /** The parser must be instantiated with an error handler. If this is not done, errors and warnings will not be reported. Presumably one can do something more sophisticated than I'm doing here with the error handler. In this case the error handler just prints the fatal error, error or warning. */ private static class LocalErrorHandler implements ErrorHandler { private void print(String kind, SAXParseException e) { System.out.println("Parse " + kind + ": " + "Exception = " + e ); } public void error( SAXParseException e) throws SAXParseException { print("error", e ); throw e; } public void fatalError( SAXParseException e) throws SAXParseException { print("fatalError", e ); throw e; } public void warning( SAXParseException e) { print("warning", e ); } } // LocalErrorHandler /** Set a feature in the parser and report any errors. */ private static void setFeature( DOMParser parser, String featureURI, String featureName ) { try { parser.setFeature(featureURI, true); } catch (SAXNotSupportedException e) { System.out.println("ParserFactory::initParser: " + featureName + " not supported by parser"); } catch (SAXNotRecognizedException e) { System.out.println("ParserFactory::initParser: " + featureName + " not recognized by parser"); } } // setFeature /** Turn on validation in the parser object. The W3C (www.w3.org) and Apache (www.apache.org) documentation is less than clear when it comes to XML Schemas and XML parsers. Initializing a parser so that it performs validation is rather obscure. The features that are set in initialization are URI strings. These are defined on the Apache web page:
http://xml.apache.org/xerces2-j/features.html
A parser feature is turned on by calling the parser method setFeature( featureStr, true ), where featureStr is one of the features defined on the Apache web page. The feature to turn on schema validation is:
http://apache.org/xml/features/validation/schema
Error reporting requries that the basic validation feature be turned on as well. This is:
http://xml.org/sax/features/validation
Note that one feature uses apache.org and the other uses xml.org. Apparently this is historical: the base validation feature was originally for DTDs. The validation/schema feature is for XML Schema Descriptions (XSDs).
@param parser the parser object @param validate true = turn validation on, false = no validation. */ private static void initParser( DOMParser parser, boolean validate) { // parser.setErrorHandler(new DefaultHandler()); parser.setErrorHandler(new LocalErrorHandler() ); if (validate) { setFeature( parser, VALIDATION_FEATURE, "VALIDATION_FEATURE" ); setFeature( parser, VALIDATION_SCHEMA_FEATURE, "VALIDATION_SCHEMA_FEATURE" ); } } // initParser /** Allocate and initialize a new DOM parser. @param validate false = no validation, true = initialize the parser for XML schema validation */ public static DOMParser newParser( boolean validate ) { DOMParser parser = new DOMParser(); initParser( parser, validate ); return parser; } // newParser } xmlexpr/Scanner.java 0100700 0000764 0000764 00000011520 10105202025 013423 0 ustar iank iank /* * Created on Jul 13, 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.util.LinkedList; /** * Scanner * Read an expression (e.g., 3 + 4 * x) and return a set of tokens that represent the components of the expression. * @author Ian Kaplan, iank@bearcave.com, Jul 13, 2004 * */ public class Scanner { private String mExp = null; private int mExpLen = 0; private boolean mEOL = false; private int mCursor = 0; private char[] mCharBuf = new char[ 80 ]; private int mBufIx = 0; /** A First In, First Out list for tokens which have been "pushed back" */ private LinkedList mPushList = new LinkedList(); /** Add a character to the array that contains the token string */ private void putChar( char ch ) { mCharBuf[ mBufIx ] = ch; mBufIx++; } /** Copy a string of identifier characters into the token character array */ private void getIdent( char ch ) { while (mCursor < mExpLen && Character.isLetterOrDigit( ch )) { putChar( ch ); mCursor++; if (mCursor < mExpLen) { ch = mExp.charAt( mCursor ); } } mCursor--; } // getIdent /** Copy a string of numbers into the token character array. */ private void getInt(char ch ) { while (mCursor < mExpLen && Character.isDigit( ch )) { putChar( ch ); mCursor++; if (mCursor < mExpLen) { ch = mExp.charAt( mCursor ); } } mCursor--; } // getInt /** Skip white space characters */ private void skipSpaces() { while (mCursor < mExpLen && Character.isWhitespace( mExp.charAt( mCursor ) ) ) { mCursor++; } } // skipSpaces /** The lexer method does simple "lexical analysis", which divides the character stream into a set of tokens. */ private TokenType lexer() { boolean start = true; TokenType type = TokenType.NULL_TYPE; skipSpaces(); mBufIx = 0; if (mCursor < mExpLen) { char ch = mExp.charAt( mCursor ); if (Character.isLetter( ch )) { getIdent( ch ); type = TokenType.IDENT; } else if (Character.isDigit( ch )) { getInt( ch ); type = TokenType.INT; } else if (ch == '=') { type = TokenType.EQUAL; } else if (ch == '+') { type = TokenType.PLUS; } else if (ch == '-') { type = TokenType.MINUS; } else if (ch == '*') { type = TokenType.TIMES; } else if (ch == '/') { type = TokenType.DIV; } else if (ch == '%') { type = TokenType.MOD; } else if (ch == '(') { type = TokenType.LPAREN; } else if (ch == ')') { type = TokenType.RPAREN; } mCursor++; } else { mEOL = true; type = TokenType.EOL; } return type; } // lexer public Scanner( String exp ) { mExp = exp; mExpLen = exp.length(); } // Scanner /** Return the next expression token */ public Token getToken() { Token tok = null; if (mPushList.size() == 0) { TokenType type = null; if (isEOL()) { type = TokenType.EOL; } else { type = lexer(); } tok = new Token( type ); if (type == TokenType.IDENT || type == TokenType.INT) { String str = new String( mCharBuf, 0, mBufIx ); tok.setString( str ); } } else { tok = (Token)mPushList.removeFirst(); } return tok; } // getToken /** "Push" a token back into the token stream. If a token is pushed back and then getToken() is called, the pushed token will the the token returned by getToken(). */ public void pushToken( Token t ) { mPushList.add( t ); } // pushToken /** @return true, if the scanner has reached the end of the line (expression) */ public boolean isEOL() { return mEOL; } // isEOL } xmlexpr/TestScanner.java 0100600 0000764 0000764 00000002470 10105202500 014264 0 ustar iank iank /** \file * * Jul 24, 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; /** * TestScanner * Jul 24, 2004 * A test objectRead an expression (in quotes: "3 * 4 + 5") from the command line and call the Scanner.getToken() method to tokenize the string.
* @author Ian Kaplan, www.bearcave.com, iank@bearcave.com */ public class TestScanner { private void test( String argv[] ) { if (argv.length == 1) { String expr = argv[0]; System.out.println("expr = " + expr ); Scanner scan = new Scanner( expr ); for (Token tok = scan.getToken(); tok.getType() != TokenType.EOL; tok = scan.getToken()) { System.out.println("token = " + tok ); } } else { System.out.println("test expression on the command line expected"); } } public static void main(String[] argv) { TestScanner t = new TestScanner(); t.test( argv ); } } xmlexpr/TestXerces.java 0100600 0000764 0000764 00000010131 10106243253 014130 0 ustar iank iank /** \file 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 To compile and run this class you need the following "jars" in your CLASSPATH: xercesImpl.jar xml-apis.jar xmlParserAPIs.jar In my case these are in the directory /usr/java/xerces/xerces-2_3_0. The following minimal CLASSPATH seems to work (apparently Java is picking up the Java standard classes elsewhere): CLASSPATH=.:/usr/java/xerces-2_6_2/xercesImpl.jar:/usr/java/xerces-2_6_2/xml-apis.jar:/usr/java/xerces-2_6_2/xmlParserAPIs.jar */ package xmlexpr; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import org.apache.xerces.parsers.DOMParser; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * TestXerces * */ public class TestXerces { private void usage() { String name = getClass().getName(); System.out.println("usage: " + name + "Documentation for this class can be found here. Apparently TypeSafeEnums are supported in the new version of Java. So at some point the classes that use this class should be replaced with something more generic.
*/ public abstract class TypeSafeEnum { private static class enumInfo { public int hashCode; public int count; public ArrayList values; enumInfo( int hash ) { hashCode = hash; count = 0; values = new ArrayList(); } } // class enumInfo private static ArrayList infoVec = new ArrayList(); private String mName; private int mValue; public TypeSafeEnum( String name, Class cls ) { mName = name; enumInfo elem = findInfo( cls, true ); mValue = elem.count; elem.count++; elem.values.add( this ); } // TypeSafeEnum constructor public static Iterator enumValues( Class cls ) { Iterator iter = null; enumInfo elem = findInfo( cls, false ); if (elem != null) { iter = elem.values.iterator(); } return iter; } // enumValues public String getName() { return mName; } public int getValue() { return mValue; } public String toString() { return getName(); } /** Find the entry for the enumeration, if it exists. If not, add it to the end of the enumInfo. Note that this function has linear time, but the assumption is that there will not a large number of enumeration classes. */ private static enumInfo findInfo(Class cls, boolean add) { enumInfo foundElem = null; int hashCode = cls.hashCode(); for (Iterator iter = infoVec.iterator(); iter.hasNext(); ) { enumInfo elem = (enumInfo)iter.next(); if (elem.hashCode == hashCode) { foundElem = elem; break; } } if (foundElem == null && add) { foundElem = new enumInfo(hashCode); infoVec.add( foundElem ); } return foundElem; } // findInfo } xmlexpr/XMLExpressions.java 0100700 0000764 0000764 00000005142 10105774176 014765 0 ustar iank iank /* * Created on Jul 4, 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.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import org.apache.xerces.parsers.DOMParser; /** An interactive expression interpreter.An expression (x = a + b * c, for example) is converted to XML and then evaluated. The expression evaluator supports a symbol table that allows assignment and retrieval of symbol values. Only integer expressions are supported.
*/ public class XMLExpressions { static final String TOP_TAG = "EXPRESSION"; static final String NAME_SPACE = "http://www.bearcave.com/expression"; static final String PREFIX = "ex"; public static void main(String[] argv) { String line = ""; int len = 0; ParseExpToXML xmlParse = new ParseExpToXML(); EvalXML evalStmtOrExp = new EvalXML(); DOMParser parser = ParserFactory.newParser( true ); try { BufferedReader is = new BufferedReader( new InputStreamReader( System.in ) ); do { System.out.print("> "); line = is.readLine(); if (line != null ) { line = line.trim(); len = line.length(); if (len > 0) { try { String xml = xmlParse.parse( line, TOP_TAG, PREFIX, NAME_SPACE, "xmlexpr/expression.xsd"); Integer rslt = evalStmtOrExp.eval(parser, xml.getBytes() ); if (rslt != null) { System.out.println( rslt ); } } catch (ExpParseException e1) { System.out.println("Expression parse error: " + e1 ); } } } } while (line != null && len > 0); } catch (IOException e2) { System.out.println("IOException: " + e2.getMessage()); } } // main } xmlexpr/doxygenXmlExpr 0100600 0000764 0000764 00000127316 10114101666 014132 0 ustar iank iank # Doxyfile 1.3.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = XmlExpr # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = xmlexpr_doc # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, # Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en # (Japanese with English messages), Korean, Norwegian, Polish, Portuguese, # Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. OUTPUT_LANGUAGE = English # This tag can be used to specify the encoding used in the generated output. # The encoding is not always determined by the language that is chosen, # but also whether or not the output is meant for Windows or non-Windows users. # In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES # forces the Windows encoding (this is the default for the Windows binary), # whereas setting the tag to NO uses a Unix-style encoding (the default for # all platforms other than Windows). USE_WINDOWS_ENCODING = YES # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited # members of a class in the documentation of that class as if those members were # ordinary class members. Constructors, destructors and assignment operators of # the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = NO # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. It is allowed to use relative paths in the argument list. STRIP_FROM_PATH = # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like the Qt-style comments (thus requiring an # explict @brief command for a brief description. JAVADOC_AUTOBRIEF = YES # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = YES # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # reimplements. INHERIT_DOCS = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources # only. Doxygen will then generate output that is more tailored for Java. # For instance, namespaces will be presented as packages, qualified scopes # will look different, etc. OPTIMIZE_OUTPUT_JAVA = YES # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl *.cs FILE_PATTERNS = DocumentFactory.java EvalXML.java ExpParseException.java ParseExpToXML.java ParserFactory.java Scanner.java Token.java TokenType.java TypeSafeEnum.java XMLExpressions.java # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. EXCLUDE_PATTERNS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command