001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: Expression.java 468655 2006-10-28 07:12:06Z minchau $ 020 */ 021 package org.apache.xpath; 022 023 import javax.xml.transform.ErrorListener; 024 import javax.xml.transform.TransformerException; 025 026 import org.apache.xalan.res.XSLMessages; 027 import org.apache.xml.dtm.DTM; 028 import org.apache.xml.dtm.DTMIterator; 029 import org.apache.xml.utils.XMLString; 030 import org.apache.xpath.objects.XNodeSet; 031 import org.apache.xpath.objects.XObject; 032 import org.apache.xpath.res.XPATHErrorResources; 033 034 import org.xml.sax.ContentHandler; 035 036 /** 037 * This abstract class serves as the base for all expression objects. An 038 * Expression can be executed to return a {@link org.apache.xpath.objects.XObject}, 039 * normally has a location within a document or DOM, can send error and warning 040 * events, and normally do not hold state and are meant to be immutable once 041 * construction has completed. An exception to the immutibility rule is iterators 042 * and walkers, which must be cloned in order to be used -- the original must 043 * still be immutable. 044 */ 045 public abstract class Expression implements java.io.Serializable, ExpressionNode, XPathVisitable 046 { 047 static final long serialVersionUID = 565665869777906902L; 048 /** 049 * The location where this expression was built from. Need for diagnostic 050 * messages. May be null. 051 * @serial 052 */ 053 private ExpressionNode m_parent; 054 055 /** 056 * Tell if this expression or it's subexpressions can traverse outside 057 * the current subtree. 058 * 059 * @return true if traversal outside the context node's subtree can occur. 060 */ 061 public boolean canTraverseOutsideSubtree() 062 { 063 return false; 064 } 065 066 // /** 067 // * Set the location where this expression was built from. 068 // * 069 // * 070 // * @param locator the location where this expression was built from, may be 071 // * null. 072 // */ 073 // public void setSourceLocator(SourceLocator locator) 074 // { 075 // m_slocator = locator; 076 // } 077 078 /** 079 * Execute an expression in the XPath runtime context, and return the 080 * result of the expression. 081 * 082 * 083 * @param xctxt The XPath runtime context. 084 * @param currentNode The currentNode. 085 * 086 * @return The result of the expression in the form of a <code>XObject</code>. 087 * 088 * @throws javax.xml.transform.TransformerException if a runtime exception 089 * occurs. 090 */ 091 public XObject execute(XPathContext xctxt, int currentNode) 092 throws javax.xml.transform.TransformerException 093 { 094 095 // For now, the current node is already pushed. 096 return execute(xctxt); 097 } 098 099 /** 100 * Execute an expression in the XPath runtime context, and return the 101 * result of the expression. 102 * 103 * 104 * @param xctxt The XPath runtime context. 105 * @param currentNode The currentNode. 106 * @param dtm The DTM of the current node. 107 * @param expType The expanded type ID of the current node. 108 * 109 * @return The result of the expression in the form of a <code>XObject</code>. 110 * 111 * @throws javax.xml.transform.TransformerException if a runtime exception 112 * occurs. 113 */ 114 public XObject execute( 115 XPathContext xctxt, int currentNode, DTM dtm, int expType) 116 throws javax.xml.transform.TransformerException 117 { 118 119 // For now, the current node is already pushed. 120 return execute(xctxt); 121 } 122 123 /** 124 * Execute an expression in the XPath runtime context, and return the 125 * result of the expression. 126 * 127 * 128 * @param xctxt The XPath runtime context. 129 * 130 * @return The result of the expression in the form of a <code>XObject</code>. 131 * 132 * @throws javax.xml.transform.TransformerException if a runtime exception 133 * occurs. 134 */ 135 public abstract XObject execute(XPathContext xctxt) 136 throws javax.xml.transform.TransformerException; 137 138 /** 139 * Execute an expression in the XPath runtime context, and return the 140 * result of the expression, but tell that a "safe" object doesn't have 141 * to be returned. The default implementation just calls execute(xctxt). 142 * 143 * 144 * @param xctxt The XPath runtime context. 145 * @param destructiveOK true if a "safe" object doesn't need to be returned. 146 * 147 * @return The result of the expression in the form of a <code>XObject</code>. 148 * 149 * @throws javax.xml.transform.TransformerException if a runtime exception 150 * occurs. 151 */ 152 public XObject execute(XPathContext xctxt, boolean destructiveOK) 153 throws javax.xml.transform.TransformerException 154 { 155 return execute(xctxt); 156 } 157 158 159 /** 160 * Evaluate expression to a number. 161 * 162 * 163 * @param xctxt The XPath runtime context. 164 * @return The expression evaluated as a double. 165 * 166 * @throws javax.xml.transform.TransformerException 167 */ 168 public double num(XPathContext xctxt) 169 throws javax.xml.transform.TransformerException 170 { 171 return execute(xctxt).num(); 172 } 173 174 /** 175 * Evaluate expression to a boolean. 176 * 177 * 178 * @param xctxt The XPath runtime context. 179 * @return false 180 * 181 * @throws javax.xml.transform.TransformerException 182 */ 183 public boolean bool(XPathContext xctxt) 184 throws javax.xml.transform.TransformerException 185 { 186 return execute(xctxt).bool(); 187 } 188 189 /** 190 * Cast result object to a string. 191 * 192 * 193 * @param xctxt The XPath runtime context. 194 * @return The string this wraps or the empty string if null 195 * 196 * @throws javax.xml.transform.TransformerException 197 */ 198 public XMLString xstr(XPathContext xctxt) 199 throws javax.xml.transform.TransformerException 200 { 201 return execute(xctxt).xstr(); 202 } 203 204 /** 205 * Tell if the expression is a nodeset expression. In other words, tell 206 * if you can execute {@link #asNode(XPathContext) asNode} without an exception. 207 * @return true if the expression can be represented as a nodeset. 208 */ 209 public boolean isNodesetExpr() 210 { 211 return false; 212 } 213 214 /** 215 * Return the first node out of the nodeset, if this expression is 216 * a nodeset expression. 217 * @param xctxt The XPath runtime context. 218 * @return the first node out of the nodeset, or DTM.NULL. 219 * 220 * @throws javax.xml.transform.TransformerException 221 */ 222 public int asNode(XPathContext xctxt) 223 throws javax.xml.transform.TransformerException 224 { 225 DTMIterator iter = execute(xctxt).iter(); 226 return iter.nextNode(); 227 } 228 229 /** 230 * Given an select expression and a context, evaluate the XPath 231 * and return the resulting iterator. 232 * 233 * @param xctxt The execution context. 234 * @param contextNode The node that "." expresses. 235 * 236 * 237 * @return A valid DTMIterator. 238 * @throws TransformerException thrown if the active ProblemListener decides 239 * the error condition is severe enough to halt processing. 240 * 241 * @throws javax.xml.transform.TransformerException 242 * @xsl.usage experimental 243 */ 244 public DTMIterator asIterator(XPathContext xctxt, int contextNode) 245 throws javax.xml.transform.TransformerException 246 { 247 248 try 249 { 250 xctxt.pushCurrentNodeAndExpression(contextNode, contextNode); 251 252 return execute(xctxt).iter(); 253 } 254 finally 255 { 256 xctxt.popCurrentNodeAndExpression(); 257 } 258 } 259 260 /** 261 * Given an select expression and a context, evaluate the XPath 262 * and return the resulting iterator, but do not clone. 263 * 264 * @param xctxt The execution context. 265 * @param contextNode The node that "." expresses. 266 * 267 * 268 * @return A valid DTMIterator. 269 * @throws TransformerException thrown if the active ProblemListener decides 270 * the error condition is severe enough to halt processing. 271 * 272 * @throws javax.xml.transform.TransformerException 273 * @xsl.usage experimental 274 */ 275 public DTMIterator asIteratorRaw(XPathContext xctxt, int contextNode) 276 throws javax.xml.transform.TransformerException 277 { 278 279 try 280 { 281 xctxt.pushCurrentNodeAndExpression(contextNode, contextNode); 282 283 XNodeSet nodeset = (XNodeSet)execute(xctxt); 284 return nodeset.iterRaw(); 285 } 286 finally 287 { 288 xctxt.popCurrentNodeAndExpression(); 289 } 290 } 291 292 293 /** 294 * Execute an expression in the XPath runtime context, and return the 295 * result of the expression. 296 * 297 * 298 * @param xctxt The XPath runtime context. 299 * NEEDSDOC @param handler 300 * 301 * @return The result of the expression in the form of a <code>XObject</code>. 302 * 303 * @throws javax.xml.transform.TransformerException if a runtime exception 304 * occurs. 305 * @throws org.xml.sax.SAXException 306 */ 307 public void executeCharsToContentHandler( 308 XPathContext xctxt, ContentHandler handler) 309 throws javax.xml.transform.TransformerException, 310 org.xml.sax.SAXException 311 { 312 313 XObject obj = execute(xctxt); 314 315 obj.dispatchCharactersEvents(handler); 316 obj.detach(); 317 } 318 319 /** 320 * Tell if this expression returns a stable number that will not change during 321 * iterations within the expression. This is used to determine if a proximity 322 * position predicate can indicate that no more searching has to occur. 323 * 324 * 325 * @return true if the expression represents a stable number. 326 */ 327 public boolean isStableNumber() 328 { 329 return false; 330 } 331 332 /** 333 * This function is used to fixup variables from QNames to stack frame 334 * indexes at stylesheet build time. 335 * @param vars List of QNames that correspond to variables. This list 336 * should be searched backwards for the first qualified name that 337 * corresponds to the variable reference qname. The position of the 338 * QName in the vector from the start of the vector will be its position 339 * in the stack frame (but variables above the globalsTop value will need 340 * to be offset to the current stack frame). 341 * NEEDSDOC @param globalsSize 342 */ 343 public abstract void fixupVariables(java.util.Vector vars, int globalsSize); 344 345 /** 346 * Compare this object with another object and see 347 * if they are equal, include the sub heararchy. 348 * 349 * @param expr Another expression object. 350 * @return true if this objects class and the expr 351 * object's class are the same, and the data contained 352 * within both objects are considered equal. 353 */ 354 public abstract boolean deepEquals(Expression expr); 355 356 /** 357 * This is a utility method to tell if the passed in 358 * class is the same class as this. It is to be used by 359 * the deepEquals method. I'm bottlenecking it here 360 * because I'm not totally confident that comparing the 361 * class objects is the best way to do this. 362 * @return true of the passed in class is the exact same 363 * class as this class. 364 */ 365 protected final boolean isSameClass(Expression expr) 366 { 367 if(null == expr) 368 return false; 369 370 return (getClass() == expr.getClass()); 371 } 372 373 /** 374 * Warn the user of an problem. 375 * 376 * @param xctxt The XPath runtime context. 377 * @param msg An error msgkey that corresponds to one of the conststants found 378 * in {@link org.apache.xpath.res.XPATHErrorResources}, which is 379 * a key for a format string. 380 * @param args An array of arguments represented in the format string, which 381 * may be null. 382 * 383 * @throws TransformerException if the current ErrorListoner determines to 384 * throw an exception. 385 * 386 * @throws javax.xml.transform.TransformerException 387 */ 388 public void warn(XPathContext xctxt, String msg, Object[] args) 389 throws javax.xml.transform.TransformerException 390 { 391 392 java.lang.String fmsg = XSLMessages.createXPATHWarning(msg, args); 393 394 if (null != xctxt) 395 { 396 ErrorListener eh = xctxt.getErrorListener(); 397 398 // TO DO: Need to get stylesheet Locator from here. 399 eh.warning(new TransformerException(fmsg, xctxt.getSAXLocator())); 400 } 401 } 402 403 /** 404 * Tell the user of an assertion error, and probably throw an 405 * exception. 406 * 407 * @param b If false, a runtime exception will be thrown. 408 * @param msg The assertion message, which should be informative. 409 * 410 * @throws RuntimeException if the b argument is false. 411 * 412 * @throws javax.xml.transform.TransformerException 413 */ 414 public void assertion(boolean b, java.lang.String msg) 415 { 416 417 if (!b) 418 { 419 java.lang.String fMsg = XSLMessages.createXPATHMessage( 420 XPATHErrorResources.ER_INCORRECT_PROGRAMMER_ASSERTION, 421 new Object[]{ msg }); 422 423 throw new RuntimeException(fMsg); 424 } 425 } 426 427 /** 428 * Tell the user of an error, and probably throw an 429 * exception. 430 * 431 * @param xctxt The XPath runtime context. 432 * @param msg An error msgkey that corresponds to one of the constants found 433 * in {@link org.apache.xpath.res.XPATHErrorResources}, which is 434 * a key for a format string. 435 * @param args An array of arguments represented in the format string, which 436 * may be null. 437 * 438 * @throws TransformerException if the current ErrorListoner determines to 439 * throw an exception. 440 * 441 * @throws javax.xml.transform.TransformerException 442 */ 443 public void error(XPathContext xctxt, String msg, Object[] args) 444 throws javax.xml.transform.TransformerException 445 { 446 447 java.lang.String fmsg = XSLMessages.createXPATHMessage(msg, args); 448 449 if (null != xctxt) 450 { 451 ErrorListener eh = xctxt.getErrorListener(); 452 TransformerException te = new TransformerException(fmsg, this); 453 454 eh.fatalError(te); 455 } 456 } 457 458 /** 459 * Get the first non-Expression parent of this node. 460 * @return null or first ancestor that is not an Expression. 461 */ 462 public ExpressionNode getExpressionOwner() 463 { 464 ExpressionNode parent = exprGetParent(); 465 while((null != parent) && (parent instanceof Expression)) 466 parent = parent.exprGetParent(); 467 return parent; 468 } 469 470 //=============== ExpressionNode methods ================ 471 472 /** This pair of methods are used to inform the node of its 473 parent. */ 474 public void exprSetParent(ExpressionNode n) 475 { 476 assertion(n != this, "Can not parent an expression to itself!"); 477 m_parent = n; 478 } 479 480 public ExpressionNode exprGetParent() 481 { 482 return m_parent; 483 } 484 485 /** This method tells the node to add its argument to the node's 486 list of children. */ 487 public void exprAddChild(ExpressionNode n, int i) 488 { 489 assertion(false, "exprAddChild method not implemented!"); 490 } 491 492 /** This method returns a child node. The children are numbered 493 from zero, left to right. */ 494 public ExpressionNode exprGetChild(int i) 495 { 496 return null; 497 } 498 499 /** Return the number of children the node has. */ 500 public int exprGetNumChildren() 501 { 502 return 0; 503 } 504 505 //=============== SourceLocator methods ================ 506 507 /** 508 * Return the public identifier for the current document event. 509 * 510 * <p>The return value is the public identifier of the document 511 * entity or of the external parsed entity in which the markup that 512 * triggered the event appears.</p> 513 * 514 * @return A string containing the public identifier, or 515 * null if none is available. 516 * @see #getSystemId 517 */ 518 public String getPublicId() 519 { 520 if(null == m_parent) 521 return null; 522 return m_parent.getPublicId(); 523 } 524 525 /** 526 * Return the system identifier for the current document event. 527 * 528 * <p>The return value is the system identifier of the document 529 * entity or of the external parsed entity in which the markup that 530 * triggered the event appears.</p> 531 * 532 * <p>If the system identifier is a URL, the parser must resolve it 533 * fully before passing it to the application.</p> 534 * 535 * @return A string containing the system identifier, or null 536 * if none is available. 537 * @see #getPublicId 538 */ 539 public String getSystemId() 540 { 541 if(null == m_parent) 542 return null; 543 return m_parent.getSystemId(); 544 } 545 546 /** 547 * Return the line number where the current document event ends. 548 * 549 * <p><strong>Warning:</strong> The return value from the method 550 * is intended only as an approximation for the sake of error 551 * reporting; it is not intended to provide sufficient information 552 * to edit the character content of the original XML document.</p> 553 * 554 * <p>The return value is an approximation of the line number 555 * in the document entity or external parsed entity where the 556 * markup that triggered the event appears.</p> 557 * 558 * @return The line number, or -1 if none is available. 559 * @see #getColumnNumber 560 */ 561 public int getLineNumber() 562 { 563 if(null == m_parent) 564 return 0; 565 return m_parent.getLineNumber(); 566 } 567 568 /** 569 * Return the character position where the current document event ends. 570 * 571 * <p><strong>Warning:</strong> The return value from the method 572 * is intended only as an approximation for the sake of error 573 * reporting; it is not intended to provide sufficient information 574 * to edit the character content of the original XML document.</p> 575 * 576 * <p>The return value is an approximation of the column number 577 * in the document entity or external parsed entity where the 578 * markup that triggered the event appears.</p> 579 * 580 * @return The column number, or -1 if none is available. 581 * @see #getLineNumber 582 */ 583 public int getColumnNumber() 584 { 585 if(null == m_parent) 586 return 0; 587 return m_parent.getColumnNumber(); 588 } 589 }