Package client.net.sf.saxon.ce.expr.instruct

Source Code of client.net.sf.saxon.ce.expr.instruct.Choose

package client.net.sf.saxon.ce.expr.instruct;
import client.net.sf.saxon.ce.LogController;
import client.net.sf.saxon.ce.expr.*;
import client.net.sf.saxon.ce.expr.parser.CodeInjector;
import client.net.sf.saxon.ce.functions.BooleanFn;
import client.net.sf.saxon.ce.om.Item;
import client.net.sf.saxon.ce.om.SequenceIterator;
import client.net.sf.saxon.ce.om.StandardNames;
import client.net.sf.saxon.ce.om.StructuredQName;
import client.net.sf.saxon.ce.style.StyleElement;
import client.net.sf.saxon.ce.trace.XSLTTraceListener;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.tree.iter.EmptyIterator;
import client.net.sf.saxon.ce.tree.util.FastStringBuffer;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.type.Type;
import client.net.sf.saxon.ce.type.TypeHierarchy;
import client.net.sf.saxon.ce.value.BooleanValue;
import client.net.sf.saxon.ce.value.Cardinality;
import client.net.sf.saxon.ce.value.SequenceType;

import java.util.ArrayList;
import java.util.Iterator;

import com.google.gwt.logging.client.LogConfiguration;

/**
* Compiled representation of an xsl:choose or xsl:if element in the stylesheet.
* Also used for typeswitch in XQuery.
*/

public class Choose extends Instruction {

    // The class implements both xsl:choose and xsl:if. There is a list of boolean
    // expressions (conditions) and a list of corresponding actions: the conditions
    // are evaluated in turn, and when one is found that is true, the corresponding
    // action is evaluated. For xsl:if, there is always one condition and one action.
    // An xsl:otherwise is compiled as if it were xsl:when test="true()". If no
    // condition is satisfied, the instruction returns an empty sequence.

    private Expression[] conditions;
    private Expression[] actions;


    /**
    * Construct an xsl:choose instruction
    * @param conditions the conditions to be tested, in order
    * @param actions the actions to be taken when the corresponding condition is true
    */

    public Choose(Expression[] conditions, Expression[] actions) {
        this.conditions = conditions;
        this.actions = actions;
        if (conditions.length != actions.length) {
            throw new IllegalArgumentException("Choose: unequal length arguments");
        }
        for (int i=0; i<conditions.length; i++) {
            adoptChildExpression(conditions[i]);
            adoptChildExpression(actions[i]);
        }
    }

    /**
     * Make a simple conditional expression (if (condition) then (thenExp) else (elseExp)
     * @param condition the condition to be tested
     * @param thenExp the expression to be evaluated if the condition is true
     * @param elseExp the expression to be evaluated if the condition is false
     * @return the expression
     */

    public static Expression makeConditional(Expression condition, Expression thenExp, Expression elseExp) {
        if (Literal.isEmptySequence(elseExp)) {
            Expression[] conditions = new Expression[] {condition};
            Expression[] actions = new Expression[] {thenExp};
            return new Choose(conditions, actions);
        } else {
            Expression[] conditions = new Expression[] {condition, new Literal(BooleanValue.TRUE)};
            Expression[] actions = new Expression[] {thenExp, elseExp};
            return new Choose(conditions, actions);
        }
    }

    /**
     * Make a simple conditional expression (if (condition) then (thenExp) else ()
     * @param condition the condition to be tested
     * @param thenExp the expression to be evaluated if the condition is true
     * @return the expression
     */

    public static Expression makeConditional(Expression condition, Expression thenExp) {
        Expression[] conditions = new Expression[] {condition};
        Expression[] actions = new Expression[] {thenExp};
        return new Choose(conditions, actions);
    }

    /**
     * Test whether an expression is a single-branch choose, that is, an expression of the form
     * if (condition) then exp else ()
     * @param exp the expression to be tested
     * @return true if the expression is a choose expression and there is only one condition,
     * so that the expression returns () if this condition is false
     */

