1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id: ElemApplyTemplates.java 468643 2006-10-28 06:56:03Z minchau $
20  */
21 package org.apache.xalan.templates;
22 
23 import java.util.Vector;
24 
25 import javax.xml.transform.TransformerException;
26 
27 import org.apache.xalan.transformer.TransformerImpl;
28 import org.apache.xml.dtm.DTM;
29 import org.apache.xml.dtm.DTMIterator;
30 import org.apache.xml.serializer.SerializationHandler;
31 import org.apache.xml.utils.IntStack;
32 import org.apache.xml.utils.QName;
33 import org.apache.xpath.VariableStack;
34 import org.apache.xpath.XPath;
35 import org.apache.xpath.XPathContext;
36 import org.apache.xpath.objects.XObject;
37 import org.xml.sax.SAXException;
38 
39 /**
40  * Implement xsl:apply-templates.
41  * <pre>
42  * &amp;!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
43  * &amp;!ATTLIST xsl:apply-templates
44  *   select %expr; "node()"
45  *   mode %qname; #IMPLIED
46  * &amp;
47  * </pre>
48  * @see <a href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules in XSLT Specification</a>
49  * @xsl.usage advanced
50  */
51 public class ElemApplyTemplates extends ElemCallTemplate
52 {
53     static final long serialVersionUID = 2903125371542621004L;
54 
55   /**
56    * mode %qname; #IMPLIED
57    * @serial
58    */
59   private QName m_mode = null;
60 
61   /**
62    * Set the mode attribute for this element.
63    *
64    * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>.
65    */
setMode(QName mode)66   public void setMode(QName mode)
67   {
68     m_mode = mode;
69   }
70 
71   /**
72    * Get the mode attribute for this element.
73    *
74    * @return The mode attribute for this element
75    */
getMode()76   public QName getMode()
77   {
78     return m_mode;
79   }
80 
81   /**
82    * Tells if this belongs to a default template,
83    * in which case it will act different with
84    * regard to processing modes.
85    * @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in XSLT Specification</a>
86    * @serial
87    */
88   private boolean m_isDefaultTemplate = false;
89 
90 //  /**
91 //   * List of namespace/localname IDs, for identification of xsl:with-param to
92 //   * xsl:params.  Initialized in the compose() method.
93 //   */
94 //  private int[] m_paramIDs;
95 
96   /**
97    * Set if this belongs to a default template,
98    * in which case it will act different with
99    * regard to processing modes.
100    * @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in XSLT Specification</a>
101    *
102    * @param b boolean value to set.
103    */
setIsDefaultTemplate(boolean b)104   public void setIsDefaultTemplate(boolean b)
105   {
106     m_isDefaultTemplate = b;
107   }
108 
109   /**
110    * Get an int constant identifying the type of element.
111    * @see org.apache.xalan.templates.Constants
112    *
113    * @return Token ID for this element types
114    */
getXSLToken()115   public int getXSLToken()
116   {
117     return Constants.ELEMNAME_APPLY_TEMPLATES;
118   }
119 
120   /**
121    * This function is called after everything else has been
122    * recomposed, and allows the template to set remaining
123    * values that may be based on some other property that
124    * depends on recomposition.
125    */
compose(StylesheetRoot sroot)126   public void compose(StylesheetRoot sroot) throws TransformerException
127   {
128     super.compose(sroot);
129   }
130 
131   /**
132    * Return the node name.
133    *
134    * @return Element name
135    */
getNodeName()136   public String getNodeName()
137   {
138     return Constants.ELEMNAME_APPLY_TEMPLATES_STRING;
139   }
140 
141   /**
142    * Apply the context node to the matching templates.
143    * @see <a href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules in XSLT Specification</a>
144    *
145    * @param transformer non-null reference to the the current transform-time state.
146    *
147    * @throws TransformerException
148    */
execute(TransformerImpl transformer)149   public void execute(TransformerImpl transformer) throws TransformerException
150   {
151 
152     transformer.pushCurrentTemplateRuleIsNull(false);
153 
154     boolean pushMode = false;
155 
156     try
157     {
158       // %REVIEW% Do we need this check??
159       //      if (null != sourceNode)
160       //      {
161       // boolean needToTurnOffInfiniteLoopCheck = false;
162       QName mode = transformer.getMode();
163 
164       if (!m_isDefaultTemplate)
165       {
166         if (((null == mode) && (null != m_mode))
167                 || ((null != mode) &&!mode.equals(m_mode)))
168         {
169           pushMode = true;
170 
171           transformer.pushMode(m_mode);
172         }
173       }
174 
175       transformSelectedNodes(transformer);
176     }
177     finally
178     {
179       if (pushMode)
180         transformer.popMode();
181 
182       transformer.popCurrentTemplateRuleIsNull();
183     }
184   }
185 
186 
187   /**
188    * Perform a query if needed, and call transformNode for each child.
189    *
190    * @param transformer non-null reference to the the current transform-time state.
191    *
192    * @throws TransformerException Thrown in a variety of circumstances.
193    * @xsl.usage advanced
194    */
transformSelectedNodes(TransformerImpl transformer)195   public void transformSelectedNodes(TransformerImpl transformer)
196             throws TransformerException
197   {
198 
199     final XPathContext xctxt = transformer.getXPathContext();
200     final int sourceNode = xctxt.getCurrentNode();
201     DTMIterator sourceNodes = m_selectExpression.asIterator(xctxt, sourceNode);
202     VariableStack vars = xctxt.getVarStack();
203     int nParams = getParamElemCount();
204     int thisframe = vars.getStackFrame();
205 
206     boolean pushContextNodeListFlag = false;
207 
208     try
209     {
210 
211             xctxt.pushCurrentNode(DTM.NULL);
212             xctxt.pushCurrentExpressionNode(DTM.NULL);
213             xctxt.pushSAXLocatorNull();
214             transformer.pushElemTemplateElement(null);
215       final Vector keys = (m_sortElems == null)
216                           ? null
217                           : transformer.processSortKeys(this, sourceNode);
218 
219       // Sort if we need to.
220       if (null != keys)
221         sourceNodes = sortNodes(xctxt, keys, sourceNodes);
222 
223       final SerializationHandler rth = transformer.getSerializationHandler();
224 //      ContentHandler chandler = rth.getContentHandler();
225       final StylesheetRoot sroot = transformer.getStylesheet();
226       final TemplateList tl = sroot.getTemplateListComposed();
227       final boolean quiet = transformer.getQuietConflictWarnings();
228 
229       // Should be able to get this from the iterator but there must be a bug.
230       DTM dtm = xctxt.getDTM(sourceNode);
231 
232       int argsFrame = -1;
233       if(nParams > 0)
234       {
235         // This code will create a section on the stack that is all the
236         // evaluated arguments.  These will be copied into the real params
237         // section of each called template.
238         argsFrame = vars.link(nParams);
239         vars.setStackFrame(thisframe);
240 
241         for (int i = 0; i < nParams; i++)
242         {
243           ElemWithParam ewp = m_paramElems[i];
244           XObject obj = ewp.getValue(transformer, sourceNode);
245 
246           vars.setLocalVariable(i, obj, argsFrame);
247         }
248         vars.setStackFrame(argsFrame);
249       }
250 
251       xctxt.pushContextNodeList(sourceNodes);
252       pushContextNodeListFlag = true;
253 
254       IntStack currentNodes = xctxt.getCurrentNodeStack();
255 
256       IntStack currentExpressionNodes = xctxt.getCurrentExpressionNodeStack();
257 
258       // pushParams(transformer, xctxt);
259 
260       int child;
261       while (DTM.NULL != (child = sourceNodes.nextNode()))
262       {
263         currentNodes.setTop(child);
264         currentExpressionNodes.setTop(child);
265 
266         if(xctxt.getDTM(child) != dtm)
267         {
268           dtm = xctxt.getDTM(child);
269         }
270 
271         final int exNodeType = dtm.getExpandedTypeID(child);
272 
273         final int nodeType = dtm.getNodeType(child);
274 
275         final QName mode = transformer.getMode();
276 
277         ElemTemplate template = tl.getTemplateFast(xctxt, child, exNodeType, mode,
278                                       -1, quiet, dtm);
279 
280         // If that didn't locate a node, fall back to a default template rule.
281         // See http://www.w3.org/TR/xslt#built-in-rule.
282         if (null == template)
283         {
284           switch (nodeType)
285           {
286           case DTM.DOCUMENT_FRAGMENT_NODE :
287           case DTM.ELEMENT_NODE :
288             template = sroot.getDefaultRule();
289             // %OPT% direct faster?
290             break;
291           case DTM.ATTRIBUTE_NODE :
292           case DTM.CDATA_SECTION_NODE :
293           case DTM.TEXT_NODE :
294             // if(rth.m_elemIsPending || rth.m_docPending)
295             //  rth.flushPending(true);
296             transformer.pushPairCurrentMatched(sroot.getDefaultTextRule(), child);
297             transformer.setCurrentElement(sroot.getDefaultTextRule());
298             // dtm.dispatchCharactersEvents(child, chandler, false);
299             dtm.dispatchCharactersEvents(child, rth, false);
300             transformer.popCurrentMatched();
301             continue;
302           case DTM.DOCUMENT_NODE :
303             template = sroot.getDefaultRootRule();
304             break;
305           default :
306 
307             // No default rules for processing instructions and the like.
308             continue;
309           }
310         }
311         else
312         {
313         	transformer.setCurrentElement(template);
314         }
315 
316         transformer.pushPairCurrentMatched(template, child);
317 
318         int currentFrameBottom;  // See comment with unlink, below
319         if(template.m_frameSize > 0)
320         {
321           xctxt.pushRTFContext();
322           currentFrameBottom = vars.getStackFrame();  // See comment with unlink, below
323           vars.link(template.m_frameSize);
324           // You can't do the check for nParams here, otherwise the
325           // xsl:params might not be nulled.
326           if(/* nParams > 0 && */ template.m_inArgsSize > 0)
327           {
328             int paramIndex = 0;
329             for (ElemTemplateElement elem = template.getFirstChildElem();
330                  null != elem; elem = elem.getNextSiblingElem())
331             {
332               if(Constants.ELEMNAME_PARAMVARIABLE == elem.getXSLToken())
333               {
334                 ElemParam ep = (ElemParam)elem;
335 
336                 int i;
337                 for (i = 0; i < nParams; i++)
338                 {
339                   ElemWithParam ewp = m_paramElems[i];
340                   if(ewp.m_qnameID == ep.m_qnameID)
341                   {
342                     XObject obj = vars.getLocalVariable(i, argsFrame);
343                     vars.setLocalVariable(paramIndex, obj);
344                     break;
345                   }
346                 }
347                 if(i == nParams)
348                   vars.setLocalVariable(paramIndex, null);
349               }
350               else
351                 break;
352               paramIndex++;
353             }
354 
355           }
356         }
357         else
358         	currentFrameBottom = 0;
359 
360         // And execute the child templates.
361         // Loop through the children of the template, calling execute on
362         // each of them.
363         for (ElemTemplateElement t = template.m_firstChild;
364              t != null; t = t.m_nextSibling)
365         {
366           xctxt.setSAXLocator(t);
367           try
368           {
369           	transformer.pushElemTemplateElement(t);
370           	t.execute(transformer);
371           }
372           finally
373           {
374           	transformer.popElemTemplateElement();
375           }
376         }
377 
378         if(template.m_frameSize > 0)
379         {
380           // See Frank Weiss bug around 03/19/2002 (no Bugzilla report yet).
381           // While unlink will restore to the proper place, the real position
382           // may have been changed for xsl:with-param, so that variables
383           // can be accessed.
384           // of right now.
385           // More:
386           // When we entered this function, the current
387           // frame buffer (cfb) index in the variable stack may
388           // have been manually set.  If we just call
389           // unlink(), however, it will restore the cfb to the
390           // previous link index from the link stack, rather than
391           // the manually set cfb.  So,
392           // the only safe solution is to restore it back
393           // to the same position it was on entry, since we're
394           // really not working in a stack context here. (Bug4218)
395           vars.unlink(currentFrameBottom);
396           xctxt.popRTFContext();
397         }
398 
399         transformer.popCurrentMatched();
400 
401       } // end while (DTM.NULL != (child = sourceNodes.nextNode()))
402     }
403     catch (SAXException se)
404     {
405       transformer.getErrorListener().fatalError(new TransformerException(se));
406     }
407     finally
408     {
409       // Unlink to the original stack frame
410       if(nParams > 0)
411         vars.unlink(thisframe);
412       xctxt.popSAXLocator();
413       if (pushContextNodeListFlag) xctxt.popContextNodeList();
414       transformer.popElemTemplateElement();
415       xctxt.popCurrentExpressionNode();
416       xctxt.popCurrentNode();
417       sourceNodes.detach();
418     }
419   }
420 
421 }
422