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    }