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