FileDocCategorySizeDatePackage
ExpressionParseTree.javaAPI DocApache Tomcat 6.0.1412059Fri Jul 20 04:20:30 BST 2007org.apache.catalina.ssi

ExpressionParseTree

public class ExpressionParseTree extends Object
Represents a parsed expression.
version
$Revision: 531303 $
author
Paul Speed

Fields Summary
private LinkedList
nodeStack
Contains the current set of completed nodes. This is a workspace for the parser.
private LinkedList
oppStack
Contains operator nodes that don't yet have values. This is a workspace for the parser.
private Node
root
The root node after the expression has been parsed.
private SSIMediator
ssiMediator
The SSIMediator to use when evaluating the expressions.
private static final int
PRECEDENCE_NOT
private static final int
PRECEDENCE_COMPARE
private static final int
PRECEDENCE_LOGICAL
Constructors Summary
public ExpressionParseTree(String expr, SSIMediator ssiMediator)
Creates a new parse tree for the specified expression.



                  
        
              
        this.ssiMediator = ssiMediator;
        parseExpression(expr);
    
Methods Summary
public booleanevaluateTree()
Evaluates the tree and returns true or false. The specified SSIMediator is used to resolve variable references.

        return root.evaluate();
    
private voidparseExpression(java.lang.String expr)
Parses the specified expression into a tree of parse nodes.

        StringNode currStringNode = null;
        // We cheat a little and start an artificial
        // group right away. It makes finishing easier.
        pushOpp(null);
        ExpressionTokenizer et = new ExpressionTokenizer(expr);
        while (et.hasMoreTokens()) {
            int token = et.nextToken();
            if (token != ExpressionTokenizer.TOKEN_STRING)
                currStringNode = null;
            switch (token) {
                case ExpressionTokenizer.TOKEN_STRING :
                    if (currStringNode == null) {
                        currStringNode = new StringNode(et.getTokenValue());
                        nodeStack.add(0, currStringNode);
                    } else {
                        // Add to the existing
                        currStringNode.value.append(" ");
                        currStringNode.value.append(et.getTokenValue());
                    }
                    break;
                case ExpressionTokenizer.TOKEN_AND :
                    pushOpp(new AndNode());
                    break;
                case ExpressionTokenizer.TOKEN_OR :
                    pushOpp(new OrNode());
                    break;
                case ExpressionTokenizer.TOKEN_NOT :
                    pushOpp(new NotNode());
                    break;
                case ExpressionTokenizer.TOKEN_EQ :
                    pushOpp(new EqualNode());
                    break;
                case ExpressionTokenizer.TOKEN_NOT_EQ :
                    pushOpp(new NotNode());
                    // Sneak the regular node in. The NOT will
                    // be resolved when the next opp comes along.
                    oppStack.add(0, new EqualNode());
                    break;
                case ExpressionTokenizer.TOKEN_RBRACE :
                    // Closeout the current group
                    resolveGroup();
                    break;
                case ExpressionTokenizer.TOKEN_LBRACE :
                    // Push a group marker
                    pushOpp(null);
                    break;
                case ExpressionTokenizer.TOKEN_GE :
                    pushOpp(new NotNode());
                    // Similar stategy to NOT_EQ above, except this
                    // is NOT less than
                    oppStack.add(0, new LessThanNode());
                    break;
                case ExpressionTokenizer.TOKEN_LE :
                    pushOpp(new NotNode());
                    // Similar stategy to NOT_EQ above, except this
                    // is NOT greater than
                    oppStack.add(0, new GreaterThanNode());
                    break;
                case ExpressionTokenizer.TOKEN_GT :
                    pushOpp(new GreaterThanNode());
                    break;
                case ExpressionTokenizer.TOKEN_LT :
                    pushOpp(new LessThanNode());
                    break;
                case ExpressionTokenizer.TOKEN_END :
                    break;
            }
        }
        // Finish off the rest of the opps
        resolveGroup();
        if (nodeStack.size() == 0) {
            throw new ParseException("No nodes created.", et.getIndex());
        }
        if (nodeStack.size() > 1) {
            throw new ParseException("Extra nodes created.", et.getIndex());
        }
        if (oppStack.size() != 0) {
            throw new ParseException("Unused opp nodes exist.", et.getIndex());
        }
        root = (Node)nodeStack.get(0);
    
private voidpushOpp(org.apache.catalina.ssi.ExpressionParseTree$OppNode node)
Pushes a new operator onto the opp stack, resolving existing opps as needed.

        // If node is null then it's just a group marker
        if (node == null) {
            oppStack.add(0, node);
            return;
        }
        while (true) {
            if (oppStack.size() == 0) break;
            OppNode top = (OppNode)oppStack.get(0);
            // If the top is a spacer then don't pop
            // anything
            if (top == null) break;
            // If the top node has a lower precedence then
            // let it stay
            if (top.getPrecedence() < node.getPrecedence()) break;
            // Remove the top node
            oppStack.remove(0);
            // Let it fill its branches
            top.popValues(nodeStack);
            // Stick it on the resolved node stack
            nodeStack.add(0, top);
        }
        // Add the new node to the opp stack
        oppStack.add(0, node);
    
private voidresolveGroup()
Resolves all pending opp nodes on the stack until the next group marker is reached.

        OppNode top = null;
        while ((top = (OppNode)oppStack.remove(0)) != null) {
            // Let it fill its branches
            top.popValues(nodeStack);
            // Stick it on the resolved node stack
            nodeStack.add(0, top);
        }