Home | History | Annotate | Download | only in templates
      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    */
     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