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: XPath.java 468655 2006-10-28 07:12:06Z minchau $ 20 */ 21 package org.apache.xpath; 22 23 import java.io.Serializable; 24 25 import javax.xml.transform.ErrorListener; 26 import javax.xml.transform.SourceLocator; 27 import javax.xml.transform.TransformerException; 28 29 import org.apache.xalan.res.XSLMessages; 30 import org.apache.xml.dtm.DTM; 31 import org.apache.xml.utils.PrefixResolver; 32 import org.apache.xml.utils.SAXSourceLocator; 33 import org.apache.xpath.compiler.Compiler; 34 import org.apache.xpath.compiler.FunctionTable; 35 import org.apache.xpath.compiler.XPathParser; 36 import org.apache.xpath.functions.Function; 37 import org.apache.xpath.objects.XObject; 38 import org.apache.xpath.res.XPATHErrorResources; 39 40 /** 41 * The XPath class wraps an expression object and provides general services 42 * for execution of that expression. 43 * @xsl.usage advanced 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(java.util.Vector vars, int globalsSize)86 public void fixupVariables(java.util.Vector 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 org.apache.xml.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 */ XPath( String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type, ErrorListener errorListener, FunctionTable aTable)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 org.apache.xml.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 */ XPath( String exprString, SourceLocator locator, PrefixResolver prefixResolver, int type)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 */ XPath(Expression expr)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 */ execute( XPathContext xctxt, org.w3c.dom.Node contextNode, PrefixResolver namespaceContext)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 */ execute( XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)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 org.apache.xml.utils.WrappedRuntimeException) 353 { 354 e = ((org.apache.xml.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 */ bool( XPathContext xctxt, int contextNode, PrefixResolver namespaceContext)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 org.apache.xml.utils.WrappedRuntimeException) 428 { 429 e = ((org.apache.xml.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 */ getMatchScore(XPathContext xctxt, int context)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 org.apache.xpath.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 */ warn( XPathContext xctxt, int sourceNode, String msg, Object[] args)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 */ assertion(boolean b, String msg)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 org.apache.xpath.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 */ error( XPathContext xctxt, int sourceNode, String msg, Object[] args)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 */ callVisitors(ExpressionOwner owner, XPathVisitor visitor)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 } 644