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: ElemVariable.java 468643 2006-10-28 06:56:03Z minchau $ 020 */ 021 package org.apache.xalan.templates; 022 023 import javax.xml.transform.TransformerException; 024 025 import org.apache.xalan.transformer.TransformerImpl; 026 import org.apache.xml.utils.QName; 027 import org.apache.xpath.XPath; 028 import org.apache.xpath.XPathContext; 029 import org.apache.xpath.objects.XObject; 030 import org.apache.xpath.objects.XRTreeFrag; 031 import org.apache.xpath.objects.XRTreeFragSelectWrapper; 032 import org.apache.xpath.objects.XString; 033 import org.apache.xalan.res.XSLTErrorResources; 034 035 /** 036 * Implement xsl:variable. 037 * <pre> 038 * <!ELEMENT xsl:variable %template;> 039 * <!ATTLIST xsl:variable 040 * name %qname; #REQUIRED 041 * select %expr; #IMPLIED 042 * > 043 * </pre> 044 * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a> 045 * @xsl.usage advanced 046 */ 047 public class ElemVariable extends ElemTemplateElement 048 { 049 static final long serialVersionUID = 9111131075322790061L; 050 051 /** 052 * Constructor ElemVariable 053 * 054 */ 055 public ElemVariable(){} 056 057 /** 058 * This is the index into the stack frame. 059 */ 060 protected int m_index; 061 062 /** 063 * The stack frame size for this variable if it is a global variable 064 * that declares an RTF, which is equal to the maximum number 065 * of variables that can be declared in the variable at one time. 066 */ 067 int m_frameSize = -1; 068 069 070 /** 071 * Sets the relative position of this variable within the stack frame (if local) 072 * or the global area (if global). Note that this should be called only for 073 * global variables since the local position is computed in the compose() method. 074 */ 075 public void setIndex(int index) 076 { 077 m_index = index; 078 } 079 080 /** 081 * If this element is not at the top-level, get the relative position of the 082 * variable into the stack frame. If this variable is at the top-level, get 083 * the relative position within the global area. 084 */ 085 public int getIndex() 086 { 087 return m_index; 088 } 089 090 /** 091 * The value of the "select" attribute. 092 * @serial 093 */ 094 private XPath m_selectPattern; 095 096 /** 097 * Set the "select" attribute. 098 * If the variable-binding element has a select attribute, 099 * 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 if (transformer.getDebug()) 244 transformer.getTraceManager().fireTraceEvent(this); 245 246 int sourceNode = transformer.getXPathContext().getCurrentNode(); 247 248 XObject var = getValue(transformer, sourceNode); 249 250 // transformer.getXPathContext().getVarStack().pushVariable(m_qname, var); 251 transformer.getXPathContext().getVarStack().setLocalVariable(m_index, var); 252 253 if (transformer.getDebug()) 254 transformer.getTraceManager().fireTraceEndEvent(this); 255 } 256 257 /** 258 * Get the XObject representation of the variable. 259 * 260 * @param transformer non-null reference to the the current transform-time state. 261 * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>. 262 * 263 * @return the XObject representation of the variable. 264 * 265 * @throws TransformerException 266 */ 267 public XObject getValue(TransformerImpl transformer, int sourceNode) 268 throws TransformerException 269 { 270 271 XObject var; 272 XPathContext xctxt = transformer.getXPathContext(); 273 274 xctxt.pushCurrentNode(sourceNode); 275 276 try 277 { 278 if (null != m_selectPattern) 279 { 280 var = m_selectPattern.execute(xctxt, sourceNode, this); 281 282 var.allowDetachToRelease(false); 283 284 if (transformer.getDebug()) 285 transformer.getTraceManager().fireSelectedEvent(sourceNode, this, 286 "select", m_selectPattern, var); 287 } 288 else if (null == getFirstChildElem()) 289 { 290 var = XString.EMPTYSTRING; 291 } 292 else 293 { 294 295 // Use result tree fragment. 296 // Global variables may be deferred (see XUnresolvedVariable) and hence 297 // need to be assigned to a different set of DTMs than local variables 298 // so they aren't popped off the stack on return from a template. 299 int df; 300 301 // Bugzilla 7118: A variable set via an RTF may create local 302 // variables during that computation. To keep them from overwriting 303 // variables at this level, push a new variable stack. 304 ////// PROBLEM: This is provoking a variable-used-before-set 305 ////// problem in parameters. Needs more study. 306 try 307 { 308 //////////xctxt.getVarStack().link(0); 309 if(m_parentNode instanceof Stylesheet) // Global variable 310 df = transformer.transformToGlobalRTF(this); 311 else 312 df = transformer.transformToRTF(this); 313 } 314 finally{ 315 //////////////xctxt.getVarStack().unlink(); 316 } 317 318 var = new XRTreeFrag(df, xctxt, this); 319 } 320 } 321 finally 322 { 323 xctxt.popCurrentNode(); 324 } 325 326 return var; 327 } 328 329 330 /** 331 * This function is called after everything else has been 332 * recomposed, and allows the template to set remaining 333 * values that may be based on some other property that 334 * depends on recomposition. 335 */ 336 public void compose(StylesheetRoot sroot) throws TransformerException 337 { 338 // See if we can reduce an RTF to a select with a string expression. 339 if(null == m_selectPattern 340 && sroot.getOptimizer()) 341 { 342 XPath newSelect = rewriteChildToExpression(this); 343 if(null != newSelect) 344 m_selectPattern = newSelect; 345 } 346 347 StylesheetRoot.ComposeState cstate = sroot.getComposeState(); 348 349 // This should be done before addVariableName, so we don't have visibility 350 // to the variable now being defined. 351 java.util.Vector vnames = cstate.getVariableNames(); 352 if(null != m_selectPattern) 353 m_selectPattern.fixupVariables(vnames, cstate.getGlobalsSize()); 354 355 // Only add the variable if this is not a global. If it is a global, 356 // it was already added by stylesheet root. 357 if(!(m_parentNode instanceof Stylesheet) && m_qname != null) 358 { 359 m_index = cstate.addVariableName(m_qname) - cstate.getGlobalsSize(); 360 } 361 else if (m_parentNode instanceof Stylesheet) 362 { 363 // If this is a global, then we need to treat it as if it's a xsl:template, 364 // and count the number of variables it contains. So we set the count to 365 // zero here. 366 cstate.resetStackFrameSize(); 367 } 368 369 // This has to be done after the addVariableName, so that the variable 370 // pushed won't be immediately popped again in endCompose. 371 super.compose(sroot); 372 } 373 374 /** 375 * This after the template's children have been composed. We have to get 376 * the count of how many variables have been declared, so we can do a link 377 * and unlink. 378 */ 379 public void endCompose(StylesheetRoot sroot) throws TransformerException 380 { 381 super.endCompose(sroot); 382 if(m_parentNode instanceof Stylesheet) 383 { 384 StylesheetRoot.ComposeState cstate = sroot.getComposeState(); 385 m_frameSize = cstate.getFrameSize(); 386 cstate.resetStackFrameSize(); 387 } 388 } 389 390 391 392 // /** 393 // * This after the template's children have been composed. 394 // */ 395 // public void endCompose() throws TransformerException 396 // { 397 // super.endCompose(); 398 // } 399 400 401 /** 402 * If the children of a variable is a single xsl:value-of or text literal, 403 * it is cheaper to evaluate this as an expression, so try and adapt the 404 * child an an expression. 405 * 406 * @param varElem Should be a ElemParam, ElemVariable, or ElemWithParam. 407 * 408 * @return An XPath if rewrite is possible, else null. 409 * 410 * @throws TransformerException 411 */ 412 static XPath rewriteChildToExpression(ElemTemplateElement varElem) 413 throws TransformerException 414 { 415 416 ElemTemplateElement t = varElem.getFirstChildElem(); 417 418 // Down the line this can be done with multiple string objects using 419 // the concat function. 420 if (null != t && null == t.getNextSiblingElem()) 421 { 422 int etype = t.getXSLToken(); 423 424 if (Constants.ELEMNAME_VALUEOF == etype) 425 { 426 ElemValueOf valueof = (ElemValueOf) t; 427 428 // %TBD% I'm worried about extended attributes here. 429 if (valueof.getDisableOutputEscaping() == false 430 && valueof.getDOMBackPointer() == null) 431 { 432 varElem.m_firstChild = null; 433 434 return new XPath(new XRTreeFragSelectWrapper(valueof.getSelect().getExpression())); 435 } 436 } 437 else if (Constants.ELEMNAME_TEXTLITERALRESULT == etype) 438 { 439 ElemTextLiteral lit = (ElemTextLiteral) t; 440 441 if (lit.getDisableOutputEscaping() == false 442 && lit.getDOMBackPointer() == null) 443 { 444 String str = lit.getNodeValue(); 445 XString xstr = new XString(str); 446 447 varElem.m_firstChild = null; 448 449 return new XPath(new XRTreeFragSelectWrapper(xstr)); 450 } 451 } 452 } 453 454 return null; 455 } 456 457 /** 458 * This function is called during recomposition to 459 * control how this element is composed. 460 * @param root The root stylesheet for this transformation. 461 */ 462 public void recompose(StylesheetRoot root) 463 { 464 root.recomposeVariables(this); 465 } 466 467 /** 468 * Set the parent as an ElemTemplateElement. 469 * 470 * @param p This node's parent as an ElemTemplateElement 471 */ 472 public void setParentElem(ElemTemplateElement p) 473 { 474 super.setParentElem(p); 475 p.m_hasVariableDecl = true; 476 } 477 478 /** 479 * Accept a visitor and call the appropriate method 480 * for this class. 481 * 482 * @param visitor The visitor whose appropriate method will be called. 483 * @return true if the children of the object should be visited. 484 */ 485 protected boolean accept(XSLTVisitor visitor) 486 { 487 return visitor.visitVariableOrParamDecl(this); 488 } 489 490 491 /** 492 * Call the children visitors. 493 * @param visitor The visitor whose appropriate method will be called. 494 */ 495 protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs) 496 { 497 if(null != m_selectPattern) 498 m_selectPattern.getExpression().callVisitors(m_selectPattern, visitor); 499 super.callChildVisitors(visitor, callAttrs); 500 } 501 502 /** 503 * Tell if this is a psuedo variable reference, declared by Xalan instead 504 * of by the user. 505 */ 506 public boolean isPsuedoVar() 507 { 508 java.lang.String ns = m_qname.getNamespaceURI(); 509 if((null != ns) && ns.equals(RedundentExprEliminator.PSUEDOVARNAMESPACE)) 510 { 511 if(m_qname.getLocalName().startsWith("#")) 512 return true; 513 } 514 return false; 515 } 516 517 /** 518 * Add a child to the child list. If the select attribute 519 * is present, an error will be raised. 520 * 521 * @param elem New element to append to this element's children list 522 * 523 * @return null if the select attribute was present, otherwise the 524 * child just added to the child list 525 */ 526 public ElemTemplateElement appendChild(ElemTemplateElement elem) 527 { 528 // cannot have content and select 529 if (m_selectPattern != null) 530 { 531 error(XSLTErrorResources.ER_CANT_HAVE_CONTENT_AND_SELECT, 532 new Object[]{"xsl:" + this.getNodeName()}); 533 return null; 534 } 535 return super.appendChild(elem); 536 } 537 538 }