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    */
XNodeSet()44   protected XNodeSet()
45   {
46   }
47 
48   /**
49    * Construct a XNodeSet object.
50    *
51    * @param val Value of the XNodeSet object
52    */
XNodeSet(DTMIterator val)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    */
XNodeSet(XNodeSet val)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    */
XNodeSet(DTMManager dtmMgr)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    */
XNodeSet(int n, DTMManager dtmMgr)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    */
getType()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    */
getTypeString()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    */
getNumberFromNode(int n)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    */
num()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    */
numWithSideEffects()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    */
bool()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    */
boolWithSideEffects()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    */
getStringFromNode(int n)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    */
dispatchCharactersEvents(org.xml.sax.ContentHandler ch)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    */
xstr()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    */
appendToFsb(org.apache.xml.utils.FastStringBuffer fsb)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    */
str()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    */
object()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    */
nodeset()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    */
nodelist()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    */
iterRaw()371   public DTMIterator iterRaw()
372   {
373     return this;
374   }
375 
release(DTMIterator iter)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    */
iter()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    */
getFresh()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    */
mutableNodeset()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    */
compare(XObject obj2, Comparator comparator)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    */
lessThan(XObject obj2)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    */
lessThanOrEqual(XObject obj2)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    */
greaterThan(XObject obj2)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    */
greaterThanOrEqual(XObject obj2)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    */
equals(XObject obj2)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    */
notEquals(XObject obj2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)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    */
compareStrings(XMLString s1, XMLString s2)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    */
compareNumbers(double n1, double n2)965   boolean compareNumbers(double n1, double n2)
966   {
967     return n1 != n2;
968   }
969 }
970