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 }