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 */
21package org.apache.xalan.templates;
22
23import javax.xml.transform.TransformerException;
24
25import org.apache.xalan.transformer.TransformerImpl;
26import org.apache.xml.utils.QName;
27import org.apache.xpath.XPath;
28import org.apache.xpath.XPathContext;
29import org.apache.xpath.objects.XObject;
30import org.apache.xpath.objects.XRTreeFrag;
31import org.apache.xpath.objects.XRTreeFragSelectWrapper;
32import org.apache.xpath.objects.XString;
33import 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 */
47public 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