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