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: ElemCallTemplate.java 468643 2006-10-28 06:56:03Z minchau $
20 */
21package org.apache.xalan.templates;
22
23import javax.xml.transform.SourceLocator;
24import javax.xml.transform.TransformerException;
25
26import org.apache.xalan.res.XSLMessages;
27import org.apache.xalan.res.XSLTErrorResources;
28import org.apache.xalan.transformer.TransformerImpl;
29import org.apache.xml.utils.QName;
30import org.apache.xpath.VariableStack;
31import org.apache.xpath.XPathContext;
32import org.apache.xpath.objects.XObject;
33
34/**
35 * Implement xsl:call-template.
36 * <pre>
37 * &amp;!ELEMENT xsl:call-template (xsl:with-param)*>
38 * &amp;!ATTLIST xsl:call-template
39 *   name %qname; #REQUIRED
40 * &amp;
41 * </pre>
42 * @see <a href="http://www.w3.org/TR/xslt#named-templates">named-templates in XSLT Specification</a>
43 * @xsl.usage advanced
44 */
45public class ElemCallTemplate extends ElemForEach
46{
47    static final long serialVersionUID = 5009634612916030591L;
48
49  /**
50   * An xsl:call-template element invokes a template by name;
51   * it has a required name attribute that identifies the template to be invoked.
52   * @serial
53   */
54  public QName m_templateName = null;
55
56  /**
57   * Set the "name" attribute.
58   * An xsl:call-template element invokes a template by name;
59   * it has a required name attribute that identifies the template to be invoked.
60   *
61   * @param name Name attribute to set
62   */
63  public void setName(QName name)
64  {
65    m_templateName = name;
66  }
67
68  /**
69   * Get the "name" attribute.
70   * An xsl:call-template element invokes a template by name;
71   * it has a required name attribute that identifies the template to be invoked.
72   *
73   * @return Name attribute of this element
74   */
75  public QName getName()
76  {
77    return m_templateName;
78  }
79
80  /**
81   * The template which is named by QName.
82   * @serial
83   */
84  private ElemTemplate m_template = null;
85
86  /**
87   * Get an int constant identifying the type of element.
88   * @see org.apache.xalan.templates.Constants
89   *
90   * @return The token ID for this element
91   */
92  public int getXSLToken()
93  {
94    return Constants.ELEMNAME_CALLTEMPLATE;
95  }
96
97  /**
98   * Return the node name.
99   *
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 (null != m_template)
197    {
198      XPathContext xctxt = transformer.getXPathContext();
199      VariableStack vars = xctxt.getVarStack();
200
201      int thisframe = vars.getStackFrame();
202      int nextFrame = vars.link(m_template.m_frameSize);
203
204      // We have to clear the section of the stack frame that has params
205      // so that the default param evaluation will work correctly.
206      if(m_template.m_inArgsSize > 0)
207      {
208        vars.clearLocalSlots(0, m_template.m_inArgsSize);
209
210        if(null != m_paramElems)
211        {
212          int currentNode = xctxt.getCurrentNode();
213          vars.setStackFrame(thisframe);
214          int size = m_paramElems.length;
215
216          for (int i = 0; i < size; i++)
217          {
218            ElemWithParam ewp = m_paramElems[i];
219            if(ewp.m_index >= 0)
220            {
221              XObject obj = ewp.getValue(transformer, currentNode);
222
223              // Note here that the index for ElemWithParam must have been
224              // statically made relative to the xsl:template being called,
225              // NOT this xsl:template.
226              vars.setLocalVariable(ewp.m_index, obj, nextFrame);
227            }
228          }
229          vars.setStackFrame(nextFrame);
230        }
231      }
232
233      SourceLocator savedLocator = xctxt.getSAXLocator();
234
235      try
236      {
237        xctxt.setSAXLocator(m_template);
238
239        // template.executeChildTemplates(transformer, sourceNode, mode, true);
240        transformer.pushElemTemplateElement(m_template);
241        m_template.execute(transformer);
242      }
243      finally
244      {
245        transformer.popElemTemplateElement();
246        xctxt.setSAXLocator(savedLocator);
247        // When we entered this function, the current
248        // frame buffer (cfb) index in the variable stack may
249        // have been manually set.  If we just call
250        // unlink(), however, it will restore the cfb to the
251        // previous link index from the link stack, rather than
252        // the manually set cfb.  So,
253        // the only safe solution is to restore it back
254        // to the same position it was on entry, since we're
255        // really not working in a stack context here. (Bug4218)
256        vars.unlink(thisframe);
257      }
258    }
259    else
260    {
261      transformer.getMsgMgr().error(this, XSLTErrorResources.ER_TEMPLATE_NOT_FOUND,
262                                    new Object[]{ m_templateName });  //"Could not find template named: '"+templateName+"'");
263    }
264
265  }
266
267  /** Vector of xsl:param elements associated with this element.
268   *  @serial */
269  protected ElemWithParam[] m_paramElems = null;
270
271  /**
272   * Get the count xsl:param elements associated with this element.
273   * @return The number of xsl:param elements.
274   */
275  public int getParamElemCount()
276  {
277    return (m_paramElems == null) ? 0 : m_paramElems.length;
278  }
279
280  /**
281   * Get a xsl:param element associated with this element.
282   *
283   * @param i Index of element to find
284   *
285   * @return xsl:param element at given index
286   */
287  public ElemWithParam getParamElem(int i)
288  {
289    return m_paramElems[i];
290  }
291
292  /**
293   * Set a xsl:param element associated with this element.
294   *
295   * @param ParamElem xsl:param element to set.
296   */
297  public void setParamElem(ElemWithParam ParamElem)
298  {
299    if (null == m_paramElems)
300    {
301      m_paramElems = new ElemWithParam[1];
302      m_paramElems[0] = ParamElem;
303    }
304    else
305    {
306      // Expensive 1 at a time growth, but this is done at build time, so
307      // I think it's OK.
308      int length = m_paramElems.length;
309      ElemWithParam[] ewp = new ElemWithParam[length + 1];
310      System.arraycopy(m_paramElems, 0, ewp, 0, length);
311      m_paramElems = ewp;
312      ewp[length] = ParamElem;
313    }
314  }
315
316  /**
317   * Add a child to the child list.
318   * <!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
319   * <!ATTLIST xsl:apply-templates
320   *   select %expr; "node()"
321   *   mode %qname; #IMPLIED
322   * >
323   *
324   * @param newChild Child to add to this node's children list
325   *
326   * @return The child that was just added the children list
327   *
328   * @throws DOMException
329   */
330  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
331  {
332
333    int type = ((ElemTemplateElement) newChild).getXSLToken();
334
335    if (Constants.ELEMNAME_WITHPARAM == type)
336    {
337      setParamElem((ElemWithParam) newChild);
338    }
339
340    // You still have to append, because this element can
341    // contain a for-each, and other elements.
342    return super.appendChild(newChild);
343  }
344
345    /**
346     * Call the children visitors.
347     * @param visitor The visitor whose appropriate method will be called.
348     */
349    public void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
350    {
351//      if (null != m_paramElems)
352//      {
353//        int size = m_paramElems.length;
354//
355//        for (int i = 0; i < size; i++)
356//        {
357//          ElemWithParam ewp = m_paramElems[i];
358//          ewp.callVisitors(visitor);
359//        }
360//      }
361
362      super.callChildVisitors(visitor, callAttrs);
363    }
364}
365