1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.templates;
  58. //import org.w3c.dom.*;
  59. import org.apache.xml.dtm.DTM;
  60. import org.xml.sax.*;
  61. import org.apache.xpath.*;
  62. import org.apache.xpath.Expression;
  63. import org.apache.xpath.objects.XObject;
  64. import org.apache.xpath.objects.XString;
  65. import org.apache.xpath.objects.XRTreeFrag;
  66. import org.apache.xpath.objects.XRTreeFragSelectWrapper;
  67. import org.apache.xml.utils.QName;
  68. import org.apache.xalan.trace.SelectionEvent;
  69. import org.apache.xalan.res.XSLTErrorResources;
  70. import org.apache.xalan.transformer.TransformerImpl;
  71. import javax.xml.transform.TransformerException;
  72. /**
  73. * <meta name="usage" content="advanced"/>
  74. * Implement xsl:variable.
  75. * <pre>
  76. * <!ELEMENT xsl:variable %template;>
  77. * <!ATTLIST xsl:variable
  78. * name %qname; #REQUIRED
  79. * select %expr; #IMPLIED
  80. * >
  81. * </pre>
  82. * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a>
  83. */
  84. public class ElemVariable extends ElemTemplateElement
  85. {
  86. /**
  87. * Constructor ElemVariable
  88. *
  89. */
  90. public ElemVariable(){}
  91. /**
  92. * This is the index into the stack frame.
  93. */
  94. protected int m_index;
  95. /**
  96. * The stack frame size for this variable if it is a global variable
  97. * that declares an RTF, which is equal to the maximum number
  98. * of variables that can be declared in the variable at one time.
  99. */
  100. int m_frameSize = -1;
  101. /**
  102. * Sets the relative position of this variable within the stack frame (if local)
  103. * or the global area (if global). Note that this should be called only for
  104. * global variables since the local position is computed in the compose() method.
  105. */
  106. public void setIndex(int index)
  107. {
  108. m_index = index;
  109. }
  110. /**
  111. * If this element is not at the top-level, get the relative position of the
  112. * variable into the stack frame. If this variable is at the top-level, get
  113. * the relative position within the global area.
  114. */
  115. public int getIndex()
  116. {
  117. return m_index;
  118. }
  119. /**
  120. * The value of the "select" attribute.
  121. * @serial
  122. */
  123. private XPath m_selectPattern;
  124. /**
  125. * Set the "select" attribute.
  126. * If the variable-binding element has a select attribute,
  127. * then the value of the attribute must be an expression and
  128. * the value of the variable is the object that results from
  129. * evaluating the expression. In this case, the content
  130. * of the variable must be empty.
  131. *
  132. * @param v Value to set for the "select" attribute.
  133. */
  134. public void setSelect(XPath v)
  135. {
  136. m_selectPattern = v;
  137. }
  138. /**
  139. * Get the "select" attribute.
  140. * If the variable-binding element has a select attribute,
  141. * then the value of the attribute must be an expression and
  142. * the value of the variable is the object that results from
  143. * evaluating the expression. In this case, the content
  144. * of the variable must be empty.
  145. *
  146. * @return Value of the "select" attribute.
  147. */
  148. public XPath getSelect()
  149. {
  150. return m_selectPattern;
  151. }
  152. /**
  153. * The value of the "name" attribute.
  154. * @serial
  155. */
  156. protected QName m_qname;
  157. /**
  158. * Set the "name" attribute.
  159. * Both xsl:variable and xsl:param have a required name
  160. * attribute, which specifies the name of the variable. The
  161. * value of the name attribute is a QName, which is expanded
  162. * as described in [2.4 Qualified Names].
  163. * @see <a href="http://www.w3.org/TR/xslt#qname">qname in XSLT Specification</a>
  164. *
  165. * @param v Value to set for the "name" attribute.
  166. */
  167. public void setName(QName v)
  168. {
  169. m_qname = v;
  170. }
  171. /**
  172. * Get the "name" attribute.
  173. * Both xsl:variable and xsl:param have a required name
  174. * attribute, which specifies the name of the variable. The
  175. * value of the name attribute is a QName, which is expanded
  176. * as described in [2.4 Qualified Names].
  177. * @see <a href="http://www.w3.org/TR/xslt#qname">qname in XSLT Specification</a>
  178. *
  179. * @return Value of the "name" attribute.
  180. */
  181. public QName getName()
  182. {
  183. return m_qname;
  184. }
  185. /**
  186. * Tells if this is a top-level variable or param, or not.
  187. * @serial
  188. */
  189. private boolean m_isTopLevel = false;
  190. /**
  191. * Set if this is a top-level variable or param, or not.
  192. * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
  193. *
  194. * @param v Boolean indicating whether this is a top-level variable
  195. * or param, or not.
  196. */
  197. public void setIsTopLevel(boolean v)
  198. {
  199. m_isTopLevel = v;
  200. }
  201. /**
  202. * Get if this is a top-level variable or param, or not.
  203. * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
  204. *
  205. * @return Boolean indicating whether this is a top-level variable
  206. * or param, or not.
  207. */
  208. public boolean getIsTopLevel()
  209. {
  210. return m_isTopLevel;
  211. }
  212. /**
  213. * Get an integer representation of the element type.
  214. *
  215. * @return An integer representation of the element, defined in the
  216. * Constants class.
  217. * @see org.apache.xalan.templates.Constants
  218. */
  219. public int getXSLToken()
  220. {
  221. return Constants.ELEMNAME_VARIABLE;
  222. }
  223. /**
  224. * Return the node name.
  225. *
  226. * @return The node name
  227. */
  228. public String getNodeName()
  229. {
  230. return Constants.ELEMNAME_VARIABLE_STRING;
  231. }
  232. /**
  233. * Copy constructor.
  234. *
  235. * @param param An element created from an xsl:variable
  236. *
  237. * @throws TransformerException
  238. */
  239. public ElemVariable(ElemVariable param) throws TransformerException
  240. {
  241. m_selectPattern = param.m_selectPattern;
  242. m_qname = param.m_qname;
  243. m_isTopLevel = param.m_isTopLevel;
  244. // m_value = param.m_value;
  245. // m_varContext = param.m_varContext;
  246. }
  247. /**
  248. * Execute a variable declaration and push it onto the variable stack.
  249. * @see <a href="http://www.w3.org/TR/xslt#variables">variables in XSLT Specification</a>
  250. *
  251. * @param transformer non-null reference to the the current transform-time state.
  252. * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
  253. * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>.
  254. *
  255. * @throws TransformerException
  256. */
  257. public void execute(TransformerImpl transformer) throws TransformerException
  258. {
  259. if (TransformerImpl.S_DEBUG)
  260. transformer.getTraceManager().fireTraceEvent(this);
  261. int sourceNode = transformer.getXPathContext().getCurrentNode();
  262. XObject var = getValue(transformer, sourceNode);
  263. // transformer.getXPathContext().getVarStack().pushVariable(m_qname, var);
  264. transformer.getXPathContext().getVarStack().setLocalVariable(m_index, var);
  265. if (TransformerImpl.S_DEBUG)
  266. transformer.getTraceManager().fireTraceEndEvent(this);
  267. }
  268. /**
  269. * Get the XObject representation of the variable.
  270. *
  271. * @param transformer non-null reference to the the current transform-time state.
  272. * @param sourceNode non-null reference to the <a href="http://www.w3.org/TR/xslt#dt-current-node">current source node</a>.
  273. *
  274. * @return the XObject representation of the variable.
  275. *
  276. * @throws TransformerException
  277. */
  278. public XObject getValue(TransformerImpl transformer, int sourceNode)
  279. throws TransformerException
  280. {
  281. XObject var;
  282. XPathContext xctxt = transformer.getXPathContext();
  283. xctxt.pushCurrentNode(sourceNode);
  284. try
  285. {
  286. if (null != m_selectPattern)
  287. {
  288. var = m_selectPattern.execute(xctxt, sourceNode, this);
  289. var.allowDetachToRelease(false);
  290. if (TransformerImpl.S_DEBUG)
  291. transformer.getTraceManager().fireSelectedEvent(sourceNode, this,
  292. "select", m_selectPattern, var);
  293. }
  294. else if (null == getFirstChildElem())
  295. {
  296. var = XString.EMPTYSTRING;
  297. }
  298. else
  299. {
  300. // Use result tree fragment.
  301. // Global variables may be deferred (see XUnresolvedVariable) and hence
  302. // need to be assigned to a different set of DTMs than local variables
  303. // so they aren't popped off the stack on return from a template.
  304. int df;
  305. // Bugzilla 7118: A variable set via an RTF may create local
  306. // variables during that computation. To keep them from overwriting
  307. // variables at this level, push a new variable stack.
  308. ////// PROBLEM: This is provoking a variable-used-before-set
  309. ////// problem in parameters. Needs more study.
  310. try
  311. {
  312. //////////xctxt.getVarStack().link(0);
  313. if(m_parentNode instanceof Stylesheet) // Global variable
  314. df = transformer.transformToGlobalRTF(this);
  315. else
  316. df = transformer.transformToRTF(this);
  317. }
  318. finally{
  319. //////////////xctxt.getVarStack().unlink();
  320. }
  321. var = new XRTreeFrag(df, xctxt, this);
  322. }
  323. }
  324. finally
  325. {
  326. xctxt.popCurrentNode();
  327. }
  328. return var;
  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. && org.apache.xalan.processor.TransformerFactoryImpl.m_optimize)
  341. {
  342. XPath newSelect = rewriteChildToExpression(this);
  343. if(null != newSelect)
  344. m_selectPattern = newSelect;
  345. }
  346. StylesheetRoot.ComposeState cstate = sroot.getComposeState();
  347. // This should be done before addVariableName, so we don't have visibility
  348. // to the variable now being defined.
  349. java.util.Vector vnames = cstate.getVariableNames();
  350. if(null != m_selectPattern)
  351. m_selectPattern.fixupVariables(vnames, cstate.getGlobalsSize());
  352. // Only add the variable if this is not a global. If it is a global,
  353. // it was already added by stylesheet root.
  354. if(!(m_parentNode instanceof Stylesheet))
  355. {
  356. m_index = cstate.addVariableName(m_qname) - cstate.getGlobalsSize();
  357. }
  358. else
  359. {
  360. // If this is a global, then we need to treat it as if it's a xsl:template,
  361. // and count the number of variables it contains. So we set the count to
  362. // zero here.
  363. cstate.resetStackFrameSize();
  364. }
  365. // This has to be done after the addVariableName, so that the variable
  366. // pushed won't be immediately popped again in endCompose.
  367. super.compose(sroot);
  368. }
  369. /**
  370. * This after the template's children have been composed. We have to get
  371. * the count of how many variables have been declared, so we can do a link
  372. * and unlink.
  373. */
  374. public void endCompose(StylesheetRoot sroot) throws TransformerException
  375. {
  376. super.endCompose(sroot);
  377. if(m_parentNode instanceof Stylesheet)
  378. {
  379. StylesheetRoot.ComposeState cstate = sroot.getComposeState();
  380. m_frameSize = cstate.getFrameSize();
  381. cstate.resetStackFrameSize();
  382. }
  383. }
  384. // /**
  385. // * This after the template's children have been composed.
  386. // */
  387. // public void endCompose() throws TransformerException
  388. // {
  389. // super.endCompose();
  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. ElemTemplateElement t = varElem.getFirstChildElem();
  406. // Down the line this can be done with multiple string objects using
  407. // the concat function.
  408. if (null != t && null == t.getNextSiblingElem())
  409. {
  410. int etype = t.getXSLToken();
  411. if (Constants.ELEMNAME_VALUEOF == etype)
  412. {
  413. ElemValueOf valueof = (ElemValueOf) t;
  414. // %TBD% I'm worried about extended attributes here.
  415. if (valueof.getDisableOutputEscaping() == false
  416. && valueof.getDOMBackPointer() == null)
  417. {
  418. varElem.m_firstChild = null;
  419. return new XPath(new XRTreeFragSelectWrapper(valueof.getSelect().getExpression()));
  420. }
  421. }
  422. else if (Constants.ELEMNAME_TEXTLITERALRESULT == etype)
  423. {
  424. ElemTextLiteral lit = (ElemTextLiteral) t;
  425. if (lit.getDisableOutputEscaping() == false
  426. && lit.getDOMBackPointer() == null)
  427. {
  428. String str = lit.getNodeValue();
  429. XString xstr = new XString(str);
  430. varElem.m_firstChild = null;
  431. return new XPath(new XRTreeFragSelectWrapper(xstr));
  432. }
  433. }
  434. }
  435. return null;
  436. }
  437. /**
  438. * This function is called during recomposition to
  439. * control how this element is composed.
  440. * @param root The root stylesheet for this transformation.
  441. */
  442. public void recompose(StylesheetRoot root)
  443. {
  444. root.recomposeVariables(this);
  445. }
  446. /**
  447. * Set the parent as an ElemTemplateElement.
  448. *
  449. * @param parent This node's parent as an ElemTemplateElement
  450. */
  451. public void setParentElem(ElemTemplateElement p)
  452. {
  453. super.setParentElem(p);
  454. p.m_hasVariableDecl = true;
  455. }
  456. /**
  457. * Accept a visitor and call the appropriate method
  458. * for this class.
  459. *
  460. * @param visitor The visitor whose appropriate method will be called.
  461. * @return true if the children of the object should be visited.
  462. */
  463. protected boolean accept(XSLTVisitor visitor)
  464. {
  465. return visitor.visitVariableOrParamDecl(this);
  466. }
  467. /**
  468. * Call the children visitors.
  469. * @param visitor The visitor whose appropriate method will be called.
  470. */
  471. protected void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
  472. {
  473. if(null != m_selectPattern)
  474. m_selectPattern.getExpression().callVisitors(m_selectPattern, visitor);
  475. super.callChildVisitors(visitor, callAttrs);
  476. }
  477. /**
  478. * Tell if this is a psuedo variable reference, declared by Xalan instead
  479. * of by the user.
  480. */
  481. public boolean isPsuedoVar()
  482. {
  483. java.lang.String ns = m_qname.getNamespaceURI();
  484. if((null != ns) && ns.equals(RedundentExprEliminator.PSUEDOVARNAMESPACE))
  485. {
  486. if(m_qname.getLocalName().startsWith("#"))
  487. return true;
  488. }
  489. return false;
  490. }
  491. }