package net.sf.saxon.expr;
import net.sf.saxon.instruct.TailCall;
import net.sf.saxon.instruct.TailCallReturner;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.NamePool;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.SchemaType;
import net.sf.saxon.type.Type;
import net.sf.saxon.type.TypeHierarchy;
import net.sf.saxon.value.BooleanValue;
import net.sf.saxon.value.Cardinality;
import net.sf.saxon.value.EmptySequence;
import net.sf.saxon.value.Value;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
/**
* An IfExpression returns the value of either the "then" part or the "else" part,
* depending on the value of the condition
*/
public class IfExpression extends ComputedExpression implements TailCallReturner {
private Expression condition;
private Expression thenExp;
private Expression elseExp;
/**
* Constructor
*/
public IfExpression(Expression condition, Expression thenExp, Expression elseExp) {
this.condition = condition;
this.thenExp = thenExp;
this.elseExp = elseExp;
adoptChildExpression(condition);
adoptChildExpression(thenExp);
adoptChildExpression(elseExp);
}
public Expression getCondition() {
return condition;
}
public Expression getThenExpression() {
return thenExp;
}
public Expression getElseExpression() {
return elseExp;
}
public void setCondition(Expression exp) {
condition = exp;
adoptChildExpression(exp);
}
public void setThenExpression(Expression exp) {
thenExp = exp;
adoptChildExpression(exp);
}
/**
* Simplify an expression
*/
public Expression simplify(StaticContext env) throws XPathException {
condition = condition.simplify(env);
if (condition instanceof Value) {
final boolean b;
try {
b = condition.effectiveBooleanValue(env.makeEarlyEvaluationContext());
} catch (XPathException err) {
err.setLocator(this);
throw err;
}
return (b ? thenExp.simplify(env) : elseExp.simplify(env));
}
thenExp = thenExp.simplify(env);
elseExp = elseExp.simplify(env);
return this;
}
/**
* Type-check the expression
*/
public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
condition = condition.typeCheck(env, contextItemType);
// If the condition after typechecking is reduced to a constant,
// cut it down to the appropriate branch. This is especially important
// when handling typeswitch, as otherwise the unused branches will
// generate a type error.
if (condition instanceof BooleanValue) {
if (((BooleanValue)condition).getBooleanValue()) {
return thenExp.typeCheck(env, contextItemType);
} else {
return elseExp.typeCheck(env, contextItemType);
}
} else {
XPathException err = TypeChecker.ebvError(condition, env.getNamePool().getTypeHierarchy());
if (err != null) {
err.setLocator(this);
throw err;
}
thenExp = thenExp.typeCheck(env, contextItemType);
elseExp = elseExp.typeCheck(env, contextItemType);
adoptChildExpression(condition);
adoptChildExpression(thenExp);
adoptChildExpression(elseExp);
return this;
}
}
public Expression optimize(Optimizer opt, StaticContext env, ItemType contextItemType) throws XPathException {
condition = condition.optimize(opt, env, contextItemType);
thenExp = thenExp.optimize(opt, env, contextItemType);
elseExp = elseExp.optimize(opt, env, contextItemType);
return this;
}
/**
* Promote this expression if possible
*/
public Expression promote(PromotionOffer offer) throws XPathException {
Expression exp = offer.accept(this);
if (exp != null) {
return exp;
} else {
// Promote subexpressions in the condition, but not in the "then" and "else"
// branches, because these are guaranteed not to be evaluated if the condition
// is false (bzw true).
condition = doPromotion(condition, offer);
// allow some types of promotion to trickle down to the subexpressions
if (offer.action == PromotionOffer.UNORDERED ||
offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES ||
offer.action == PromotionOffer.REPLACE_CURRENT) {
thenExp = doPromotion(thenExp, offer);
elseExp = doPromotion(elseExp, offer);
}
return this;
}
}
/**
* Get the immediate subexpressions of this expression
*/
public Iterator iterateSubExpressions() {
ArrayList a = new ArrayList(3);
a.add(condition);
a.add(thenExp);
a.add(elseExp);
return a.iterator();
}
/**
* Suppress validation on contained element constructors, on the grounds that the parent element
* is already performing validation. The default implementation does nothing.
*/
public void suppressValidation(int validationMode) {
if (thenExp instanceof ComputedExpression) {
((ComputedExpression)thenExp).suppressValidation(validationMode);
}
if (elseExp instanceof ComputedExpression) {
((ComputedExpression)elseExp).suppressValidation(validationMode);
}
}
/**
* Mark tail calls on used-defined functions. For most expressions, this does nothing.
*/
public boolean markTailFunctionCalls() {
boolean a = ExpressionTool.markTailFunctionCalls(thenExp);
boolean b = ExpressionTool.markTailFunctionCalls(elseExp);
return a || b;
}
/**
* Check that any elements and attributes constructed or returned by this expression are acceptable
* in the content model of a given complex type. It's always OK to say yes, since the check will be
* repeated at run-time. The process of checking element and attribute constructors against the content
* model of a complex type also registers the type of content expected of those constructors, so the
* static validation can continue recursively.
*/
public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
thenExp.checkPermittedContents(parentType, env, whole);
elseExp.checkPermittedContents(parentType, env, whole);
}
/**
* Evaluate the conditional expression in a given context
* @param context the evaluation context
*/
public Item evaluateItem(XPathContext context) throws XPathException {
if (condition.effectiveBooleanValue(context)) {
return thenExp.evaluateItem(context);
} else {
return elseExp.evaluateItem(context);
}
}
/**
* Iterate the path-expression in a given context
* @param context the evaluation context
*/
public SequenceIterator iterate(XPathContext context) throws XPathException {
if (condition.effectiveBooleanValue(context)) {
return thenExp.iterate(context);
} else {
return elseExp.iterate(context);
}
}
/**
* Process this expression as an instruction, writing results to the current
* receiver
*/
public void process(XPathContext context) throws XPathException {
if (condition.effectiveBooleanValue(context)) {
thenExp.process(context);
} else {
elseExp.process(context);
}
}
/**
* ProcessLeavingTail: called to do the real work of this instruction. This method
* must be implemented in each subclass. The results of the instruction are written
* to the current Receiver, which can be obtained via the Controller.
*
* @param context The dynamic context of the transformation, giving access to the current node,
* the current variables, etc.
* @return null if the instruction has completed execution; or a TailCall indicating
* a function call or template call that is delegated to the caller, to be made after the stack has
* been unwound so as to save stack space.
*/
public TailCall processLeavingTail(XPathContext context) throws XPathException {
if (condition.effectiveBooleanValue(context)) {
if (thenExp instanceof TailCallReturner) {
return ((TailCallReturner)thenExp).processLeavingTail(context);
} else {
thenExp.process(context);
return null;
}
} else {
if (elseExp instanceof TailCallReturner) {
return ((TailCallReturner)elseExp).processLeavingTail(context);
} else {
elseExp.process(context);
return null;
}
}
}
/**
* Get data type of items in sequence returned by expression
* @param th
*/
public ItemType getItemType(TypeHierarchy th) {
return Type.getCommonSuperType(thenExp.getItemType(th), elseExp.getItemType(th), th);
}
/**
* Determine the static cardinality of the result
*/
public int computeCardinality() {
return Cardinality.union(thenExp.getCardinality(), elseExp.getCardinality());
}
/**
* 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.
*/
public int computeSpecialProperties() {
// if one branch is empty, return the properties of the other branch
if (thenExp instanceof EmptySequence) {
return elseExp.getSpecialProperties();
}
if (elseExp instanceof EmptySequence) {
return thenExp.getSpecialProperties();
}
// otherwise return the properties that are shared by both subexpressions
return thenExp.getSpecialProperties() & elseExp.getSpecialProperties();
}
/**
* Diagnostic print of expression structure
*/
public void display(int level, NamePool pool, PrintStream out) {
out.println(ExpressionTool.indent(level) + "if (");
condition.display(level+1, pool, out);
out.println(ExpressionTool.indent(level) + "then");
thenExp.display(level+1, pool, out);
out.println(ExpressionTool.indent(level) + "else");
elseExp.display(level+1, pool, out);
}
}
//
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is Michael H. Kay.
//
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
//
// Contributor(s): none.
//
|