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 */
21package org.apache.xalan.templates;
22
23import java.util.Vector;
24
25import javax.xml.transform.TransformerException;
26
27import org.apache.xalan.transformer.TransformerImpl;
28import org.apache.xml.dtm.DTM;
29import org.apache.xml.dtm.DTMIterator;
30import org.apache.xml.serializer.SerializationHandler;
31import org.apache.xml.utils.IntStack;
32import org.apache.xml.utils.QName;
33import org.apache.xpath.VariableStack;
34import org.apache.xpath.XPath;
35import org.apache.xpath.XPathContext;
36import org.apache.xpath.objects.XObject;
37import 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 */
51public 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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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