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