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.pattern.NoNodeTest;
009: import net.sf.saxon.trans.DynamicError;
010: import net.sf.saxon.trans.XPathException;
011: import net.sf.saxon.type.*;
012: import net.sf.saxon.value.Cardinality;
013: import net.sf.saxon.value.EmptySequence;
014: import net.sf.saxon.value.SequenceExtent;
015:
016: import java.io.PrintStream;
017: import java.util.*;
018:
019: /**
020: * Implements an imaginary xsl:block instruction which simply evaluates
021: * its contents. Used for top-level templates, xsl:otherwise, etc.
022: */
023:
024: public class Block extends Instruction {
025:
026: private Expression[] children;
027:
028: public Block() {
029: }
030:
031: public static Expression makeBlock(Expression e1, Expression e2) {
032: if (e1 == null || e1 instanceof EmptySequence) {
033: return e2;
034: }
035: if (e2 == null || e2 instanceof EmptySequence) {
036: return e1;
037: }
038: if (e1 instanceof Block || e2 instanceof Block) {
039: Iterator it1 = (e1 instanceof Block ? e1
040: .iterateSubExpressions() : new MonoIterator(e1));
041: Iterator it2 = (e2 instanceof Block ? e2
042: .iterateSubExpressions() : new MonoIterator(e2));
043: List list = new ArrayList(10);
044: while (it1.hasNext()) {
045: list.add(it1.next());
046: }
047: while (it2.hasNext()) {
048: list.add(it2.next());
049: }
050: Expression[] exps = new Expression[list.size()];
051: exps = (Expression[]) list.toArray(exps);
052: Block b = new Block();
053: b.setChildren(exps);
054: return b;
055: } else {
056: Expression[] exps = { e1, e2 };
057: Block b = new Block();
058: b.setChildren(exps);
059: return b;
060: }
061: }
062:
063: /**
064: * Set the children of this instruction
065: * @param children The instructions that are children of this instruction
066: */
067:
068: public void setChildren(Expression[] children) {
069: if (children == null || children.length == 0) {
070: this .children = null;
071: } else {
072: this .children = children;
073: for (int c = 0; c < children.length; c++) {
074: adoptChildExpression(children[c]);
075: }
076: }
077: }
078:
079: /**
080: * Get the children of this instruction
081: * @return the children of this instruction, as an array of Instruction objects. May return either
082: * a zero-length array or null if there are no children
083: */
084:
085: public Expression[] getChildren() {
086: return children;
087: }
088:
089: public Iterator iterateSubExpressions() {
090: if (children == null) {
091: return Collections.EMPTY_LIST.iterator();
092: } else {
093: return Arrays.asList(children).iterator();
094: }
095: }
096:
097: /**
098: * Determine the data type of the items returned by this expression
099: * @return the data type
100: * @param th
101: */
102:
103: public final ItemType getItemType(TypeHierarchy th) {
104: if (children == null || children.length == 0) {
105: return NoNodeTest.getInstance();
106: }
107: ItemType t1 = children[0].getItemType(th);
108: for (int i = 1; i < children.length; i++) {
109: t1 = Type.getCommonSuperType(t1, children[i]
110: .getItemType(th), th);
111: if (t1 instanceof AnyItemType) {
112: return t1; // no point going any further
113: }
114: }
115: return t1;
116: }
117:
118: /**
119: * Determine the cardinality of the expression
120: */
121:
122: public final int getCardinality() {
123: if (children == null || children.length == 0) {
124: return StaticProperty.EMPTY;
125: }
126: int c1 = children[0].getCardinality();
127: for (int i = 1; i < children.length; i++) {
128: c1 = Cardinality.add(c1, children[i].getCardinality());
129: if (c1 == StaticProperty.ALLOWS_ZERO_OR_MORE) {
130: break;
131: }
132: }
133: return c1;
134: }
135:
136: /**
137: * Determine whether this instruction creates new nodes.
138: * This implementation returns true if any child instruction
139: * returns true.
140: */
141:
142: public final boolean createsNewNodes() {
143: if (children == null) {
144: return false;
145: }
146: ;
147: for (int i = 0; i < children.length; i++) {
148: int props = children[i].getSpecialProperties();
149: if ((props & StaticProperty.NON_CREATIVE) == 0) {
150: return true;
151: }
152: }
153: return false;
154: }
155:
156: /**
157: * Simplify an expression. This performs any static optimization (by rewriting the expression
158: * as a different expression). The default implementation does nothing.
159: *
160: * @exception XPathException if an error is discovered during expression
161: * rewriting
162: * @return the simplified expression
163: */
164:
165: public Expression simplify(StaticContext env) throws XPathException {
166: boolean allAtomic = true;
167: boolean nested = false;
168: if (children != null) {
169: for (int c = 0; c < children.length; c++) {
170: children[c] = children[c].simplify(env);
171: if (!(children[c] instanceof Item)) {
172: allAtomic = false;
173: }
174: if (children[c] instanceof Block) {
175: nested = true;
176: } else if (children[c] instanceof EmptySequence) {
177: nested = true;
178: }
179: }
180: if (children.length == 1) {
181: return getChildren()[0];
182: }
183: if (children.length == 0) {
184: return EmptySequence.getInstance();
185: }
186: if (nested) {
187: List list = new ArrayList(children.length * 2);
188: flatten(list);
189: children = new Expression[list.size()];
190: for (int i = 0; i < children.length; i++) {
191: children[i] = (Expression) list.get(i);
192: adoptChildExpression(children[i]);
193: }
194: }
195: if (allAtomic) {
196: Item[] values = new Item[children.length];
197: for (int c = 0; c < children.length; c++) {
198: values[c] = (Item) children[c];
199: }
200: return new SequenceExtent(values);
201: }
202: } else {
203: return EmptySequence.getInstance();
204: }
205:
206: return this ;
207: }
208:
209: private void flatten(List list) {
210: for (int i = 0; i < children.length; i++) {
211: if (children[i] instanceof Block) {
212: ((Block) children[i]).flatten(list);
213: } else if (children[i] instanceof EmptySequence) {
214: // no-op
215: } else {
216: list.add(children[i]);
217: }
218: }
219: }
220:
221: public Expression typeCheck(StaticContext env,
222: ItemType contextItemType) throws XPathException {
223: boolean nested = false;
224: if (children != null) {
225: for (int c = 0; c < children.length; c++) {
226: children[c] = children[c].typeCheck(env,
227: contextItemType);
228: adoptChildExpression(children[c]);
229: if (children[c] instanceof Block) {
230: nested = true;
231: } else if (children[c] instanceof EmptySequence) {
232: nested = true;
233: }
234: }
235: }
236: if (nested) {
237: List list = new ArrayList(children.length * 2);
238: flatten(list);
239: children = new Expression[list.size()];
240: for (int i = 0; i < children.length; i++) {
241: children[i] = (Expression) list.get(i);
242: adoptChildExpression(children[i]);
243: }
244: }
245: if (children.length == 0) {
246: return EmptySequence.getInstance();
247: } else if (children.length == 1) {
248: return children[0];
249: } else {
250: return this ;
251: }
252: }
253:
254: public Expression optimize(Optimizer opt, StaticContext env,
255: ItemType contextItemType) throws XPathException {
256: if (children != null) {
257: for (int c = 0; c < children.length; c++) {
258: children[c] = children[c].optimize(opt, env,
259: contextItemType);
260: adoptChildExpression(children[c]);
261: }
262: }
263: return this ;
264: }
265:
266: /**
267: * Handle promotion offers, that is, non-local tree rewrites.
268: * @param offer The type of rewrite being offered
269: * @throws XPathException
270: */
271:
272: protected void promoteInst(PromotionOffer offer)
273: throws XPathException {
274: if (children != null) {
275: for (int c = 0; c < children.length; c++) {
276: children[c] = doPromotion(children[c], offer);
277: if (offer.accepted) {
278: break; // can only handle one promotion at a time
279: }
280: }
281: }
282: }
283:
284: /**
285: * Check that any elements and attributes constructed or returned by this expression are acceptable
286: * in the content model of a given complex type. It's always OK to say yes, since the check will be
287: * repeated at run-time. The process of checking element and attribute constructors against the content
288: * model of a complex type also registers the type of content expected of those constructors, so the
289: * static validation can continue recursively.
290: */
291:
292: public void checkPermittedContents(SchemaType parentType,
293: StaticContext env, boolean whole) throws XPathException {
294: if (children != null) {
295: for (int c = 0; c < children.length; c++) {
296: children[c].checkPermittedContents(parentType, env,
297: false);
298: }
299: }
300: }
301:
302: /**
303: * Diagnostic print of expression structure. The expression is written to the System.err
304: * output stream
305: *
306: * @param level indentation level for this expression
307: * @param out
308: */
309:
310: public void display(int level, NamePool pool, PrintStream out) {
311: if (children != null) {
312: displayChildren(children, level + 1, pool, out);
313: }
314: }
315:
316: /**
317: * Display the children of an instruction for diagostics
318: */
319:
320: public static void displayChildren(Expression[] children,
321: int level, NamePool pool, PrintStream out) {
322: if (children != null) {
323: for (int c = 0; c < children.length; c++) {
324: children[c].display(level + 1, pool, out);
325: }
326: }
327: }
328:
329: public TailCall processLeavingTail(XPathContext context)
330: throws XPathException {
331:
332: if (children == null) {
333: return null;
334: }
335:
336: TailCall tc = null;
337: for (int i = 0; i < children.length; i++) {
338: try {
339: if (children[i] instanceof TailCallReturner) {
340: tc = ((TailCallReturner) children[i])
341: .processLeavingTail(context);
342: } else {
343: children[i].process(context);
344: tc = null;
345: }
346: } catch (DynamicError e) {
347: if (e.getXPathContext() == null) {
348: e.setXPathContext(context);
349: }
350: if (e.getLocator() == null) {
351: e
352: .setLocator(ExpressionTool
353: .getLocator(children[i]));
354: }
355: throw e;
356: }
357: }
358: return tc;
359: }
360:
361: /**
362: * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
363: * This method indicates which of these methods is provided. This implementation provides both iterate() and
364: * process() methods natively.
365: */
366:
367: public int getImplementationMethod() {
368: return ITERATE_METHOD | PROCESS_METHOD;
369: }
370:
371: /**
372: * Iterate over the results of all the child expressions
373: */
374:
375: public SequenceIterator iterate(XPathContext context)
376: throws XPathException {
377: if (children == null || children.length == 0) {
378: return EmptyIterator.getInstance();
379: } else if (children.length == 1) {
380: return children[0].iterate(context);
381: } else {
382: return new BlockIterator(context);
383: }
384: }
385:
386: /**
387: * Iterate over the instructions in the Block, concatenating the result of each instruction
388: * into a single combined sequence.
389: */
390:
391: private class BlockIterator implements SequenceIterator {
392:
393: private int i = 0;
394: private SequenceIterator child;
395: private XPathContext context;
396: private Item current;
397: private int position = 0;
398:
399: public BlockIterator(XPathContext context) {
400: this .context = context;
401: }
402:
403: /**
404: * Get the next item in the sequence. <BR>
405: *
406: * @return the next item, or null if there are no more items.
407: * @throws net.sf.saxon.trans.XPathException
408: * if an error occurs retrieving the next item
409: */
410:
411: public Item next() throws XPathException {
412: if (position < 0) {
413: return null;
414: }
415: while (true) {
416: if (child == null) {
417: child = children[i++].iterate(context);
418: }
419: current = child.next();
420: if (current != null) {
421: position++;
422: return current;
423: }
424: child = null;
425: if (i >= children.length) {
426: current = null;
427: position = -1;
428: return null;
429: }
430: }
431: }
432:
433: /**
434: * Get the current value in the sequence (the one returned by the
435: * most recent call on next()). This will be null before the first
436: * call of next().
437: *
438: * @return the current item, the one most recently returned by a call on
439: * next(); or null, if next() has not been called, or if the end
440: * of the sequence has been reached.
441: */
442:
443: public Item current() {
444: return current;
445: }
446:
447: /**
448: * Get the current position. This will be zero before the first call
449: * on next(), otherwise it will be the number of times that next() has
450: * been called.
451: *
452: * @return the current position, the position of the item returned by the
453: * most recent call of next()
454: */
455:
456: public int position() {
457: return position;
458: }
459:
460: /**
461: * Get another SequenceIterator that iterates over the same items as the original,
462: * but which is repositioned at the start of the sequence.
463: *
464: * @return a SequenceIterator that iterates over the same items,
465: * positioned before the first item
466: * @throws net.sf.saxon.trans.XPathException
467: * if any error occurs
468: */
469:
470: public SequenceIterator getAnother() throws XPathException {
471: return new BlockIterator(context);
472: }
473:
474: /**
475: * Get properties of this iterator, as a bit-significant integer.
476: *
477: * @return the properties of this iterator. This will be some combination of
478: * properties such as {@link GROUNDED}, {@link LAST_POSITION_FINDER},
479: * and {@link LOOKAHEAD}. It is always
480: * acceptable to return the value zero, indicating that there are no known special properties.
481: * It is acceptable for the properties of the iterator to change depending on its state.
482: */
483:
484: public int getProperties() {
485: return 0;
486: }
487: }
488: }
489: //
490: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
491: // you may not use this file except in compliance with the License. You may obtain a copy of the
492: // License at http://www.mozilla.org/MPL/
493: //
494: // Software distributed under the License is distributed on an "AS IS" basis,
495: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
496: // See the License for the specific language governing rights and limitations under the License.
497: //
498: // The Original Code is: all this file.
499: //
500: // The Initial Developer of the Original Code is Michael H. Kay.
501: //
502: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
503: //
504: // Contributor(s): none.
505: //
|