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