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 * &!ELEMENT xsl:call-template (xsl:with-param)*> 038 * &!ATTLIST xsl:call-template 039 * name %qname; #REQUIRED 040 * & 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 }