001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.Controller;
004: import net.sf.saxon.expr.*;
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.PatternSponsor;
009: import net.sf.saxon.sort.*;
010: import net.sf.saxon.style.StandardNames;
011: import net.sf.saxon.trace.TraceListener;
012: import net.sf.saxon.trans.XPathException;
013: import net.sf.saxon.type.ItemType;
014: import net.sf.saxon.type.SchemaType;
015: import net.sf.saxon.type.TypeHierarchy;
016: import net.sf.saxon.value.EmptySequence;
017: import net.sf.saxon.value.Value;
018:
019: import java.io.PrintStream;
020: import java.util.ArrayList;
021: import java.util.Comparator;
022: import java.util.Iterator;
023:
024: /**
025: * Handler for xsl:for-each-group elements in stylesheet. This is a new instruction
026: * defined in XSLT 2.0
027: */
028:
029: public class ForEachGroup extends Instruction implements
030: MappingFunction {
031:
032: public static final int GROUP_BY = 0;
033: public static final int GROUP_ADJACENT = 1;
034: public static final int GROUP_STARTING = 2;
035: public static final int GROUP_ENDING = 3;
036:
037: private Expression select;
038: private Expression action;
039: private byte algorithm;
040: private Expression key; // for group-starting and group-ending, this is a PatternSponsor
041: private Comparator collator = null;
042: private SortKeyDefinition[] sortKeys = null;
043:
044: public ForEachGroup(Expression select, Expression action,
045: byte algorithm, Expression key, Comparator collator,
046: SortKeyDefinition[] sortKeys) {
047: this .select = select;
048: this .action = action;
049: this .algorithm = algorithm;
050: this .key = key;
051: this .collator = collator;
052: this .sortKeys = sortKeys;
053: Iterator kids = iterateSubExpressions();
054: while (kids.hasNext()) {
055: Expression child = (Expression) kids.next();
056: adoptChildExpression(child);
057: }
058: }
059:
060: /**
061: * Get the name of this instruction for diagnostic and tracing purposes
062: */
063:
064: public int getInstructionNameCode() {
065: return StandardNames.XSL_FOR_EACH_GROUP;
066: }
067:
068: /**
069: * Get the action expression (the content of the for-each)
070: */
071:
072: public Expression getActionExpression() {
073: return action;
074: }
075:
076: /**
077: * Simplify an expression. This performs any static optimization (by rewriting the expression
078: * as a different expression).
079: *
080: * @return the simplified expression
081: * @throws XPathException if an error is discovered during expression
082: * rewriting
083: */
084:
085: public Expression simplify(StaticContext env) throws XPathException {
086: select = select.simplify(env);
087: action = action.simplify(env);
088: key = key.simplify(env);
089: return this ;
090: }
091:
092: public Expression typeCheck(StaticContext env,
093: ItemType contextItemType) throws XPathException {
094: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
095: select = select.typeCheck(env, contextItemType);
096: action = action.typeCheck(env, select.getItemType(th));
097: key = key.typeCheck(env, select.getItemType(th));
098: adoptChildExpression(select);
099: adoptChildExpression(action);
100: adoptChildExpression(key);
101: if (select instanceof EmptySequence) {
102: return EmptySequence.getInstance();
103: }
104: if (action instanceof EmptySequence) {
105: return EmptySequence.getInstance();
106: }
107: return this ;
108: }
109:
110: public Expression optimize(Optimizer opt, StaticContext env,
111: ItemType contextItemType) throws XPathException {
112: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
113: select = select.optimize(opt, env, contextItemType);
114: action = action.optimize(opt, env, select.getItemType(th));
115: key = key.optimize(opt, env, select.getItemType(th));
116: adoptChildExpression(select);
117: adoptChildExpression(action);
118: adoptChildExpression(key);
119: if (select instanceof EmptySequence) {
120: return EmptySequence.getInstance();
121: }
122: if (action instanceof EmptySequence) {
123: return EmptySequence.getInstance();
124: }
125: return this ;
126: }
127:
128: /**
129: * Get the item type of the items returned by evaluating this instruction
130: *
131: * @return the static item type of the instruction
132: * @param th
133: */
134:
135: public ItemType getItemType(TypeHierarchy th) {
136: return action.getItemType(th);
137: }
138:
139: /**
140: * Compute the dependencies of an expression, as the union of the
141: * dependencies of its subexpressions. (This is overridden for path expressions
142: * and filter expressions, where the dependencies of a subexpression are not all
143: * propogated). This method should be called only once, to compute the dependencies;
144: * after that, getDependencies should be used.
145: *
146: * @return the depencies, as a bit-mask
147: */
148:
149: public int computeDependencies() {
150: // some of the dependencies in the "action" part and in the grouping and sort keys aren't relevant,
151: // because they don't depend on values set outside the for-each-group expression
152: int dependencies = 0;
153: dependencies |= select.getDependencies();
154: dependencies |= key.getDependencies()
155: & ~StaticProperty.DEPENDS_ON_FOCUS;
156: dependencies |= (action.getDependencies() & ~(StaticProperty.DEPENDS_ON_FOCUS | StaticProperty.DEPENDS_ON_CURRENT_GROUP));
157: if (sortKeys != null) {
158: for (int i = 0; i < sortKeys.length; i++) {
159: dependencies |= (sortKeys[i].getSortKey()
160: .getDependencies() & ~StaticProperty.DEPENDS_ON_FOCUS);
161: Expression e = sortKeys[i].getCaseOrder();
162: if (e != null && !(e instanceof Value)) {
163: dependencies |= (e.getDependencies());
164: }
165: e = sortKeys[i].getDataTypeExpression();
166: if (e != null && !(e instanceof Value)) {
167: dependencies |= (e.getDependencies());
168: }
169: e = sortKeys[i].getLanguage();
170: if (e != null && !(e instanceof Value)) {
171: dependencies |= (e.getDependencies());
172: }
173: }
174: }
175: return dependencies;
176: }
177:
178: /**
179: * Determine whether this instruction creates new nodes.
180: * This implementation returns true if the "action" creates new nodes.
181: * (Nodes created by the condition can't contribute to the result).
182: */
183:
184: public final boolean createsNewNodes() {
185: int props = action.getSpecialProperties();
186: return ((props & StaticProperty.NON_CREATIVE) == 0);
187: }
188:
189: /**
190: * Handle promotion offers, that is, non-local tree rewrites.
191: *
192: * @param offer The type of rewrite being offered
193: * @throws XPathException
194: */
195:
196: protected void promoteInst(PromotionOffer offer)
197: throws XPathException {
198: select = doPromotion(select, offer);
199: action = doPromotion(action, offer);
200: key = doPromotion(key, offer);
201: }
202:
203: /**
204: * Get all the XPath expressions associated with this instruction
205: * (in XSLT terms, the expression present on attributes of the instruction,
206: * as distinct from the child instructions in a sequence construction)
207: */
208:
209: public Iterator iterateSubExpressions() {
210: ArrayList list = new ArrayList(3);
211: list.add(select);
212: list.add(action);
213: list.add(key);
214: if (sortKeys != null) {
215: for (int i = 0; i < sortKeys.length; i++) {
216: list.add(sortKeys[i].getSortKey());
217: Expression e = sortKeys[i].getOrder();
218: if (e != null && !(e instanceof Value)) {
219: list.add(e);
220: }
221: e = sortKeys[i].getCaseOrder();
222: if (e != null && !(e instanceof Value)) {
223: list.add(e);
224: }
225: e = sortKeys[i].getDataTypeExpression();
226: if (e != null && !(e instanceof Value)) {
227: list.add(e);
228: }
229: e = sortKeys[i].getLanguage();
230: if (e != null && !(e instanceof Value)) {
231: list.add(e);
232: }
233: }
234: }
235: return list.iterator();
236: }
237:
238: /**
239: * Check that any elements and attributes constructed or returned by this expression are acceptable
240: * in the content model of a given complex type. It's always OK to say yes, since the check will be
241: * repeated at run-time. The process of checking element and attribute constructors against the content
242: * model of a complex type also registers the type of content expected of those constructors, so the
243: * static validation can continue recursively.
244: */
245:
246: public void checkPermittedContents(SchemaType parentType,
247: StaticContext env, boolean whole) throws XPathException {
248: action.checkPermittedContents(parentType, env, false);
249: }
250:
251: public TailCall processLeavingTail(XPathContext context)
252: throws XPathException {
253: Controller controller = context.getController();
254:
255: GroupIterator groupIterator = getGroupIterator(context);
256:
257: XPathContextMajor c2 = context.newContext();
258: c2.setOrigin(this );
259: c2.setCurrentIterator(groupIterator);
260: c2.setCurrentGroupIterator(groupIterator);
261: c2.setCurrentTemplate(null);
262:
263: if (controller.isTracing()) {
264: TraceListener listener = controller.getTraceListener();
265: while (true) {
266: Item item = groupIterator.next();
267: if (item == null) {
268: break;
269: }
270: listener.startCurrentItem(item);
271: action.process(c2);
272: listener.endCurrentItem(item);
273: }
274: } else {
275: while (true) {
276: Item item = groupIterator.next();
277: if (item == null) {
278: break;
279: }
280: action.process(c2);
281: }
282: }
283:
284: return null;
285: }
286:
287: private GroupIterator getGroupIterator(XPathContext context)
288: throws XPathException {
289: SequenceIterator population = select.iterate(context);
290:
291: // get an iterator over the groups in "order of first appearance"
292:
293: GroupIterator groupIterator;
294: switch (algorithm) {
295: case GROUP_BY: {
296: XPathContext c2 = context.newMinorContext();
297: c2.setOrigin(this );
298: c2.setCurrentIterator(population);
299: groupIterator = new GroupByIterator(population, key, c2,
300: collator);
301: break;
302: }
303: case GROUP_ADJACENT: {
304: groupIterator = new GroupAdjacentIterator(population, key,
305: context, collator);
306: break;
307: }
308: case GROUP_STARTING:
309: groupIterator = new GroupStartingIterator(population,
310: ((PatternSponsor) key).getPattern(), context);
311: break;
312: case GROUP_ENDING:
313: groupIterator = new GroupEndingIterator(population,
314: ((PatternSponsor) key).getPattern(), context);
315: break;
316: default:
317: throw new AssertionError("Unknown grouping algorithm");
318: }
319:
320: // now iterate over the leading nodes of the groups
321:
322: if (sortKeys != null) {
323: // TODO: avoid reducing the sort keys if they have no dependencies
324: FixedSortKeyDefinition[] reducedSortKeys = new FixedSortKeyDefinition[sortKeys.length];
325: XPathContext xpc = context.newMinorContext();
326: for (int s = 0; s < sortKeys.length; s++) {
327: reducedSortKeys[s] = sortKeys[s].reduce(xpc);
328: }
329: groupIterator = new SortedGroupIterator(xpc, groupIterator,
330: reducedSortKeys, this );
331: }
332: return groupIterator;
333: }
334:
335: /**
336: * Return an Iterator to iterate over the values of a sequence. The value of every
337: * expression can be regarded as a sequence, so this method is supported for all
338: * expressions. This default implementation relies on the process() method: it
339: * "pushes" the results of the instruction to a sequence in memory, and then
340: * iterates over this in-memory sequence.
341: * <p/>
342: * In principle instructions should implement a pipelined iterate() method that
343: * avoids the overhead of intermediate storage.
344: *
345: * @param context supplies the context for evaluation
346: * @return a SequenceIterator that can be used to iterate over the result
347: * of the expression
348: * @throws XPathException if any dynamic error occurs evaluating the
349: * expression
350: */
351:
352: public SequenceIterator iterate(XPathContext context)
353: throws XPathException {
354: GroupIterator master = getGroupIterator(context);
355: XPathContextMajor c2 = context.newContext();
356: c2.setOrigin(this );
357: c2.setCurrentIterator(master);
358: c2.setCurrentGroupIterator(master);
359: c2.setCurrentTemplate(null);
360: return new MappingIterator(master, this , c2);
361: }
362:
363: /**
364: * Map one item to a sequence.
365: *
366: * @param item The item to be mapped.
367: * If context is supplied, this must be the same as context.currentItem().
368: * @param context The processing context. This is supplied only for mapping constructs that
369: * set the context node, position, and size. Otherwise it is null.
370: * @return either (a) a SequenceIterator over the sequence of items that the supplied input
371: * item maps to, or (b) an Item if it maps to a single item, or (c) null if it maps to an empty
372: * sequence.
373: */
374:
375: public Object map(Item item, XPathContext context)
376: throws XPathException {
377: return action.iterate(context);
378: }
379:
380: /**
381: * Diagnostic print of expression structure. The expression is written to the System.err
382: * output stream
383: *
384: * @param level indentation level for this expression
385: * @param out
386: */
387:
388: public void display(int level, NamePool pool, PrintStream out) {
389: out.println(ExpressionTool.indent(level) + "for-each-group");
390: out.println(ExpressionTool.indent(level) + "select");
391: select.display(level + 1, pool, out);
392: out.println(ExpressionTool.indent(level) + "return");
393: action.display(level + 1, pool, out);
394: }
395: }
396:
397: //
398: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
399: // you may not use this file except in compliance with the License. You may obtain a copy of the
400: // License at http://www.mozilla.org/MPL/
401: //
402: // Software distributed under the License is distributed on an "AS IS" basis,
403: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
404: // See the License for the specific language governing rights and limitations under the License.
405: //
406: // The Original Code is: all this file.
407: //
408: // The Initial Developer of the Original Code is Michael H. Kay.
409: //
410: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
411: //
412: // Contributor(s): none.
413: //
|