001: package net.sf.saxon.expr;
002:
003: import net.sf.saxon.instruct.TailCall;
004: import net.sf.saxon.instruct.TailCallReturner;
005: import net.sf.saxon.om.Item;
006: import net.sf.saxon.om.NamePool;
007: import net.sf.saxon.om.SequenceIterator;
008: import net.sf.saxon.trans.XPathException;
009: import net.sf.saxon.type.ItemType;
010: import net.sf.saxon.type.SchemaType;
011: import net.sf.saxon.type.Type;
012: import net.sf.saxon.type.TypeHierarchy;
013: import net.sf.saxon.value.BooleanValue;
014: import net.sf.saxon.value.Cardinality;
015: import net.sf.saxon.value.EmptySequence;
016: import net.sf.saxon.value.Value;
017:
018: import java.io.PrintStream;
019: import java.util.ArrayList;
020: import java.util.Iterator;
021:
022: /**
023: * An IfExpression returns the value of either the "then" part or the "else" part,
024: * depending on the value of the condition
025: */
026:
027: public class IfExpression extends ComputedExpression implements
028: TailCallReturner {
029:
030: private Expression condition;
031: private Expression thenExp;
032: private Expression elseExp;
033:
034: /**
035: * Constructor
036: */
037:
038: public IfExpression(Expression condition, Expression thenExp,
039: Expression elseExp) {
040: this .condition = condition;
041: this .thenExp = thenExp;
042: this .elseExp = elseExp;
043: adoptChildExpression(condition);
044: adoptChildExpression(thenExp);
045: adoptChildExpression(elseExp);
046: }
047:
048: public Expression getCondition() {
049: return condition;
050: }
051:
052: public Expression getThenExpression() {
053: return thenExp;
054: }
055:
056: public Expression getElseExpression() {
057: return elseExp;
058: }
059:
060: public void setCondition(Expression exp) {
061: condition = exp;
062: adoptChildExpression(exp);
063: }
064:
065: public void setThenExpression(Expression exp) {
066: thenExp = exp;
067: adoptChildExpression(exp);
068: }
069:
070: /**
071: * Simplify an expression
072: */
073:
074: public Expression simplify(StaticContext env) throws XPathException {
075:
076: condition = condition.simplify(env);
077:
078: if (condition instanceof Value) {
079: final boolean b;
080: try {
081: b = condition.effectiveBooleanValue(env
082: .makeEarlyEvaluationContext());
083: } catch (XPathException err) {
084: err.setLocator(this );
085: throw err;
086: }
087: return (b ? thenExp.simplify(env) : elseExp.simplify(env));
088: }
089: thenExp = thenExp.simplify(env);
090: elseExp = elseExp.simplify(env);
091: return this ;
092: }
093:
094: /**
095: * Type-check the expression
096: */
097:
098: public Expression typeCheck(StaticContext env,
099: ItemType contextItemType) throws XPathException {
100: condition = condition.typeCheck(env, contextItemType);
101:
102: // If the condition after typechecking is reduced to a constant,
103: // cut it down to the appropriate branch. This is especially important
104: // when handling typeswitch, as otherwise the unused branches will
105: // generate a type error.
106: if (condition instanceof BooleanValue) {
107: if (((BooleanValue) condition).getBooleanValue()) {
108: return thenExp.typeCheck(env, contextItemType);
109: } else {
110: return elseExp.typeCheck(env, contextItemType);
111: }
112: } else {
113: XPathException err = TypeChecker.ebvError(condition, env
114: .getNamePool().getTypeHierarchy());
115: if (err != null) {
116: err.setLocator(this );
117: throw err;
118: }
119: thenExp = thenExp.typeCheck(env, contextItemType);
120: elseExp = elseExp.typeCheck(env, contextItemType);
121: adoptChildExpression(condition);
122: adoptChildExpression(thenExp);
123: adoptChildExpression(elseExp);
124: return this ;
125: }
126: }
127:
128: public Expression optimize(Optimizer opt, StaticContext env,
129: ItemType contextItemType) throws XPathException {
130: condition = condition.optimize(opt, env, contextItemType);
131: thenExp = thenExp.optimize(opt, env, contextItemType);
132: elseExp = elseExp.optimize(opt, env, contextItemType);
133: return this ;
134: }
135:
136: /**
137: * Promote this expression if possible
138: */
139:
140: public Expression promote(PromotionOffer offer)
141: throws XPathException {
142: Expression exp = offer.accept(this );
143: if (exp != null) {
144: return exp;
145: } else {
146: // Promote subexpressions in the condition, but not in the "then" and "else"
147: // branches, because these are guaranteed not to be evaluated if the condition
148: // is false (bzw true).
149: condition = doPromotion(condition, offer);
150:
151: // allow some types of promotion to trickle down to the subexpressions
152: if (offer.action == PromotionOffer.UNORDERED
153: || offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES
154: || offer.action == PromotionOffer.REPLACE_CURRENT) {
155: thenExp = doPromotion(thenExp, offer);
156: elseExp = doPromotion(elseExp, offer);
157: }
158: return this ;
159: }
160: }
161:
162: /**
163: * Get the immediate subexpressions of this expression
164: */
165:
166: public Iterator iterateSubExpressions() {
167: ArrayList a = new ArrayList(3);
168: a.add(condition);
169: a.add(thenExp);
170: a.add(elseExp);
171: return a.iterator();
172: }
173:
174: /**
175: * Suppress validation on contained element constructors, on the grounds that the parent element
176: * is already performing validation. The default implementation does nothing.
177: */
178:
179: public void suppressValidation(int validationMode) {
180: if (thenExp instanceof ComputedExpression) {
181: ((ComputedExpression) thenExp)
182: .suppressValidation(validationMode);
183: }
184: if (elseExp instanceof ComputedExpression) {
185: ((ComputedExpression) elseExp)
186: .suppressValidation(validationMode);
187: }
188: }
189:
190: /**
191: * Mark tail calls on used-defined functions. For most expressions, this does nothing.
192: */
193:
194: public boolean markTailFunctionCalls() {
195: boolean a = ExpressionTool.markTailFunctionCalls(thenExp);
196: boolean b = ExpressionTool.markTailFunctionCalls(elseExp);
197: return a || b;
198: }
199:
200: /**
201: * Check that any elements and attributes constructed or returned by this expression are acceptable
202: * in the content model of a given complex type. It's always OK to say yes, since the check will be
203: * repeated at run-time. The process of checking element and attribute constructors against the content
204: * model of a complex type also registers the type of content expected of those constructors, so the
205: * static validation can continue recursively.
206: */
207:
208: public void checkPermittedContents(SchemaType parentType,
209: StaticContext env, boolean whole) throws XPathException {
210: thenExp.checkPermittedContents(parentType, env, whole);
211: elseExp.checkPermittedContents(parentType, env, whole);
212: }
213:
214: /**
215: * Evaluate the conditional expression in a given context
216: * @param context the evaluation context
217: */
218:
219: public Item evaluateItem(XPathContext context)
220: throws XPathException {
221: if (condition.effectiveBooleanValue(context)) {
222: return thenExp.evaluateItem(context);
223: } else {
224: return elseExp.evaluateItem(context);
225: }
226: }
227:
228: /**
229: * Iterate the path-expression in a given context
230: * @param context the evaluation context
231: */
232:
233: public SequenceIterator iterate(XPathContext context)
234: throws XPathException {
235: if (condition.effectiveBooleanValue(context)) {
236: return thenExp.iterate(context);
237: } else {
238: return elseExp.iterate(context);
239: }
240: }
241:
242: /**
243: * Process this expression as an instruction, writing results to the current
244: * receiver
245: */
246:
247: public void process(XPathContext context) throws XPathException {
248: if (condition.effectiveBooleanValue(context)) {
249: thenExp.process(context);
250: } else {
251: elseExp.process(context);
252: }
253: }
254:
255: /**
256: * ProcessLeavingTail: called to do the real work of this instruction. This method
257: * must be implemented in each subclass. The results of the instruction are written
258: * to the current Receiver, which can be obtained via the Controller.
259: *
260: * @param context The dynamic context of the transformation, giving access to the current node,
261: * the current variables, etc.
262: * @return null if the instruction has completed execution; or a TailCall indicating
263: * a function call or template call that is delegated to the caller, to be made after the stack has
264: * been unwound so as to save stack space.
265: */
266:
267: public TailCall processLeavingTail(XPathContext context)
268: throws XPathException {
269: if (condition.effectiveBooleanValue(context)) {
270: if (thenExp instanceof TailCallReturner) {
271: return ((TailCallReturner) thenExp)
272: .processLeavingTail(context);
273: } else {
274: thenExp.process(context);
275: return null;
276: }
277: } else {
278: if (elseExp instanceof TailCallReturner) {
279: return ((TailCallReturner) elseExp)
280: .processLeavingTail(context);
281: } else {
282: elseExp.process(context);
283: return null;
284: }
285: }
286: }
287:
288: /**
289: * Get data type of items in sequence returned by expression
290: * @param th
291: */
292:
293: public ItemType getItemType(TypeHierarchy th) {
294: return Type.getCommonSuperType(thenExp.getItemType(th), elseExp
295: .getItemType(th), th);
296: }
297:
298: /**
299: * Determine the static cardinality of the result
300: */
301:
302: public int computeCardinality() {
303: return Cardinality.union(thenExp.getCardinality(), elseExp
304: .getCardinality());
305: }
306:
307: /**
308: * Get the static properties of this expression (other than its type). The result is
309: * bit-signficant. These properties are used for optimizations. In general, if
310: * property bit is set, it is true, but if it is unset, the value is unknown.
311: */
312:
313: public int computeSpecialProperties() {
314: // if one branch is empty, return the properties of the other branch
315: if (thenExp instanceof EmptySequence) {
316: return elseExp.getSpecialProperties();
317: }
318: if (elseExp instanceof EmptySequence) {
319: return thenExp.getSpecialProperties();
320: }
321: // otherwise return the properties that are shared by both subexpressions
322: return thenExp.getSpecialProperties()
323: & elseExp.getSpecialProperties();
324: }
325:
326: /**
327: * Diagnostic print of expression structure
328: */
329:
330: public void display(int level, NamePool pool, PrintStream out) {
331: out.println(ExpressionTool.indent(level) + "if (");
332: condition.display(level + 1, pool, out);
333: out.println(ExpressionTool.indent(level) + "then");
334: thenExp.display(level + 1, pool, out);
335: out.println(ExpressionTool.indent(level) + "else");
336: elseExp.display(level + 1, pool, out);
337: }
338:
339: }
340:
341: //
342: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
343: // you may not use this file except in compliance with the License. You may obtain a copy of the
344: // License at http://www.mozilla.org/MPL/
345: //
346: // Software distributed under the License is distributed on an "AS IS" basis,
347: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
348: // See the License for the specific language governing rights and limitations under the License.
349: //
350: // The Original Code is: all this file.
351: //
352: // The Initial Developer of the Original Code is Michael H. Kay.
353: //
354: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
355: //
356: // Contributor(s): none.
357: //
|