    public static boolean isSingleBranchChoice(Expression exp) {
        return (exp instanceof Choose && ((Choose)exp).conditions.length == 1);
    }

    /**
     * Get the array of conditions to be tested
     * @return the array of condition expressions
     */

    public Expression[] getConditions() {
        return conditions;
    }

    /**
     * Get the array of actions to be performed
     * @return the array of expressions to be evaluated when the corresponding condition is true
     */

    public Expression[] getActions() {
        return actions;
    }
   
    private String[] conditionTests = null;
   
    public void setConditionTests(String[] conditionTests) {
      this.conditionTests = conditionTests;
    }

    /**
    * Get the name of this instruction for diagnostic and tracing purposes
    * We assume that if there was
     * only one condition then it was an xsl:if; this is not necessarily so, but
     * it's adequate for tracing purposes.
    */


    public int getInstructionNameCode() {
        return (conditions.length==1 ? StandardNames.XSL_IF : StandardNames.XSL_CHOOSE);
    }

    /**
     * Simplify an expression. This performs any static optimization (by rewriting the expression
     * as a different expression).
     *
     * @exception XPathException if an error is discovered during expression
     *     rewriting
     * @return the simplified expression
     * @param visitor expression visitor object
     */

    public Expression simplify(ExpressionVisitor visitor) throws XPathException {
        for (int i=0; i<conditions.length; i++) {
            conditions[i] = visitor.simplify(conditions[i]);
            try {
                actions[i] = visitor.simplify(actions[i]);
            } catch (XPathException err) {
                // mustn't throw the error unless the branch is actually selected, unless its a type error
                if (err.isTypeError()) {
                    throw err;
                } else {
                    actions[i] = new ErrorExpression(err);
                }
            }
        }
        return this;
    }

