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