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.*;
006: import net.sf.saxon.style.StandardNames;
007: import net.sf.saxon.trace.InstructionInfo;
008: import net.sf.saxon.trace.Location;
009: import net.sf.saxon.trace.TraceListener;
010: import net.sf.saxon.trans.DynamicError;
011: import net.sf.saxon.trans.Mode;
012: import net.sf.saxon.trans.XPathException;
013: import net.sf.saxon.trans.SaxonErrorCode;
014: import net.sf.saxon.type.ItemType;
015: import net.sf.saxon.type.Type;
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.Iterator;
022:
023: /**
024: * An instruction representing an xsl:apply-templates element in the stylesheet
025: */
026:
027: public class ApplyTemplates extends Instruction {
028:
029: private Expression select;
030: private WithParam[] actualParams = null;
031: private WithParam[] tunnelParams = null;
032: private boolean useCurrentMode = false;
033: private boolean useTailRecursion = false;
034: private Mode mode;
035: private boolean backwardsCompatible;
036:
037: public ApplyTemplates(Expression select, boolean useCurrentMode,
038: boolean useTailRecursion, Mode mode,
039: boolean backwardsCompatible) {
040: this .select = select;
041: this .useCurrentMode = useCurrentMode;
042: this .useTailRecursion = useTailRecursion;
043: this .mode = mode;
044: this .backwardsCompatible = backwardsCompatible;
045: adoptChildExpression(select);
046: }
047:
048: /**
049: * Set the actual parameters on the call
050: */
051:
052: public void setActualParameters(WithParam[] actualParams,
053: WithParam[] tunnelParams) {
054: this .actualParams = actualParams;
055: this .tunnelParams = tunnelParams;
056: }
057:
058: /**
059: * Get the name of this instruction for diagnostic and tracing purposes
060: */
061:
062: public int getInstructionNameCode() {
063: return StandardNames.XSL_APPLY_TEMPLATES;
064: }
065:
066: /**
067: * Set additional trace properties appropriate to the kind of instruction. This
068: * implementation adds the mode attribute
069: */
070:
071: public InstructionInfo getInstructionInfo() {
072: InstructionDetails details = (InstructionDetails) super
073: .getInstructionInfo();
074: details.setProperty("mode", mode);
075: return details;
076: }
077:
078: /**
079: * Simplify an expression. This performs any static optimization (by rewriting the expression
080: * as a different expression).
081: *
082: * @exception XPathException if an error is discovered during expression
083: * rewriting
084: * @return the simplified expression
085: */
086:
087: public Expression simplify(StaticContext env) throws XPathException {
088: WithParam.simplify(actualParams, env);
089: WithParam.simplify(tunnelParams, env);
090: select = select.simplify(env);
091: return this ;
092: }
093:
094: public Expression typeCheck(StaticContext env,
095: ItemType contextItemType) throws XPathException {
096: WithParam.typeCheck(actualParams, env, contextItemType);
097: WithParam.typeCheck(tunnelParams, env, contextItemType);
098: select = select.typeCheck(env, contextItemType);
099: adoptChildExpression(select);
100: if (select instanceof EmptySequence) {
101: return EmptySequence.getInstance();
102: }
103: return this ;
104: }
105:
106: public Expression optimize(Optimizer opt, StaticContext env,
107: ItemType contextItemType) throws XPathException {
108: WithParam.optimize(opt, actualParams, env, contextItemType);
109: WithParam.optimize(opt, tunnelParams, env, contextItemType);
110: select = select.typeCheck(env, contextItemType); // More info available second time around
111: select = select.optimize(opt, env, contextItemType);
112: adoptChildExpression(select);
113: if (select instanceof EmptySequence) {
114: return EmptySequence.getInstance();
115: }
116: return this ;
117: }
118:
119: /**
120: * Determine whether this instruction creates new nodes.
121: * This implementation returns true (which is almost invariably the case, so it's not worth
122: * doing any further analysis to find out more precisely).
123: */
124:
125: public final boolean createsNewNodes() {
126: return true;
127: }
128:
129: public void process(XPathContext context) throws XPathException {
130: apply(context, false);
131: }
132:
133: public TailCall processLeavingTail(XPathContext context)
134: throws XPathException {
135: return apply(context, useTailRecursion);
136: }
137:
138: private TailCall apply(XPathContext context, boolean returnTailCall)
139: throws XPathException {
140: Mode this Mode = mode;
141: if (useCurrentMode) {
142: this Mode = context.getCurrentMode();
143: }
144:
145: // handle parameters if any
146:
147: ParameterSet params = assembleParams(context, actualParams);
148: ParameterSet tunnels = assembleTunnelParams(context,
149: tunnelParams);
150:
151: if (returnTailCall) {
152: XPathContextMajor c2 = context.newContext();
153: c2.setOrigin(this );
154: return new ApplyTemplatesPackage(ExpressionTool
155: .lazyEvaluate(select, context, 1), this Mode,
156: params, tunnels, c2, getLocationId());
157: }
158:
159: // Get an iterator to iterate through the selected nodes in original order
160:
161: SequenceIterator iter = select.iterate(context);
162:
163: // Quick exit if the iterator is empty
164:
165: if (iter instanceof EmptyIterator) {
166: return null;
167: }
168:
169: // process the selected nodes now
170: XPathContextMajor c2 = context.newContext();
171: c2.setOrigin(this );
172: try {
173: TailCall tc = applyTemplates(iter, this Mode, params,
174: tunnels, c2, backwardsCompatible, getLocationId());
175: while (tc != null) {
176: tc = tc.processLeavingTail(c2);
177: }
178: } catch (StackOverflowError e) {
179: DynamicError err = new DynamicError(
180: "Too many nested apply-templates calls. The stylesheet is probably looping.");
181: err.setErrorCode(SaxonErrorCode.SXLM0001);
182: err.setLocator(this );
183: err.setXPathContext(context);
184: throw err;
185: }
186: return null;
187:
188: }
189:
190: /**
191: * Process selected nodes using the handlers registered for a particular
192: * mode.
193: *
194: * @exception XPathException if any dynamic error occurs
195: * @param iterator an Iterator over the nodes to be processed, in the
196: * correct (sorted) order
197: * @param mode Identifies the processing mode. It should match the
198: * mode defined when the element handler was registered using
199: * setHandler with a mode parameter. Set this parameter to null to
200: * invoke the default mode.
201: * @param parameters A ParameterSet containing the parameters to
202: * the handler/template being invoked. Specify null if there are no
203: * parameters.
204: * @param tunnelParameters A ParameterSet containing the parameters to
205: * the handler/template being invoked. Specify null if there are no
206: * parameters.
207: * @param context A newly-created context object
208: * @param locationId
209: * @return a TailCall returned by the last template to be invoked, or null,
210: * indicating that there are no outstanding tail calls.
211: */
212:
213: public static TailCall applyTemplates(SequenceIterator iterator,
214: Mode mode, ParameterSet parameters,
215: ParameterSet tunnelParameters, XPathContextMajor context,
216: boolean backwardsCompatible, int locationId)
217: throws XPathException {
218: Controller controller = context.getController();
219: TailCall tc = null;
220:
221: XPathContextMajor c2 = context;
222:
223: // Iterate over this sequence
224:
225: if (controller.isTracing()) {
226:
227: c2.setCurrentIterator(iterator);
228: c2.setCurrentMode(mode);
229: while (true) {
230: // process any tail calls returned from previous nodes
231: while (tc != null) {
232: tc = tc.processLeavingTail(c2);
233: }
234:
235: NodeInfo node = (NodeInfo) iterator.next();
236: // We can assume it's a node - we did static type checking
237: if (node == null)
238: break;
239:
240: // find the template rule for this node
241: Template eh = controller.getRuleManager()
242: .getTemplateRule(node, mode, c2);
243:
244: if (eh == null) { // Use the default action for the node
245: // No need to open a new stack frame!
246: defaultAction(node, parameters, tunnelParameters,
247: c2, backwardsCompatible, locationId);
248:
249: } else {
250: //if (tunnelParameters != null || eh.needsStackFrame()) {
251: TraceListener traceListener = controller
252: .getTraceListener();
253: c2.setLocalParameters(parameters);
254: c2.setTunnelParameters(tunnelParameters);
255: c2.openStackFrame(eh.getStackFrameMap());
256: traceListener.startCurrentItem(node);
257: tc = eh.processLeavingTail(c2);
258: traceListener.endCurrentItem(node);
259: //} else {
260: // TraceListener traceListener = controller.getTraceListener();
261: // traceListener.startCurrentItem(node);
262: // tc = eh.processLeavingTail(c2);
263: // traceListener.endCurrentItem(node);
264: //}
265: }
266: }
267:
268: } else { // not tracing
269:
270: c2.setCurrentIterator(iterator);
271: c2.setCurrentMode(mode);
272: while (true) {
273:
274: // process any tail calls returned from previous nodes
275: while (tc != null) {
276: tc = tc.processLeavingTail(c2);
277: }
278:
279: NodeInfo node = (NodeInfo) iterator.next();
280: // We can assume it's a node - we did static type checking
281: if (node == null)
282: break;
283:
284: // find the template rule for this node
285:
286: Template eh = controller.getRuleManager()
287: .getTemplateRule(node, mode, c2);
288:
289: if (eh == null) { // Use the default action for the node
290: // No need to open a new stack frame!
291: defaultAction(node, parameters, tunnelParameters,
292: c2, backwardsCompatible, locationId);
293:
294: } else {
295: //if (tunnelParameters != null || eh.needsStackFrame()) {
296: c2.openStackFrame(eh.getStackFrameMap());
297: c2.setLocalParameters(parameters);
298: c2.setTunnelParameters(tunnelParameters);
299: tc = eh.processLeavingTail(c2);
300: //} else {
301: // tc = eh.processLeavingTail(c2);
302: //}
303: }
304: }
305: }
306: // return the TailCall returned from the last node processed
307: return tc;
308: }
309:
310: /**
311: * Perform the built-in template action for a given node.
312: *
313: * @param node the node to be processed
314: * @param parameters the parameters supplied to apply-templates
315: * @param tunnelParams the tunnel parameters to be passed through
316: * @param backwardsCompatible true if in 1.0 mode (currently makes no difference)
317: * @param locationId location of the instruction (apply-templates, apply-imports etc) that caused
318: * the built-in template to be invoked
319: * @exception XPathException if any dynamic error occurs
320: */
321:
322: public static void defaultAction(NodeInfo node,
323: ParameterSet parameters, ParameterSet tunnelParams,
324: XPathContext context, boolean backwardsCompatible,
325: int locationId) throws XPathException {
326: switch (node.getNodeKind()) {
327: case Type.DOCUMENT:
328: case Type.ELEMENT:
329: SequenceIterator iter = node.iterateAxis(Axis.CHILD);
330: XPathContextMajor c2 = context.newContext();
331: c2.setOrigin(builtInDetails);
332: TailCall tc = applyTemplates(iter,
333: context.getCurrentMode(), parameters, tunnelParams,
334: c2, backwardsCompatible, locationId);
335: while (tc != null) {
336: tc = tc.processLeavingTail(c2);
337: }
338: return;
339: case Type.TEXT:
340: // NOTE: I tried changing this to use the text node's copy() method, but
341: // performance was worse
342: case Type.ATTRIBUTE:
343: context.getReceiver().characters(node.getStringValueCS(),
344: locationId, 0);
345: return;
346: case Type.COMMENT:
347: case Type.PROCESSING_INSTRUCTION:
348: case Type.NAMESPACE:
349: // no action
350: return;
351: }
352: }
353:
354: /**
355: * Instruction details for the built-in template
356: */
357:
358: private static InstructionDetails builtInDetails = new InstructionDetails();
359: static {
360: builtInDetails.setConstructType(Location.BUILT_IN_TEMPLATE);
361: }
362:
363: /**
364: * Get all the XPath expressions associated with this instruction
365: * (in XSLT terms, the expression present on attributes of the instruction,
366: * as distinct from the child instructions in a sequence construction)
367: */
368:
369: public Iterator iterateSubExpressions() {
370: ArrayList list = new ArrayList(10);
371: list.add(select);
372: WithParam.getXPathExpressions(actualParams, list);
373: WithParam.getXPathExpressions(tunnelParams, list);
374: return list.iterator();
375: }
376:
377: /**
378: * Handle promotion offers, that is, non-local tree rewrites.
379: * @param offer The type of rewrite being offered
380: * @throws XPathException
381: */
382:
383: protected void promoteInst(PromotionOffer offer)
384: throws XPathException {
385: select = doPromotion(select, offer);
386: WithParam.promoteParams(actualParams, offer);
387: WithParam.promoteParams(tunnelParams, offer);
388: }
389:
390: /**
391: * Diagnostic print of expression structure. The expression is written to the System.err
392: * output stream
393: *
394: * @param level indentation level for this expression
395: * @param out
396: */
397:
398: public void display(int level, NamePool pool, PrintStream out) {
399: out.println(ExpressionTool.indent(level) + "apply-templates "
400: + "select=");
401: select.display(level + 1, pool, out);
402: }
403:
404: /**
405: * An ApplyTemplatesPackage is an object that encapsulates the sequence of nodes to be processed,
406: * the mode, the parameters to be supplied, and the execution context. This object can be returned as a tail
407: * call, so that the actual call is made from a lower point on the stack, allowing a tail-recursive
408: * template to execute in a finite stack size
409: */
410:
411: private static class ApplyTemplatesPackage implements TailCall {
412:
413: private ValueRepresentation selectedNodes;
414: private Mode mode;
415: private ParameterSet params;
416: private ParameterSet tunnelParams;
417: private XPathContextMajor evaluationContext;
418: private int locationId;
419:
420: ApplyTemplatesPackage(ValueRepresentation selectedNodes,
421: Mode mode, ParameterSet params,
422: ParameterSet tunnelParams, XPathContextMajor context,
423: int locationId) {
424: this .selectedNodes = selectedNodes;
425: this .mode = mode;
426: this .params = params;
427: this .tunnelParams = tunnelParams;
428: this .evaluationContext = context;
429: this .locationId = locationId;
430: }
431:
432: public TailCall processLeavingTail(XPathContext context)
433: throws XPathException {
434: TailCall tc = applyTemplates(Value
435: .getIterator(selectedNodes), mode, params,
436: tunnelParams, evaluationContext, false, locationId);
437: return tc;
438: }
439: }
440:
441: }
442:
443: //
444: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
445: // you may not use this file except in compliance with the License. You may obtain a copy of the
446: // License at http://www.mozilla.org/MPL/
447: //
448: // Software distributed under the License is distributed on an "AS IS" basis,
449: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
450: // See the License for the specific language governing rights and limitations under the License.
451: //
452: // The Original Code is: all this file.
453: //
454: // The Initial Developer of the Original Code is Michael H. Kay.
455: //
456: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
457: //
458: // Contributor(s): none.
459: //
|