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