net.sf.saxon.expr.instruct.Choose Maven / Gradle / Ivy
Show all versions of saxon-he Show documentation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Saxonica Limited.
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.expr.instruct;
import net.sf.saxon.Configuration;
import net.sf.saxon.TypeCheckerEnvironment;
import net.sf.saxon.evpull.EmptyEventIterator;
import net.sf.saxon.evpull.EventIterator;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.parser.*;
import net.sf.saxon.functions.BooleanFn;
import net.sf.saxon.functions.SystemFunctionCall;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.pattern.ConditionalPattern;
import net.sf.saxon.pattern.Pattern;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.iter.EmptyIterator;
import net.sf.saxon.tree.util.FastStringBuffer;
import net.sf.saxon.type.*;
import net.sf.saxon.value.BooleanValue;
import net.sf.saxon.value.Cardinality;
import net.sf.saxon.value.SequenceType;
import java.util.Iterator;
import java.util.List;
/**
* 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 or "when (test) then ()"
if (/*Literal.isConstantBoolean(conditions[conditions.length-1], true) && */
Literal.isEmptySequence(actions[actions.length-1])) {
if (conditions.length == 1) {
return Literal.makeEmptySequence();
} else {
Expression[] c = new Expression[conditions.length-1];
System.arraycopy(conditions, 0, c, 0, conditions.length-1);
Expression[] a = new Expression[actions.length-1];
System.arraycopy(actions, 0, a, 0, actions.length-1);
conditions = c;
actions = a;
}
}
// Flatten an "else if"
if (Literal.isConstantBoolean(conditions[conditions.length-1], true) &&
actions[actions.length-1] instanceof Choose) {
Choose choose2 = (Choose)actions[actions.length-1];
int newLen = conditions.length + choose2.conditions.length - 1;
Expression[] c2 = new Expression[newLen];
Expression[] a2 = new Expression[newLen];
System.arraycopy(conditions, 0, c2, 0, conditions.length - 1);
System.arraycopy(actions, 0, a2, 0, actions.length - 1);
System.arraycopy(choose2.conditions, 0, c2, conditions.length - 1, choose2.conditions.length);
System.arraycopy(choose2.actions, 0, a2, actions.length - 1, choose2.actions.length);
conditions = c2;
actions = a2;
}
// Rewrite "if (EXP) then true() else false()" as boolean(EXP)
if (conditions.length == 2 &&
Literal.isConstantBoolean(actions[0], true) &&
Literal.isConstantBoolean(actions[1], false) &&
Literal.isConstantBoolean(conditions[1], true)) {
TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
if (th.isSubType(conditions[0].getItemType(th), BuiltInAtomicType.BOOLEAN) &&
conditions[0].getCardinality() == StaticProperty.EXACTLY_ONE) {
return conditions[0];
} else {
return SystemFunctionCall.makeSystemFunction("boolean", new Expression[]{conditions[0]});
}
}
return this;
}
/*@NotNull*/
public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
for (int i=0; i iterateSubExpressions() {
return new Iterator() {
boolean doingConditions = true;
int index = 0;
public boolean hasNext() {
return doingConditions || index iterateSubExpressionInfo() {
return new Iterator() {
boolean doingConditions = true;
int index = 0;
public boolean hasNext() {
return doingConditions || index
* The default implementation of this method assumes that an expression does no navigation other than
* the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
* same context as the containing expression. The method must be overridden for any expression
* where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
* and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
* functions because they create a new navigation root. Implementations also exist for PathExpression and
* FilterExpression because they have subexpressions that are evaluated in a different context from the
* calling expression.
*
* @param pathMap the PathMap to which the expression should be added
* @param pathMapNodeSet the set of PathMap nodes to which the paths from this expression should be appended
* @return the pathMapNode representing the focus established by this expression, in the case where this
* expression is the first operand of a path expression or filter expression. For an expression that does
* navigation, it represents the end of the arc in the path map that describes the navigation route. For other
* expressions, it is the same as the input pathMapNode.
*/
public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
// expressions used in a condition contribute paths, but these do not contribute to the result
for (Expression condition : conditions) {
condition.addToPathMap(pathMap, pathMapNodeSet);
}
PathMap.PathMapNodeSet result = new PathMap.PathMapNodeSet();
for (Expression action : actions) {
PathMap.PathMapNodeSet temp = action.addToPathMap(pathMap, pathMapNodeSet);
result.addNodeSet(temp);
}
return result;
}
/**
* 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 iterate(XPathContext context) throws XPathException {
for (int i=0; i
* The events (of class {@link net.sf.saxon.evpull.PullEvent}) are either complete
* items, or one of startElement, endElement, startDocument, or endDocument, known
* as semi-nodes. The stream of events may also include a nested EventIterator.
* If a start-end pair exists in the sequence, then the events between
* this pair represent the content of the document or element. The content sequence will
* have been processed to the extent that any attribute and namespace nodes in the
* content sequence will have been merged into the startElement event. Namespace fixup
* will have been performed: that is, unique prefixes will have been allocated to element
* and attribute nodes, and all namespaces will be declared by means of a namespace node
* in the startElement event or in an outer startElement forming part of the sequence.
* However, duplicate namespaces may appear in the sequence.
* The content of an element or document may include adjacent or zero-length text nodes,
* atomic values, and nodes represented as nodes rather than broken down into events.
*
* @param context The dynamic evaluation context
* @return the result of the expression as an iterator over a sequence of PullEvent objects
* @throws net.sf.saxon.trans.XPathException
* if a dynamic error occurs during expression evaluation
*/
public EventIterator iterateEvents(XPathContext context) throws XPathException {
for (int i=0; i