001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: XNodeSet.java 469368 2006-10-31 04:41:36Z minchau $
020 */
021 package org.apache.xpath.objects;
022
023 import org.apache.xml.dtm.DTM;
024 import org.apache.xml.dtm.DTMIterator;
025 import org.apache.xml.dtm.DTMManager;
026 import org.apache.xml.utils.XMLString;
027 import org.apache.xpath.NodeSetDTM;
028 import org.apache.xpath.axes.NodeSequence;
029
030 import org.w3c.dom.NodeList;
031 import org.w3c.dom.traversal.NodeIterator;
032
033 /**
034 * This class represents an XPath nodeset object, and is capable of
035 * converting the nodeset to other types, such as a string.
036 * @xsl.usage general
037 */
038 public class XNodeSet extends NodeSequence
039 {
040 static final long serialVersionUID = 1916026368035639667L;
041 /**
042 * Default constructor for derived objects.
043 */
044 protected XNodeSet()
045 {
046 }
047
048 /**
049 * Construct a XNodeSet object.
050 *
051 * @param val Value of the XNodeSet object
052 */
053 public XNodeSet(DTMIterator val)
054 {
055 super();
056 if(val instanceof XNodeSet)
057 {
058 final XNodeSet nodeSet = (XNodeSet) val;
059 setIter(nodeSet.m_iter);
060 m_dtmMgr = nodeSet.m_dtmMgr;
061 m_last = nodeSet.m_last;
062 // First make sure the DTMIterator val has a cache,
063 // so if it doesn't have one, make one.
064 if(!nodeSet.hasCache())
065 nodeSet.setShouldCacheNodes(true);
066
067 // Get the cache from val and use it ourselves (we share it).
068 setObject(nodeSet.getIteratorCache());
069 }
070 else
071 setIter(val);
072 }
073
074 /**
075 * Construct a XNodeSet object.
076 *
077 * @param val Value of the XNodeSet object
078 */
079 public XNodeSet(XNodeSet val)
080 {
081 super();
082 setIter(val.m_iter);
083 m_dtmMgr = val.m_dtmMgr;
084 m_last = val.m_last;
085 if(!val.hasCache())
086 val.setShouldCacheNodes(true);
087 setObject(val.m_obj);
088 }
089
090
091 /**
092 * Construct an empty XNodeSet object. This is used to create a mutable
093 * nodeset to which random nodes may be added.
094 */
095 public XNodeSet(DTMManager dtmMgr)
096 {
097 this(DTM.NULL,dtmMgr);
098 }
099
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 }