package lux.xpath;

import lux.ExpressionVisitor;

/**
 * An abstract XPath or XQuery expression.
 * 
 * This class and its subclasses represent XPath expressions.  Their
 * toString() methods return valid XPath.
 */

public abstract class AbstractExpression implements Visitable {
    
    protected AbstractExpression subs[];

    public enum Type {
        PathExpression, PathStep, Predicate, Binary, SetOperation,
        Literal, Root, Dot, FunctionCall, Sequence, UnaryMinus, Subsequence,
        Let, Variable, Element, Attribute, Text, FLWOR, Conditional, Comment, 
        DocumentConstructor, ProcessingInstruction, Satisfies, InstanceOf
    };

    private final Type type;
    
    protected AbstractExpression (Type type) {
        this.type = type;
    }

    /** The type of this expression; most types will correspond one-one
     * with a Java class which must be a subclass of AbstractExpression, but this
     * enumerated value provides an integer equivalent that should be
     * useful for efficient switch operations, encoding and the like.
     */
    public Type getType () {
        return type;
    }
    
    public void acceptSubs (ExpressionVisitor visitor) {
        for (int i = 0; i < subs.length && !visitor.isDone(); i++) {
            int j = visitor.isReverse() ? (subs.length-i-1) : i;
            AbstractExpression sub = subs[j].accept (visitor);
            if (sub != subs[j]) {
                subs[j]= sub;
            }
        }
    }

    /**
     * The sub-expressions of this expression. Most have 0, 1, or 2.  Only
     * functions can have variable numbers of sub-expressions (arguments).
     */
    public AbstractExpression [] getSubs() {
        return subs;
    }

    /** Each subclass must implement the toString(StringBuilder) method by
     * rendering itself as a syntatically valid XPath/XQuery expression in
     * the given buffer.
     * @param buf
     */
    public abstract void toString(StringBuilder buf);

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        toString (buf);
        return buf.toString();
    }

    /**
     * @return whether this expression is a Root or another expression that introduces
     * a new query scope, such as a PathExpression beginning with a Root (/), or a subsequence
     * of another absolute expression.  This method returns false, supplying the common default.
     */
    public boolean isAbsolute() {
        return false;
    }
    
    /**
     * @return whether this expression is proven to return results in document order.  This method 
     * returns true iff all its subs return true, or it has none.  Warning: incorrect results may occur if 
     * document-ordering is falsely asserted.
     */
    public boolean isDocumentOrdered() {
        if (subs != null) {
            for (AbstractExpression sub : subs) {
                if (!sub.isDocumentOrdered())
                    return false;
            }
        }
        return true;
    }
    
    /** 
     * If this has an absolute subexpression, replace it with the function call expression
     * 
     * @param search the search function call to use in place of '/'
     */
    public AbstractExpression replaceRoot(FunCall search) {
        if (subs != null && subs.length > 0) {
            subs[0] = subs[0].replaceRoot(search);
        }
        return this;
    }
    
    /**
     * append the sub-expression to the buffer, wrapping it in parentheses if its precedence is
     * lower than this expression's.
     * @param buf the buffer to append to
     * @param sub the sub-expression
     */
    protected void appendSub(StringBuilder buf, AbstractExpression sub) {
        if (sub.getPrecedence() < getPrecedence()) {
            buf.append ('(');
            sub.toString(buf);
            buf.append (')');            
        } else {
            sub.toString(buf);
        }
    }
    


    /**
     * @return the tail of this expression - only has meaning for PathExpressions, which strip off the 
     * return everything after the leftmost step.
     */
    public AbstractExpression getTail() {
        return null;
    }

    public enum Direction { Left, Right };

    /**
     * @return a number indicating the *outer* precedence of this expression.
     * Expressions with lower precedence numbers have lower
     * precedence, ie bind more loosely, than expressions with higher
     * precedence. Expressions with no sub-expressions are assigned a high
     * precedence.  Complex expressions can be seen as having an inner and an outer
     * precedence; for example function call expressions behave as a comma with regard 
     * to their sub-expressions, the arguments, and like parentheses to their enclosing expression.
     */
    public abstract int getPrecedence ();

}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

@@ -3521,16 +3521,28 @@
 er than 
+or equal to 
 this exp
@@ -3551,16 +3551,414 @@
 ssion's.
+  We need parens when precedence is equal because%0A     * otherwise operations simply group left or right, but we have the actual grouping encoded %0A     * in the expression tree and need to preserve that.%0A     * %0A     * Note: we can't just blindly wrap everything in parentheses because parens have special meaning%0A     * in some XPath expressions where they can introduce document-ordering.%0A     * 
 %0A     * 
@@ -4107,24 +4107,24 @@
 sion sub) %7B%0A
-
         if (
@@ -4144,16 +4144,17 @@
 ence() %3C
+=
  getPrec

wrap parentheses around child exprs of equal precedence