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 * &!ELEMENT xsl:call-template (xsl:with-param)*> 38 * &!ATTLIST xsl:call-template 39 * name %qname; #REQUIRED 40 * & 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