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.trans.DynamicError;
009: import net.sf.saxon.trans.XPathException;
010: import net.sf.saxon.type.ItemType;
011:
012: import java.io.PrintStream;
013: import java.util.ArrayList;
014: import java.util.Iterator;
015:
016: /**
017: * Instruction representing an xsl:call-template element in the stylesheet.
018: */
019:
020: public class CallTemplate extends Instruction {
021:
022: private Template template = null;
023: private WithParam[] actualParams = null;
024: private WithParam[] tunnelParams = null;
025: private boolean useTailRecursion = false;
026: private Expression calledTemplateExpression; // allows name to be an AVT
027: private NamespaceResolver nsContext; // needed only for a dynamic call
028:
029: /**
030: * Construct a CallTemplate instruction.
031: * @param template the Template object identifying the template to be called, in the normal
032: * case where this is known statically
033: * @param useTailRecursion
034: * @param calledTemplateExpression expression to calculate the name of the template to be called
035: * at run-time, this supports the saxon:allow-avt option
036: * @param nsContext the static namespace context of the instruction, needed only in the case
037: * where the name of the called template is to be calculated dynamically
038: */
039:
040: public CallTemplate(Template template, boolean useTailRecursion,
041: Expression calledTemplateExpression,
042: NamespaceResolver nsContext) {
043:
044: this .template = template;
045: this .useTailRecursion = useTailRecursion;
046: this .calledTemplateExpression = calledTemplateExpression;
047: this .nsContext = nsContext;
048: adoptChildExpression(calledTemplateExpression);
049: }
050:
051: /**
052: * Set the actual parameters on the call
053: */
054:
055: public void setActualParameters(WithParam[] actualParams,
056: WithParam[] tunnelParams) {
057: this .actualParams = actualParams;
058: this .tunnelParams = tunnelParams;
059: for (int i = 0; i < actualParams.length; i++) {
060: adoptChildExpression(actualParams[i]);
061: }
062: for (int i = 0; i < tunnelParams.length; i++) {
063: adoptChildExpression(tunnelParams[i]);
064: }
065: }
066:
067: /**
068: * Return the name of this instruction.
069: */
070:
071: public int getInstructionNameCode() {
072: return StandardNames.XSL_CALL_TEMPLATE;
073: }
074:
075: /**
076: * Set additional trace properties appropriate to the kind of instruction. This
077: * implementation adds the template property, which identities the template to be called
078: */
079:
080: public InstructionInfo getInstructionInfo() {
081: InstructionDetails details = (InstructionDetails) super
082: .getInstructionInfo();
083: if (template != null) {
084: details.setProperty("template", template);
085: }
086: return details;
087: }
088:
089: /**
090: * Simplify an expression. This performs any static optimization (by rewriting the expression
091: * as a different expression).
092: *
093: * @exception XPathException if an error is discovered during expression
094: * rewriting
095: * @return the simplified expression
096: */
097:
098: public Expression simplify(StaticContext env) throws XPathException {
099: WithParam.simplify(actualParams, env);
100: WithParam.simplify(tunnelParams, env);
101: if (calledTemplateExpression != null) {
102: calledTemplateExpression = calledTemplateExpression
103: .simplify(env);
104: }
105: return this ;
106: }
107:
108: public Expression typeCheck(StaticContext env,
109: ItemType contextItemType) throws XPathException {
110: WithParam.typeCheck(actualParams, env, contextItemType);
111: WithParam.typeCheck(tunnelParams, env, contextItemType);
112: if (calledTemplateExpression != null) {
113: calledTemplateExpression = calledTemplateExpression
114: .typeCheck(env, contextItemType);
115: adoptChildExpression(calledTemplateExpression);
116: }
117: return this ;
118: }
119:
120: public Expression optimize(Optimizer opt, StaticContext env,
121: ItemType contextItemType) throws XPathException {
122: WithParam.optimize(opt, actualParams, env, contextItemType);
123: WithParam.optimize(opt, tunnelParams, env, contextItemType);
124: if (calledTemplateExpression != null) {
125: calledTemplateExpression = calledTemplateExpression
126: .optimize(opt, env, contextItemType);
127: adoptChildExpression(calledTemplateExpression);
128: }
129: return this ;
130: }
131:
132: public int getIntrinsicDependencies() {
133: // we could go to the called template and find which parts of the context it depends on, but this
134: // would create the risk of infinite recursion. So we just assume that the dependencies exist
135: return StaticProperty.DEPENDS_ON_XSLT_CONTEXT
136: | StaticProperty.DEPENDS_ON_FOCUS;
137: }
138:
139: /**
140: * Determine whether this instruction creates new nodes.
141: * This implementation currently returns true unconditionally.
142: */
143:
144: public final boolean createsNewNodes() {
145: return true;
146: }
147:
148: /**
149: * Get all the XPath expressions associated with this instruction
150: * (in XSLT terms, the expression present on attributes of the instruction,
151: * as distinct from the child instructions in a sequence construction)
152: */
153:
154: public Iterator iterateSubExpressions() {
155: ArrayList list = new ArrayList(10);
156: if (calledTemplateExpression != null) {
157: list.add(calledTemplateExpression);
158: }
159: WithParam.getXPathExpressions(actualParams, list);
160: WithParam.getXPathExpressions(tunnelParams, list);
161: return list.iterator();
162: }
163:
164: /**
165: * Handle promotion offers, that is, non-local tree rewrites.
166: * @param offer The type of rewrite being offered
167: * @throws net.sf.saxon.trans.XPathException
168: */
169:
170: protected void promoteInst(PromotionOffer offer)
171: throws XPathException {
172: if (calledTemplateExpression != null) {
173: calledTemplateExpression = doPromotion(
174: calledTemplateExpression, offer);
175: }
176: WithParam.promoteParams(actualParams, offer);
177: WithParam.promoteParams(tunnelParams, offer);
178: }
179:
180: /**
181: * Process this instruction, without leaving any tail calls.
182: * @param context the dynamic context for this transformation
183: * @throws XPathException if a dynamic error occurs
184: */
185:
186: public void process(XPathContext context) throws XPathException {
187:
188: Template t = getTargetTemplate(context);
189: XPathContextMajor c2 = context.newContext();
190: c2.setOrigin(this );
191: c2.openStackFrame(t.getStackFrameMap());
192: c2.setLocalParameters(assembleParams(context, actualParams));
193: c2.setTunnelParameters(assembleTunnelParams(context,
194: tunnelParams));
195:
196: try {
197: TailCall tc = t.expand(c2);
198: while (tc != null) {
199: tc = tc.processLeavingTail(c2);
200: }
201: } catch (StackOverflowError e) {
202: DynamicError err = new DynamicError(
203: "Too many nested template or function calls. The stylesheet is probably looping.");
204: err.setLocator(this );
205: err.setXPathContext(context);
206: throw err;
207: }
208: }
209:
210: /**
211: * Process this instruction. If the called template contains a tail call (which may be
212: * an xsl:call-template of xsl:apply-templates instruction) then the tail call will not
213: * actually be evaluated, but will be returned in a TailCall object for the caller to execute.
214: * @param context the dynamic context for this transformation
215: * @return an object containing information about the tail call to be executed by the
216: * caller. Returns null if there is no tail call.
217: */
218:
219: public TailCall processLeavingTail(XPathContext context)
220: throws XPathException {
221: if (!useTailRecursion) {
222: process(context);
223: return null;
224: }
225:
226: // if name is determined dynamically, determine it now
227:
228: Template target = getTargetTemplate(context);
229:
230: // handle parameters if any
231:
232: ParameterSet params = assembleParams(context, actualParams);
233: ParameterSet tunnels = assembleTunnelParams(context,
234: tunnelParams);
235:
236: // Call the named template. Actually, don't call it; rather construct a call package
237: // and return it to the caller, who will then process this package.
238:
239: //System.err.println("Call template using tail recursion");
240: if (params == null) { // bug 490967
241: params = ParameterSet.EMPTY_PARAMETER_SET;
242: }
243:
244: // clear all the local variables: they are no longer needed
245: StackFrame frame = context.getStackFrame();
246: ValueRepresentation[] vars = frame.getStackFrameValues();
247: for (int i = 0; i < vars.length; i++) {
248: vars[i] = null;
249: }
250:
251: return new CallTemplatePackage(target, params, tunnels, context);
252: }
253:
254: /**
255: * Get the template, in the case where it is specified dynamically.
256: * @param context The dynamic context of the transformation
257: * @return The template to be called
258: * @throws XPathException if a dynamic error occurs: specifically, if the
259: * template name is computed at run-time (Saxon extension) and the name is invalid
260: * or does not reference a known template
261: */
262:
263: public Template getTargetTemplate(XPathContext context)
264: throws XPathException {
265: if (calledTemplateExpression != null) {
266: Controller controller = context.getController();
267: String qname = calledTemplateExpression
268: .evaluateAsString(context);
269:
270: String prefix;
271: String localName;
272: try {
273: String[] parts = controller.getConfiguration()
274: .getNameChecker().getQNameParts(qname);
275: prefix = parts[0];
276: localName = parts[1];
277: } catch (QNameException err) {
278: dynamicError("Invalid template name. "
279: + err.getMessage(), "XTSE0650", context);
280: return null;
281: }
282: String uri = nsContext.getURIForPrefix(prefix, false);
283: if (uri == null) {
284: dynamicError("Namespace prefix " + prefix
285: + " has not been declared", "XTSE0650", context);
286: }
287: int fprint = controller.getNamePool().getFingerprint(uri,
288: localName);
289: Template target = controller.getExecutable()
290: .getNamedTemplate(fprint);
291: if (target == null) {
292: dynamicError("Template " + qname
293: + " has not been defined", "XTSE0650", context);
294: }
295: return target;
296: } else {
297: return template;
298: }
299: }
300:
301: /**
302: * Diagnostic print of expression structure. The expression is written to the System.err
303: * output stream
304: *
305: * @param level indentation level for this expression
306: * @param out
307: */
308:
309: public void display(int level, NamePool pool, PrintStream out) {
310: out.println(ExpressionTool.indent(level) + "call-template");
311: }
312:
313: /**
314: * A CallTemplatePackage is an object that encapsulates the name of a template to be called,
315: * the parameters to be supplied, and the execution context. This object can be returned as a tail
316: * call, so that the actual call is made from a lower point on the stack, allowing a tail-recursive
317: * template to execute in a finite stack size
318: */
319:
320: private class CallTemplatePackage implements TailCall {
321:
322: private Template target;
323: private ParameterSet params;
324: private ParameterSet tunnelParams;
325: private XPathContext evaluationContext;
326:
327: /**
328: * Construct a CallTemplatePackage that contains information about a call.
329: * @param template the Template to be called
330: * @param params the parameters to be supplied to the called template
331: * @param evaluationContext saved context information from the Controller (current mode, etc)
332: * which must be reset to ensure that the template is called with all the context information
333: * intact
334: */
335:
336: public CallTemplatePackage(Template template,
337: ParameterSet params, ParameterSet tunnelParams,
338: XPathContext evaluationContext) {
339: this .target = template;
340: this .params = params;
341: this .tunnelParams = tunnelParams;
342: this .evaluationContext = evaluationContext;
343: }
344:
345: /**
346: * Process the template call encapsulated by this package.
347: * @param context the dynamic context for this transformation
348: * @return another TailCall. This will never be the original call, but it may be the next
349: * recursive call. For example, if A calls B which calls C which calls D, then B may return
350: * a TailCall to A representing the call from B to C; when this is processed, the result may be
351: * a TailCall representing the call from C to D.
352: * @throws XPathException if a dynamic error occurs
353: */
354:
355: public TailCall processLeavingTail(XPathContext context)
356: throws XPathException {
357: // TODO: the idea of tail call optimization is to reuse the caller's stack frame rather than
358: // creating a new one. We're doing this for the Java stack, but not for the context stack where
359: // local variables are held. It should be possible to avoid creating a new context, and instead
360: // to update the existing one in situ.
361: XPathContextMajor c2 = evaluationContext.newContext();
362: c2.setOrigin(CallTemplate.this );
363: c2.setLocalParameters(params);
364: c2.setTunnelParameters(tunnelParams);
365: c2.openStackFrame(target.getStackFrameMap());
366:
367: // System.err.println("Tail call on template");
368:
369: TailCall tc = target.expand(c2);
370: return tc;
371: }
372: }
373:
374: }
375:
376: //
377: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
378: // you may not use this file except in compliance with the License. You may obtain a copy of the
379: // License at http://www.mozilla.org/MPL/
380: //
381: // Software distributed under the License is distributed on an "AS IS" basis,
382: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
383: // See the License for the specific language governing rights and limitations under the License.
384: //
385: // The Original Code is: all this file.
386: //
387: // The Initial Developer of the Original Code is Michael H. Kay.
388: //
389: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
390: //
391: // Contributor(s): none.
392: //
|