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: XObject.java 469368 2006-10-31 04:41:36Z minchau $ 20 */ 21 package org.apache.xpath.objects; 22 23 import java.io.Serializable; 24 25 import org.apache.xalan.res.XSLMessages; 26 import org.apache.xml.dtm.DTM; 27 import org.apache.xml.dtm.DTMIterator; 28 import org.apache.xml.utils.XMLString; 29 import org.apache.xpath.Expression; 30 import org.apache.xpath.ExpressionOwner; 31 import org.apache.xpath.NodeSetDTM; 32 import org.apache.xpath.XPathContext; 33 import org.apache.xpath.XPathException; 34 import org.apache.xpath.XPathVisitor; 35 import org.apache.xpath.res.XPATHErrorResources; 36 37 import org.w3c.dom.DocumentFragment; 38 import org.w3c.dom.NodeList; 39 import org.w3c.dom.traversal.NodeIterator; 40 41 /** 42 * This class represents an XPath object, and is capable of 43 * converting the object to various types, such as a string. 44 * This class acts as the base class to other XPath type objects, 45 * such as XString, and provides polymorphic casting capabilities. 46 * @xsl.usage general 47 */ 48 public class XObject extends Expression implements Serializable, Cloneable 49 { 50 static final long serialVersionUID = -821887098985662951L; 51 52 /** 53 * The java object which this object wraps. 54 * @serial 55 */ 56 protected Object m_obj; // This may be NULL!!! 57 58 /** 59 * Create an XObject. 60 */ 61 public XObject(){} 62 63 /** 64 * Create an XObject. 65 * 66 * @param obj Can be any object, should be a specific type 67 * for derived classes, or null. 68 */ 69 public XObject(Object obj) 70 { 71 setObject(obj); 72 } 73 74 protected void setObject(Object obj) { 75 m_obj = obj; 76 } 77 78 /** 79 * For support of literal objects in xpaths. 80 * 81 * @param xctxt The XPath execution context. 82 * 83 * @return This object. 84 * 85 * @throws javax.xml.transform.TransformerException 86 */ 87 public XObject execute(XPathContext xctxt) 88 throws javax.xml.transform.TransformerException 89 { 90 return this; 91 } 92 93 /** 94 * Specify if it's OK for detach to release the iterator for reuse. 95 * This function should be called with a value of false for objects that are 96 * stored in variables. 97 * Calling this with a value of false on a XNodeSet will cause the nodeset 98 * to be cached. 99 * 100 * @param allowRelease true if it is OK for detach to release this iterator 101 * for pooling. 102 */ 103 public void allowDetachToRelease(boolean allowRelease){} 104 105 /** 106 * Detaches the <code>DTMIterator</code> from the set which it iterated 107 * over, releasing any computational resources and placing the iterator 108 * in the INVALID state. After <code>detach</code> has been invoked, 109 * calls to <code>nextNode</code> or <code>previousNode</code> will 110 * raise a runtime exception. 111 */ 112 public void detach(){} 113 114 /** 115 * Forces the object to release it's resources. This is more harsh than 116 * detach(). 117 */ 118 public void destruct() 119 { 120 121 if (null != m_obj) 122 { 123 allowDetachToRelease(true); 124 detach(); 125 126 setObject(null); 127 } 128 } 129 130 /** 131 * Reset for fresh reuse. 132 */ 133 public void reset() 134 { 135 } 136 137 /** 138 * Directly call the 139 * characters method on the passed ContentHandler for the 140 * string-value. Multiple calls to the 141 * ContentHandler's characters methods may well occur for a single call to 142 * this method. 143 * 144 * @param ch A non-null reference to a ContentHandler. 145 * 146 * @throws org.xml.sax.SAXException 147 */ 148 public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch) 149 throws org.xml.sax.SAXException 150 { 151 xstr().dispatchCharactersEvents(ch); 152 } 153 154 /** 155 * Create the right XObject based on the type of the object passed. This 156 * function can not make an XObject that exposes DOM Nodes, NodeLists, and 157 * NodeIterators to the XSLT stylesheet as node-sets. 158 * 159 * @param val The java object which this object will wrap. 160 * 161 * @return the right XObject based on the type of the object passed. 162 */ 163 static public XObject create(Object val) 164 { 165 return XObjectFactory.create(val); 166 } 167 168 /** 169 * Create the right XObject based on the type of the object passed. 170 * This function <emph>can</emph> make an XObject that exposes DOM Nodes, NodeLists, and 171 * NodeIterators to the XSLT stylesheet as node-sets. 172 * 173 * @param val The java object which this object will wrap. 174 * @param xctxt The XPath context. 175 * 176 * @return the right XObject based on the type of the object passed. 177 */ 178 static public XObject create(Object val, XPathContext xctxt) 179 { 180 return XObjectFactory.create(val, xctxt); 181 } 182 183 /** Constant for NULL object type */ 184 public static final int CLASS_NULL = -1; 185 186 /** Constant for UNKNOWN object type */ 187 public static final int CLASS_UNKNOWN = 0; 188 189 /** Constant for BOOLEAN object type */ 190 public static final int CLASS_BOOLEAN = 1; 191 192 /** Constant for NUMBER object type */ 193 public static final int CLASS_NUMBER = 2; 194 195 /** Constant for STRING object type */ 196 public static final int CLASS_STRING = 3; 197 198 /** Constant for NODESET object type */ 199 public static final int CLASS_NODESET = 4; 200 201 /** Constant for RESULT TREE FRAGMENT object type */ 202 public static final int CLASS_RTREEFRAG = 5; 203 204 /** Represents an unresolved variable type as an integer. */ 205 public static final int CLASS_UNRESOLVEDVARIABLE = 600; 206 207 /** 208 * Tell what kind of class this is. 209 * 210 * @return CLASS_UNKNOWN 211 */ 212 public int getType() 213 { 214 return CLASS_UNKNOWN; 215 } 216 217 /** 218 * Given a request type, return the equivalent string. 219 * For diagnostic purposes. 220 * 221 * @return type string "#UNKNOWN" + object class name 222 */ 223 public String getTypeString() 224 { 225 return "#UNKNOWN (" + object().getClass().getName() + ")"; 226 } 227 228 /** 229 * Cast result object to a number. Always issues an error. 230 * 231 * @return 0.0 232 * 233 * @throws javax.xml.transform.TransformerException 234 */ 235 public double num() throws javax.xml.transform.TransformerException 236 { 237 238 error(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER, 239 new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a number"); 240 241 return 0.0; 242 } 243 244 /** 245 * Cast result object to a number, but allow side effects, such as the 246 * incrementing of an iterator. 247 * 248 * @return numeric value of the string conversion from the 249 * next node in the NodeSetDTM, or NAN if no node was found 250 */ 251 public double numWithSideEffects() throws javax.xml.transform.TransformerException 252 { 253 return num(); 254 } 255 256 /** 257 * Cast result object to a boolean. Always issues an error. 258 * 259 * @return false 260 * 261 * @throws javax.xml.transform.TransformerException 262 */ 263 public boolean bool() throws javax.xml.transform.TransformerException 264 { 265 266 error(XPATHErrorResources.ER_CANT_CONVERT_TO_NUMBER, 267 new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a number"); 268 269 return false; 270 } 271 272 /** 273 * Cast result object to a boolean, but allow side effects, such as the 274 * incrementing of an iterator. 275 * 276 * @return True if there is a next node in the nodeset 277 */ 278 public boolean boolWithSideEffects() throws javax.xml.transform.TransformerException 279 { 280 return bool(); 281 } 282 283 284 /** 285 * Cast result object to a string. 286 * 287 * @return The string this wraps or the empty string if null 288 */ 289 public XMLString xstr() 290 { 291 return XMLStringFactoryImpl.getFactory().newstr(str()); 292 } 293 294 /** 295 * Cast result object to a string. 296 * 297 * @return The object as a string 298 */ 299 public String str() 300 { 301 return (m_obj != null) ? m_obj.toString() : ""; 302 } 303 304 /** 305 * Return the string representation of the object 306 * 307 * 308 * @return the string representation of the object 309 */ 310 public String toString() 311 { 312 return str(); 313 } 314 315 /** 316 * Cast result object to a result tree fragment. 317 * 318 * @param support XPath context to use for the conversion 319 * 320 * @return the objec as a result tree fragment. 321 */ 322 public int rtf(XPathContext support) 323 { 324 325 int result = rtf(); 326 327 if (DTM.NULL == result) 328 { 329 DTM frag = support.createDocumentFragment(); 330 331 // %OPT% 332 frag.appendTextChild(str()); 333 334 result = frag.getDocument(); 335 } 336 337 return result; 338 } 339 340 /** 341 * Cast result object to a result tree fragment. 342 * 343 * @param support XPath context to use for the conversion 344 * 345 * @return the objec as a result tree fragment. 346 */ 347 public DocumentFragment rtree(XPathContext support) 348 { 349 DocumentFragment docFrag = null; 350 int result = rtf(); 351 352 if (DTM.NULL == result) 353 { 354 DTM frag = support.createDocumentFragment(); 355 356 // %OPT% 357 frag.appendTextChild(str()); 358 359 docFrag = (DocumentFragment)frag.getNode(frag.getDocument()); 360 } 361 else 362 { 363 DTM frag = support.getDTM(result); 364 docFrag = (DocumentFragment)frag.getNode(frag.getDocument()); 365 } 366 367 return docFrag; 368 } 369 370 371 /** 372 * For functions to override. 373 * 374 * @return null 375 */ 376 public DocumentFragment rtree() 377 { 378 return null; 379 } 380 381 /** 382 * For functions to override. 383 * 384 * @return null 385 */ 386 public int rtf() 387 { 388 return DTM.NULL; 389 } 390 391 /** 392 * Return a java object that's closest to the representation 393 * that should be handed to an extension. 394 * 395 * @return The object that this class wraps 396 */ 397 public Object object() 398 { 399 return m_obj; 400 } 401 402 /** 403 * Cast result object to a nodelist. Always issues an error. 404 * 405 * @return null 406 * 407 * @throws javax.xml.transform.TransformerException 408 */ 409 public DTMIterator iter() throws javax.xml.transform.TransformerException 410 { 411 412 error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST, 413 new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeList!"); 414 415 return null; 416 } 417 418 /** 419 * Get a fresh copy of the object. For use with variables. 420 * 421 * @return This object, unless overridden by subclass. 422 */ 423 public XObject getFresh() 424 { 425 return this; 426 } 427 428 429 /** 430 * Cast result object to a nodelist. Always issues an error. 431 * 432 * @return null 433 * 434 * @throws javax.xml.transform.TransformerException 435 */ 436 public NodeIterator nodeset() throws javax.xml.transform.TransformerException 437 { 438 439 error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST, 440 new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeList!"); 441 442 return null; 443 } 444 445 /** 446 * Cast result object to a nodelist. Always issues an error. 447 * 448 * @return null 449 * 450 * @throws javax.xml.transform.TransformerException 451 */ 452 public NodeList nodelist() throws javax.xml.transform.TransformerException 453 { 454 455 error(XPATHErrorResources.ER_CANT_CONVERT_TO_NODELIST, 456 new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeList!"); 457 458 return null; 459 } 460 461 462 /** 463 * Cast result object to a nodelist. Always issues an error. 464 * 465 * @return The object as a NodeSetDTM. 466 * 467 * @throws javax.xml.transform.TransformerException 468 */ 469 public NodeSetDTM mutableNodeset() 470 throws javax.xml.transform.TransformerException 471 { 472 473 error(XPATHErrorResources.ER_CANT_CONVERT_TO_MUTABLENODELIST, 474 new Object[]{ getTypeString() }); //"Can not convert "+getTypeString()+" to a NodeSetDTM!"); 475 476 return (NodeSetDTM) m_obj; 477 } 478 479 /** 480 * Cast object to type t. 481 * 482 * @param t Type of object to cast this to 483 * @param support XPath context to use for the conversion 484 * 485 * @return This object as the given type t 486 * 487 * @throws javax.xml.transform.TransformerException 488 */ 489 public Object castToType(int t, XPathContext support) 490 throws javax.xml.transform.TransformerException 491 { 492 493 Object result; 494 495 switch (t) 496 { 497 case CLASS_STRING : 498 result = str(); 499 break; 500 case CLASS_NUMBER : 501 result = new Double(num()); 502 break; 503 case CLASS_NODESET : 504 result = iter(); 505 break; 506 case CLASS_BOOLEAN : 507 result = new Boolean(bool()); 508 break; 509 case CLASS_UNKNOWN : 510 result = m_obj; 511 break; 512 513 // %TBD% What to do here? 514 // case CLASS_RTREEFRAG : 515 // result = rtree(support); 516 // break; 517 default : 518 error(XPATHErrorResources.ER_CANT_CONVERT_TO_TYPE, 519 new Object[]{ getTypeString(), 520 Integer.toString(t) }); //"Can not convert "+getTypeString()+" to a type#"+t); 521 522 result = null; 523 } 524 525 return result; 526 } 527 528 /** 529 * Tell if one object is less than the other. 530 * 531 * @param obj2 Object to compare this to 532 * 533 * @return True if this object is less than the given object 534 * 535 * @throws javax.xml.transform.TransformerException 536 */ 537 public boolean lessThan(XObject obj2) 538 throws javax.xml.transform.TransformerException 539 { 540 541 // In order to handle the 'all' semantics of 542 // nodeset comparisons, we always call the 543 // nodeset function. Because the arguments 544 // are backwards, we call the opposite comparison 545 // function. 546 if (obj2.getType() == XObject.CLASS_NODESET) 547 return obj2.greaterThan(this); 548 549 return this.num() < obj2.num(); 550 } 551 552 /** 553 * Tell if one object is less than or equal to the other. 554 * 555 * @param obj2 Object to compare this to 556 * 557 * @return True if this object is less than or equal to the given object 558 * 559 * @throws javax.xml.transform.TransformerException 560 */ 561 public boolean lessThanOrEqual(XObject obj2) 562 throws javax.xml.transform.TransformerException 563 { 564 565 // In order to handle the 'all' semantics of 566 // nodeset comparisons, we always call the 567 // nodeset function. Because the arguments 568 // are backwards, we call the opposite comparison 569 // function. 570 if (obj2.getType() == XObject.CLASS_NODESET) 571 return obj2.greaterThanOrEqual(this); 572 573 return this.num() <= obj2.num(); 574 } 575 576 /** 577 * Tell if one object is greater than the other. 578 * 579 * @param obj2 Object to compare this to 580 * 581 * @return True if this object is greater than the given object 582 * 583 * @throws javax.xml.transform.TransformerException 584 */ 585 public boolean greaterThan(XObject obj2) 586 throws javax.xml.transform.TransformerException 587 { 588 589 // In order to handle the 'all' semantics of 590 // nodeset comparisons, we always call the 591 // nodeset function. Because the arguments 592 // are backwards, we call the opposite comparison 593 // function. 594 if (obj2.getType() == XObject.CLASS_NODESET) 595 return obj2.lessThan(this); 596 597 return this.num() > obj2.num(); 598 } 599 600 /** 601 * Tell if one object is greater than or equal to the other. 602 * 603 * @param obj2 Object to compare this to 604 * 605 * @return True if this object is greater than or equal to the given object 606 * 607 * @throws javax.xml.transform.TransformerException 608 */ 609 public boolean greaterThanOrEqual(XObject obj2) 610 throws javax.xml.transform.TransformerException 611 { 612 613 // In order to handle the 'all' semantics of 614 // nodeset comparisons, we always call the 615 // nodeset function. Because the arguments 616 // are backwards, we call the opposite comparison 617 // function. 618 if (obj2.getType() == XObject.CLASS_NODESET) 619 return obj2.lessThanOrEqual(this); 620 621 return this.num() >= obj2.num(); 622 } 623 624 /** 625 * Tell if two objects are functionally equal. 626 * 627 * @param obj2 Object to compare this to 628 * 629 * @return True if this object is equal to the given object 630 * 631 * @throws javax.xml.transform.TransformerException 632 */ 633 public boolean equals(XObject obj2) 634 { 635 636 // In order to handle the 'all' semantics of 637 // nodeset comparisons, we always call the 638 // nodeset function. 639 if (obj2.getType() == XObject.CLASS_NODESET) 640 return obj2.equals(this); 641 642 if (null != m_obj) 643 { 644 return m_obj.equals(obj2.m_obj); 645 } 646 else 647 { 648 return obj2.m_obj == null; 649 } 650 } 651 652 /** 653 * Tell if two objects are functionally not equal. 654 * 655 * @param obj2 Object to compare this to 656 * 657 * @return True if this object is not equal to the given object 658 * 659 * @throws javax.xml.transform.TransformerException 660 */ 661 public boolean notEquals(XObject obj2) 662 throws javax.xml.transform.TransformerException 663 { 664 665 // In order to handle the 'all' semantics of 666 // nodeset comparisons, we always call the 667 // nodeset function. 668 if (obj2.getType() == XObject.CLASS_NODESET) 669 return obj2.notEquals(this); 670 671 return !equals(obj2); 672 } 673 674 /** 675 * Tell the user of an error, and probably throw an 676 * exception. 677 * 678 * @param msg Error message to issue 679 * 680 * @throws javax.xml.transform.TransformerException 681 */ 682 protected void error(String msg) 683 throws javax.xml.transform.TransformerException 684 { 685 error(msg, null); 686 } 687 688 /** 689 * Tell the user of an error, and probably throw an 690 * exception. 691 * 692 * @param msg Error message to issue 693 * @param args Arguments to use in the message 694 * 695 * @throws javax.xml.transform.TransformerException 696 */ 697 protected void error(String msg, Object[] args) 698 throws javax.xml.transform.TransformerException 699 { 700 701 String fmsg = XSLMessages.createXPATHMessage(msg, args); 702 703 // boolean shouldThrow = support.problem(m_support.XPATHPROCESSOR, 704 // m_support.ERROR, 705 // null, 706 // null, fmsg, 0, 0); 707 // if(shouldThrow) 708 { 709 throw new XPathException(fmsg, this); 710 } 711 } 712 713 714 /** 715 * XObjects should not normally need to fix up variables. 716 */ 717 public void fixupVariables(java.util.Vector vars, int globalsSize) 718 { 719 // no-op 720 } 721 722 723 /** 724 * Cast result object to a string. 725 * 726 * 727 * NEEDSDOC @param fsb 728 * @return The string this wraps or the empty string if null 729 */ 730 public void appendToFsb(org.apache.xml.utils.FastStringBuffer fsb) 731 { 732 fsb.append(str()); 733 } 734 735 /** 736 * @see org.apache.xpath.XPathVisitable#callVisitors(ExpressionOwner, XPathVisitor) 737 */ 738 public void callVisitors(ExpressionOwner owner, XPathVisitor visitor) 739 { 740 assertion(false, "callVisitors should not be called for this object!!!"); 741 } 742 /** 743 * @see Expression#deepEquals(Expression) 744 */ 745 public boolean deepEquals(Expression expr) 746 { 747 if(!isSameClass(expr)) 748 return false; 749 750 // If equals at the expression level calls deepEquals, I think we're 751 // still safe from infinite recursion since this object overrides 752 // equals. I hope. 753 if(!this.equals((XObject)expr)) 754 return false; 755 756 return true; 757 } 758 759 } 760