1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Oct 2017 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 22 package com.sun.org.apache.xpath.internal; 23 24 import com.sun.org.apache.xalan.internal.res.XSLMessages; 25 import com.sun.org.apache.xml.internal.dtm.DTM; 26 import com.sun.org.apache.xml.internal.dtm.DTMIterator; 27 import com.sun.org.apache.xml.internal.utils.QName; 28 import com.sun.org.apache.xml.internal.utils.XMLString; 29 import com.sun.org.apache.xpath.internal.objects.XNodeSet; 30 import com.sun.org.apache.xpath.internal.objects.XObject; 31 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources; 32 import java.util.List; 33 import javax.xml.transform.ErrorListener; 34 import javax.xml.transform.TransformerException; 35 import org.xml.sax.ContentHandler; 36 37 /** 38 * This abstract class serves as the base for all expression objects. An 39 * Expression can be executed to return a {@link com.sun.org.apache.xpath.internal.objects.XObject}, 40 * normally has a location within a document or DOM, can send error and warning 41 * events, and normally do not hold state and are meant to be immutable once 42 * construction has completed. An exception to the immutibility rule is iterators 43 * and walkers, which must be cloned in order to be used -- the original must 44 * still be immutable. 45 */ 46 public abstract class Expression implements java.io.Serializable, ExpressionNode, XPathVisitable 47 { 48 static final long serialVersionUID = 565665869777906902L; 49 /** 50 * The location where this expression was built from. Need for diagnostic 51 * messages. May be null. 52 * @serial 53 */ 54 private ExpressionNode m_parent; 55 56 /** 57 * Tell if this expression or it's subexpressions can traverse outside 58 * the current subtree. 59 * 60 * @return true if traversal outside the context node's subtree can occur. 61 */ 62 public boolean canTraverseOutsideSubtree() 63 { 64 return false; 65 } 66 67 // /** 68 // * Set the location where this expression was built from. 69 // * 70 // * 71 // * @param locator the location where this expression was built from, may be 72 // * null. 73 // */ 74 // public void setSourceLocator(SourceLocator locator) 75 // { 76 // m_slocator = locator; 77 // } 78 79 /** 80 * Execute an expression in the XPath runtime context, and return the 81 * result of the expression. 82 * 83 * 84 * @param xctxt The XPath runtime context. 85 * @param currentNode The currentNode. 86 * 87 * @return The result of the expression in the form of a <code>XObject</code>. 88 * 89 * @throws javax.xml.transform.TransformerException if a runtime exception 90 * occurs. 91 */ 92 public XObject execute(XPathContext xctxt, int currentNode) 93 throws javax.xml.transform.TransformerException 94 { 95 96 // For now, the current node is already pushed. 97 return execute(xctxt); 98 } 99 100 /** 101 * Execute an expression in the XPath runtime context, and return the 102 * result of the expression. 103 * 104 * 105 * @param xctxt The XPath runtime context. 106 * @param currentNode The currentNode. 107 * @param dtm The DTM of the current node. 108 * @param expType The expanded type ID of the current node. 109 * 110 * @return The result of the expression in the form of a <code>XObject</code>. 111 * 112 * @throws javax.xml.transform.TransformerException if a runtime exception 113 * occurs. 114 */ 115 public XObject execute( 116 XPathContext xctxt, int currentNode, DTM dtm, int expType) 117 throws javax.xml.transform.TransformerException 118 { 119 120 // For now, the current node is already pushed. 121 return execute(xctxt); 122 } 123 124 /** 125 * Execute an expression in the XPath runtime context, and return the 126 * result of the expression. 127 * 128 * 129 * @param xctxt The XPath runtime context. 130 * 131 * @return The result of the expression in the form of a <code>XObject</code>. 132 * 133 * @throws javax.xml.transform.TransformerException if a runtime exception 134 * occurs. 135 */ 136 public abstract XObject execute(XPathContext xctxt) 137 throws javax.xml.transform.TransformerException; 138 139 /** 140 * Execute an expression in the XPath runtime context, and return the 141 * result of the expression, but tell that a "safe" object doesn't have 142 * to be returned. The default implementation just calls execute(xctxt). 143 * 144 * 145 * @param xctxt The XPath runtime context. 146 * @param destructiveOK true if a "safe" object doesn't need to be returned. 147 * 148 * @return The result of the expression in the form of a <code>XObject</code>. 149 * 150 * @throws javax.xml.transform.TransformerException if a runtime exception 151 * occurs. 152 */ 153 public XObject execute(XPathContext xctxt, boolean destructiveOK) 154 throws javax.xml.transform.TransformerException 155 { 156 return execute(xctxt); 157 } 158 159 160 /** 161 * Evaluate expression to a number. 162 * 163 * 164 * @param xctxt The XPath runtime context. 165 * @return The expression evaluated as a double. 166 * 167 * @throws javax.xml.transform.TransformerException 168 */ 169 public double num(XPathContext xctxt) 170 throws javax.xml.transform.TransformerException 171 { 172 return execute(xctxt).num(); 173 } 174 175 /** 176 * Evaluate expression to a boolean. 177 * 178 * 179 * @param xctxt The XPath runtime context. 180 * @return false 181 * 182 * @throws javax.xml.transform.TransformerException 183 */ 184 public boolean bool(XPathContext xctxt) 185 throws javax.xml.transform.TransformerException 186 { 187 return execute(xctxt).bool(); 188 } 189 190 /** 191 * Cast result object to a string. 192 * 193 * 194 * @param xctxt The XPath runtime context. 195 * @return The string this wraps or the empty string if null 196 * 197 * @throws javax.xml.transform.TransformerException 198 */ 199 public XMLString xstr(XPathContext xctxt) 200 throws javax.xml.transform.TransformerException 201 { 202 return execute(xctxt).xstr(); 203 } 204 205 /** 206 * Tell if the expression is a nodeset expression. In other words, tell 207 * if you can execute {@link #asNode(XPathContext) asNode} without an exception. 208 * @return true if the expression can be represented as a nodeset. 209 */ 210 public boolean isNodesetExpr() 211 { 212 return false; 213 } 214 215 /** 216 * Return the first node out of the nodeset, if this expression is 217 * a nodeset expression. 218 * @param xctxt The XPath runtime context. 219 * @return the first node out of the nodeset, or DTM.NULL. 220 * 221 * @throws javax.xml.transform.TransformerException 222 */ 223 public int asNode(XPathContext xctxt) 224 throws javax.xml.transform.TransformerException 225 { 226 DTMIterator iter = execute(xctxt).iter(); 227 return iter.nextNode(); 228 } 229 230 /** 231 * Given an select expression and a context, evaluate the XPath 232 * and return the resulting iterator. 233 * 234 * @param xctxt The execution context. 235 * @param contextNode The node that "." expresses. 236 * 237 * 238 * @return A valid DTMIterator. 239 * @throws TransformerException thrown if the active ProblemListener decides 240 * the error condition is severe enough to halt processing. 241 * 242 * @throws javax.xml.transform.TransformerException 243 * @xsl.usage experimental 244 */ 245 public DTMIterator asIterator(XPathContext xctxt, int contextNode) 246 throws javax.xml.transform.TransformerException 247 { 248 249 try 250 { 251 xctxt.pushCurrentNodeAndExpression(contextNode, contextNode); 252 253 return execute(xctxt).iter(); 254 } 255 finally 256 { 257 xctxt.popCurrentNodeAndExpression(); 258 } 259 } 260 261 /** 262 * Given an select expression and a context, evaluate the XPath 263 * and return the resulting iterator, but do not clone. 264 * 265 * @param xctxt The execution context. 266 * @param contextNode The node that "." expresses. 267 * 268 * 269 * @return A valid DTMIterator. 270 * @throws TransformerException thrown if the active ProblemListener decides 271 * the error condition is severe enough to halt processing. 272 * 273 * @throws javax.xml.transform.TransformerException 274 * @xsl.usage experimental 275 */ 276 public DTMIterator asIteratorRaw(XPathContext xctxt, int contextNode) 277 throws javax.xml.transform.TransformerException 278 { 279 280 try 281 { 282 xctxt.pushCurrentNodeAndExpression(contextNode, contextNode); 283 284 XNodeSet nodeset = (XNodeSet)execute(xctxt); 285 return nodeset.iterRaw(); 286 } 287 finally 288 { 289 xctxt.popCurrentNodeAndExpression(); 290 } 291 } 292 293 294 /** 295 * Execute an expression in the XPath runtime context, and return the 296 * result of the expression. 297 * 298 * 299 * @param xctxt The XPath runtime context. 300 * NEEDSDOC @param handler 301 * 302 * @return The result of the expression in the form of a <code>XObject</code>. 303 * 304 * @throws javax.xml.transform.TransformerException if a runtime exception 305 * occurs. 306 * @throws org.xml.sax.SAXException 307 */ 308 public void executeCharsToContentHandler( 309 XPathContext xctxt, ContentHandler handler) 310 throws javax.xml.transform.TransformerException, 311 org.xml.sax.SAXException 312 { 313 314 XObject obj = execute(xctxt); 315 316 obj.dispatchCharactersEvents(handler); 317 obj.detach(); 318 } 319 320 /** 321 * Tell if this expression returns a stable number that will not change during 322 * iterations within the expression. This is used to determine if a proximity 323 * position predicate can indicate that no more searching has to occur. 324 * 325 * 326 * @return true if the expression represents a stable number. 327 */ 328 public boolean isStableNumber() 329 { 330 return false; 331 } 332 333 /** 334 * This function is used to fixup variables from QNames to stack frame 335 * indexes at stylesheet build time. 336 * @param vars List of QNames that correspond to variables. This list 337 * should be searched backwards for the first qualified name that 338 * corresponds to the variable reference qname. The position of the 339 * QName in the vector from the start of the vector will be its position 340 * in the stack frame (but variables above the globalsTop value will need 341 * to be offset to the current stack frame). 342 * NEEDSDOC @param globalsSize 343 */ 344 public abstract void fixupVariables(List<QName> vars, int globalsSize); 345 346 /** 347 * Compare this object with another object and see 348 * if they are equal, include the sub heararchy. 349 * 350 * @param expr Another expression object. 351 * @return true if this objects class and the expr 352 * object's class are the same, and the data contained 353 * within both objects are considered equal. 354 */ 355 public abstract boolean deepEquals(Expression expr); 356 357 /** 358 * This is a utility method to tell if the passed in 359 * class is the same class as this. It is to be used by 360 * the deepEquals method. I'm bottlenecking it here 361 * because I'm not totally confident that comparing the 362 * class objects is the best way to do this. 363 * @return true of the passed in class is the exact same 364 * class as this class. 365 */ 366 protected final boolean isSameClass(Expression expr) 367 { 368 if(null == expr) 369 return false; 370 371 return (getClass() == expr.getClass()); 372 } 373 374 /** 375 * Warn the user of an problem. 376 * 377 * @param xctxt The XPath runtime context. 378 * @param msg An error msgkey that corresponds to one of the conststants found 379 * in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is 380 * a key for a format string. 381 * @param args An array of arguments represented in the format string, which 382 * may be null. 383 * 384 * @throws TransformerException if the current ErrorListoner determines to 385 * throw an exception. 386 * 387 * @throws javax.xml.transform.TransformerException 388 */ 389 public void warn(XPathContext xctxt, String msg, Object[] args) 390 throws javax.xml.transform.TransformerException 391 { 392 393 java.lang.String fmsg = XSLMessages.createXPATHWarning(msg, args); 394 395 if (null != xctxt) 396 { 397 ErrorListener eh = xctxt.getErrorListener(); 398 399 // TO DO: Need to get stylesheet Locator from here. 400 eh.warning(new TransformerException(fmsg, xctxt.getSAXLocator())); 401 } 402 } 403 404 /** 405 * Tell the user of an assertion error, and probably throw an 406 * exception. 407 * 408 * @param b If false, a runtime exception will be thrown. 409 * @param msg The assertion message, which should be informative. 410 * 411 * @throws RuntimeException if the b argument is false. 412 * 413 * @throws javax.xml.transform.TransformerException 414 */ 415 public void assertion(boolean b, java.lang.String msg) 416 { 417 418 if (!b) 419 { 420 java.lang.String fMsg = XSLMessages.createXPATHMessage( 421 XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION, 422 new Object[]{ msg }); 423 424 throw new RuntimeException(fMsg); 425 } 426 } 427 428 /** 429 * Tell the user of an error, and probably throw an 430 * exception. 431 * 432 * @param xctxt The XPath runtime context. 433 * @param msg An error msgkey that corresponds to one of the constants found 434 * in {@link com.sun.org.apache.xpath.internal.res.XPATHErrorResources}, which is 435 * a key for a format string. 436 * @param args An array of arguments represented in the format string, which 437 * may be null. 438 * 439 * @throws TransformerException if the current ErrorListoner determines to 440 * throw an exception. 441 * 442 * @throws javax.xml.transform.TransformerException 443 */ 444 public void error(XPathContext xctxt, String msg, Object[] args) 445 throws javax.xml.transform.TransformerException 446 { 447 448 java.lang.String fmsg = XSLMessages.createXPATHMessage(msg, args); 449 450 if (null != xctxt) 451 { 452 ErrorListener eh = xctxt.getErrorListener(); 453 TransformerException te = new TransformerException(fmsg, this); 454 455 eh.fatalError(te); 456 } 457 } 458 459 /** 460 * Get the first non-Expression parent of this node. 461 * @return null or first ancestor that is not an Expression. 462 */ 463 public ExpressionNode getExpressionOwner() 464 { 465 ExpressionNode parent = exprGetParent(); 466 while((null != parent) && (parent instanceof Expression)) 467 parent = parent.exprGetParent(); 468 return parent; 469 } 470 471 //=============== ExpressionNode methods ================ 472 473 /** This pair of methods are used to inform the node of its 474 parent. */ 475 public void exprSetParent(ExpressionNode n) 476 { 477 assertion(n != this, "Can not parent an expression to itself!"); 478 m_parent = n; 479 } 480 481 public ExpressionNode exprGetParent() 482 { 483 return m_parent; 484 } 485 486 /** This method tells the node to add its argument to the node's 487 list of children. */ 488 public void exprAddChild(ExpressionNode n, int i) 489 { 490 assertion(false, "exprAddChild method not implemented!"); 491 } 492 493 /** This method returns a child node. The children are numbered 494 from zero, left to right. */ 495 public ExpressionNode exprGetChild(int i) 496 { 497 return null; 498 } 499 500 /** Return the number of children the node has. */ 501 public int exprGetNumChildren() 502 { 503 return 0; 504 } 505 506 //=============== SourceLocator methods ================ 507 508 /** 509 * Return the public identifier for the current document event. 510 * 511 * <p>The return value is the public identifier of the document 512 * entity or of the external parsed entity in which the markup that 513 * triggered the event appears.</p> 514 * 515 * @return A string containing the public identifier, or 516 * null if none is available. 517 * @see #getSystemId 518 */ 519 public String getPublicId() 520 { 521 if(null == m_parent) 522 return null; 523 return m_parent.getPublicId(); 524 } 525 526 /** 527 * Return the system identifier for the current document event. 528 * 529 * <p>The return value is the system identifier of the document 530 * entity or of the external parsed entity in which the markup that 531 * triggered the event appears.</p> 532 * 533 * <p>If the system identifier is a URL, the parser must resolve it 534 * fully before passing it to the application.</p> 535 * 536 * @return A string containing the system identifier, or null 537 * if none is available. 538 * @see #getPublicId 539 */ 540 public String getSystemId() 541 { 542 if(null == m_parent) 543 return null; 544 return m_parent.getSystemId(); 545 } 546 547 /** 548 * Return the line number where the current document event ends. 549 * 550 * <p><strong>Warning:</strong> The return value from the method 551 * is intended only as an approximation for the sake of error 552 * reporting; it is not intended to provide sufficient information 553 * to edit the character content of the original XML document.</p> 554 * 555 * <p>The return value is an approximation of the line number 556 * in the document entity or external parsed entity where the 557 * markup that triggered the event appears.</p> 558 * 559 * @return The line number, or -1 if none is available. 560 * @see #getColumnNumber 561 */ 562 public int getLineNumber() 563 { 564 if(null == m_parent) 565 return 0; 566 return m_parent.getLineNumber(); 567 } 568 569 /** 570 * Return the character position where the current document event ends. 571 * 572 * <p><strong>Warning:</strong> The return value from the method 573 * is intended only as an approximation for the sake of error 574 * reporting; it is not intended to provide sufficient information 575 * to edit the character content of the original XML document.</p> 576 * 577 * <p>The return value is an approximation of the column number 578 * in the document entity or external parsed entity where the 579 * markup that triggered the event appears.</p> 580 * 581 * @return The column number, or -1 if none is available. 582 * @see #getLineNumber 583 */ 584 public int getColumnNumber() 585 { 586 if(null == m_parent) 587 return 0; 588 return m_parent.getColumnNumber(); 589 } 590 }