Home | History | Annotate | Download | only in operations
      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: Variable.java 468655 2006-10-28 07:12:06Z minchau $
     20  */
     21 package org.apache.xpath.operations;
     22 
     23 import javax.xml.transform.TransformerException;
     24 
     25 import org.apache.xalan.res.XSLMessages;
     26 import org.apache.xml.utils.QName;
     27 import org.apache.xpath.Expression;
     28 import org.apache.xpath.ExpressionOwner;
     29 import org.apache.xpath.XPath;
     30 import org.apache.xpath.XPathContext;
     31 import org.apache.xpath.XPathVisitor;
     32 import org.apache.xpath.axes.PathComponent;
     33 import org.apache.xpath.axes.WalkerFactory;
     34 import org.apache.xpath.objects.XNodeSet;
     35 import org.apache.xpath.objects.XObject;
     36 import org.apache.xpath.res.XPATHErrorResources;
     37 
     38 
     39 /**
     40  * The variable reference expression executer.
     41  */
     42 public class Variable extends Expression implements PathComponent
     43 {
     44     static final long serialVersionUID = -4334975375609297049L;
     45   /** Tell if fixupVariables was called.
     46    *  @serial   */
     47   private boolean m_fixUpWasCalled = false;
     48 
     49   /** The qualified name of the variable.
     50    *  @serial   */
     51   protected QName m_qname;
     52 
     53   /**
     54    * The index of the variable, which is either an absolute index to a
     55    * global, or, if higher than the globals area, must be adjusted by adding
     56    * the offset to the current stack frame.
     57    */
     58   protected int m_index;
     59 
     60   /**
     61    * Set the index for the variable into the stack.  For advanced use only. You
     62    * must know what you are doing to use this.
     63    *
     64    * @param index a global or local index.
     65    */
     66   public void setIndex(int index)
     67   {
     68   	m_index = index;
     69   }
     70 
     71   /**
     72    * Set the index for the variable into the stack.  For advanced use only.
     73    *
     74    * @return index a global or local index.
     75    */
     76   public int getIndex()
     77   {
     78   	return m_index;
     79   }
     80 
     81   /**
     82    * Set whether or not this is a global reference.  For advanced use only.
     83    *
     84    * @param isGlobal true if this should be a global variable reference.
     85    */
     86   public void setIsGlobal(boolean isGlobal)
     87   {
     88   	m_isGlobal = isGlobal;
     89   }
     90 
     91   /**
     92    * Set the index for the variable into the stack.  For advanced use only.
     93    *
     94    * @return true if this should be a global variable reference.
     95    */
     96   public boolean getGlobal()
     97   {
     98   	return m_isGlobal;
     99   }
    100 
    101 
    102 
    103 
    104 
    105   protected boolean m_isGlobal = false;
    106 
    107   /**
    108    * This function is used to fixup variables from QNames to stack frame
    109    * indexes at stylesheet build time.
    110    * @param vars List of QNames that correspond to variables.  This list
    111    * should be searched backwards for the first qualified name that
    112    * corresponds to the variable reference qname.  The position of the
    113    * QName in the vector from the start of the vector will be its position
    114    * in the stack frame (but variables above the globalsTop value will need
    115    * to be offset to the current stack frame).
    116    */
    117   public void fixupVariables(java.util.Vector vars, int globalsSize)
    118   {
    119     m_fixUpWasCalled = true;
    120     int sz = vars.size();
    121 
    122     for (int i = vars.size()-1; i >= 0; i--)
    123     {
    124       QName qn = (QName)vars.elementAt(i);
    125       // System.out.println("qn: "+qn);
    126       if(qn.equals(m_qname))
    127       {
    128 
    129         if(i < globalsSize)
    130         {
    131           m_isGlobal = true;
    132           m_index = i;
    133         }
    134         else
    135         {
    136           m_index = i-globalsSize;
    137         }
    138 
    139         return;
    140       }
    141     }
    142 
    143     java.lang.String msg = XSLMessages.createXPATHMessage(XPATHErrorResources.ER_COULD_NOT_FIND_VAR,
    144                                              new Object[]{m_qname.toString()});
    145 
    146     TransformerException te = new TransformerException(msg, this);
    147 
    148     throw new org.apache.xml.utils.WrappedRuntimeException(te);
    149 
    150   }
    151 
    152 
    153   /**
    154    * Set the qualified name of the variable.
    155    *
    156    * @param qname Must be a non-null reference to a qualified name.
    157    */
    158   public void setQName(QName qname)
    159   {
    160     m_qname = qname;
    161   }
    162 
    163   /**
    164    * Get the qualified name of the variable.
    165    *
    166    * @return A non-null reference to a qualified name.
    167    */
    168   public QName getQName()
    169   {
    170     return m_qname;
    171   }
    172 
    173   /**
    174    * Execute an expression in the XPath runtime context, and return the
    175    * result of the expression.
    176    *
    177    *
    178    * @param xctxt The XPath runtime context.
    179    *
    180    * @return The result of the expression in the form of a <code>XObject</code>.
    181    *
    182    * @throws javax.xml.transform.TransformerException if a runtime exception
    183    *         occurs.
    184    */
    185   public XObject execute(XPathContext xctxt)
    186     throws javax.xml.transform.TransformerException
    187   {
    188   	return execute(xctxt, false);
    189   }
    190 
    191 
    192   /**
    193    * Dereference the variable, and return the reference value.  Note that lazy
    194    * evaluation will occur.  If a variable within scope is not found, a warning
    195    * will be sent to the error listener, and an empty nodeset will be returned.
    196    *
    197    *
    198    * @param xctxt The runtime execution context.
    199    *
    200    * @return The evaluated variable, or an empty nodeset if not found.
    201    *
    202    * @throws javax.xml.transform.TransformerException
    203    */
    204   public XObject execute(XPathContext xctxt, boolean destructiveOK) throws javax.xml.transform.TransformerException
    205   {
    206     org.apache.xml.utils.PrefixResolver xprefixResolver = xctxt.getNamespaceContext();
    207 
    208     XObject result;
    209     // Is the variable fetched always the same?
    210     // XObject result = xctxt.getVariable(m_qname);
    211     if(m_fixUpWasCalled)
    212     {
    213       if(m_isGlobal)
    214         result = xctxt.getVarStack().getGlobalVariable(xctxt, m_index, destructiveOK);
    215       else
    216         result = xctxt.getVarStack().getLocalVariable(xctxt, m_index, destructiveOK);
    217     }
    218     else {
    219     	result = xctxt.getVarStack().getVariableOrParam(xctxt,m_qname);
    220     }
    221 
    222       if (null == result)
    223       {
    224         // This should now never happen...
    225         warn(xctxt, XPATHErrorResources.WG_ILLEGAL_VARIABLE_REFERENCE,
    226              new Object[]{ m_qname.getLocalPart() });  //"VariableReference given for variable out "+
    227   //      (new RuntimeException()).printStackTrace();
    228   //      error(xctxt, XPATHErrorResources.ER_COULDNOT_GET_VAR_NAMED,
    229   //            new Object[]{ m_qname.getLocalPart() });  //"Could not get variable named "+varName);
    230 
    231         result = new XNodeSet(xctxt.getDTMManager());
    232       }
    233 
    234       return result;
    235 //    }
    236 //    else
    237 //    {
    238 //      // Hack city... big time.  This is needed to evaluate xpaths from extensions,
    239 //      // pending some bright light going off in my head.  Some sort of callback?
    240 //      synchronized(this)
    241 //      {
    242 //      	org.apache.xalan.templates.ElemVariable vvar= getElemVariable();
    243 //      	if(null != vvar)
    244 //      	{
    245 //          m_index = vvar.getIndex();
    246 //          m_isGlobal = vvar.getIsTopLevel();
    247 //          m_fixUpWasCalled = true;
    248 //          return execute(xctxt);
    249 //      	}
    250 //      }
    251 //      throw new javax.xml.transform.TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VAR_NOT_RESOLVABLE, new Object[]{m_qname.toString()})); //"Variable not resolvable: "+m_qname);
    252 //    }
    253   }
    254 
    255   /**
    256    * Get the XSLT ElemVariable that this sub-expression references.  In order for
    257    * this to work, the SourceLocator must be the owning ElemTemplateElement.
    258    * @return The dereference to the ElemVariable, or null if not found.
    259    */
    260   public org.apache.xalan.templates.ElemVariable getElemVariable()
    261   {
    262 
    263     // Get the current ElemTemplateElement, and then walk backwards in
    264     // document order, searching
    265     // for an xsl:param element or xsl:variable element that matches our
    266     // qname.  If we reach the top level, use the StylesheetRoot's composed
    267     // list of top level variables and parameters.
    268 
    269     org.apache.xalan.templates.ElemVariable vvar = null;
    270     org.apache.xpath.ExpressionNode owner = getExpressionOwner();
    271 
    272     if (null != owner && owner instanceof org.apache.xalan.templates.ElemTemplateElement)
    273     {
    274 
    275       org.apache.xalan.templates.ElemTemplateElement prev =
    276         (org.apache.xalan.templates.ElemTemplateElement) owner;
    277 
    278       if (!(prev instanceof org.apache.xalan.templates.Stylesheet))
    279       {
    280         while ( prev != null && !(prev.getParentNode() instanceof org.apache.xalan.templates.Stylesheet) )
    281         {
    282           org.apache.xalan.templates.ElemTemplateElement savedprev = prev;
    283 
    284           while (null != (prev = prev.getPreviousSiblingElem()))
    285           {
    286             if(prev instanceof org.apache.xalan.templates.ElemVariable)
    287             {
    288               vvar = (org.apache.xalan.templates.ElemVariable) prev;
    289 
    290               if (vvar.getName().equals(m_qname))
    291               {
    292                 return vvar;
    293               }
    294               vvar = null;
    295             }
    296           }
    297           prev = savedprev.getParentElem();
    298         }
    299       }
    300       if (prev != null)
    301         vvar = prev.getStylesheetRoot().getVariableOrParamComposed(m_qname);
    302     }
    303     return vvar;
    304 
    305   }
    306 
    307   /**
    308    * Tell if this expression returns a stable number that will not change during
    309    * iterations within the expression.  This is used to determine if a proximity
    310    * position predicate can indicate that no more searching has to occur.
    311    *
    312    *
    313    * @return true if the expression represents a stable number.
    314    */
    315   public boolean isStableNumber()
    316   {
    317     return true;
    318   }
    319 
    320   /**
    321    * Get the analysis bits for this walker, as defined in the WalkerFactory.
    322    * @return One of WalkerFactory#BIT_DESCENDANT, etc.
    323    */
    324   public int getAnalysisBits()
    325   {
    326   	org.apache.xalan.templates.ElemVariable vvar = getElemVariable();
    327   	if(null != vvar)
    328   	{
    329   		XPath xpath = vvar.getSelect();
    330   		if(null != xpath)
    331   		{
    332 	  		Expression expr = xpath.getExpression();
    333 	  		if(null != expr && expr instanceof PathComponent)
    334 	  		{
    335 	  			return ((PathComponent)expr).getAnalysisBits();
    336 	  		}
    337   		}
    338   	}
    339     return WalkerFactory.BIT_FILTER;
    340   }
    341 
    342 
    343   /**
    344    * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor)
    345    */
    346   public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
    347   {
    348   	visitor.visitVariableRef(owner, this);
    349   }
    350   /**
    351    * @see Expression#deepEquals(Expression)
    352    */
    353   public boolean deepEquals(Expression expr)
    354   {
    355   	if(!isSameClass(expr))
    356   		return false;
    357 
    358   	if(!m_qname.equals(((Variable)expr).m_qname))
    359   		return false;
    360 
    361   	// We have to make sure that the qname really references
    362   	// the same variable element.
    363     if(getElemVariable() != ((Variable)expr).getElemVariable())
    364     	return false;
    365 
    366   	return true;
    367   }
    368 
    369   static final java.lang.String PSUEDOVARNAMESPACE = "http://xml.apache.org/xalan/psuedovar";
    370 
    371   /**
    372    * Tell if this is a psuedo variable reference, declared by Xalan instead
    373    * of by the user.
    374    */
    375   public boolean isPsuedoVarRef()
    376   {
    377   	java.lang.String ns = m_qname.getNamespaceURI();
    378   	if((null != ns) && ns.equals(PSUEDOVARNAMESPACE))
    379   	{
    380   		if(m_qname.getLocalName().startsWith("#"))
    381   			return true;
    382   	}
    383   	return false;
    384   }
    385 
    386 
    387 }
    388