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: ExsltSets.java,v 1.10 2004/02/11 17:56:36 minchau Exp $
  18. */
  19. package com.sun.org.apache.xalan.internal.lib;
  20. import java.util.Hashtable;
  21. import com.sun.org.apache.xml.internal.utils.DOMHelper;
  22. import com.sun.org.apache.xpath.internal.NodeSet;
  23. import org.w3c.dom.Node;
  24. import org.w3c.dom.NodeList;
  25. /**
  26. * This class contains EXSLT set extension functions.
  27. * It is accessed by specifying a namespace URI as follows:
  28. * <pre>
  29. * xmlns:set="http://exslt.org/sets"
  30. * </pre>
  31. *
  32. * The documentation for each function has been copied from the relevant
  33. * EXSLT Implementer page.
  34. *
  35. * @see <a href="http://www.exslt.org/">EXSLT</a>
  36. * @xsl.usage general
  37. */
  38. public class ExsltSets extends ExsltBase
  39. {
  40. /**
  41. * The set:leading function returns the nodes in the node set passed as the first argument that
  42. * precede, in document order, the first node in the node set passed as the second argument. If
  43. * the first node in the second node set is not contained in the first node set, then an empty
  44. * node set is returned. If the second node set is empty, then the first node set is returned.
  45. *
  46. * @param nl1 NodeList for first node-set.
  47. * @param nl2 NodeList for second node-set.
  48. * @return a NodeList containing the nodes in nl1 that precede in document order the first
  49. * node in nl2; an empty node-set if the first node in nl2 is not in nl1; all of nl1 if nl2
  50. * is empty.
  51. *
  52. * @see <a href="http://www.exslt.org/">EXSLT</a>
  53. */
  54. public static NodeList leading (NodeList nl1, NodeList nl2)
  55. {
  56. if (nl2.getLength() == 0)
  57. return nl1;
  58. NodeSet ns1 = new NodeSet(nl1);
  59. NodeSet leadNodes = new NodeSet();
  60. Node endNode = nl2.item(0);
  61. if (!ns1.contains(endNode))
  62. return leadNodes; // empty NodeSet
  63. for (int i = 0; i < nl1.getLength(); i++)
  64. {
  65. Node testNode = nl1.item(i);
  66. if (DOMHelper.isNodeAfter(testNode, endNode)
  67. && !DOMHelper.isNodeTheSame(testNode, endNode))
  68. leadNodes.addElement(testNode);
  69. }
  70. return leadNodes;
  71. }
  72. /**
  73. * The set:trailing function returns the nodes in the node set passed as the first argument that
  74. * follow, in document order, the first node in the node set passed as the second argument. If
  75. * the first node in the second node set is not contained in the first node set, then an empty
  76. * node set is returned. If the second node set is empty, then the first node set is returned.
  77. *
  78. * @param nl1 NodeList for first node-set.
  79. * @param nl2 NodeList for second node-set.
  80. * @return a NodeList containing the nodes in nl1 that follow in document order the first
  81. * node in nl2; an empty node-set if the first node in nl2 is not in nl1; all of nl1 if nl2
  82. * is empty.
  83. *
  84. * @see <a href="http://www.exslt.org/">EXSLT</a>
  85. */
  86. public static NodeList trailing (NodeList nl1, NodeList nl2)
  87. {
  88. if (nl2.getLength() == 0)
  89. return nl1;
  90. NodeSet ns1 = new NodeSet(nl1);
  91. NodeSet trailNodes = new NodeSet();
  92. Node startNode = nl2.item(0);
  93. if (!ns1.contains(startNode))
  94. return trailNodes; // empty NodeSet
  95. for (int i = 0; i < nl1.getLength(); i++)
  96. {
  97. Node testNode = nl1.item(i);
  98. if (DOMHelper.isNodeAfter(startNode, testNode)
  99. && !DOMHelper.isNodeTheSame(startNode, testNode))
  100. trailNodes.addElement(testNode);
  101. }
  102. return trailNodes;
  103. }
  104. /**
  105. * The set:intersection function returns a node set comprising the nodes that are within
  106. * both the node sets passed as arguments to it.
  107. *
  108. * @param nl1 NodeList for first node-set.
  109. * @param nl2 NodeList for second node-set.
  110. * @return a NodeList containing the nodes in nl1 that are also
  111. * in nl2.
  112. *
  113. * @see <a href="http://www.exslt.org/">EXSLT</a>
  114. */
  115. public static NodeList intersection(NodeList nl1, NodeList nl2)
  116. {
  117. NodeSet ns1 = new NodeSet(nl1);
  118. NodeSet ns2 = new NodeSet(nl2);
  119. NodeSet inter = new NodeSet();
  120. inter.setShouldCacheNodes(true);
  121. for (int i = 0; i < ns1.getLength(); i++)
  122. {
  123. Node n = ns1.elementAt(i);
  124. if (ns2.contains(n))
  125. inter.addElement(n);
  126. }
  127. return inter;
  128. }
  129. /**
  130. * The set:difference function returns the difference between two node sets - those nodes that
  131. * are in the node set passed as the first argument that are not in the node set passed as the
  132. * second argument.
  133. *
  134. * @param nl1 NodeList for first node-set.
  135. * @param nl2 NodeList for second node-set.
  136. * @return a NodeList containing the nodes in nl1 that are not in nl2.
  137. *
  138. * @see <a href="http://www.exslt.org/">EXSLT</a>
  139. */
  140. public static NodeList difference(NodeList nl1, NodeList nl2)
  141. {
  142. NodeSet ns1 = new NodeSet(nl1);
  143. NodeSet ns2 = new NodeSet(nl2);
  144. NodeSet diff = new NodeSet();
  145. diff.setShouldCacheNodes(true);
  146. for (int i = 0; i < ns1.getLength(); i++)
  147. {
  148. Node n = ns1.elementAt(i);
  149. if (!ns2.contains(n))
  150. diff.addElement(n);
  151. }
  152. return diff;
  153. }
  154. /**
  155. * The set:distinct function returns a subset of the nodes contained in the node-set NS passed
  156. * as the first argument. Specifically, it selects a node N if there is no node in NS that has
  157. * the same string value as N, and that precedes N in document order.
  158. *
  159. * @param nl NodeList for the node-set.
  160. * @return a NodeList with nodes from nl containing distinct string values.
  161. * In other words, if more than one node in nl contains the same string value,
  162. * only include the first such node found.
  163. *
  164. * @see <a href="http://www.exslt.org/">EXSLT</a>
  165. */
  166. public static NodeList distinct(NodeList nl)
  167. {
  168. NodeSet dist = new NodeSet();
  169. dist.setShouldCacheNodes(true);
  170. Hashtable stringTable = new Hashtable();
  171. for (int i = 0; i < nl.getLength(); i++)
  172. {
  173. Node currNode = nl.item(i);
  174. String key = toString(currNode);
  175. if (key == null)
  176. dist.addElement(currNode);
  177. else if (!stringTable.containsKey(key))
  178. {
  179. stringTable.put(key, currNode);
  180. dist.addElement(currNode);
  181. }
  182. }
  183. return dist;
  184. }
  185. /**
  186. * The set:has-same-node function returns true if the node set passed as the first argument shares
  187. * any nodes with the node set passed as the second argument. If there are no nodes that are in both
  188. * node sets, then it returns false.
  189. *
  190. * The Xalan extensions MethodResolver converts 'has-same-node' to 'hasSameNode'.
  191. *
  192. * Note: Not to be confused with hasSameNodes in the Xalan namespace, which returns true if
  193. * the two node sets contain the exactly the same nodes (perhaps in a different order),
  194. * otherwise false.
  195. *
  196. * @see <a href="http://www.exslt.org/">EXSLT</a>
  197. */
  198. public static boolean hasSameNode(NodeList nl1, NodeList nl2)
  199. {
  200. NodeSet ns1 = new NodeSet(nl1);
  201. NodeSet ns2 = new NodeSet(nl2);
  202. for (int i = 0; i < ns1.getLength(); i++)
  203. {
  204. if (ns2.contains(ns1.elementAt(i)))
  205. return true;
  206. }
  207. return false;
  208. }
  209. }