1 /*
2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * Licensed to the Apache Software Foundation (ASF) under one or more
6  * contributor license agreements.  See the NOTICE file distributed with
7  * this work for additional information regarding copyright ownership.
8  * The ASF licenses this file to You under the Apache License, Version 2.0
9  * (the "License"); you may not use this file except in compliance with
10  * the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 package com.sun.org.apache.xpath.internal.objects;
22 
23 import com.sun.org.apache.xml.internal.dtm.DTM;
24 import com.sun.org.apache.xml.internal.dtm.DTMIterator;
25 import com.sun.org.apache.xml.internal.dtm.DTMManager;
26 import com.sun.org.apache.xml.internal.utils.XMLString;
27 import com.sun.org.apache.xpath.internal.NodeSetDTM;
28 import com.sun.org.apache.xpath.internal.axes.NodeSequence;
29 import java.util.ArrayList;
30 import java.util.List;
31 import org.w3c.dom.NodeList;
32 import org.w3c.dom.traversal.NodeIterator;
33 
34 /**
35  * This class represents an XPath nodeset object, and is capable of
36  * converting the nodeset to other types, such as a string.
37  * @xsl.usage general
38  * @LastModified: Oct 2017
39  */
40 public class XNodeSet extends NodeSequence
41 {
42     static final long serialVersionUID = 1916026368035639667L;
43   /**
44    * Default constructor for derived objects.
45    */
XNodeSet()46   protected XNodeSet()
47   {
48   }
49 
50   /**
51    * Construct a XNodeSet object.
52    *
53    * @param val Value of the XNodeSet object
54    */
XNodeSet(DTMIterator val)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    */
XNodeSet(XNodeSet val)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    */
XNodeSet(DTMManager dtmMgr)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    */
XNodeSet(int n, DTMManager dtmMgr)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    */
getType()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    */
getTypeString()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    */
getNumberFromNode(int n)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    */
num()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    */
numWithSideEffects()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    */
bool()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    */
boolWithSideEffects()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    */
getStringFromNode(int n)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    */
dispatchCharactersEvents(org.xml.sax.ContentHandler ch)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    */
xstr()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    */
appendToFsb(com.sun.org.apache.xml.internal.utils.FastStringBuffer fsb)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    */
str()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    */
object()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    */
nodeset()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    */
nodelist()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    */
iterRaw()373   public DTMIterator iterRaw()
374   {
375     return this;
376   }
377 
release(DTMIterator iter)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    */
iter()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    */
getFresh()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    */
mutableNodeset()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    */
compare(XObject obj2, Comparator comparator)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    */
lessThan(XObject obj2)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    */
lessThanOrEqual(XObject obj2)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    */
greaterThan(XObject obj2)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    */
greaterThanOrEqual(XObject obj2)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    */
equals(XObject obj2)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    */
notEquals(XObject obj2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)967   boolean compareNumbers(double n1, double n2)
968   {
969     return n1 != n2;
970   }
971 }
972