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    */
ElemVariable()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    */
setIndex(int index)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    */
getIndex()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    */
setSelect(XPath v)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    */
getSelect()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    */
setName(QName v)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    */
getName()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    */
setIsTopLevel(boolean v)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    */
getIsTopLevel()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    */
getXSLToken()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    */
getNodeName()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    */
ElemVariable(ElemVariable param)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    */
execute(TransformerImpl transformer)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    */
getValue(TransformerImpl transformer, int sourceNode)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    */
compose(StylesheetRoot sroot)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    */
endCompose(StylesheetRoot sroot)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    */
rewriteChildToExpression(ElemTemplateElement varElem)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    */
recompose(StylesheetRoot root)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    */
setParentElem(ElemTemplateElement p)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    */
accept(XSLTVisitor visitor)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    */
callChildVisitors(XSLTVisitor visitor, boolean callAttrs)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    */
isPsuedoVar()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    */
appendChild(ElemTemplateElement elem)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