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