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