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: ElemVariable.java 468643 2006-10-28 06:56:03Z minchau $ 20 */ 21 package org.apache.xalan.templates; 22 23 import javax.xml.transform.TransformerException; 24 25 import org.apache.xalan.transformer.TransformerImpl; 26 import org.apache.xml.utils.QName; 27 import org.apache.xpath.XPath; 28 import org.apache.xpath.XPathContext; 29 import org.apache.xpath.objects.XObject; 30 import org.apache.xpath.objects.XRTreeFrag; 31 import org.apache.xpath.objects.XRTreeFragSelectWrapper; 32 import org.apache.xpath.objects.XString; 33 import org.apache.xalan.res.XSLTErrorResources; 34 35 /** 36 * Implement xsl:variable. 37 * <pre> 38 * <!ELEMENT xsl:variable %template;> 39 * <!ATTLIST xsl:variable 40 * name %qname; #REQUIRED 41 * select %expr; #IMPLIED 42 * > 43 * </pre> 44 * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a> 45 * @xsl.usage advanced 46 */ 47 public class ElemVariable extends ElemTemplateElement 48 { 49 static final long serialVersionUID = 9111131075322790061L; 50 51 /** 52 * Constructor ElemVariable 53 * 54 */ 55 public ElemVariable(){} 56 57 /** 58 * This is the index into the stack frame. 59 */ 60 protected int m_index; 61 62 /** 63 * The stack frame size for this variable if it is a global variable 64 * that declares an RTF, which is equal to the maximum number 65 * of variables that can be declared in the variable at one time. 66 */ 67 int m_frameSize = -1; 68 69 70 /** 71 * Sets the relative position of this variable within the stack frame (if local) 72 * or the global area (if global). Note that this should be called only for 73 * global variables since the local position is computed in the compose() method. 74 */ 75 public void setIndex(int index) 76 { 77 m_index = index; 78 } 79 80 /** 81 * If this element is not at the top-level, get the relative position of the 82 * variable into the stack frame. If this variable is at the top-level, get 83 * the relative position within the global area. 84 */ 85 public int getIndex() 86 { 87 return m_index; 88 } 89 90 /** 91 * The value of the "select" attribute. 92 * @serial 93 */ 94 private XPath m_selectPattern; 95 96 /** 97 * Set the "select" attribute. 98 * If the variable-binding element has a select attribute, 99 * then the value of the attribute must be an expression and 100 * the value of the variable is the object that results from 101 * evaluating the expression. In this case, the content 102 * of the variable must be empty. 103 * 104 * @param v Value to set for the "select" attribute. 105 */ 106 public void setSelect(XPath v) 107 { 108 m_selectPattern = v; 109 } 110 111 /** 112 * Get the "select" attribute. 113 * If the variable-binding element has a select attribute, 114 * then the value of the attribute must be an expression and 115 * the value of the variable is the object that results from 116 * evaluating the expression. In this case, the content 117 * of the variable must be empty. 118 * 119 * @return Value of the "select" attribute. 120 */ 121 public XPath getSelect() 122 { 123 return m_selectPattern; 124 } 125 126 /** 127 * The value of the "name" attribute. 128 * @serial 129 */ 130 protected QName m_qname; 131 132 /** 133 * Set the "name" attribute. 134 * Both xsl:variable and xsl:param have a required name 135 * attribute, which specifies the name of the variable. The 136 * value of the name attribute is a QName, which is expanded 137 * as described in [2.4 Qualified Names]. 138 * @see <a href="http://www.w3.org/TR/xslt#qname">qname in XSLT Specification</a> 139 * 140 * @param v Value to set for the "name" attribute. 141 */ 142 public void setName(QName v) 143 { 144 m_qname = v; 145 } 146 147 /** 148 * Get the "name" attribute. 149 * Both xsl:variable and xsl:param have a required name 150 * attribute, which specifies the name of the variable. The 151 * value of the name attribute is a QName, which is expanded 152 * as described in [2.4 Qualified Names]. 153 * @see <a href="http://www.w3.org/TR/xslt#qname">qname in XSLT Specification</a> 154 * 155 * @return Value of the "name" attribute. 156 */ 157 public QName getName() 158 { 159 return m_qname; 160 } 161 162 /** 163 * Tells if this is a top-level variable or param, or not. 164 * @serial 165 */ 166 private boolean m_isTopLevel = false; 167 168 /** 169 * Set if this is a top-level variable or param, or not. 170 * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a> 171 * 172 * @param v Boolean indicating whether this is a top-level variable 173 * or param, or not. 174 */ 175 public void setIsTopLevel(boolean v) 176 { 177 m_isTopLevel = v; 178 } 179 180 /** 181 * Get if this is a top-level variable or param, or not. 182 * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a> 183 * 184 * @return Boolean indicating whether this is a top-level variable 185 * or param, or not. 186 */ 187 public boolean getIsTopLevel() 188 { 189 return m_isTopLevel; 190 } 191 192 /** 193 * Get an integer representation of the element type. 194 * 195 * @return An integer representation of the element, defined in the 196 * Constants class. 197 * @see org.apache.xalan.templates.Constants 198 */ 199 public int getXSLToken() 200 { 201 return Constants.ELEMNAME_VARIABLE; 202 } 203 204 /** 205 * Return the node name. 206 * 207 * @return The node name 208 */ 209 public String getNodeName() 210 { 211 return Constants.ELEMNAME_VARIABLE_STRING; 212 } 213 214 /** 215 * Copy constructor. 216 * 217 * @param param An element created from an xsl:variable 218 * 219 * @throws TransformerException 220 */ 221 public ElemVariable(ElemVariable param) throws TransformerException 222 { 223 224 m_selectPattern = param.m_selectPattern; 225 m_qname = param.m_qname; 226 m_isTopLevel = param.m_isTopLevel; 227 228 // m_value = param.m_value; 229 // m_varContext = param.m_varContext; 230 } 231 232 /** 233 * Execute a variable declaration and push it onto the variable stack. 234 * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a> 235 * 236 * @param transformer non-null reference to the the current transform-time state. 237 * 238 * @throws TransformerException 239 */ 240 public void execute(TransformerImpl transformer) throws TransformerException 241 { 242 243 int sourceNode = transformer.getXPathContext().getCurrentNode(); 244 245 XObject var = getValue(transformer, sourceNode); 246 247 // transformer.getXPathContext().getVarStack().pushVariable(m_qname, var); 248 transformer.getXPathContext().getVarStack().setLocalVariable(m_index, var); 249 } 250 251 /** 252 * Get the XObject representation of the variable. 253 * 254 * @param transformer non-null reference to the the current transform-time state. 255 * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>. 256 * 257 * @return the XObject representation of the variable. 258 * 259 * @throws TransformerException 260 */ 261 public XObject getValue(TransformerImpl transformer, int sourceNode) 262 throws TransformerException 263 { 264 265 XObject var; 266 XPathContext xctxt = transformer.getXPathContext(); 267 268 xctxt.pushCurrentNode(sourceNode); 269 270 try 271 { 272 if (null != m_selectPattern) 273 { 274 var = m_selectPattern.execute(xctxt, sourceNode, this); 275 276 var.allowDetachToRelease(false); 277 } 278 else if (null == getFirstChildElem()) 279 { 280 var = XString.EMPTYSTRING; 281 } 282 else 283 { 284 285 // Use result tree fragment. 286 // Global variables may be deferred (see XUnresolvedVariable) and hence 287 // need to be assigned to a different set of DTMs than local variables 288 // so they aren't popped off the stack on return from a template. 289 int df; 290 291 // Bugzilla 7118: A variable set via an RTF may create local 292 // variables during that computation. To keep them from overwriting 293 // variables at this level, push a new variable stack. 294 ////// PROBLEM: This is provoking a variable-used-before-set 295 ////// problem in parameters. Needs more study. 296 try 297 { 298 //////////xctxt.getVarStack().link(0); 299 if(m_parentNode instanceof Stylesheet) // Global variable 300 df = transformer.transformToGlobalRTF(this); 301 else 302 df = transformer.transformToRTF(this); 303 } 304 finally{ 305 //////////////xctxt.getVarStack().unlink(); 306 } 307 308 var = new XRTreeFrag(df, xctxt, this); 309 } 310 } 311 finally 312 { 313 xctxt.popCurrentNode(); 314 } 315 316 return var; 317 } 318 319 320 /** 321 * This function is called after everything else has been 322 * recomposed, and allows the template to set remaining 323 * values that may be based on some other property that 324 * depends on recomposition. 325 */ 326 public void compose(StylesheetRoot sroot) throws TransformerException 327 { 328 // See if we can reduce an RTF to a select with a string expression. 329 if(null == m_selectPattern 330 && sroot.getOptimizer()) 331 { 332 XPath newSelect = rewriteChildToExpression(this); 333 if(null != newSelect) 334 m_selectPattern = newSelect; 335 } 336 337 StylesheetRoot.ComposeState cstate = sroot.getComposeState(); 338 339 // This should be done before addVariableName, so we don't have visibility 340 // to the variable now being defined. 341 java.util.Vector vnames = cstate.getVariableNames(); 342 if(null != m_selectPattern) 343 m_selectPattern.fixupVariables(vnames, cstate.getGlobalsSize()); 344 345 // Only add the variable if this is not a global. If it is a global, 346 // it was already added by stylesheet root. 347 if(!(m_parentNode instanceof Stylesheet) && m_qname != null) 348 { 349 m_index = cstate.addVariableName(m_qname) - cstate.getGlobalsSize(); 350 } 351 else if (m_parentNode instanceof Stylesheet) 352 { 353 // If this is a global, then we need to treat it as if it's a xsl:template, 354 // and count the number of variables it contains. So we set the count to 355 // zero here. 356 cstate.resetStackFrameSize(); 357 } 358 359 // This has to be done after the addVariableName, so that the variable 360 // pushed won't be immediately popped again in endCompose. 361 super.compose(sroot); 362 } 363 364 /** 365 * This after the template's children have been composed. We have to get 366 * the count of how many variables have been declared, so we can do a link 367 * and unlink. 368 */ 369 public void endCompose(StylesheetRoot sroot) throws TransformerException 370 { 371 super.endCompose(sroot); 372 if(m_parentNode instanceof Stylesheet) 373 { 374 StylesheetRoot.ComposeState cstate = sroot.getComposeState(); 375 m_frameSize = cstate.getFrameSize(); 376 cstate.resetStackFrameSize(); 377 } 378 } 379 380 381 382 // /** 383 // * This after the template's children have been composed. 384 // */ 385 // public void endCompose() throws TransformerException 386 // { 387 // super.endCompose(); 388 // } 389 390 391 /** 392 * If the children of a variable is a single xsl:value-of or text literal, 393 * it is cheaper to evaluate this as an expression, so try and adapt the 394 * child an an expression. 395 * 396 * @param varElem Should be a ElemParam, ElemVariable, or ElemWithParam. 397 * 398 * @return An XPath if rewrite is possible, else null. 399 * 400 * @throws TransformerException 401 */ 402 static XPath rewriteChildToExpression(ElemTemplateElement varElem) 403 throws TransformerException 404 { 405 406 ElemTemplateElement t = varElem.getFirstChildElem(); 407 408 // Down the line this can be done with multiple string objects using 409 // the concat function. 410 if (null != t && null == t.getNextSiblingElem()) 411 { 412 int etype = t.getXSLToken(); 413 414 if (Constants.ELEMNAME_VALUEOF == etype) 415 { 416 ElemValueOf valueof = (ElemValueOf) t; 417 418 // %TBD% I'm worried about extended attributes here. 419 if (valueof.getDisableOutputEscaping() == false 420 && valueof.getDOMBackPointer() == null) 421 { 422 varElem.m_firstChild = null; 423 424 return new XPath(new XRTreeFragSelectWrapper(valueof.getSelect().getExpression())); 425 } 426 } 427 else if (Constants.ELEMNAME_TEXTLITERALRESULT == etype) 428 { 429 ElemTextLiteral lit = (ElemTextLiteral) t; 430 431 if (lit.getDisableOutputEscaping() == false 432 && lit.getDOMBackPointer() == null) 433 { 434 String str = lit.getNodeValue(); 435 XString xstr = new XString(str); 436 437 varElem.m_firstChild = null; 438 439 return new XPath(new XRTreeFragSelectWrapper(xstr)); 440 } 441 } 442 } 443 444 return null; 445 } 446 447 /** 448 * This function is called during recomposition to 449 * control how this element is composed. 450 * @param root The root stylesheet for this transformation. 451 */ 452 public void recompose(StylesheetRoot root) 453 { 454 root.recomposeVariables(this); 455 } 456 457 /** 458 * Set the parent as an ElemTemplateElement. 459 * 460 * @param p This node's parent as an ElemTemplateElement 461 */ 462 public void setParentElem(ElemTemplateElement p) 463 { 464 super.setParentElem(p); 465 p.m_hasVariableDecl = true; 466 } 467 468 /** 469 * Accept a visitor and call the appropriate method 470 * for this class. 471 * 472 * @param visitor The visitor whose appropriate method will be called. 473 * @return true if the children of the object should be visited. 474 */ 475 protected boolean accept(XSLTVisitor visitor) 476 { 477 return visitor.visitVariableOrParamDecl(this); 478 } 479 480 481 /** 482 * Call the children visitors. 483 * @param visitor The visitor whose appropriate method will be called. 484 */ 485 protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs) 486 { 487 if(null != m_selectPattern) 488 m_selectPattern.getExpression().callVisitors(m_selectPattern, visitor); 489 super.callChildVisitors(visitor, callAttrs); 490 } 491 492 /** 493 * Tell if this is a psuedo variable reference, declared by Xalan instead 494 * of by the user. 495 */ 496 public boolean isPsuedoVar() 497 { 498 java.lang.String ns = m_qname.getNamespaceURI(); 499 if((null != ns) && ns.equals(RedundentExprEliminator.PSUEDOVARNAMESPACE)) 500 { 501 if(m_qname.getLocalName().startsWith("#")) 502 return true; 503 } 504 return false; 505 } 506 507 /** 508 * Add a child to the child list. If the select attribute 509 * is present, an error will be raised. 510 * 511 * @param elem New element to append to this element's children list 512 * 513 * @return null if the select attribute was present, otherwise the 514 * child just added to the child list 515 */ 516 public ElemTemplateElement appendChild(ElemTemplateElement elem) 517 { 518 // cannot have content and select 519 if (m_selectPattern != null) 520 { 521 error(XSLTErrorResources.ER_CANT_HAVE_CONTENT_AND_SELECT, 522 new Object[]{"xsl:" + this.getNodeName()}); 523 return null; 524 } 525 return super.appendChild(elem); 526 } 527 528 } 529