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