1 /* 2 * Copyright (c) 2017, 2019, 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: May 2019 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 */ initFunctionTable()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 */ getExpression()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 */ fixupVariables(List<QName> vars, int globalsSize)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 */ setExpression(Expression exp)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 */ getLocator()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 */ getPatternString()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 */ XPath( String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type, ErrorListener errorListener)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( 183 XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, 184 new Object[]{Integer.toString(type)})); 185 186 // System.out.println("----------------"); 187 Expression expr = compiler.compileExpression(0); 188 189 // System.out.println("expr: "+expr); 190 this.setExpression(expr); 191 192 if((null != locator) && locator instanceof ExpressionNode) 193 { 194 expr.exprSetParent((ExpressionNode)locator); 195 } 196 197 } 198 199 /** 200 * Construct an XPath object. 201 * 202 * (Needs review -sc) This method initializes an XPathParser/ 203 * Compiler and compiles the expression. 204 * @param exprString The XPath expression. 205 * @param locator The location of the expression, may be null. 206 * @param prefixResolver A prefix resolver to use to resolve prefixes to 207 * namespace URIs. 208 * @param type one of {@link #SELECT} or {@link #MATCH}. 209 * @param errorListener The error listener, or null if default should be used. 210 * 211 * @throws javax.xml.transform.TransformerException if syntax or other error. 212 */ XPath( String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type, ErrorListener errorListener, FunctionTable aTable)213 public XPath( 214 String exprString, SourceLocator locator, 215 PrefixResolver prefixResolver, int type, 216 ErrorListener errorListener, FunctionTable aTable) 217 throws javax.xml.transform.TransformerException 218 { 219 m_funcTable = aTable; 220 if(null == errorListener) 221 errorListener = new com.sun.org.apache.xml.internal.utils.DefaultErrorHandler(); 222 223 m_patternString = exprString; 224 225 XPathParser parser = new XPathParser(errorListener, locator); 226 Compiler compiler = new Compiler(errorListener, locator, m_funcTable); 227 228 if (SELECT == type) 229 parser.initXPath(compiler, exprString, prefixResolver); 230 else if (MATCH == type) 231 parser.initMatchPattern(compiler, exprString, prefixResolver); 232 else 233 throw new RuntimeException(XSLMessages.createXPATHMessage( 234 XPATHErrorResources.ER_CANNOT_DEAL_XPATH_TYPE, 235 new Object[]{Integer.toString(type)})); 236 //"Can not deal with XPath type: " + type); 237 238 // System.out.println("----------------"); 239 Expression expr = compiler.compileExpression(0); 240 241 // System.out.println("expr: "+expr); 242 this.setExpression(expr); 243 244 if((null != locator) && locator instanceof ExpressionNode) 245 { 246 expr.exprSetParent((ExpressionNode)locator); 247 } 248 249 } 250 251 /** 252 * Construct an XPath object. 253 * 254 * (Needs review -sc) This method initializes an XPathParser/ 255 * Compiler and compiles the expression. 256 * @param exprString The XPath expression. 257 * @param locator The location of the expression, may be null. 258 * @param prefixResolver A prefix resolver to use to resolve prefixes to 259 * namespace URIs. 260 * @param type one of {@link #SELECT} or {@link #MATCH}. 261 * 262 * @throws javax.xml.transform.TransformerException if syntax or other error. 263 */ XPath( String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type)264 public XPath( 265 String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type) 266 throws javax.xml.transform.TransformerException 267 { 268 this(exprString, locator, prefixResolver, type, null); 269 } 270 271 /** 272 * Construct an XPath object. 273 * 274 * @param expr The Expression object. 275 * 276 * @throws javax.xml.transform.TransformerException if syntax or other error. 277 */ XPath(Expression expr)278 public XPath(Expression expr) 279 { 280 this.setExpression(expr); 281 initFunctionTable(); 282 } 283 284 /** 285 * Given an expression and a context, evaluate the XPath 286 * and return the result. 287 * 288 * @param xctxt The execution context. 289 * @param contextNode The node that "." expresses. 290 * @param namespaceContext The context in which namespaces in the 291 * XPath are supposed to be expanded. 292 * 293 * @return The result of the XPath or null if callbacks are used. 294 * @throws TransformerException thrown if 295 * the error condition is severe enough to halt processing. 296 * 297 * @throws javax.xml.transform.TransformerException 298 * @xsl.usage experimental 299 */ execute( XPathContext xctxt, org.w3c.dom.Node contextNode, PrefixResolver namespaceContext)300 public XObject execute( 301 XPathContext xctxt, org.w3c.dom.Node contextNode, 302 PrefixResolver namespaceContext) 303 throws javax.xml.transform.TransformerException 304 { 305 return execute( 306 xctxt, xctxt.getDTMHandleFromNode(contextNode), 307 namespaceContext); 308 } 309 310 311 /** 312 * Given an expression and a context, evaluate the XPath 313 * and return the result. 314 * 315 * @param xctxt The execution context. 316 * @param contextNode The node that "." expresses. 317 * @param namespaceContext The context in which namespaces in the 318 * XPath are supposed to be expanded. 319 * 320 * @throws TransformerException thrown if the active ProblemListener decides 321 * the error condition is severe enough to halt processing. 322 * 323 * @throws javax.xml.transform.TransformerException 324 * @xsl.usage experimental 325 */ execute( XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)326 public XObject execute( 327 XPathContext xctxt, int contextNode, PrefixResolver namespaceContext) 328 throws javax.xml.transform.TransformerException 329 { 330 331 xctxt.pushNamespaceContext(namespaceContext); 332 333 xctxt.pushCurrentNodeAndExpression(contextNode, contextNode); 334 335 XObject xobj = null; 336 337 try 338 { 339 xobj = m_mainExp.execute(xctxt); 340 } 341 catch (TransformerException te) 342 { 343 te.setLocator(this.getLocator()); 344 ErrorListener el = xctxt.getErrorListener(); 345 if(null != el) // defensive, should never happen. 346 { 347 el.error(te); 348 } 349 else 350 throw te; 351 } 352 catch (Exception e) 353 { 354 while (e instanceof com.sun.org.apache.xml.internal.utils.WrappedRuntimeException) 355 { 356 e = ((com.sun.org.apache.xml.internal.utils.WrappedRuntimeException) e).getException(); 357 } 358 // e.printStackTrace(); 359 360 String msg = e.getMessage(); 361 362 if (msg == null || msg.length() == 0) { 363 msg = XSLMessages.createXPATHMessage( 364 XPATHErrorResources.ER_XPATH_ERROR, null); 365 366 } 367 TransformerException te = new TransformerException(msg, 368 getLocator(), e); 369 ErrorListener el = xctxt.getErrorListener(); 370 // te.printStackTrace(); 371 if(null != el) // defensive, should never happen. 372 { 373 el.fatalError(te); 374 } 375 else 376 throw te; 377 } 378 finally 379 { 380 xctxt.popNamespaceContext(); 381 382 xctxt.popCurrentNodeAndExpression(); 383 } 384 385 return xobj; 386 } 387 388 /** 389 * Given an expression and a context, evaluate the XPath 390 * and return the result. 391 * 392 * @param xctxt The execution context. 393 * @param contextNode The node that "." expresses. 394 * @param namespaceContext The context in which namespaces in the 395 * XPath are supposed to be expanded. 396 * 397 * @throws TransformerException thrown if the active ProblemListener decides 398 * the error condition is severe enough to halt processing. 399 * 400 * @throws javax.xml.transform.TransformerException 401 * @xsl.usage experimental 402 */ bool( XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)403 public boolean bool( 404 XPathContext xctxt, int contextNode, PrefixResolver namespaceContext) 405 throws javax.xml.transform.TransformerException 406 { 407 408 xctxt.pushNamespaceContext(namespaceContext); 409 410 xctxt.pushCurrentNodeAndExpression(contextNode, contextNode); 411 412 try 413 { 414 return m_mainExp.bool(xctxt); 415 } 416 catch (TransformerException te) 417 { 418 te.setLocator(this.getLocator()); 419 ErrorListener el = xctxt.getErrorListener(); 420 if(null != el) // defensive, should never happen. 421 { 422 el.error(te); 423 } 424 else 425 throw te; 426 } 427 catch (Exception e) 428 { 429 while (e instanceof com.sun.org.apache.xml.internal.utils.WrappedRuntimeException) 430 { 431 e = ((com.sun.org.apache.xml.internal.utils.WrappedRuntimeException) e).getException(); 432 } 433 // e.printStackTrace(); 434 435 String msg = e.getMessage(); 436 437 if (msg == null || msg.length() == 0) { 438 msg = XSLMessages.createXPATHMessage( 439 XPATHErrorResources.ER_XPATH_ERROR, null); 440 441 } 442 443 TransformerException te = new TransformerException(msg, 444 getLocator(), e); 445 ErrorListener el = xctxt.getErrorListener(); 446 // te.printStackTrace(); 447 if(null != el) // defensive, should never happen. 448 { 449 el.fatalError(te); 450 } 451 else 452 throw te; 453 } 454 finally 455 { 456 xctxt.popNamespaceContext(); 457 458 xctxt.popCurrentNodeAndExpression(); 459 } 460 461 return false; 462 } 463 464 /** Set to true to get diagnostic messages about the result of 465 * match pattern testing. */ 466 private static final boolean DEBUG_MATCHES = false; 467 468 /** 469 * Get the match score of the given node. 470 * 471 * @param xctxt XPath runtime context. 472 * @param context The current source tree context node. 473 * 474 * @return score, one of {@link #MATCH_SCORE_NODETEST}, 475 * {@link #MATCH_SCORE_NONE}, {@link #MATCH_SCORE_OTHER}, 476 * or {@link #MATCH_SCORE_QNAME}. 477 * 478 * @throws javax.xml.transform.TransformerException 479 */ getMatchScore(XPathContext xctxt, int context)480 public double getMatchScore(XPathContext xctxt, int context) 481 throws javax.xml.transform.TransformerException 482 { 483 484 xctxt.pushCurrentNode(context); 485 xctxt.pushCurrentExpressionNode(context); 486 487 try 488 { 489 XObject score = m_mainExp.execute(xctxt); 490 491 if (DEBUG_MATCHES) 492 { 493 DTM dtm = xctxt.getDTM(context); 494 System.out.println("score: " + score.num() + " for " 495 + dtm.getNodeName(context) + " for xpath " 496 + this.getPatternString()); 497 } 498 499 return score.num(); 500 } 501 finally 502 { 503 xctxt.popCurrentNode(); 504 xctxt.popCurrentExpressionNode(); 505 } 506 507 // return XPath.MATCH_SCORE_NONE; 508 } 509 510 511 /** 512 * Warn the user of an problem. 513 * 514 * @param xctxt The XPath runtime context. 515 * @param sourceNode Not used. 516 * @param msg An error msgkey that corresponds to one of the constants found 517 * in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is 518 * a key for a format string. 519 * @param args An array of arguments represented in the format string, which 520 * may be null. 521 * 522 * @throws TransformerException if the current ErrorListoner determines to 523 * throw an exception. 524 */ warn( XPathContext xctxt, int sourceNode, String msg, Object[] args)525 public void warn( 526 XPathContext xctxt, int sourceNode, String msg, Object[] args) 527 throws javax.xml.transform.TransformerException 528 { 529 530 String fmsg = XSLMessages.createXPATHWarning(msg, args); 531 ErrorListener ehandler = xctxt.getErrorListener(); 532 533 if (null != ehandler) 534 { 535 536 // TO DO: Need to get stylesheet Locator from here. 537 ehandler.warning(new TransformerException(fmsg, (SAXSourceLocator)xctxt.getSAXLocator())); 538 } 539 } 540 541 /** 542 * Tell the user of an assertion error, and probably throw an 543 * exception. 544 * 545 * @param b If false, a runtime exception will be thrown. 546 * @param msg The assertion message, which should be informative. 547 * 548 * @throws RuntimeException if the b argument is false. 549 */ assertion(boolean b, String msg)550 public void assertion(boolean b, String msg) 551 { 552 553 if (!b) 554 { 555 String fMsg = XSLMessages.createXPATHMessage( 556 XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION, 557 new Object[]{ msg }); 558 559 throw new RuntimeException(fMsg); 560 } 561 } 562 563 /** 564 * Tell the user of an error, and probably throw an 565 * exception. 566 * 567 * @param xctxt The XPath runtime context. 568 * @param sourceNode Not used. 569 * @param msg An error msgkey that corresponds to one of the constants found 570 * in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is 571 * a key for a format string. 572 * @param args An array of arguments represented in the format string, which 573 * may be null. 574 * 575 * @throws TransformerException if the current ErrorListoner determines to 576 * throw an exception. 577 */ error( XPathContext xctxt, int sourceNode, String msg, Object[] args)578 public void error( 579 XPathContext xctxt, int sourceNode, String msg, Object[] args) 580 throws javax.xml.transform.TransformerException 581 { 582 583 String fmsg = XSLMessages.createXPATHMessage(msg, args); 584 ErrorListener ehandler = xctxt.getErrorListener(); 585 586 if (null != ehandler) 587 { 588 ehandler.fatalError(new TransformerException(fmsg, 589 (SAXSourceLocator)xctxt.getSAXLocator())); 590 } 591 else 592 { 593 SourceLocator slocator = xctxt.getSAXLocator(); 594 System.out.println(fmsg + "; file " + slocator.getSystemId() 595 + "; line " + slocator.getLineNumber() + "; column " 596 + slocator.getColumnNumber()); 597 } 598 } 599 600 /** 601 * This will traverse the heararchy, calling the visitor for 602 * each member. If the called visitor method returns 603 * false, the subtree should not be called. 604 * 605 * @param owner The owner of the visitor, where that path may be 606 * rewritten if needed. 607 * @param visitor The visitor whose appropriate method will be called. 608 */ callVisitors(ExpressionOwner owner, XPathVisitor visitor)609 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) 610 { 611 m_mainExp.callVisitors(this, visitor); 612 } 613 614 /** 615 * The match score if no match is made. 616 * @xsl.usage advanced 617 */ 618 public static final double MATCH_SCORE_NONE = Double.NEGATIVE_INFINITY; 619 620 /** 621 * The match score if the pattern has the form 622 * of a QName optionally preceded by an @ character. 623 * @xsl.usage advanced 624 */ 625 public static final double MATCH_SCORE_QNAME = 0.0; 626 627 /** 628 * The match score if the pattern pattern has the form NCName:*. 629 * @xsl.usage advanced 630 */ 631 public static final double MATCH_SCORE_NSWILD = -0.25; 632 633 /** 634 * The match score if the pattern consists of just a NodeTest. 635 * @xsl.usage advanced 636 */ 637 public static final double MATCH_SCORE_NODETEST = -0.5; 638 639 /** 640 * The match score if the pattern consists of something 641 * other than just a NodeTest or just a qname. 642 * @xsl.usage advanced 643 */ 644 public static final double MATCH_SCORE_OTHER = 0.5; 645 } 646