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 */ 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(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 */ 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 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 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 */ 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 } 644