1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * @LastModified: Oct 2017
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xpath.internal;
  23 
  24 import com.sun.org.apache.xalan.internal.res.XSLMessages;
  25 import com.sun.org.apache.xml.internal.dtm.DTM;
  26 import com.sun.org.apache.xml.internal.dtm.DTMIterator;
  27 import com.sun.org.apache.xml.internal.utils.QName;
  28 import com.sun.org.apache.xml.internal.utils.XMLString;
  29 import com.sun.org.apache.xpath.internal.objects.XNodeSet;
  30 import com.sun.org.apache.xpath.internal.objects.XObject;
  31 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
  32 import java.util.List;
  33 import javax.xml.transform.ErrorListener;
  34 import javax.xml.transform.TransformerException;
  35 import org.xml.sax.ContentHandler;
  36 
  37 /**
  38  * This abstract class serves as the base for all expression objects.  An
  39  * Expression can be executed to return a {@link com.sun.org.apache.xpath.internal.objects.XObject},
  40  * normally has a location within a document or DOM, can send error and warning
  41  * events, and normally do not hold state and are meant to be immutable once
  42  * construction has completed.  An exception to the immutibility rule is iterators
  43  * and walkers, which must be cloned in order to be used -- the original must
  44  * still be immutable.
  45  */
  46 public abstract class Expression implements java.io.Serializable, ExpressionNode, XPathVisitable
  47 {
  48     static final long serialVersionUID = 565665869777906902L;
  49   /**
  50    * The location where this expression was built from.  Need for diagnostic
  51    *  messages. May be null.
  52    *  @serial
  53    */
  54   private ExpressionNode m_parent;
  55 
  56   /**
  57    * Tell if this expression or it's subexpressions can traverse outside
  58    * the current subtree.
  59    *
  60    * @return true if traversal outside the context node's subtree can occur.
  61    */
  62   public boolean canTraverseOutsideSubtree()
  63   {
  64     return false;
  65   }
  66 
  67 //  /**
  68 //   * Set the location where this expression was built from.
  69 //   *
  70 //   *
  71 //   * @param locator the location where this expression was built from, may be
  72 //   *                null.
  73 //   */
  74 //  public void setSourceLocator(SourceLocator locator)
  75 //  {
  76 //    m_slocator = locator;
  77 //  }
  78 
  79   /**
  80    * Execute an expression in the XPath runtime context, and return the
  81    * result of the expression.
  82    *
  83    *
  84    * @param xctxt The XPath runtime context.
  85    * @param currentNode The currentNode.
  86    *
  87    * @return The result of the expression in the form of a <code>XObject</code>.
  88    *
  89    * @throws javax.xml.transform.TransformerException if a runtime exception
  90    *         occurs.
  91    */
  92   public XObject execute(XPathContext xctxt, int currentNode)
  93           throws javax.xml.transform.TransformerException
  94   {
  95 
  96     // For now, the current node is already pushed.
  97     return execute(xctxt);
  98   }
  99 
 100   /**
 101    * Execute an expression in the XPath runtime context, and return the
 102    * result of the expression.
 103    *
 104    *
 105    * @param xctxt The XPath runtime context.
 106    * @param currentNode The currentNode.
 107    * @param dtm The DTM of the current node.
 108    * @param expType The expanded type ID of the current node.
 109    *
 110    * @return The result of the expression in the form of a <code>XObject</code>.
 111    *
 112    * @throws javax.xml.transform.TransformerException if a runtime exception
 113    *         occurs.
 114    */
 115   public XObject execute(
 116           XPathContext xctxt, int currentNode, DTM dtm, int expType)
 117             throws javax.xml.transform.TransformerException
 118   {
 119 
 120     // For now, the current node is already pushed.
 121     return execute(xctxt);
 122   }
 123 
 124   /**
 125    * Execute an expression in the XPath runtime context, and return the
 126    * result of the expression.
 127    *
 128    *
 129    * @param xctxt The XPath runtime context.
 130    *
 131    * @return The result of the expression in the form of a <code>XObject</code>.
 132    *
 133    * @throws javax.xml.transform.TransformerException if a runtime exception
 134    *         occurs.
 135    */
 136   public abstract XObject execute(XPathContext xctxt)
 137     throws javax.xml.transform.TransformerException;
 138 
 139   /**
 140    * Execute an expression in the XPath runtime context, and return the
 141    * result of the expression, but tell that a "safe" object doesn't have
 142    * to be returned.  The default implementation just calls execute(xctxt).
 143    *
 144    *
 145    * @param xctxt The XPath runtime context.
 146    * @param destructiveOK true if a "safe" object doesn't need to be returned.
 147    *
 148    * @return The result of the expression in the form of a <code>XObject</code>.
 149    *
 150    * @throws javax.xml.transform.TransformerException if a runtime exception
 151    *         occurs.
 152    */
 153   public XObject execute(XPathContext xctxt, boolean destructiveOK)
 154     throws javax.xml.transform.TransformerException
 155   {
 156         return execute(xctxt);
 157   }
 158 
 159 
 160   /**
 161    * Evaluate expression to a number.
 162    *
 163    *
 164    * @param xctxt The XPath runtime context.
 165    * @return The expression evaluated as a double.
 166    *
 167    * @throws javax.xml.transform.TransformerException
 168    */
 169   public double num(XPathContext xctxt)
 170           throws javax.xml.transform.TransformerException
 171   {
 172     return execute(xctxt).num();
 173   }
 174 
 175   /**
 176    * Evaluate expression to a boolean.
 177    *
 178    *
 179    * @param xctxt The XPath runtime context.
 180    * @return false
 181    *
 182    * @throws javax.xml.transform.TransformerException
 183    */
 184   public boolean bool(XPathContext xctxt)
 185           throws javax.xml.transform.TransformerException
 186   {
 187     return execute(xctxt).bool();
 188   }
 189 
 190   /**
 191    * Cast result object to a string.
 192    *
 193    *
 194    * @param xctxt The XPath runtime context.
 195    * @return The string this wraps or the empty string if null
 196    *
 197    * @throws javax.xml.transform.TransformerException
 198    */
 199   public XMLString xstr(XPathContext xctxt)
 200           throws javax.xml.transform.TransformerException
 201   {
 202     return execute(xctxt).xstr();
 203   }
 204 
 205   /**
 206    * Tell if the expression is a nodeset expression.  In other words, tell
 207    * if you can execute {@link #asNode(XPathContext) asNode} without an exception.
 208    * @return true if the expression can be represented as a nodeset.
 209    */
 210   public boolean isNodesetExpr()
 211   {
 212     return false;
 213   }
 214 
 215   /**
 216    * Return the first node out of the nodeset, if this expression is
 217    * a nodeset expression.
 218    * @param xctxt The XPath runtime context.
 219    * @return the first node out of the nodeset, or DTM.NULL.
 220    *
 221    * @throws javax.xml.transform.TransformerException
 222    */
 223   public int asNode(XPathContext xctxt)
 224           throws javax.xml.transform.TransformerException
 225   {
 226         DTMIterator iter = execute(xctxt).iter();
 227     return iter.nextNode();
 228   }
 229 
 230   /**
 231    * Given an select expression and a context, evaluate the XPath
 232    * and return the resulting iterator.
 233    *
 234    * @param xctxt The execution context.
 235    * @param contextNode The node that "." expresses.
 236    *
 237    *
 238    * @return A valid DTMIterator.
 239    * @throws TransformerException thrown if the active ProblemListener decides
 240    * the error condition is severe enough to halt processing.
 241    *
 242    * @throws javax.xml.transform.TransformerException
 243    * @xsl.usage experimental
 244    */
 245   public DTMIterator asIterator(XPathContext xctxt, int contextNode)
 246           throws javax.xml.transform.TransformerException
 247   {
 248 
 249     try
 250     {
 251       xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
 252 
 253       return execute(xctxt).iter();
 254     }
 255     finally
 256     {
 257       xctxt.popCurrentNodeAndExpression();
 258     }
 259   }
 260 
 261   /**
 262    * Given an select expression and a context, evaluate the XPath
 263    * and return the resulting iterator, but do not clone.
 264    *
 265    * @param xctxt The execution context.
 266    * @param contextNode The node that "." expresses.
 267    *
 268    *
 269    * @return A valid DTMIterator.
 270    * @throws TransformerException thrown if the active ProblemListener decides
 271    * the error condition is severe enough to halt processing.
 272    *
 273    * @throws javax.xml.transform.TransformerException
 274    * @xsl.usage experimental
 275    */
 276   public DTMIterator asIteratorRaw(XPathContext xctxt, int contextNode)
 277           throws javax.xml.transform.TransformerException
 278   {
 279 
 280     try
 281     {
 282       xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
 283 
 284       XNodeSet nodeset = (XNodeSet)execute(xctxt);
 285       return nodeset.iterRaw();
 286     }
 287     finally
 288     {
 289       xctxt.popCurrentNodeAndExpression();
 290     }
 291   }
 292 
 293 
 294   /**
 295    * Execute an expression in the XPath runtime context, and return the
 296    * result of the expression.
 297    *
 298    *
 299    * @param xctxt The XPath runtime context.
 300    * NEEDSDOC @param handler
 301    *
 302    * @return The result of the expression in the form of a <code>XObject</code>.
 303    *
 304    * @throws javax.xml.transform.TransformerException if a runtime exception
 305    *         occurs.
 306    * @throws org.xml.sax.SAXException
 307    */
 308   public void executeCharsToContentHandler(
 309           XPathContext xctxt, ContentHandler handler)
 310             throws javax.xml.transform.TransformerException,
 311                    org.xml.sax.SAXException
 312   {
 313 
 314     XObject obj = execute(xctxt);
 315 
 316     obj.dispatchCharactersEvents(handler);
 317     obj.detach();
 318   }
 319 
 320   /**
 321    * Tell if this expression returns a stable number that will not change during
 322    * iterations within the expression.  This is used to determine if a proximity
 323    * position predicate can indicate that no more searching has to occur.
 324    *
 325    *
 326    * @return true if the expression represents a stable number.
 327    */
 328   public boolean isStableNumber()
 329   {
 330     return false;
 331   }
 332 
 333   /**
 334    * This function is used to fixup variables from QNames to stack frame
 335    * indexes at stylesheet build time.
 336    * @param vars List of QNames that correspond to variables.  This list
 337    * should be searched backwards for the first qualified name that
 338    * corresponds to the variable reference qname.  The position of the
 339    * QName in the vector from the start of the vector will be its position
 340    * in the stack frame (but variables above the globalsTop value will need
 341    * to be offset to the current stack frame).
 342    * NEEDSDOC @param globalsSize
 343    */
 344   public abstract void fixupVariables(List<QName> vars, int globalsSize);
 345 
 346   /**
 347    * Compare this object with another object and see
 348    * if they are equal, include the sub heararchy.
 349    *
 350    * @param expr Another expression object.
 351    * @return true if this objects class and the expr
 352    * object's class are the same, and the data contained
 353    * within both objects are considered equal.
 354    */
 355   public abstract boolean deepEquals(Expression expr);
 356 
 357   /**
 358    * This is a utility method to tell if the passed in
 359    * class is the same class as this.  It is to be used by
 360    * the deepEquals method.  I'm bottlenecking it here
 361    * because I'm not totally confident that comparing the
 362    * class objects is the best way to do this.
 363    * @return true of the passed in class is the exact same
 364    * class as this class.
 365    */
 366   protected final boolean isSameClass(Expression expr)
 367   {
 368         if(null == expr)
 369           return false;
 370 
 371         return (getClass() == expr.getClass());
 372   }
 373 
 374   /**
 375    * Warn the user of an problem.
 376    *
 377    * @param xctxt The XPath runtime context.
 378    * @param msg An error msgkey that corresponds to one of the conststants found
 379    *            in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
 380    *            a key for a format string.
 381    * @param args An array of arguments represented in the format string, which
 382    *             may be null.
 383    *
 384    * @throws TransformerException if the current ErrorListoner determines to
 385    *                              throw an exception.
 386    *
 387    * @throws javax.xml.transform.TransformerException
 388    */
 389   public void warn(XPathContext xctxt, String msg, Object[] args)
 390           throws javax.xml.transform.TransformerException
 391   {
 392 
 393     java.lang.String fmsg = XSLMessages.createXPATHWarning(msg, args);
 394 
 395     if (null != xctxt)
 396     {
 397       ErrorListener eh = xctxt.getErrorListener();
 398 
 399       // TO DO: Need to get stylesheet Locator from here.
 400       eh.warning(new TransformerException(fmsg, xctxt.getSAXLocator()));
 401     }
 402   }
 403 
 404   /**
 405    * Tell the user of an assertion error, and probably throw an
 406    * exception.
 407    *
 408    * @param b  If false, a runtime exception will be thrown.
 409    * @param msg The assertion message, which should be informative.
 410    *
 411    * @throws RuntimeException if the b argument is false.
 412    *
 413    * @throws javax.xml.transform.TransformerException
 414    */
 415   public void assertion(boolean b, java.lang.String msg)
 416   {
 417 
 418     if (!b)
 419     {
 420       java.lang.String fMsg = XSLMessages.createXPATHMessage(
 421         XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
 422         new Object[]{ msg });
 423 
 424       throw new RuntimeException(fMsg);
 425     }
 426   }
 427 
 428   /**
 429    * Tell the user of an error, and probably throw an
 430    * exception.
 431    *
 432    * @param xctxt The XPath runtime context.
 433    * @param msg An error msgkey that corresponds to one of the constants found
 434    *            in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
 435    *            a key for a format string.
 436    * @param args An array of arguments represented in the format string, which
 437    *             may be null.
 438    *
 439    * @throws TransformerException if the current ErrorListoner determines to
 440    *                              throw an exception.
 441    *
 442    * @throws javax.xml.transform.TransformerException
 443    */
 444   public void error(XPathContext xctxt, String msg, Object[] args)
 445           throws javax.xml.transform.TransformerException
 446   {
 447 
 448     java.lang.String fmsg = XSLMessages.createXPATHMessage(msg, args);
 449 
 450     if (null != xctxt)
 451     {
 452       ErrorListener eh = xctxt.getErrorListener();
 453       TransformerException te = new TransformerException(fmsg, this);
 454 
 455       eh.fatalError(te);
 456     }
 457   }
 458 
 459   /**
 460    * Get the first non-Expression parent of this node.
 461    * @return null or first ancestor that is not an Expression.
 462    */
 463   public ExpressionNode getExpressionOwner()
 464   {
 465         ExpressionNode parent = exprGetParent();
 466         while((null != parent) && (parent instanceof Expression))
 467                 parent = parent.exprGetParent();
 468         return parent;
 469   }
 470 
 471   //=============== ExpressionNode methods ================
 472 
 473   /** This pair of methods are used to inform the node of its
 474     parent. */
 475   public void exprSetParent(ExpressionNode n)
 476   {
 477         assertion(n != this, "Can not parent an expression to itself!");
 478         m_parent = n;
 479   }
 480 
 481   public ExpressionNode exprGetParent()
 482   {
 483         return m_parent;
 484   }
 485 
 486   /** This method tells the node to add its argument to the node's
 487     list of children.  */
 488   public void exprAddChild(ExpressionNode n, int i)
 489   {
 490         assertion(false, "exprAddChild method not implemented!");
 491   }
 492 
 493   /** This method returns a child node.  The children are numbered
 494      from zero, left to right. */
 495   public ExpressionNode exprGetChild(int i)
 496   {
 497         return null;
 498   }
 499 
 500   /** Return the number of children the node has. */
 501   public int exprGetNumChildren()
 502   {
 503         return 0;
 504   }
 505 
 506   //=============== SourceLocator methods ================
 507 
 508   /**
 509    * Return the public identifier for the current document event.
 510    *
 511    * <p>The return value is the public identifier of the document
 512    * entity or of the external parsed entity in which the markup that
 513    * triggered the event appears.</p>
 514    *
 515    * @return A string containing the public identifier, or
 516    *         null if none is available.
 517    * @see #getSystemId
 518    */
 519   public String getPublicId()
 520   {
 521         if(null == m_parent)
 522           return null;
 523         return m_parent.getPublicId();
 524   }
 525 
 526   /**
 527    * Return the system identifier for the current document event.
 528    *
 529    * <p>The return value is the system identifier of the document
 530    * entity or of the external parsed entity in which the markup that
 531    * triggered the event appears.</p>
 532    *
 533    * <p>If the system identifier is a URL, the parser must resolve it
 534    * fully before passing it to the application.</p>
 535    *
 536    * @return A string containing the system identifier, or null
 537    *         if none is available.
 538    * @see #getPublicId
 539    */
 540   public String getSystemId()
 541   {
 542         if(null == m_parent)
 543           return null;
 544         return m_parent.getSystemId();
 545   }
 546 
 547   /**
 548    * Return the line number where the current document event ends.
 549    *
 550    * <p><strong>Warning:</strong> The return value from the method
 551    * is intended only as an approximation for the sake of error
 552    * reporting; it is not intended to provide sufficient information
 553    * to edit the character content of the original XML document.</p>
 554    *
 555    * <p>The return value is an approximation of the line number
 556    * in the document entity or external parsed entity where the
 557    * markup that triggered the event appears.</p>
 558    *
 559    * @return The line number, or -1 if none is available.
 560    * @see #getColumnNumber
 561    */
 562   public int getLineNumber()
 563   {
 564         if(null == m_parent)
 565           return 0;
 566         return m_parent.getLineNumber();
 567   }
 568 
 569   /**
 570    * Return the character position where the current document event ends.
 571    *
 572    * <p><strong>Warning:</strong> The return value from the method
 573    * is intended only as an approximation for the sake of error
 574    * reporting; it is not intended to provide sufficient information
 575    * to edit the character content of the original XML document.</p>
 576    *
 577    * <p>The return value is an approximation of the column number
 578    * in the document entity or external parsed entity where the
 579    * markup that triggered the event appears.</p>
 580    *
 581    * @return The column number, or -1 if none is available.
 582    * @see #getLineNumber
 583    */
 584   public int getColumnNumber()
 585   {
 586         if(null == m_parent)
 587           return 0;
 588         return m_parent.getColumnNumber();
 589   }
 590 }