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     * &amp;!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
044     * &amp;!ATTLIST xsl:apply-templates
045     *   select %expr; "node()"
046     *   mode %qname; #IMPLIED
047     * &amp;
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    }