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;
  22 
  23 import com.sun.org.apache.xalan.internal.res.XSLMessages;
  24 import com.sun.org.apache.xml.internal.dtm.DTM;
  25 import com.sun.org.apache.xml.internal.utils.PrefixResolver;
  26 import com.sun.org.apache.xml.internal.utils.QName;
  27 import com.sun.org.apache.xml.internal.utils.SAXSourceLocator;
  28 import com.sun.org.apache.xpath.internal.compiler.Compiler;
  29 import com.sun.org.apache.xpath.internal.compiler.FunctionTable;
  30 import com.sun.org.apache.xpath.internal.compiler.XPathParser;
  31 import com.sun.org.apache.xpath.internal.objects.XObject;
  32 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
  33 import java.io.Serializable;
  34 import java.util.List;
  35 import javax.xml.transform.ErrorListener;
  36 import javax.xml.transform.SourceLocator;
  37 import javax.xml.transform.TransformerException;
  38 
  39 /**
  40  * The XPath class wraps an expression object and provides general services
  41  * for execution of that expression.
  42  * @xsl.usage advanced
  43  * @LastModified: Oct 2017
  44  */
  45 public class XPath implements Serializable, ExpressionOwner
  46 {
  47     static final long serialVersionUID = 3976493477939110553L;
  48 
  49   /** The top of the expression tree.
  50    *  @serial */
  51   private Expression m_mainExp;
  52 
  53   /**
  54    * The function table for xpath build-in functions
  55    */
  56   private transient FunctionTable m_funcTable = null;
  57 
  58   /**
  59    * initial the function table
  60    */
  61   private void initFunctionTable(){
  62               m_funcTable = new FunctionTable();
  63   }
  64 
  65   /**
  66    * Get the raw Expression object that this class wraps.
  67    *
  68    *
  69    * @return the raw Expression object, which should not normally be null.
  70    */
  71   public Expression getExpression()
  72   {
  73     return m_mainExp;
  74   }
  75 
  76   /**
  77    * This function is used to fixup variables from QNames to stack frame
  78    * indexes at stylesheet build time.
  79    * @param vars List of QNames that correspond to variables.  This list
  80    * should be searched backwards for the first qualified name that
  81    * corresponds to the variable reference qname.  The position of the
  82    * QName in the vector from the start of the vector will be its position
  83    * in the stack frame (but variables above the globalsTop value will need
  84    * to be offset to the current stack frame).
  85    */
  86   public void fixupVariables(List<QName> vars, int globalsSize)
  87   {
  88     m_mainExp.fixupVariables(vars, globalsSize);
  89   }
  90 
  91   /**
  92    * Set the raw expression object for this object.
  93    *
  94    *
  95    * @param exp the raw Expression object, which should not normally be null.
  96    */
  97   public void setExpression(Expression exp)
  98   {
  99         if(null != m_mainExp)
 100         exp.exprSetParent(m_mainExp.exprGetParent()); // a bit bogus
 101     m_mainExp = exp;
 102   }
 103 
 104   /**
 105    * Get the SourceLocator on the expression object.
 106    *
 107    *
 108    * @return the SourceLocator on the expression object, which may be null.
 109    */
 110   public SourceLocator getLocator()
 111   {
 112     return m_mainExp;
 113   }
 114 
 115 //  /**
 116 //   * Set the SourceLocator on the expression object.
 117 //   *
 118 //   *
 119 //   * @param l the SourceLocator on the expression object, which may be null.
 120 //   */
 121 //  public void setLocator(SourceLocator l)
 122 //  {
 123 //    // Note potential hazards -- l may not be serializable, or may be changed
 124 //      // after being assigned here.
 125 //    m_mainExp.setSourceLocator(l);
 126 //  }
 127 
 128   /** The pattern string, mainly kept around for diagnostic purposes.
 129    *  @serial  */
 130   String m_patternString;
 131 
 132   /**
 133    * Return the XPath string associated with this object.
 134    *
 135    *
 136    * @return the XPath string associated with this object.
 137    */
 138   public String getPatternString()
 139   {
 140     return m_patternString;
 141   }
 142 
 143   /** Represents a select type expression. */
 144   public static final int SELECT = 0;
 145 
 146   /** Represents a match type expression.  */
 147   public static final int MATCH = 1;
 148 
 149   /**
 150    * Construct an XPath object.
 151    *
 152    * (Needs review -sc) This method initializes an XPathParser/
 153    * Compiler and compiles the expression.
 154    * @param exprString The XPath expression.
 155    * @param locator The location of the expression, may be null.
 156    * @param prefixResolver A prefix resolver to use to resolve prefixes to
 157    *                       namespace URIs.
 158    * @param type one of {@link #SELECT} or {@link #MATCH}.
 159    * @param errorListener The error listener, or null if default should be used.
 160    *
 161    * @throws javax.xml.transform.TransformerException if syntax or other error.
 162    */
 163   public XPath(
 164           String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type,
 165           ErrorListener errorListener)
 166             throws javax.xml.transform.TransformerException
 167   {
 168     initFunctionTable();
 169     if(null == errorListener)
 170       errorListener = new com.sun.org.apache.xml.internal.utils.DefaultErrorHandler();
 171 
 172     m_patternString = exprString;
 173 
 174     XPathParser parser = new XPathParser(errorListener, locator);
 175     Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
 176 
 177     if (SELECT == type)
 178       parser.initXPath(compiler, exprString, prefixResolver);
 179     else if (MATCH == type)
 180       parser.initMatchPattern(compiler, exprString, prefixResolver);
 181     else
 182       throw new RuntimeException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, new Object[]{Integer.toString(type)})); //"Can not deal with XPath type: " + type);
 183 
 184     // System.out.println("----------------");
 185     Expression expr = compiler.compile(0);
 186 
 187     // System.out.println("expr: "+expr);
 188     this.setExpression(expr);
 189 
 190     if((null != locator) && locator instanceof ExpressionNode)
 191     {
 192         expr.exprSetParent((ExpressionNode)locator);
 193     }
 194 
 195   }
 196 
 197   /**
 198    * Construct an XPath object.
 199    *
 200    * (Needs review -sc) This method initializes an XPathParser/
 201    * Compiler and compiles the expression.
 202    * @param exprString The XPath expression.
 203    * @param locator The location of the expression, may be null.
 204    * @param prefixResolver A prefix resolver to use to resolve prefixes to
 205    *                       namespace URIs.
 206    * @param type one of {@link #SELECT} or {@link #MATCH}.
 207    * @param errorListener The error listener, or null if default should be used.
 208    *
 209    * @throws javax.xml.transform.TransformerException if syntax or other error.
 210    */
 211   public XPath(
 212           String exprString, SourceLocator locator,
 213           PrefixResolver prefixResolver, int type,
 214           ErrorListener errorListener, FunctionTable aTable)
 215             throws javax.xml.transform.TransformerException
 216   {
 217     m_funcTable = aTable;
 218     if(null == errorListener)
 219       errorListener = new com.sun.org.apache.xml.internal.utils.DefaultErrorHandler();
 220 
 221     m_patternString = exprString;
 222 
 223     XPathParser parser = new XPathParser(errorListener, locator);
 224     Compiler compiler = new Compiler(errorListener, locator, m_funcTable);
 225 
 226     if (SELECT == type)
 227       parser.initXPath(compiler, exprString, prefixResolver);
 228     else if (MATCH == type)
 229       parser.initMatchPattern(compiler, exprString, prefixResolver);
 230     else
 231       throw new RuntimeException(XSLMessages.createXPATHMessage(
 232             XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE,
 233             new Object[]{Integer.toString(type)}));
 234             //"Can not deal with XPath type: " + type);
 235 
 236     // System.out.println("----------------");
 237     Expression expr = compiler.compile(0);
 238 
 239     // System.out.println("expr: "+expr);
 240     this.setExpression(expr);
 241 
 242     if((null != locator) && locator instanceof ExpressionNode)
 243     {
 244         expr.exprSetParent((ExpressionNode)locator);
 245     }
 246 
 247   }
 248 
 249   /**
 250    * Construct an XPath object.
 251    *
 252    * (Needs review -sc) This method initializes an XPathParser/
 253    * Compiler and compiles the expression.
 254    * @param exprString The XPath expression.
 255    * @param locator The location of the expression, may be null.
 256    * @param prefixResolver A prefix resolver to use to resolve prefixes to
 257    *                       namespace URIs.
 258    * @param type one of {@link #SELECT} or {@link #MATCH}.
 259    *
 260    * @throws javax.xml.transform.TransformerException if syntax or other error.
 261    */
 262   public XPath(
 263           String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type)
 264             throws javax.xml.transform.TransformerException
 265   {
 266     this(exprString, locator, prefixResolver, type, null);
 267   }
 268 
 269   /**
 270    * Construct an XPath object.
 271    *
 272    * @param expr The Expression object.
 273    *
 274    * @throws javax.xml.transform.TransformerException if syntax or other error.
 275    */
 276   public XPath(Expression expr)
 277   {
 278     this.setExpression(expr);
 279     initFunctionTable();
 280   }
 281 
 282   /**
 283    * Given an expression and a context, evaluate the XPath
 284    * and return the result.
 285    *
 286    * @param xctxt The execution context.
 287    * @param contextNode The node that "." expresses.
 288    * @param namespaceContext The context in which namespaces in the
 289    * XPath are supposed to be expanded.
 290    *
 291    * @return The result of the XPath or null if callbacks are used.
 292    * @throws TransformerException thrown if
 293    * the error condition is severe enough to halt processing.
 294    *
 295    * @throws javax.xml.transform.TransformerException
 296    * @xsl.usage experimental
 297    */
 298   public XObject execute(
 299           XPathContext xctxt, org.w3c.dom.Node contextNode,
 300           PrefixResolver namespaceContext)
 301             throws javax.xml.transform.TransformerException
 302   {
 303     return execute(
 304           xctxt, xctxt.getDTMHandleFromNode(contextNode),
 305           namespaceContext);
 306   }
 307 
 308 
 309   /**
 310    * Given an expression and a context, evaluate the XPath
 311    * and return the result.
 312    *
 313    * @param xctxt The execution context.
 314    * @param contextNode The node that "." expresses.
 315    * @param namespaceContext The context in which namespaces in the
 316    * XPath are supposed to be expanded.
 317    *
 318    * @throws TransformerException thrown if the active ProblemListener decides
 319    * the error condition is severe enough to halt processing.
 320    *
 321    * @throws javax.xml.transform.TransformerException
 322    * @xsl.usage experimental
 323    */
 324   public XObject execute(
 325           XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)
 326             throws javax.xml.transform.TransformerException
 327   {
 328 
 329     xctxt.pushNamespaceContext(namespaceContext);
 330 
 331     xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
 332 
 333     XObject xobj = null;
 334 
 335     try
 336     {
 337       xobj = m_mainExp.execute(xctxt);
 338     }
 339     catch (TransformerException te)
 340     {
 341       te.setLocator(this.getLocator());
 342       ErrorListener el = xctxt.getErrorListener();
 343       if(null != el) // defensive, should never happen.
 344       {
 345         el.error(te);
 346       }
 347       else
 348         throw te;
 349     }
 350     catch (Exception e)
 351     {
 352       while (e instanceof com.sun.org.apache.xml.internal.utils.WrappedRuntimeException)
 353       {
 354         e = ((com.sun.org.apache.xml.internal.utils.WrappedRuntimeException) e).getException();
 355       }
 356       // e.printStackTrace();
 357 
 358       String msg = e.getMessage();
 359 
 360       if (msg == null || msg.length() == 0) {
 361            msg = XSLMessages.createXPATHMessage(
 362                XPATHErrorResources.ER_XPATH_ERROR, null);
 363 
 364       }
 365       TransformerException te = new TransformerException(msg,
 366               getLocator(), e);
 367       ErrorListener el = xctxt.getErrorListener();
 368       // te.printStackTrace();
 369       if(null != el) // defensive, should never happen.
 370       {
 371         el.fatalError(te);
 372       }
 373       else
 374         throw te;
 375     }
 376     finally
 377     {
 378       xctxt.popNamespaceContext();
 379 
 380       xctxt.popCurrentNodeAndExpression();
 381     }
 382 
 383     return xobj;
 384   }
 385 
 386   /**
 387    * Given an expression and a context, evaluate the XPath
 388    * and return the result.
 389    *
 390    * @param xctxt The execution context.
 391    * @param contextNode The node that "." expresses.
 392    * @param namespaceContext The context in which namespaces in the
 393    * XPath are supposed to be expanded.
 394    *
 395    * @throws TransformerException thrown if the active ProblemListener decides
 396    * the error condition is severe enough to halt processing.
 397    *
 398    * @throws javax.xml.transform.TransformerException
 399    * @xsl.usage experimental
 400    */
 401   public boolean bool(
 402           XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)
 403             throws javax.xml.transform.TransformerException
 404   {
 405 
 406     xctxt.pushNamespaceContext(namespaceContext);
 407 
 408     xctxt.pushCurrentNodeAndExpression(contextNode, contextNode);
 409 
 410     try
 411     {
 412       return m_mainExp.bool(xctxt);
 413     }
 414     catch (TransformerException te)
 415     {
 416       te.setLocator(this.getLocator());
 417       ErrorListener el = xctxt.getErrorListener();
 418       if(null != el) // defensive, should never happen.
 419       {
 420         el.error(te);
 421       }
 422       else
 423         throw te;
 424     }
 425     catch (Exception e)
 426     {
 427       while (e instanceof com.sun.org.apache.xml.internal.utils.WrappedRuntimeException)
 428       {
 429         e = ((com.sun.org.apache.xml.internal.utils.WrappedRuntimeException) e).getException();
 430       }
 431       // e.printStackTrace();
 432 
 433       String msg = e.getMessage();
 434 
 435       if (msg == null || msg.length() == 0) {
 436            msg = XSLMessages.createXPATHMessage(
 437                XPATHErrorResources.ER_XPATH_ERROR, null);
 438 
 439       }
 440 
 441       TransformerException te = new TransformerException(msg,
 442               getLocator(), e);
 443       ErrorListener el = xctxt.getErrorListener();
 444       // te.printStackTrace();
 445       if(null != el) // defensive, should never happen.
 446       {
 447         el.fatalError(te);
 448       }
 449       else
 450         throw te;
 451     }
 452     finally
 453     {
 454       xctxt.popNamespaceContext();
 455 
 456       xctxt.popCurrentNodeAndExpression();
 457     }
 458 
 459     return false;
 460   }
 461 
 462   /** Set to true to get diagnostic messages about the result of
 463    *  match pattern testing.  */
 464   private static final boolean DEBUG_MATCHES = false;
 465 
 466   /**
 467    * Get the match score of the given node.
 468    *
 469    * @param xctxt XPath runtime context.
 470    * @param context The current source tree context node.
 471    *
 472    * @return score, one of {@link #MATCH_SCORE_NODETEST},
 473    * {@link #MATCH_SCORE_NONE}, {@link #MATCH_SCORE_OTHER},
 474    * or {@link #MATCH_SCORE_QNAME}.
 475    *
 476    * @throws javax.xml.transform.TransformerException
 477    */
 478   public double getMatchScore(XPathContext xctxt, int context)
 479           throws javax.xml.transform.TransformerException
 480   {
 481 
 482     xctxt.pushCurrentNode(context);
 483     xctxt.pushCurrentExpressionNode(context);
 484 
 485     try
 486     {
 487       XObject score = m_mainExp.execute(xctxt);
 488 
 489       if (DEBUG_MATCHES)
 490       {
 491         DTM dtm = xctxt.getDTM(context);
 492         System.out.println("score: " + score.num() + " for "
 493                            + dtm.getNodeName(context) + " for xpath "
 494                            + this.getPatternString());
 495       }
 496 
 497       return score.num();
 498     }
 499     finally
 500     {
 501       xctxt.popCurrentNode();
 502       xctxt.popCurrentExpressionNode();
 503     }
 504 
 505     // return XPath.MATCH_SCORE_NONE;
 506   }
 507 
 508 
 509   /**
 510    * Warn the user of an problem.
 511    *
 512    * @param xctxt The XPath runtime context.
 513    * @param sourceNode Not used.
 514    * @param msg An error msgkey that corresponds to one of the constants found
 515    *            in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
 516    *            a key for a format string.
 517    * @param args An array of arguments represented in the format string, which
 518    *             may be null.
 519    *
 520    * @throws TransformerException if the current ErrorListoner determines to
 521    *                              throw an exception.
 522    */
 523   public void warn(
 524           XPathContext xctxt, int sourceNode, String msg, Object[] args)
 525             throws javax.xml.transform.TransformerException
 526   {
 527 
 528     String fmsg = XSLMessages.createXPATHWarning(msg, args);
 529     ErrorListener ehandler = xctxt.getErrorListener();
 530 
 531     if (null != ehandler)
 532     {
 533 
 534       // TO DO: Need to get stylesheet Locator from here.
 535       ehandler.warning(new TransformerException(fmsg, (SAXSourceLocator)xctxt.getSAXLocator()));
 536     }
 537   }
 538 
 539   /**
 540    * Tell the user of an assertion error, and probably throw an
 541    * exception.
 542    *
 543    * @param b  If false, a runtime exception will be thrown.
 544    * @param msg The assertion message, which should be informative.
 545    *
 546    * @throws RuntimeException if the b argument is false.
 547    */
 548   public void assertion(boolean b, String msg)
 549   {
 550 
 551     if (!b)
 552     {
 553       String fMsg = XSLMessages.createXPATHMessage(
 554         XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION,
 555         new Object[]{ msg });
 556 
 557       throw new RuntimeException(fMsg);
 558     }
 559   }
 560 
 561   /**
 562    * Tell the user of an error, and probably throw an
 563    * exception.
 564    *
 565    * @param xctxt The XPath runtime context.
 566    * @param sourceNode Not used.
 567    * @param msg An error msgkey that corresponds to one of the constants found
 568    *            in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is
 569    *            a key for a format string.
 570    * @param args An array of arguments represented in the format string, which
 571    *             may be null.
 572    *
 573    * @throws TransformerException if the current ErrorListoner determines to
 574    *                              throw an exception.
 575    */
 576   public void error(
 577           XPathContext xctxt, int sourceNode, String msg, Object[] args)
 578             throws javax.xml.transform.TransformerException
 579   {
 580 
 581     String fmsg = XSLMessages.createXPATHMessage(msg, args);
 582     ErrorListener ehandler = xctxt.getErrorListener();
 583 
 584     if (null != ehandler)
 585     {
 586       ehandler.fatalError(new TransformerException(fmsg,
 587                               (SAXSourceLocator)xctxt.getSAXLocator()));
 588     }
 589     else
 590     {
 591       SourceLocator slocator = xctxt.getSAXLocator();
 592       System.out.println(fmsg + "; file " + slocator.getSystemId()
 593                          + "; line " + slocator.getLineNumber() + "; column "
 594                          + slocator.getColumnNumber());
 595     }
 596   }
 597 
 598   /**
 599    * This will traverse the heararchy, calling the visitor for
 600    * each member.  If the called visitor method returns
 601    * false, the subtree should not be called.
 602    *
 603    * @param owner The owner of the visitor, where that path may be
 604    *              rewritten if needed.
 605    * @param visitor The visitor whose appropriate method will be called.
 606    */
 607   public void callVisitors(ExpressionOwner owner, XPathVisitor visitor)
 608   {
 609         m_mainExp.callVisitors(this, visitor);
 610   }
 611 
 612   /**
 613    * The match score if no match is made.
 614    * @xsl.usage advanced
 615    */
 616   public static final double MATCH_SCORE_NONE = Double.NEGATIVE_INFINITY;
 617 
 618   /**
 619    * The match score if the pattern has the form
 620    * of a QName optionally preceded by an @ character.
 621    * @xsl.usage advanced
 622    */
 623   public static final double MATCH_SCORE_QNAME = 0.0;
 624 
 625   /**
 626    * The match score if the pattern pattern has the form NCName:*.
 627    * @xsl.usage advanced
 628    */
 629   public static final double MATCH_SCORE_NSWILD = -0.25;
 630 
 631   /**
 632    * The match score if the pattern consists of just a NodeTest.
 633    * @xsl.usage advanced
 634    */
 635   public static final double MATCH_SCORE_NODETEST = -0.5;
 636 
 637   /**
 638    * The match score if the pattern consists of something
 639    * other than just a NodeTest or just a qname.
 640    * @xsl.usage advanced
 641    */
 642   public static final double MATCH_SCORE_OTHER = 0.5;
 643 }