001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.expr.*;
004: import net.sf.saxon.om.EmptyIterator;
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.style.StandardNames;
009: import net.sf.saxon.trans.XPathException;
010: import net.sf.saxon.type.ItemType;
011: import net.sf.saxon.type.SchemaType;
012: import net.sf.saxon.type.Type;
013: import net.sf.saxon.type.TypeHierarchy;
014: import net.sf.saxon.value.Value;
015: import net.sf.saxon.value.EmptySequence;
016:
017: import java.io.PrintStream;
018: import java.util.ArrayList;
019: import java.util.Iterator;
020:
021: /**
022: * Compiled representation of an xsl:choose or xsl:if element in the stylesheet.
023: * Also used for typeswitch in XQuery.
024: */
025:
026: public class Choose extends Instruction {
027:
028: // The class implements both xsl:choose and xsl:if. There is a list of boolean
029: // expressions (conditions) and a list of corresponding actions: the conditions
030: // are evaluated in turn, and when one is found that is true, the corresponding
031: // action is evaluated. For xsl:if, there is always one condition and one action.
032: // An xsl:otherwise is compiled as if it were xsl:when test="true()". If no
033: // condition is satisfied, the instruction returns without doing anything.
034:
035: private Expression[] conditions;
036: private Expression[] actions;
037:
038: /**
039: * Construct an xsl:choose instruction
040: * @param conditions the conditions to be tested, in order
041: * @param actions the actions to be taken when the corresponding condition is true
042: */
043:
044: public Choose(Expression[] conditions, Expression[] actions) {
045: this .conditions = conditions;
046: this .actions = actions;
047: for (int i = 0; i < conditions.length; i++) {
048: adoptChildExpression(conditions[i]);
049: }
050: for (int i = 0; i < actions.length; i++) {
051: adoptChildExpression(actions[i]);
052: }
053: }
054:
055: /**
056: * Get the name of this instruction for diagnostic and tracing purposes
057: * We assume that if there was
058: * only one condition then it was an xsl:if; this is not necessarily so, but
059: * it's adequate for tracing purposes.
060: */
061:
062: public int getInstructionNameCode() {
063: return (conditions.length == 1 ? StandardNames.XSL_IF
064: : StandardNames.XSL_CHOOSE);
065: }
066:
067: /**
068: * Simplify an expression. This performs any static optimization (by rewriting the expression
069: * as a different expression).
070: *
071: * @exception XPathException if an error is discovered during expression
072: * rewriting
073: * @return the simplified expression
074: */
075:
076: public Expression simplify(StaticContext env) throws XPathException {
077: for (int i = 0; i < conditions.length; i++) {
078: conditions[i] = conditions[i].simplify(env);
079: }
080: for (int i = 0; i < actions.length; i++) {
081: actions[i] = actions[i].simplify(env);
082: }
083: return this ;
084: }
085:
086: public Expression typeCheck(StaticContext env,
087: ItemType contextItemType) throws XPathException {
088: for (int i = 0; i < conditions.length; i++) {
089: conditions[i] = conditions[i].typeCheck(env,
090: contextItemType);
091: adoptChildExpression(conditions[i]);
092: XPathException err = TypeChecker.ebvError(conditions[i],
093: env.getNamePool().getTypeHierarchy());
094: if (err != null) {
095: if (conditions[i] instanceof ComputedExpression) {
096: err.setLocator((ComputedExpression) conditions[i]);
097: } else if (actions[i] instanceof ComputedExpression) {
098: err.setLocator((ComputedExpression) actions[i]);
099: } else {
100: err.setLocator(this );
101: }
102: throw err;
103: }
104: }
105: for (int i = 0; i < actions.length; i++) {
106: actions[i] = actions[i].typeCheck(env, contextItemType);
107: adoptChildExpression(actions[i]);
108: }
109: return this ;
110: }
111:
112: public Expression optimize(Optimizer opt, StaticContext env,
113: ItemType contextItemType) throws XPathException {
114: for (int i = 0; i < conditions.length; i++) {
115: conditions[i] = conditions[i].optimize(opt, env,
116: contextItemType);
117: adoptChildExpression(conditions[i]);
118: if (conditions[i] instanceof Value) {
119: final boolean b;
120: try {
121: b = conditions[i].effectiveBooleanValue(env
122: .makeEarlyEvaluationContext());
123: } catch (XPathException err) {
124: err.setLocator(this );
125: throw err;
126: }
127: if (b) {
128: // if condition is always true, remove all the subsequent conditions and actions
129: if (i == 0) {
130: return actions[0];
131: } else if (i != conditions.length - 1) {
132: Expression[] c2 = new Expression[i + 1];
133: Expression[] a2 = new Expression[i + 1];
134: System.arraycopy(conditions, 0, c2, 0, i + 1);
135: System.arraycopy(actions, 0, a2, 0, i + 1);
136: conditions = c2;
137: actions = a2;
138: break;
139: }
140: } else {
141: // if condition is false, skip this test
142: Expression[] c2 = new Expression[conditions.length - 1];
143: Expression[] a2 = new Expression[conditions.length - 1];
144: System.arraycopy(conditions, 0, c2, 0, i);
145: System.arraycopy(actions, 0, a2, 0, i);
146: System.arraycopy(conditions, i + 1, c2, i,
147: conditions.length - i - 1);
148: System.arraycopy(actions, i + 1, a2, i,
149: conditions.length - i - 1);
150: conditions = c2;
151: actions = a2;
152: i--;
153: }
154: }
155: }
156: for (int i = 0; i < actions.length; i++) {
157: actions[i] = actions[i].optimize(opt, env, contextItemType);
158: adoptChildExpression(actions[i]);
159: }
160: if (actions.length == 0) {
161: return EmptySequence.getInstance();
162: }
163: return this ;
164: }
165:
166: /**
167: * Get the item type of the items returned by evaluating this instruction
168: * @return the static item type of the instruction
169: * @param th
170: */
171:
172: public ItemType getItemType(TypeHierarchy th) {
173: ItemType type = actions[0].getItemType(th);
174: for (int i = 1; i < actions.length; i++) {
175: type = Type.getCommonSuperType(type, actions[i]
176: .getItemType(th), th);
177: }
178: return type;
179: }
180:
181: /**
182: * Determine whether this instruction creates new nodes.
183: * This implementation returns true if any of the "actions" creates new nodes.
184: * (Nodes created by the conditions can't contribute to the result).
185: */
186:
187: public final boolean createsNewNodes() {
188: for (int i = 0; i < actions.length; i++) {
189: int props = actions[i].getSpecialProperties();
190: if ((props & StaticProperty.NON_CREATIVE) == 0) {
191: return true;
192: }
193: ;
194: }
195: return false;
196: }
197:
198: /**
199: * Get all the XPath expressions associated with this instruction
200: * (in XSLT terms, the expression present on attributes of the instruction,
201: * as distinct from the child instructions in a sequence construction)
202: */
203:
204: public Iterator iterateSubExpressions() {
205: ArrayList list = new ArrayList(conditions.length
206: + actions.length);
207: for (int i = 0; i < conditions.length; i++) {
208: list.add(conditions[i]);
209: }
210: for (int i = 0; i < actions.length; i++) {
211: list.add(actions[i]);
212: }
213: return list.iterator();
214: }
215:
216: /**
217: * Handle promotion offers, that is, non-local tree rewrites.
218: * @param offer The type of rewrite being offered
219: * @throws XPathException
220: */
221:
222: protected void promoteInst(PromotionOffer offer)
223: throws XPathException {
224: // xsl:when acts as a guard: expressions inside the when mustn't be evaluated if the when is false,
225: // and conditions after the first mustn't be evaluated if a previous condition is true. So we
226: // don't pass all promotion offers on
227: if (offer.action == PromotionOffer.UNORDERED
228: || offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES
229: || offer.action == PromotionOffer.REPLACE_CURRENT) {
230: for (int i = 0; i < conditions.length; i++) {
231: conditions[i] = doPromotion(conditions[i], offer);
232: }
233: for (int i = 0; i < actions.length; i++) {
234: actions[i] = doPromotion(actions[i], offer);
235: }
236: } else {
237: // in other cases, only the first xsl:when condition is promoted
238: conditions[0] = doPromotion(conditions[0], offer);
239: }
240: }
241:
242: /**
243: * Check that any elements and attributes constructed or returned by this expression are acceptable
244: * in the content model of a given complex type. It's always OK to say yes, since the check will be
245: * repeated at run-time. The process of checking element and attribute constructors against the content
246: * model of a complex type also registers the type of content expected of those constructors, so the
247: * static validation can continue recursively.
248: */
249:
250: public void checkPermittedContents(SchemaType parentType,
251: StaticContext env, boolean whole) throws XPathException {
252: for (int i = 0; i < actions.length; i++) {
253: actions[i].checkPermittedContents(parentType, env, whole);
254: }
255: }
256:
257: /**
258: * Diagnostic print of expression structure. The expression is written to the System.err
259: * output stream
260: *
261: * @param level indentation level for this expression
262: * @param out
263: */
264:
265: public void display(int level, NamePool pool, PrintStream out) {
266: for (int i = 0; i < conditions.length; i++) {
267: out.println(ExpressionTool.indent(level)
268: + (i == 0 ? "if" : "else if"));
269: conditions[i].display(level + 1, pool, out);
270: out.println(ExpressionTool.indent(level) + "then");
271: actions[i].display(level + 1, pool, out);
272: }
273: }
274:
275: /**
276: * Process this instruction, that is, choose an xsl:when or xsl:otherwise child
277: * and process it.
278: * @param context the dynamic context of this transformation
279: * @throws XPathException if any non-recoverable dynamic error occurs
280: * @return a TailCall, if the chosen branch ends with a call of call-template or
281: * apply-templates. It is the caller's responsibility to execute such a TailCall.
282: * If there is no TailCall, returns null.
283: */
284:
285: public TailCall processLeavingTail(XPathContext context)
286: throws XPathException {
287: for (int i = 0; i < conditions.length; i++) {
288: final boolean b;
289: try {
290: b = conditions[i].effectiveBooleanValue(context);
291: } catch (XPathException e) {
292: e.setLocator(this );
293: throw e;
294: }
295: if (b) {
296: if (actions[i] instanceof TailCallReturner) {
297: return ((TailCallReturner) actions[i])
298: .processLeavingTail(context);
299: } else {
300: actions[i].process(context);
301: return null;
302: }
303: }
304: }
305: return null;
306: }
307:
308: /**
309: * Evaluate an expression as a single item. This always returns either a single Item or
310: * null (denoting the empty sequence). No conversion is done. This method should not be
311: * used unless the static type of the expression is a subtype of "item" or "item?": that is,
312: * it should not be called if the expression may return a sequence. There is no guarantee that
313: * this condition will be detected.
314: *
315: * @param context The context in which the expression is to be evaluated
316: * @exception XPathException if any dynamic error occurs evaluating the
317: * expression
318: * @return the node or atomic value that results from evaluating the
319: * expression; or null to indicate that the result is an empty
320: * sequence
321: */
322:
323: public Item evaluateItem(XPathContext context)
324: throws XPathException {
325: for (int i = 0; i < conditions.length; i++) {
326: final boolean b;
327: try {
328: b = conditions[i].effectiveBooleanValue(context);
329: } catch (XPathException e) {
330: e.setLocator(this );
331: throw e;
332: }
333: if (b) {
334: return actions[i].evaluateItem(context);
335: }
336: }
337: return null;
338: }
339:
340: /**
341: * Return an Iterator to iterate over the values of a sequence. The value of every
342: * expression can be regarded as a sequence, so this method is supported for all
343: * expressions. This default implementation relies on the process() method: it
344: * "pushes" the results of the instruction to a sequence in memory, and then
345: * iterates over this in-memory sequence.
346: *
347: * In principle instructions should implement a pipelined iterate() method that
348: * avoids the overhead of intermediate storage.
349: *
350: * @exception XPathException if any dynamic error occurs evaluating the
351: * expression
352: * @param context supplies the context for evaluation
353: * @return a SequenceIterator that can be used to iterate over the result
354: * of the expression
355: */
356:
357: public SequenceIterator iterate(XPathContext context)
358: throws XPathException {
359: for (int i = 0; i < conditions.length; i++) {
360: final boolean b;
361: try {
362: b = conditions[i].effectiveBooleanValue(context);
363: } catch (XPathException e) {
364: e.setLocator(this );
365: throw e;
366: }
367: if (b) {
368: return (actions[i]).iterate(context);
369: }
370: }
371: return EmptyIterator.getInstance();
372: }
373: }
374:
375: //
376: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
377: // you may not use this file except in compliance with the License. You may obtain a copy of the
378: // License at http://www.mozilla.org/MPL/
379: //
380: // Software distributed under the License is distributed on an "AS IS" basis,
381: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
382: // See the License for the specific language governing rights and limitations under the License.
383: //
384: // The Original Code is: all this file.
385: //
386: // The Initial Developer of the Original Code is Michael H. Kay.
387: //
388: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
389: //
390: // Contributor(s):
391: // Portions marked "e.g." are from Edwin Glaser (edwin@pannenleiter.de)
392: //
|