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: ElemCallTemplate.java 468643 2006-10-28 06:56:03Z minchau $
020     */
021    package org.apache.xalan.templates;
022    
023    import javax.xml.transform.SourceLocator;
024    import javax.xml.transform.TransformerException;
025    
026    import org.apache.xalan.res.XSLMessages;
027    import org.apache.xalan.res.XSLTErrorResources;
028    import org.apache.xalan.transformer.TransformerImpl;
029    import org.apache.xml.utils.QName;
030    import org.apache.xpath.VariableStack;
031    import org.apache.xpath.XPathContext;
032    import org.apache.xpath.objects.XObject;
033    
034    /**
035     * Implement xsl:call-template.
036     * <pre>
037     * &amp;!ELEMENT xsl:call-template (xsl:with-param)*>
038     * &amp;!ATTLIST xsl:call-template
039     *   name %qname; #REQUIRED
040     * &amp;
041     * </pre>
042     * @see <a href="http://www.w3.org/TR/xslt#named-templates">named-templates in XSLT Specification</a>
043     * @xsl.usage advanced
044     */
045    public class ElemCallTemplate extends ElemForEach
046    {
047        static final long serialVersionUID = 5009634612916030591L;
048    
049      /**
050       * An xsl:call-template element invokes a template by name;
051       * it has a required name attribute that identifies the template to be invoked.
052       * @serial
053       */
054      public QName m_templateName = null;
055    
056      /**
057       * Set the "name" attribute.
058       * An xsl:call-template element invokes a template by name;
059       * it has a required name attribute that identifies the template to be invoked.
060       *
061       * @param name Name attribute to set
062       */
063      public void setName(QName name)
064      {
065        m_templateName = name;
066      }
067    
068      /**
069       * Get the "name" attribute.
070       * An xsl:call-template element invokes a template by name;
071       * it has a required name attribute that identifies the template to be invoked.
072       *
073       * @return Name attribute of this element
074       */
075      public QName getName()
076      {
077        return m_templateName;
078      }
079    
080      /**
081       * The template which is named by QName.
082       * @serial
083       */
084      private ElemTemplate m_template = null;
085    
086      /**
087       * Get an int constant identifying the type of element.
088       * @see org.apache.xalan.templates.Constants
089       *
090       * @return The token ID for this element 
091       */
092      public int getXSLToken()
093      {
094        return Constants.ELEMNAME_CALLTEMPLATE;
095      }
096    
097      /**
098       * Return the node name.
099       *
100       * @return The name of this element
101       */
102      public String getNodeName()
103      {
104        return Constants.ELEMNAME_CALLTEMPLATE_STRING;
105      }
106      
107      /**
108       * This function is called after everything else has been
109       * recomposed, and allows the template to set remaining
110       * values that may be based on some other property that
111       * depends on recomposition.
112       */
113      public void compose(StylesheetRoot sroot) throws TransformerException
114      {
115        super.compose(sroot);
116        
117        // Call compose on each param no matter if this is apply-templates 
118        // or call templates.
119        int length = getParamElemCount();
120        for (int i = 0; i < length; i++) 
121        {
122          ElemWithParam ewp = getParamElem(i);
123          ewp.compose(sroot);
124        }
125        
126            if ((null != m_templateName) && (null == m_template)) {
127                    m_template =
128                            this.getStylesheetRoot().getTemplateComposed(m_templateName);
129    
130                    if (null == m_template) {
131                            String themsg =
132                                    XSLMessages.createMessage(
133                                            XSLTErrorResources.ER_ELEMTEMPLATEELEM_ERR,
134                                            new Object[] { m_templateName });
135    
136                            throw new TransformerException(themsg, this);
137                            //"Could not find template named: '"+templateName+"'");
138                    }
139        
140          length = getParamElemCount();
141          for (int i = 0; i < length; i++) 
142          {
143            ElemWithParam ewp = getParamElem(i);
144            ewp.m_index = -1;
145            // Find the position of the param in the template being called, 
146            // and set the index of the param slot.
147            int etePos = 0;
148            for (ElemTemplateElement ete = m_template.getFirstChildElem(); 
149                 null != ete; ete = ete.getNextSiblingElem()) 
150            {
151              if(ete.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE)
152              {
153                ElemParam ep = (ElemParam)ete;
154                if(ep.getName().equals(ewp.getName()))
155                {
156                  ewp.m_index = etePos;
157                }
158              }
159              else
160                break;
161              etePos++;
162            }
163            
164          }
165        }
166      }
167      
168      /**
169       * This after the template's children have been composed.
170       */
171      public void endCompose(StylesheetRoot sroot) throws TransformerException
172      {
173        int length = getParamElemCount();
174        for (int i = 0; i < length; i++) 
175        {
176          ElemWithParam ewp = getParamElem(i);
177          ewp.endCompose(sroot);
178        }    
179        
180        super.endCompose(sroot);
181      }
182    
183      /**
184       * Invoke a named template.
185       * @see <a href="http://www.w3.org/TR/xslt#named-templates">named-templates in XSLT Specification</a>
186       *
187       * @param transformer non-null reference to the the current transform-time state.
188       *
189       * @throws TransformerException
190       */
191      public void execute(
192              TransformerImpl transformer)
193                throws TransformerException
194      {
195    
196        if (transformer.getDebug())
197          transformer.getTraceManager().fireTraceEvent(this);
198    
199        if (null != m_template)
200        {
201          XPathContext xctxt = transformer.getXPathContext();
202          VariableStack vars = xctxt.getVarStack();
203    
204          int thisframe = vars.getStackFrame();
205          int nextFrame = vars.link(m_template.m_frameSize);
206          
207          // We have to clear the section of the stack frame that has params 
208          // so that the default param evaluation will work correctly.
209          if(m_template.m_inArgsSize > 0)
210          {
211            vars.clearLocalSlots(0, m_template.m_inArgsSize);
212          
213            if(null != m_paramElems)
214            {
215              int currentNode = xctxt.getCurrentNode();
216              vars.setStackFrame(thisframe);
217              int size = m_paramElems.length;
218              
219              for (int i = 0; i < size; i++) 
220              {
221                ElemWithParam ewp = m_paramElems[i];
222                if(ewp.m_index >= 0)
223                {
224                  if (transformer.getDebug())
225                    transformer.getTraceManager().fireTraceEvent(ewp);
226                  XObject obj = ewp.getValue(transformer, currentNode);
227                  if (transformer.getDebug())
228                    transformer.getTraceManager().fireTraceEndEvent(ewp);
229                  
230                  // Note here that the index for ElemWithParam must have been 
231                  // statically made relative to the xsl:template being called, 
232                  // NOT this xsl:template.
233                  vars.setLocalVariable(ewp.m_index, obj, nextFrame);
234                }
235              }
236              vars.setStackFrame(nextFrame);
237            }
238          }
239          
240          SourceLocator savedLocator = xctxt.getSAXLocator();
241    
242          try
243          {
244            xctxt.setSAXLocator(m_template);
245    
246            // template.executeChildTemplates(transformer, sourceNode, mode, true);
247            transformer.pushElemTemplateElement(m_template);
248            m_template.execute(transformer);
249          }
250          finally
251          {
252            transformer.popElemTemplateElement();
253            xctxt.setSAXLocator(savedLocator);
254            // When we entered this function, the current 
255            // frame buffer (cfb) index in the variable stack may 
256            // have been manually set.  If we just call 
257            // unlink(), however, it will restore the cfb to the 
258            // previous link index from the link stack, rather than 
259            // the manually set cfb.  So, 
260            // the only safe solution is to restore it back 
261            // to the same position it was on entry, since we're 
262            // really not working in a stack context here. (Bug4218)
263            vars.unlink(thisframe);
264          }
265        }
266        else
267        {
268          transformer.getMsgMgr().error(this, XSLTErrorResources.ER_TEMPLATE_NOT_FOUND,
269                                        new Object[]{ m_templateName });  //"Could not find template named: '"+templateName+"'");
270        }
271        
272        if (transformer.getDebug())
273              transformer.getTraceManager().fireTraceEndEvent(this); 
274    
275      }
276      
277      /** Vector of xsl:param elements associated with this element. 
278       *  @serial */
279      protected ElemWithParam[] m_paramElems = null;
280    
281      /**
282       * Get the count xsl:param elements associated with this element.
283       * @return The number of xsl:param elements.
284       */
285      public int getParamElemCount()
286      {
287        return (m_paramElems == null) ? 0 : m_paramElems.length;
288      }
289    
290      /**
291       * Get a xsl:param element associated with this element.
292       *
293       * @param i Index of element to find
294       *
295       * @return xsl:param element at given index
296       */
297      public ElemWithParam getParamElem(int i)
298      {
299        return m_paramElems[i];
300      }
301    
302      /**
303       * Set a xsl:param element associated with this element.
304       *
305       * @param ParamElem xsl:param element to set. 
306       */
307      public void setParamElem(ElemWithParam ParamElem)
308      {
309        if (null == m_paramElems)
310        {
311          m_paramElems = new ElemWithParam[1];
312          m_paramElems[0] = ParamElem;
313        }
314        else
315        {
316          // Expensive 1 at a time growth, but this is done at build time, so 
317          // I think it's OK.
318          int length = m_paramElems.length;
319          ElemWithParam[] ewp = new ElemWithParam[length + 1];
320          System.arraycopy(m_paramElems, 0, ewp, 0, length);
321          m_paramElems = ewp;
322          ewp[length] = ParamElem;
323        }
324      }
325    
326      /**
327       * Add a child to the child list.
328       * <!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
329       * <!ATTLIST xsl:apply-templates
330       *   select %expr; "node()"
331       *   mode %qname; #IMPLIED
332       * >
333       *
334       * @param newChild Child to add to this node's children list
335       *
336       * @return The child that was just added the children list 
337       *
338       * @throws DOMException
339       */
340      public ElemTemplateElement appendChild(ElemTemplateElement newChild)
341      {
342    
343        int type = ((ElemTemplateElement) newChild).getXSLToken();
344    
345        if (Constants.ELEMNAME_WITHPARAM == type)
346        {
347          setParamElem((ElemWithParam) newChild);
348        }
349    
350        // You still have to append, because this element can
351        // contain a for-each, and other elements.
352        return super.appendChild(newChild);
353      }
354      
355        /**
356         * Call the children visitors.
357         * @param visitor The visitor whose appropriate method will be called.
358         */
359        public void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
360        {
361    //      if (null != m_paramElems)
362    //      {
363    //        int size = m_paramElems.length;
364    //
365    //        for (int i = 0; i < size; i++)
366    //        {
367    //          ElemWithParam ewp = m_paramElems[i];
368    //          ewp.callVisitors(visitor);
369    //        }
370    //      }
371    
372          super.callChildVisitors(visitor, callAttrs);
373        }
374    }