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.xml.internal.dtm.DTM; 25 import com.sun.org.apache.xml.internal.dtm.DTMIterator; 26 import com.sun.org.apache.xml.internal.dtm.DTMManager; 27 import com.sun.org.apache.xml.internal.utils.XMLString; 28 import com.sun.org.apache.xpath.internal.NodeSetDTM; 29 import com.sun.org.apache.xpath.internal.axes.NodeSequence; 30 import java.util.ArrayList; 31 import java.util.List; 32 import org.w3c.dom.NodeList; 33 import org.w3c.dom.traversal.NodeIterator; 34 35 /** 36 * This class represents an XPath nodeset object, and is capable of 37 * converting the nodeset to other types, such as a string. 38 * @xsl.usage general 39 */ 40 public class XNodeSet extends NodeSequence 41 { 42 static final long serialVersionUID = 1916026368035639667L; 43 /** 44 * Default constructor for derived objects. 45 */ 46 protected XNodeSet() 47 { 48 } 49 50 /** 51 * Construct a XNodeSet object. 52 * 53 * @param val Value of the XNodeSet object 54 */ 55 public XNodeSet(DTMIterator val) 56 { 57 super(); 58 if(val instanceof XNodeSet) 59 { 60 final XNodeSet nodeSet = (XNodeSet) val; 61 setIter(nodeSet.m_iter); 62 m_dtmMgr = nodeSet.m_dtmMgr; 63 m_last = nodeSet.m_last; 64 // First make sure the DTMIterator val has a cache, 65 // so if it doesn't have one, make one. 66 if(!nodeSet.hasCache()) 67 nodeSet.setShouldCacheNodes(true); 68 69 // Get the cache from val and use it ourselves (we share it). 70 setObject(nodeSet.getIteratorCache()); 71 } 72 else 73 setIter(val); 74 } 75 76 /** 77 * Construct a XNodeSet object. 78 * 79 * @param val Value of the XNodeSet object 80 */ 81 public XNodeSet(XNodeSet val) 82 { 83 super(); 84 setIter(val.m_iter); 85 m_dtmMgr = val.m_dtmMgr; 86 m_last = val.m_last; 87 if(!val.hasCache()) 88 val.setShouldCacheNodes(true); 89 setObject(val.m_obj); 90 } 91 92 93 /** 94 * Construct an empty XNodeSet object. This is used to create a mutable 95 * nodeset to which random nodes may be added. 96 */ 97 public XNodeSet(DTMManager dtmMgr) 98 { 99 this(DTM.NULL,dtmMgr); 100 } 101 102 /** 103 * Construct a XNodeSet object for one node. 104 * 105 * @param n Node to add to the new XNodeSet object 106 */ 107 public XNodeSet(int n, DTMManager dtmMgr) 108 { 109 110 super(new NodeSetDTM(dtmMgr)); 111 m_dtmMgr = dtmMgr; 112 113 if (DTM.NULL != n) 114 { 115 ((NodeSetDTM) m_obj).addNode(n); 116 m_last = 1; 117 } 118 else 119 m_last = 0; 120 } 121 122 /** 123 * Tell that this is a CLASS_NODESET. 124 * 125 * @return type CLASS_NODESET 126 */ 127 public int getType() 128 { 129 return CLASS_NODESET; 130 } 131 132 /** 133 * Given a request type, return the equivalent string. 134 * For diagnostic purposes. 135 * 136 * @return type string "#NODESET" 137 */ 138 public String getTypeString() 139 { 140 return "#NODESET"; 141 } 142 143 /** 144 * Get numeric value of the string conversion from a single node. 145 * 146 * @param n Node to convert 147 * 148 * @return numeric value of the string conversion from a single node. 149 */ 150 public double getNumberFromNode(int n) 151 { 152 XMLString xstr = m_dtmMgr.getDTM(n).getStringValue(n); 153 return xstr.toDouble(); 154 } 155 156 /** 157 * Cast result object to a number. 158 * 159 * @return numeric value of the string conversion from the 160 * next node in the NodeSetDTM, or NAN if no node was found 161 */ 162 public double num() 163 { 164 165 int node = item(0); 166 return (node != DTM.NULL) ? getNumberFromNode(node) : Double.NaN; 167 } 168 169 /** 170 * Cast result object to a number, but allow side effects, such as the 171 * incrementing of an iterator. 172 * 173 * @return numeric value of the string conversion from the 174 * next node in the NodeSetDTM, or NAN if no node was found 175 */ 176 public double numWithSideEffects() 177 { 178 int node = nextNode(); 179 180 return (node != DTM.NULL) ? getNumberFromNode(node) : Double.NaN; 181 } 182 183 184 /** 185 * Cast result object to a boolean. 186 * 187 * @return True if there is a next node in the nodeset 188 */ 189 public boolean bool() 190 { 191 return (item(0) != DTM.NULL); 192 } 193 194 /** 195 * Cast result object to a boolean, but allow side effects, such as the 196 * incrementing of an iterator. 197 * 198 * @return True if there is a next node in the nodeset 199 */ 200 public boolean boolWithSideEffects() 201 { 202 return (nextNode() != DTM.NULL); 203 } 204 205 206 /** 207 * Get the string conversion from a single node. 208 * 209 * @param n Node to convert 210 * 211 * @return the string conversion from a single node. 212 */ 213 public XMLString getStringFromNode(int n) 214 { 215 // %OPT% 216 // I guess we'll have to get a static instance of the DTM manager... 217 if(DTM.NULL != n) 218 { 219 return m_dtmMgr.getDTM(n).getStringValue(n); 220 } 221 else 222 { 223 return com.sun.org.apache.xpath.internal.objects.XString.EMPTYSTRING; 224 } 225 } 226 227 /** 228 * Directly call the 229 * characters method on the passed ContentHandler for the 230 * string-value. Multiple calls to the 231 * ContentHandler's characters methods may well occur for a single call to 232 * this method. 233 * 234 * @param ch A non-null reference to a ContentHandler. 235 * 236 * @throws org.xml.sax.SAXException 237 */ 238 public void dispatchCharactersEvents(org.xml.sax.ContentHandler ch) 239 throws org.xml.sax.SAXException 240 { 241 int node = item(0); 242 243 if(node != DTM.NULL) 244 { 245 m_dtmMgr.getDTM(node).dispatchCharactersEvents(node, ch, false); 246 } 247 248 } 249 250 /** 251 * Cast result object to an XMLString. 252 * 253 * @return The document fragment node data or the empty string. 254 */ 255 public XMLString xstr() 256 { 257 int node = item(0); 258 return (node != DTM.NULL) ? getStringFromNode(node) : XString.EMPTYSTRING; 259 } 260 261 /** 262 * Cast result object to a string. 263 * 264 * @return The string this wraps or the empty string if null 265 */ 266 public void appendToFsb(com.sun.org.apache.xml.internal.utils.FastStringBuffer fsb) 267 { 268 XString xstring = (XString)xstr(); 269 xstring.appendToFsb(fsb); 270 } 271 272 273 /** 274 * Cast result object to a string. 275 * 276 * @return the string conversion from the next node in the nodeset 277 * or "" if there is no next node 278 */ 279 public String str() 280 { 281 int node = item(0); 282 return (node != DTM.NULL) ? getStringFromNode(node).toString() : ""; 283 } 284 285 /** 286 * Return a java object that's closest to the representation 287 * that should be handed to an extension. 288 * 289 * @return The object that this class wraps 290 */ 291 public Object object() 292 { 293 if(null == m_obj) 294 return this; 295 else 296 return m_obj; 297 } 298 299 // %REVIEW% 300 // hmmm... 301 // /** 302 // * Cast result object to a result tree fragment. 303 // * 304 // * @param support The XPath context to use for the conversion 305 // * 306 // * @return the nodeset as a result tree fragment. 307 // */ 308 // public DocumentFragment rtree(XPathContext support) 309 // { 310 // DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 311 // DocumentBuilder db = dbf.newDocumentBuilder(); 312 // Document myDoc = db.newDocument(); 313 // 314 // DocumentFragment docFrag = myDoc.createDocumentFragment(); 315 // 316 // DTMIterator nl = iter(); 317 // int node; 318 // 319 // while (DTM.NULL != (node = nl.nextNode())) 320 // { 321 // frag.appendChild(node, true, true); 322 // } 323 // 324 // return frag.getDocument(); 325 // } 326 327 /** 328 * Cast result object to a nodelist. 329 * 330 * @return a NodeIterator. 331 * 332 * @throws javax.xml.transform.TransformerException 333 */ 334 public NodeIterator nodeset() throws javax.xml.transform.TransformerException 335 { 336 return new com.sun.org.apache.xml.internal.dtm.ref.DTMNodeIterator(iter()); 337 } 338 339 /** 340 * Cast result object to a nodelist. 341 * 342 * @return a NodeList. 343 * 344 * @throws javax.xml.transform.TransformerException 345 */ 346 public NodeList nodelist() throws javax.xml.transform.TransformerException 347 { 348 com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList nodelist = new com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList(this); 349 // Creating a DTMNodeList has the side-effect that it will create a clone 350 // XNodeSet with cache and run m_iter to the end. You cannot get any node 351 // from m_iter after this call. As a fix, we call SetVector() on the clone's 352 // cache. See Bugzilla 14406. 353 XNodeSet clone = (XNodeSet)nodelist.getDTMIterator(); 354 SetVector(clone.getVector()); 355 return nodelist; 356 } 357 358 359 // /** 360 // * Return a java object that's closest to the representation 361 // * that should be handed to an extension. 362 // * 363 // * @return The object that this class wraps 364 // */ 365 // public Object object() 366 // { 367 // return new com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList(iter()); 368 // } 369 370 /** 371 * Return the iterator without cloning, etc. 372 */ 373 public DTMIterator iterRaw() 374 { 375 return this; 376 } 377 378 public void release(DTMIterator iter) 379 { 380 } 381 382 /** 383 * Cast result object to a nodelist. 384 * 385 * @return The nodeset as a nodelist 386 */ 387 public DTMIterator iter() 388 { 389 try 390 { 391 if(hasCache()) 392 return cloneWithReset(); 393 else 394 return this; // don't bother to clone... won't do any good! 395 } 396 catch (CloneNotSupportedException cnse) 397 { 398 throw new RuntimeException(cnse.getMessage()); 399 } 400 } 401 402 /** 403 * Get a fresh copy of the object. For use with variables. 404 * 405 * @return A fresh nodelist. 406 */ 407 public XObject getFresh() 408 { 409 try 410 { 411 if(hasCache()) 412 return (XObject)cloneWithReset(); 413 else 414 return this; // don't bother to clone... won't do any good! 415 } 416 catch (CloneNotSupportedException cnse) 417 { 418 throw new RuntimeException(cnse.getMessage()); 419 } 420 } 421 422 /** 423 * Cast result object to a mutableNodeset. 424 * 425 * @return The nodeset as a mutableNodeset 426 */ 427 public NodeSetDTM mutableNodeset() 428 { 429 NodeSetDTM mnl; 430 431 if(m_obj instanceof NodeSetDTM) 432 { 433 mnl = (NodeSetDTM) m_obj; 434 } 435 else 436 { 437 mnl = new NodeSetDTM(iter()); 438 setObject(mnl); 439 setCurrentPos(0); 440 } 441 442 return mnl; 443 } 444 445 /** Less than comparator */ 446 static final LessThanComparator S_LT = new LessThanComparator(); 447 448 /** Less than or equal comparator */ 449 static final LessThanOrEqualComparator S_LTE = new LessThanOrEqualComparator(); 450 451 /** Greater than comparator */ 452 static final GreaterThanComparator S_GT = new GreaterThanComparator(); 453 454 /** Greater than or equal comparator */ 455 static final GreaterThanOrEqualComparator S_GTE = 456 new GreaterThanOrEqualComparator(); 457 458 /** Equal comparator */ 459 static final EqualComparator S_EQ = new EqualComparator(); 460 461 /** Not equal comparator */ 462 static final NotEqualComparator S_NEQ = new NotEqualComparator(); 463 464 /** 465 * Tell if one object is less than the other. 466 * 467 * @param obj2 Object to compare this nodeset to 468 * @param comparator Comparator to use 469 * 470 * @return See the comments below for each object type comparison 471 * 472 * @throws javax.xml.transform.TransformerException 473 */ 474 public boolean compare(XObject obj2, Comparator comparator) 475 throws javax.xml.transform.TransformerException 476 { 477 478 boolean result = false; 479 int type = obj2.getType(); 480 481 if (XObject.CLASS_NODESET == type) 482 { 483 // %OPT% This should be XMLString based instead of string based... 484 485 // From http://www.w3.org/TR/xpath: 486 // If both objects to be compared are node-sets, then the comparison 487 // will be true if and only if there is a node in the first node-set 488 // and a node in the second node-set such that the result of performing 489 // the comparison on the string-values of the two nodes is true. 490 // Note this little gem from the draft: 491 // NOTE: If $x is bound to a node-set, then $x="foo" 492 // does not mean the same as not($x!="foo"): the former 493 // is true if and only if some node in $x has the string-value 494 // foo; the latter is true if and only if all nodes in $x have 495 // the string-value foo. 496 DTMIterator list1 = iterRaw(); 497 DTMIterator list2 = ((XNodeSet) obj2).iterRaw(); 498 int node1; 499 List<XMLString> node2Strings = null; 500 501 while (DTM.NULL != (node1 = list1.nextNode())) 502 { 503 XMLString s1 = getStringFromNode(node1); 504 505 if (null == node2Strings) 506 { 507 int node2; 508 509 while (DTM.NULL != (node2 = list2.nextNode())) 510 { 511 XMLString s2 = getStringFromNode(node2); 512 513 if (comparator.compareStrings(s1, s2)) 514 { 515 result = true; 516 517 break; 518 } 519 520 if (null == node2Strings) 521 node2Strings = new ArrayList<>(); 522 523 node2Strings.add(s2); 524 } 525 } 526 else 527 { 528 int n = node2Strings.size(); 529 530 for (int i = 0; i < n; i++) 531 { 532 if (comparator.compareStrings(s1, node2Strings.get(i))) 533 { 534 result = true; 535 536 break; 537 } 538 } 539 } 540 } 541 list1.reset(); 542 list2.reset(); 543 } 544 else if (XObject.CLASS_BOOLEAN == type) 545 { 546 547 // From http://www.w3.org/TR/xpath: 548 // If one object to be compared is a node-set and the other is a boolean, 549 // then the comparison will be true if and only if the result of 550 // performing the comparison on the boolean and on the result of 551 // converting the node-set to a boolean using the boolean function 552 // is true. 553 double num1 = bool() ? 1.0 : 0.0; 554 double num2 = obj2.num(); 555 556 result = comparator.compareNumbers(num1, num2); 557 } 558 else if (XObject.CLASS_NUMBER == type) 559 { 560 561 // From http://www.w3.org/TR/xpath: 562 // If one object to be compared is a node-set and the other is a number, 563 // then the comparison will be true if and only if there is a 564 // node in the node-set such that the result of performing the 565 // comparison on the number to be compared and on the result of 566 // converting the string-value of that node to a number using 567 // the number function is true. 568 DTMIterator list1 = iterRaw(); 569 double num2 = obj2.num(); 570 int node; 571 572 while (DTM.NULL != (node = list1.nextNode())) 573 { 574 double num1 = getNumberFromNode(node); 575 576 if (comparator.compareNumbers(num1, num2)) 577 { 578 result = true; 579 580 break; 581 } 582 } 583 list1.reset(); 584 } 585 else if (XObject.CLASS_RTREEFRAG == type) 586 { 587 XMLString s2 = obj2.xstr(); 588 DTMIterator list1 = iterRaw(); 589 int node; 590 591 while (DTM.NULL != (node = list1.nextNode())) 592 { 593 XMLString s1 = getStringFromNode(node); 594 595 if (comparator.compareStrings(s1, s2)) 596 { 597 result = true; 598 599 break; 600 } 601 } 602 list1.reset(); 603 } 604 else if (XObject.CLASS_STRING == type) 605 { 606 607 // From http://www.w3.org/TR/xpath: 608 // If one object to be compared is a node-set and the other is a 609 // string, then the comparison will be true if and only if there 610 // is a node in the node-set such that the result of performing 611 // the comparison on the string-value of the node and the other 612 // string is true. 613 XMLString s2 = obj2.xstr(); 614 DTMIterator list1 = iterRaw(); 615 int node; 616 617 while (DTM.NULL != (node = list1.nextNode())) 618 { 619 XMLString s1 = getStringFromNode(node); 620 if (comparator.compareStrings(s1, s2)) 621 { 622 result = true; 623 624 break; 625 } 626 } 627 list1.reset(); 628 } 629 else 630 { 631 result = comparator.compareNumbers(this.num(), obj2.num()); 632 } 633 634 return result; 635 } 636 637 /** 638 * Tell if one object is less than the other. 639 * 640 * @param obj2 object to compare this nodeset to 641 * 642 * @return see this.compare(...) 643 * 644 * @throws javax.xml.transform.TransformerException 645 */ 646 public boolean lessThan(XObject obj2) throws javax.xml.transform.TransformerException 647 { 648 return compare(obj2, S_LT); 649 } 650 651 /** 652 * Tell if one object is less than or equal to the other. 653 * 654 * @param obj2 object to compare this nodeset to 655 * 656 * @return see this.compare(...) 657 * 658 * @throws javax.xml.transform.TransformerException 659 */ 660 public boolean lessThanOrEqual(XObject obj2) throws javax.xml.transform.TransformerException 661 { 662 return compare(obj2, S_LTE); 663 } 664 665 /** 666 * Tell if one object is less than the other. 667 * 668 * @param obj2 object to compare this nodeset to 669 * 670 * @return see this.compare(...) 671 * 672 * @throws javax.xml.transform.TransformerException 673 */ 674 public boolean greaterThan(XObject obj2) throws javax.xml.transform.TransformerException 675 { 676 return compare(obj2, S_GT); 677 } 678 679 /** 680 * Tell if one object is less than the other. 681 * 682 * @param obj2 object to compare this nodeset to 683 * 684 * @return see this.compare(...) 685 * 686 * @throws javax.xml.transform.TransformerException 687 */ 688 public boolean greaterThanOrEqual(XObject obj2) 689 throws javax.xml.transform.TransformerException 690 { 691 return compare(obj2, S_GTE); 692 } 693 694 /** 695 * Tell if two objects are functionally equal. 696 * 697 * @param obj2 object to compare this nodeset to 698 * 699 * @return see this.compare(...) 700 * 701 * @throws javax.xml.transform.TransformerException 702 */ 703 public boolean equals(XObject obj2) 704 { 705 try 706 { 707 return compare(obj2, S_EQ); 708 } 709 catch(javax.xml.transform.TransformerException te) 710 { 711 throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(te); 712 } 713 } 714 715 /** 716 * Tell if two objects are functionally not equal. 717 * 718 * @param obj2 object to compare this nodeset to 719 * 720 * @return see this.compare(...) 721 * 722 * @throws javax.xml.transform.TransformerException 723 */ 724 public boolean notEquals(XObject obj2) throws javax.xml.transform.TransformerException 725 { 726 return compare(obj2, S_NEQ); 727 } 728 } 729 730 /** 731 * compares nodes for various boolean operations. 732 */ 733 abstract class Comparator 734 { 735 736 /** 737 * Compare two strings 738 * 739 * 740 * @param s1 First string to compare 741 * @param s2 Second String to compare 742 * 743 * @return Whether the strings are equal or not 744 */ 745 abstract boolean compareStrings(XMLString s1, XMLString s2); 746 747 /** 748 * Compare two numbers 749 * 750 * 751 * @param n1 First number to compare 752 * @param n2 Second number to compare 753 * 754 * @return Whether the numbers are equal or not 755 */ 756 abstract boolean compareNumbers(double n1, double n2); 757 } 758 759 /** 760 * Compare strings or numbers for less than. 761 */ 762 class LessThanComparator extends Comparator 763 { 764 765 /** 766 * Compare two strings for less than. 767 * 768 * 769 * @param s1 First string to compare 770 * @param s2 Second String to compare 771 * 772 * @return True if s1 is less than s2 773 */ 774 boolean compareStrings(XMLString s1, XMLString s2) 775 { 776 return (s1.toDouble() < s2.toDouble()); 777 // return s1.compareTo(s2) < 0; 778 } 779 780 /** 781 * Compare two numbers for less than. 782 * 783 * 784 * @param n1 First number to compare 785 * @param n2 Second number to compare 786 * 787 * @return true if n1 is less than n2 788 */ 789 boolean compareNumbers(double n1, double n2) 790 { 791 return n1 < n2; 792 } 793 } 794 795 /** 796 * Compare strings or numbers for less than or equal. 797 */ 798 class LessThanOrEqualComparator extends Comparator 799 { 800 801 /** 802 * Compare two strings for less than or equal. 803 * 804 * 805 * @param s1 First string to compare 806 * @param s2 Second String to compare 807 * 808 * @return true if s1 is less than or equal to s2 809 */ 810 boolean compareStrings(XMLString s1, XMLString s2) 811 { 812 return (s1.toDouble() <= s2.toDouble()); 813 // return s1.compareTo(s2) <= 0; 814 } 815 816 /** 817 * Compare two numbers for less than or equal. 818 * 819 * 820 * @param n1 First number to compare 821 * @param n2 Second number to compare 822 * 823 * @return true if n1 is less than or equal to n2 824 */ 825 boolean compareNumbers(double n1, double n2) 826 { 827 return n1 <= n2; 828 } 829 } 830 831 /** 832 * Compare strings or numbers for greater than. 833 */ 834 class GreaterThanComparator extends Comparator 835 { 836 837 /** 838 * Compare two strings for greater than. 839 * 840 * 841 * @param s1 First string to compare 842 * @param s2 Second String to compare 843 * 844 * @return true if s1 is greater than s2 845 */ 846 boolean compareStrings(XMLString s1, XMLString s2) 847 { 848 return (s1.toDouble() > s2.toDouble()); 849 // return s1.compareTo(s2) > 0; 850 } 851 852 /** 853 * Compare two numbers for greater than. 854 * 855 * 856 * @param n1 First number to compare 857 * @param n2 Second number to compare 858 * 859 * @return true if n1 is greater than n2 860 */ 861 boolean compareNumbers(double n1, double n2) 862 { 863 return n1 > n2; 864 } 865 } 866 867 /** 868 * Compare strings or numbers for greater than or equal. 869 */ 870 class GreaterThanOrEqualComparator extends Comparator 871 { 872 873 /** 874 * Compare two strings for greater than or equal. 875 * 876 * 877 * @param s1 First string to compare 878 * @param s2 Second String to compare 879 * 880 * @return true if s1 is greater than or equal to s2 881 */ 882 boolean compareStrings(XMLString s1, XMLString s2) 883 { 884 return (s1.toDouble() >= s2.toDouble()); 885 // return s1.compareTo(s2) >= 0; 886 } 887 888 /** 889 * Compare two numbers for greater than or equal. 890 * 891 * 892 * @param n1 First number to compare 893 * @param n2 Second number to compare 894 * 895 * @return true if n1 is greater than or equal to n2 896 */ 897 boolean compareNumbers(double n1, double n2) 898 { 899 return n1 >= n2; 900 } 901 } 902 903 /** 904 * Compare strings or numbers for equality. 905 */ 906 class EqualComparator extends Comparator 907 { 908 909 /** 910 * Compare two strings for equality. 911 * 912 * 913 * @param s1 First string to compare 914 * @param s2 Second String to compare 915 * 916 * @return true if s1 is equal to s2 917 */ 918 boolean compareStrings(XMLString s1, XMLString s2) 919 { 920 return s1.equals(s2); 921 } 922 923 /** 924 * Compare two numbers for equality. 925 * 926 * 927 * @param n1 First number to compare 928 * @param n2 Second number to compare 929 * 930 * @return true if n1 is equal to n2 931 */ 932 boolean compareNumbers(double n1, double n2) 933 { 934 return n1 == n2; 935 } 936 } 937 938 /** 939 * Compare strings or numbers for non-equality. 940 */ 941 class NotEqualComparator extends Comparator 942 { 943 944 /** 945 * Compare two strings for non-equality. 946 * 947 * 948 * @param s1 First string to compare 949 * @param s2 Second String to compare 950 * 951 * @return true if s1 is not equal to s2 952 */ 953 boolean compareStrings(XMLString s1, XMLString s2) 954 { 955 return !s1.equals(s2); 956 } 957 958 /** 959 * Compare two numbers for non-equality. 960 * 961 * 962 * @param n1 First number to compare 963 * @param n2 Second number to compare 964 * 965 * @return true if n1 is not equal to n2 966 */ 967 boolean compareNumbers(double n1, double n2) 968 { 969 return n1 != n2; 970 } 971 }