001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: ElemApplyTemplates.java,v 1.36 2005/01/23 00:27:29 mcnamara Exp $
018: */
019: package org.apache.xalan.templates;
020:
021: import java.util.Vector;
022:
023: import javax.xml.transform.TransformerException;
024:
025: import org.apache.xalan.transformer.StackGuard;
026: import org.apache.xalan.transformer.TransformerImpl;
027: import org.apache.xml.dtm.DTM;
028: import org.apache.xml.dtm.DTMIterator;
029: import org.apache.xml.serializer.SerializationHandler;
030: import org.apache.xml.utils.IntStack;
031: import org.apache.xml.utils.QName;
032: import org.apache.xpath.VariableStack;
033: import org.apache.xpath.XPath;
034: import org.apache.xpath.XPathContext;
035: import org.apache.xpath.objects.XObject;
036: import org.xml.sax.SAXException;
037:
038: /**
039: * Implement xsl:apply-templates.
040: * <pre>
041: * &!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
042: * &!ATTLIST xsl:apply-templates
043: * select %expr; "node()"
044: * mode %qname; #IMPLIED
045: * &
046: * </pre>
047: * @see <a href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules in XSLT Specification</a>
048: * @xsl.usage advanced
049: */
050: public class ElemApplyTemplates extends ElemCallTemplate {
051: static final long serialVersionUID = 2903125371542621004L;
052:
053: /**
054: * mode %qname; #IMPLIED
055: * @serial
056: */
057: private QName m_mode = null;
058:
059: /**
060: * Set the mode attribute for this element.
061: *
062: * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>.
063: */
064: public void setMode(QName mode) {
065: m_mode = mode;
066: }
067:
068: /**
069: * Get the mode attribute for this element.
070: *
071: * @return The mode attribute for this element
072: */
073: public QName getMode() {
074: return m_mode;
075: }
076:
077: /**
078: * Tells if this belongs to a default template,
079: * in which case it will act different with
080: * regard to processing modes.
081: * @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in XSLT Specification</a>
082: * @serial
083: */
084: private boolean m_isDefaultTemplate = false;
085:
086: // /**
087: // * List of namespace/localname IDs, for identification of xsl:with-param to
088: // * xsl:params. Initialized in the compose() method.
089: // */
090: // private int[] m_paramIDs;
091:
092: /**
093: * Set if this belongs to a default template,
094: * in which case it will act different with
095: * regard to processing modes.
096: * @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in XSLT Specification</a>
097: *
098: * @param b boolean value to set.
099: */
100: public void setIsDefaultTemplate(boolean b) {
101: m_isDefaultTemplate = b;
102: }
103:
104: /**
105: * Get an int constant identifying the type of element.
106: * @see org.apache.xalan.templates.Constants
107: *
108: * @return Token ID for this element types
109: */
110: public int getXSLToken() {
111: return Constants.ELEMNAME_APPLY_TEMPLATES;
112: }
113:
114: /**
115: * This function is called after everything else has been
116: * recomposed, and allows the template to set remaining
117: * values that may be based on some other property that
118: * depends on recomposition.
119: */
120: public void compose(StylesheetRoot sroot)
121: throws TransformerException {
122: super .compose(sroot);
123: }
124:
125: /**
126: * Return the node name.
127: *
128: * @return Element name
129: */
130: public String getNodeName() {
131: return Constants.ELEMNAME_APPLY_TEMPLATES_STRING;
132: }
133:
134: /**
135: * Apply the context node to the matching templates.
136: * @see <a href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules in XSLT Specification</a>
137: *
138: * @param transformer non-null reference to the the current transform-time state.
139: *
140: * @throws TransformerException
141: */
142: public void execute(TransformerImpl transformer)
143: throws TransformerException {
144:
145: transformer.pushCurrentTemplateRuleIsNull(false);
146:
147: boolean pushMode = false;
148:
149: try {
150: // %REVIEW% Do we need this check??
151: // if (null != sourceNode)
152: // {
153: // boolean needToTurnOffInfiniteLoopCheck = false;
154: QName mode = transformer.getMode();
155:
156: if (!m_isDefaultTemplate) {
157: if (((null == mode) && (null != m_mode))
158: || ((null != mode) && !mode.equals(m_mode))) {
159: pushMode = true;
160:
161: transformer.pushMode(m_mode);
162: }
163: }
164: if (transformer.getDebug())
165: transformer.getTraceManager().fireTraceEvent(this );
166:
167: transformSelectedNodes(transformer);
168: } finally {
169: if (transformer.getDebug())
170: transformer.getTraceManager().fireTraceEndEvent(this );
171:
172: if (pushMode)
173: transformer.popMode();
174:
175: transformer.popCurrentTemplateRuleIsNull();
176: }
177: }
178:
179: /**
180: * Perform a query if needed, and call transformNode for each child.
181: *
182: * @param transformer non-null reference to the the current transform-time state.
183: *
184: * @throws TransformerException Thrown in a variety of circumstances.
185: * @xsl.usage advanced
186: */
187: public void transformSelectedNodes(TransformerImpl transformer)
188: throws TransformerException {
189:
190: final XPathContext xctxt = transformer.getXPathContext();
191: final int sourceNode = xctxt.getCurrentNode();
192: DTMIterator sourceNodes = m_selectExpression.asIterator(xctxt,
193: sourceNode);
194: VariableStack vars = xctxt.getVarStack();
195: int nParams = getParamElemCount();
196: int this frame = vars.getStackFrame();
197: StackGuard guard = transformer.getStackGuard();
198: boolean check = (guard.getRecursionLimit() > -1) ? true : false;
199:
200: boolean pushContextNodeListFlag = false;
201:
202: try {
203:
204: xctxt.pushCurrentNode(DTM.NULL);
205: xctxt.pushCurrentExpressionNode(DTM.NULL);
206: xctxt.pushSAXLocatorNull();
207: transformer.pushElemTemplateElement(null);
208: final Vector keys = (m_sortElems == null) ? null
209: : transformer.processSortKeys(this , sourceNode);
210:
211: // Sort if we need to.
212: if (null != keys)
213: sourceNodes = sortNodes(xctxt, keys, sourceNodes);
214:
215: if (transformer.getDebug()) {
216: transformer.getTraceManager().fireSelectedEvent(
217: sourceNode,
218: this ,
219: "select",
220: new XPath(m_selectExpression),
221: new org.apache.xpath.objects.XNodeSet(
222: sourceNodes));
223: }
224:
225: final SerializationHandler rth = transformer
226: .getSerializationHandler();
227: // ContentHandler chandler = rth.getContentHandler();
228: final StylesheetRoot sroot = transformer.getStylesheet();
229: final TemplateList tl = sroot.getTemplateListComposed();
230: final boolean quiet = transformer
231: .getQuietConflictWarnings();
232:
233: // Should be able to get this from the iterator but there must be a bug.
234: DTM dtm = xctxt.getDTM(sourceNode);
235:
236: int argsFrame = -1;
237: if (nParams > 0) {
238: // This code will create a section on the stack that is all the
239: // evaluated arguments. These will be copied into the real params
240: // section of each called template.
241: argsFrame = vars.link(nParams);
242: vars.setStackFrame(this frame);
243:
244: for (int i = 0; i < nParams; i++) {
245: ElemWithParam ewp = m_paramElems[i];
246: if (transformer.getDebug())
247: transformer.getTraceManager().fireTraceEvent(
248: ewp);
249: XObject obj = ewp.getValue(transformer, sourceNode);
250: if (transformer.getDebug())
251: transformer.getTraceManager()
252: .fireTraceEndEvent(ewp);
253:
254: vars.setLocalVariable(i, obj, argsFrame);
255: }
256: vars.setStackFrame(argsFrame);
257: }
258:
259: xctxt.pushContextNodeList(sourceNodes);
260: pushContextNodeListFlag = true;
261:
262: IntStack currentNodes = xctxt.getCurrentNodeStack();
263:
264: IntStack currentExpressionNodes = xctxt
265: .getCurrentExpressionNodeStack();
266:
267: // pushParams(transformer, xctxt);
268:
269: int child;
270: while (DTM.NULL != (child = sourceNodes.nextNode())) {
271: currentNodes.setTop(child);
272: currentExpressionNodes.setTop(child);
273:
274: if (xctxt.getDTM(child) != dtm) {
275: dtm = xctxt.getDTM(child);
276: }
277:
278: final int exNodeType = dtm.getExpandedTypeID(child);
279:
280: final int nodeType = dtm.getNodeType(child);
281:
282: final QName mode = transformer.getMode();
283:
284: ElemTemplate template = tl.getTemplateFast(xctxt,
285: child, exNodeType, mode, -1, quiet, dtm);
286:
287: // If that didn't locate a node, fall back to a default template rule.
288: // See http://www.w3.org/TR/xslt#built-in-rule.
289: if (null == template) {
290: switch (nodeType) {
291: case DTM.DOCUMENT_FRAGMENT_NODE:
292: case DTM.ELEMENT_NODE:
293: template = sroot.getDefaultRule();
294: // %OPT% direct faster?
295: break;
296: case DTM.ATTRIBUTE_NODE:
297: case DTM.CDATA_SECTION_NODE:
298: case DTM.TEXT_NODE:
299: // if(rth.m_elemIsPending || rth.m_docPending)
300: // rth.flushPending(true);
301: transformer.pushPairCurrentMatched(sroot
302: .getDefaultTextRule(), child);
303: transformer.setCurrentElement(sroot
304: .getDefaultTextRule());
305: // dtm.dispatchCharactersEvents(child, chandler, false);
306: dtm.dispatchCharactersEvents(child, rth, false);
307: transformer.popCurrentMatched();
308: continue;
309: case DTM.DOCUMENT_NODE:
310: template = sroot.getDefaultRootRule();
311: break;
312: default:
313:
314: // No default rules for processing instructions and the like.
315: continue;
316: }
317: } else {
318: transformer.setCurrentElement(template);
319: }
320:
321: transformer.pushPairCurrentMatched(template, child);
322: if (check)
323: guard.checkForInfinateLoop();
324:
325: int currentFrameBottom; // See comment with unlink, below
326: if (template.m_frameSize > 0) {
327: xctxt.pushRTFContext();
328: currentFrameBottom = vars.getStackFrame(); // See comment with unlink, below
329: vars.link(template.m_frameSize);
330: // You can't do the check for nParams here, otherwise the
331: // xsl:params might not be nulled.
332: if (/* nParams > 0 && */template.m_inArgsSize > 0) {
333: int paramIndex = 0;
334: for (ElemTemplateElement elem = template
335: .getFirstChildElem(); null != elem; elem = elem
336: .getNextSiblingElem()) {
337: if (Constants.ELEMNAME_PARAMVARIABLE == elem
338: .getXSLToken()) {
339: ElemParam ep = (ElemParam) elem;
340:
341: int i;
342: for (i = 0; i < nParams; i++) {
343: ElemWithParam ewp = m_paramElems[i];
344: if (ewp.m_qnameID == ep.m_qnameID) {
345: XObject obj = vars
346: .getLocalVariable(i,
347: argsFrame);
348: vars.setLocalVariable(
349: paramIndex, obj);
350: break;
351: }
352: }
353: if (i == nParams)
354: vars.setLocalVariable(paramIndex,
355: null);
356: } else
357: break;
358: paramIndex++;
359: }
360:
361: }
362: } else
363: currentFrameBottom = 0;
364:
365: // Fire a trace event for the template.
366: if (transformer.getDebug())
367: transformer.getTraceManager().fireTraceEvent(
368: template);
369:
370: // And execute the child templates.
371: // Loop through the children of the template, calling execute on
372: // each of them.
373: for (ElemTemplateElement t = template.m_firstChild; t != null; t = t.m_nextSibling) {
374: xctxt.setSAXLocator(t);
375: try {
376: transformer.pushElemTemplateElement(t);
377: t.execute(transformer);
378: } finally {
379: transformer.popElemTemplateElement();
380: }
381: }
382:
383: if (transformer.getDebug())
384: transformer.getTraceManager().fireTraceEndEvent(
385: template);
386:
387: if (template.m_frameSize > 0) {
388: // See Frank Weiss bug around 03/19/2002 (no Bugzilla report yet).
389: // While unlink will restore to the proper place, the real position
390: // may have been changed for xsl:with-param, so that variables
391: // can be accessed.
392: // of right now.
393: // More:
394: // When we entered this function, the current
395: // frame buffer (cfb) index in the variable stack may
396: // have been manually set. If we just call
397: // unlink(), however, it will restore the cfb to the
398: // previous link index from the link stack, rather than
399: // the manually set cfb. So,
400: // the only safe solution is to restore it back
401: // to the same position it was on entry, since we're
402: // really not working in a stack context here. (Bug4218)
403: vars.unlink(currentFrameBottom);
404: xctxt.popRTFContext();
405: }
406:
407: transformer.popCurrentMatched();
408:
409: } // end while (DTM.NULL != (child = sourceNodes.nextNode()))
410: } catch (SAXException se) {
411: transformer.getErrorListener().fatalError(
412: new TransformerException(se));
413: } finally {
414: if (transformer.getDebug())
415: transformer.getTraceManager().fireSelectedEndEvent(
416: sourceNode,
417: this ,
418: "select",
419: new XPath(m_selectExpression),
420: new org.apache.xpath.objects.XNodeSet(
421: sourceNodes));
422:
423: // Unlink to the original stack frame
424: if (nParams > 0)
425: vars.unlink(thisframe);
426: xctxt.popSAXLocator();
427: if (pushContextNodeListFlag)
428: xctxt.popContextNodeList();
429: transformer.popElemTemplateElement();
430: xctxt.popCurrentExpressionNode();
431: xctxt.popCurrentNode();
432: sourceNodes.detach();
433: }
434: }
435:
436: }
|