    public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
        for (int i=0; i<conditions.length; i++) {
            conditions[i] = visitor.typeCheck(conditions[i], contextItemType);
            XPathException err = TypeChecker.ebvError(conditions[i], visitor.getConfiguration().getTypeHierarchy());
            if (err != null) {
                err.setLocator(conditions[i].getSourceLocator());
                throw err;
            }
        }
        for (int i=0; i<actions.length; i++) {
            try {
                actions[i] = visitor.typeCheck(actions[i], contextItemType);
            } catch (XPathException err) {
                // mustn't throw the error unless the branch is actually selected, unless its a static or type error
                if (err.isStaticError()) {
                    throw err;
                } else if (err.isTypeError()) {
                    // if this is an "empty" else branch, don't be draconian about the error handling. It might be
                    // the user knows the otherwise branch isn't needed because one of the when branches will always
                    // be satisfied.
                    if (Literal.isEmptySequence(actions[i])) {
                        actions[i] = new ErrorExpression(err);
                    } else {
                        throw err;
                    }
                } else {
                    actions[i] = new ErrorExpression(err);
                }
            }
        }
        return this;
    }

    /**
     * Determine whether this expression implements its own method for static type checking
     *
     * @return true - this expression has a non-trivial implementation of the staticTypeCheck()
     *         method
     */

    public boolean implementsStaticTypeCheck() {
        return true;
    }

    /**
     * Static type checking for conditional expressions is delegated to the expression itself,
     * and is performed separately on each branch of the conditional, so that dynamic checks are
     * added only on those branches where the check is actually required. This also results in a static
     * type error if any branch is incapable of delivering a value of the required type. One reason
     * for this approach is to avoid doing dynamic type checking on a recursive function call as this
     * prevents tail-call optimization being used.
     * @param req the required type
     * @param backwardsCompatible true if backwards compatibility mode applies
     * @param role the role of the expression in relation to the required type
     * @param visitor an expression visitor
     * @return the expression after type checking (perhaps augmented with dynamic type checking code)
     * @throws XPathException if failures occur, for example if the static type of one branch of the conditional
     * is incompatible with the required type
     */

    public Expression staticTypeCheck(SequenceType req,
                                             boolean backwardsCompatible,
                                             RoleLocator role, ExpressionVisitor visitor)
    throws XPathException {
        for (int i=0; i<actions.length; i++) {
            actions[i] = TypeChecker.staticTypeCheck(actions[i], req, backwardsCompatible, role, visitor);
        }
        // If the last condition isn't true(), then we need to consider the fall-through case, which returns
        // an empty sequence
        if (!Literal.isConstantBoolean(conditions[conditions.length-1], true) &&
                !Cardinality.allowsZero(req.getCardinality())) {
            String cond = (conditions.length == 1 ? "the condition is not" : "none of the conditions is");
            XPathException err = new XPathException(
                    "Conditional expression: If " + cond + " satisfied, an empty sequence will be returned, " +
                            "but this is not allowed as the " + role.getMessage());
            err.setErrorCode(role.getErrorCode());
            err.setIsTypeError(true);
            throw err;
        }
        return this;
    }

    public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
        for (int i=0; i<conditions.length; i++) {
            conditions[i] = visitor.optimize(conditions[i], contextItemType);
            Expression ebv = BooleanFn.rewriteEffectiveBooleanValue(conditions[i], visitor, contextItemType);
            if (ebv != null && ebv != conditions[i]) {
                conditions[i] = ebv;
                adoptChildExpression(ebv);
            }
            if (conditions[i] instanceof Literal &&
                    !(((Literal)conditions[i]).getValue() instanceof BooleanValue)) {
                final boolean b;
                try {
                    b = ((Literal)conditions[i]).getValue().effectiveBooleanValue();
                } catch (XPathException err) {
                    err.setLocator(getSourceLocator());
                    throw err;
                }
                conditions[i] = new Literal(BooleanValue.get(b));
            }
        }
        for (int i=0; i<actions.length; i++) {
            try {
                actions[i] = visitor.optimize(actions[i], contextItemType);
            } catch (XPathException err) {
                // mustn't throw the error unless the branch is actually selected, unless its a type error
                if (err.isTypeError()) {
                    throw err;
                } else {
                    actions[i] = new ErrorExpression(err);
                }
            }
        }
        if (actions.length == 0) {
            return Literal.makeEmptySequence();
        }
        return this;
    }


    /**
     * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
     * This method indicates which of these methods is prefered. For instructions this is the process() method.
     */

    public int getImplementationMethod() {
        int m = Expression.PROCESS_METHOD | Expression.ITERATE_METHOD;
        if (!Cardinality.allowsMany(getCardinality())) {
            m |= Expression.EVALUATE_METHOD;
        }
        return m;
    }

    /**
     * Mark tail-recursive calls on functions. For most expressions, this does nothing.
     *
     * @return 0 if no tail call was found; 1 if a tail call on a different function was found;
     * 2 if a tail recursive call was found and if this call accounts for the whole of the value.
     */

    public int markTailFunctionCalls(StructuredQName qName, int arity) {
        int result = 0;
        for (int i=0; i<actions.length; i++) {
            result = Math.max(result, actions[i].markTailFunctionCalls(qName, arity));
        }
        return result;
    }

    /**
     * Get the item type of the items returned by evaluating this instruction
     * @return the static item type of the instruction
     * @param th Type hierarchy cache
     */

    public ItemType getItemType(TypeHierarchy th) {
        ItemType type = actions[0].getItemType(th);
        for (int i=1; i<actions.length; i++) {
            type = Type.getCommonSuperType(type, actions[i].getItemType(th), th);
        }
        return type;
    }

    /**
     * Compute the cardinality of the sequence returned by evaluating this instruction
     * @return the static cardinality
     */

    public int computeCardinality() {
        int card = 0;
        boolean includesTrue = false;
        for (int i=0; i<actions.length; i++) {
            card = Cardinality.union(card, actions[i].getCardinality());
            if (Literal.isConstantBoolean(conditions[i], true)) {
                includesTrue = true;
            }
        }
        if (!includesTrue) {
            // we may drop off the end and return an empty sequence (typical for xsl:if)
            card = Cardinality.union(card, StaticProperty.ALLOWS_ZERO);
        }
        return card;
    }

    /**
     * Get the static properties of this expression (other than its type). The result is
     * bit-signficant. These properties are used for optimizations. In general, if
     * property bit is set, it is true, but if it is unset, the value is unknown.
     *
     * @return a set of flags indicating static properties of this expression
     */

    public int computeSpecialProperties() {
        // The special properties of a conditional are those which are common to every branch of the conditional
        int props = actions[0].getSpecialProperties();
        for (int i=1; i<actions.length; i++) {
            props &= actions[i].getSpecialProperties();
        }
        return props;
    }

    /**
     * Determine whether this instruction creates new nodes.
     * This implementation returns true if any of the "actions" creates new nodes.
     * (Nodes created by the conditions can't contribute to the result).
     */

    public final boolean createsNewNodes() {
        for (int i=0; i<actions.length; i++) {
            int props = actions[i].getSpecialProperties();
            if ((props & StaticProperty.NON_CREATIVE) == 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * Get all the XPath expressions associated with this instruction
     * (in XSLT terms, the expression present on attributes of the instruction,
     * as distinct from the child instructions in a sequence construction)
     */

    public Iterator<Expression> iterateSubExpressions() {
        return new Iterator<Expression>() {
            boolean doingConditions = true;
            int index = 0;
            public boolean hasNext() {
                return doingConditions || index<actions.length;
            }

            public Expression next() {
                if (doingConditions) {
                    if (index < conditions.length) {
                        return conditions[index++];
                    } else {
                        doingConditions = false;
                        index = 0;
                        return actions[index++];
                    }
                } else {
                    if (index < actions.length) {
                        return actions[index++];
                    } else {
                        return null;
                    }
                }
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    /**
     * Replace one subexpression by a replacement subexpression
     * @param original the original subexpression
     * @param replacement the replacement subexpression
     * @return true if the original subexpression is found
     */

    public boolean replaceSubExpression(Expression original, Expression replacement) {
        boolean found = false;
        for (int i=0; i<conditions.length; i++) {
            if (conditions[i] == original) {
                conditions[i] = replacement;
                found = true;
            }
        }
        for (int i=0; i<actions.length; i++) {
            if (actions[i] == original) {
                actions[i] = replacement;
                found = true;
            }
        }
        return found;
    }

    /**
     * Handle promotion offers, that is, non-local tree rewrites.
     * @param offer The type of rewrite being offered
     * @throws XPathException
     */

    protected void promoteInst(PromotionOffer offer) throws XPathException {
        // xsl:when acts as a guard: expressions inside the when mustn't be evaluated if the when is false,
        // and conditions after the first mustn't be evaluated if a previous condition is true. So we
        // don't pass all promotion offers on
        if (offer.action == PromotionOffer.UNORDERED ||
                offer.action == PromotionOffer.REPLACE_CURRENT) {
            for (int i=0; i<conditions.length; i++) {
                conditions[i] = doPromotion(conditions[i], offer);
            }
            for (int i=0; i<actions.length; i++) {
                actions[i] = doPromotion(actions[i], offer);
            }
        } else {
            // in other cases, only the first xsl:when condition is promoted
            conditions[0= doPromotion(conditions[0], offer);
        }
    }


    /**
     * The toString() method for an expression attempts to give a representation of the expression
     * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
     * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
     * @return a representation of the expression as a string
     */

    public String toString() {
        FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
        sb.append("if (");
        for (int i=0; i<conditions.length; i++) {
            sb.append(conditions[i].toString());
            sb.append(") then (");
            sb.append(actions[i].toString());
            if (i == conditions.length - 1) {
                sb.append(")");
            } else {
                sb.append(") else if (");
            }
        }
        return sb.toString();
    }

    /**
    * Process this instruction, that is, choose an xsl:when or xsl:otherwise child
    * and process it.
    * @param context the dynamic context of this transformation
    * @throws XPathException if any non-recoverable dynamic error occurs
    * @return a TailCall, if the chosen branch ends with a call of call-template or
    * apply-templates. It is the caller's responsibility to execute such a TailCall.
    * If there is no TailCall, returns null.
    */

    public TailCall processLeavingTail(XPathContext context) throws XPathException {
        for (int i=0; i<conditions.length; i++) {
            final boolean b;
            try {
                b = conditions[i].effectiveBooleanValue(context);
            } catch (XPathException e) {
                e.maybeSetLocation(conditions[i].getSourceLocator());
                throw e;
            }
            if (b) {

              enterConditionTrace(i);
              TailCall tail;
                if (actions[i] instanceof TailCallReturner) {
                    tail = ((TailCallReturner)actions[i]).processLeavingTail(context);
                } else {
                    actions[i].process(context);
                    tail = null;
                }
                leaveConditionTrace(i);
                return tail;
            }
        }
        return null;
    }

    /**
     * Evaluate an expression as a single item. This always returns either a single Item or
     * null (denoting the empty sequence). No conversion is done. This method should not be
     * used unless the static type of the expression is a subtype of "item" or "item?": that is,
     * it should not be called if the expression may return a sequence. There is no guarantee that
     * this condition will be detected.
     *
     * @param context The context in which the expression is to be evaluated
     * @exception XPathException if any dynamic error occurs evaluating the
     *     expression
     * @return the node or atomic value that results from evaluating the
     *     expression; or null to indicate that the result is an empty
     *     sequence
     */

    public Item evaluateItem(XPathContext context) throws XPathException {
        for (int i=0; i<conditions.length; i++) {
            final boolean b;
            try {
                b = conditions[i].effectiveBooleanValue(context);
            } catch (XPathException e) {
                e.maybeSetLocation(conditions[i].getSourceLocator());
                throw e;
            }
            if (b) {
               
              enterConditionTrace(i);
                Item result = actions[i].evaluateItem(context);
                leaveConditionTrace(i);

                return result;
            }
        }
        return null;
    }
   
    /**
     * Return an Iterator to iterate over the values of a sequence. The value of every
     * expression can be regarded as a sequence, so this method is supported for all
     * expressions. This default implementation relies on the process() method: it
     * "pushes" the results of the instruction to a sequence in memory, and then
     * iterates over this in-memory sequence.
     *
     * In principle instructions should implement a pipelined iterate() method that
     * avoids the overhead of intermediate storage.
     *
     * @exception XPathException if any dynamic error occurs evaluating the
     *     expression
     * @param context supplies the context for evaluation
     * @return a SequenceIterator that can be used to iterate over the result
     *     of the expression
     */

    public SequenceIterator iterate(XPathContext context) throws XPathException {
        for (int i=0; i<conditions.length; i++) {
            final boolean b;
            try {
                b = conditions[i].effectiveBooleanValue(context);
            } catch (XPathException e) {
                e.maybeSetLocation(conditions[i].getSourceLocator());
                throw e;
            }
            SequenceIterator result;
            if (b) {
              // for XSLIF conditionTests will always be null
              enterConditionTrace(i);
                result = (actions[i]).iterate(context);
                leaveConditionTrace(i);

                return result;
            }
        }
        return EmptyIterator.getInstance();
    }
   
    private void enterConditionTrace(int i) {
      if (LogConfiguration.loggingIsEnabled() && LogController.traceIsEnabled()) {
      if(conditionTests != null) {
             XSLTTraceListener xlt = (XSLTTraceListener)LogController.getTraceListener();
             xlt.enterChooseItem(conditionTests[i]);
       }
      }
    }
   
    private void leaveConditionTrace(int i) {
      if (LogConfiguration.loggingIsEnabled() && LogController.traceIsEnabled()) {
        if(conditionTests != null) {
             XSLTTraceListener xlt = (XSLTTraceListener)LogController.getTraceListener();
             xlt.leaveChooseItem(conditionTests[i]);
       }
      }
    }


}

// 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 “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.
TOP

Related Classes of client.net.sf.saxon.ce.expr.instruct.Choose

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.