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  */
21 package org.apache.xalan.templates;
22 
23 import javax.xml.transform.SourceLocator;
24 import javax.xml.transform.TransformerException;
25 
26 import org.apache.xalan.res.XSLMessages;
27 import org.apache.xalan.res.XSLTErrorResources;
28 import org.apache.xalan.transformer.TransformerImpl;
29 import org.apache.xml.utils.QName;
30 import org.apache.xpath.VariableStack;
31 import org.apache.xpath.XPathContext;
32 import 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  */
45 public 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    */
setName(QName name)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    */
getName()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    */
getXSLToken()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    */
getNodeName()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    */
compose(StylesheetRoot sroot)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    */
endCompose(StylesheetRoot sroot)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    */
execute( TransformerImpl transformer)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    */
getParamElemCount()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    */
getParamElem(int i)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    */
setParamElem(ElemWithParam ParamElem)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    */
appendChild(ElemTemplateElement newChild)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      */
callChildVisitors(XSLTVisitor visitor, boolean callAttrs)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