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: ExsltSets.java 469688 2006-10-31 22:39:43Z minchau $ 020 */ 021 package org.apache.xalan.lib; 022 023 import java.util.HashMap; 024 import java.util.Map; 025 026 import org.apache.xml.utils.DOMHelper; 027 import org.apache.xpath.NodeSet; 028 import org.w3c.dom.Node; 029 import org.w3c.dom.NodeList; 030 031 /** 032 * This class contains EXSLT set extension functions. 033 * It is accessed by specifying a namespace URI as follows: 034 * <pre> 035 * xmlns:set="http://exslt.org/sets" 036 * </pre> 037 * 038 * The documentation for each function has been copied from the relevant 039 * EXSLT Implementer page. 040 * 041 * @see <a href="http://www.exslt.org/">EXSLT</a> 042 * @xsl.usage general 043 */ 044 public class ExsltSets extends ExsltBase 045 { 046 /** 047 * The set:leading function returns the nodes in the node set passed as the first argument that 048 * precede, in document order, the first node in the node set passed as the second argument. If 049 * the first node in the second node set is not contained in the first node set, then an empty 050 * node set is returned. If the second node set is empty, then the first node set is returned. 051 * 052 * @param nl1 NodeList for first node-set. 053 * @param nl2 NodeList for second node-set. 054 * @return a NodeList containing the nodes in nl1 that precede in document order the first 055 * node in nl2; an empty node-set if the first node in nl2 is not in nl1; all of nl1 if nl2 056 * is empty. 057 * 058 * @see <a href="http://www.exslt.org/">EXSLT</a> 059 */ 060 public static NodeList leading (NodeList nl1, NodeList nl2) 061 { 062 if (nl2.getLength() == 0) 063 return nl1; 064 065 NodeSet ns1 = new NodeSet(nl1); 066 NodeSet leadNodes = new NodeSet(); 067 Node endNode = nl2.item(0); 068 if (!ns1.contains(endNode)) 069 return leadNodes; // empty NodeSet 070 071 for (int i = 0; i < nl1.getLength(); i++) 072 { 073 Node testNode = nl1.item(i); 074 if (DOMHelper.isNodeAfter(testNode, endNode) 075 && !DOMHelper.isNodeTheSame(testNode, endNode)) 076 leadNodes.addElement(testNode); 077 } 078 return leadNodes; 079 } 080 081 /** 082 * The set:trailing function returns the nodes in the node set passed as the first argument that 083 * follow, in document order, the first node in the node set passed as the second argument. If 084 * the first node in the second node set is not contained in the first node set, then an empty 085 * node set is returned. If the second node set is empty, then the first node set is returned. 086 * 087 * @param nl1 NodeList for first node-set. 088 * @param nl2 NodeList for second node-set. 089 * @return a NodeList containing the nodes in nl1 that follow in document order the first 090 * node in nl2; an empty node-set if the first node in nl2 is not in nl1; all of nl1 if nl2 091 * is empty. 092 * 093 * @see <a href="http://www.exslt.org/">EXSLT</a> 094 */ 095 public static NodeList trailing (NodeList nl1, NodeList nl2) 096 { 097 if (nl2.getLength() == 0) 098 return nl1; 099 100 NodeSet ns1 = new NodeSet(nl1); 101 NodeSet trailNodes = new NodeSet(); 102 Node startNode = nl2.item(0); 103 if (!ns1.contains(startNode)) 104 return trailNodes; // empty NodeSet 105 106 for (int i = 0; i < nl1.getLength(); i++) 107 { 108 Node testNode = nl1.item(i); 109 if (DOMHelper.isNodeAfter(startNode, testNode) 110 && !DOMHelper.isNodeTheSame(startNode, testNode)) 111 trailNodes.addElement(testNode); 112 } 113 return trailNodes; 114 } 115 116 /** 117 * The set:intersection function returns a node set comprising the nodes that are within 118 * both the node sets passed as arguments to it. 119 * 120 * @param nl1 NodeList for first node-set. 121 * @param nl2 NodeList for second node-set. 122 * @return a NodeList containing the nodes in nl1 that are also 123 * in nl2. 124 * 125 * @see <a href="http://www.exslt.org/">EXSLT</a> 126 */ 127 public static NodeList intersection(NodeList nl1, NodeList nl2) 128 { 129 NodeSet ns1 = new NodeSet(nl1); 130 NodeSet ns2 = new NodeSet(nl2); 131 NodeSet inter = new NodeSet(); 132 133 inter.setShouldCacheNodes(true); 134 135 for (int i = 0; i < ns1.getLength(); i++) 136 { 137 Node n = ns1.elementAt(i); 138 139 if (ns2.contains(n)) 140 inter.addElement(n); 141 } 142 143 return inter; 144 } 145 146 /** 147 * The set:difference function returns the difference between two node sets - those nodes that 148 * are in the node set passed as the first argument that are not in the node set passed as the 149 * second argument. 150 * 151 * @param nl1 NodeList for first node-set. 152 * @param nl2 NodeList for second node-set. 153 * @return a NodeList containing the nodes in nl1 that are not in nl2. 154 * 155 * @see <a href="http://www.exslt.org/">EXSLT</a> 156 */ 157 public static NodeList difference(NodeList nl1, NodeList nl2) 158 { 159 NodeSet ns1 = new NodeSet(nl1); 160 NodeSet ns2 = new NodeSet(nl2); 161 162 NodeSet diff = new NodeSet(); 163 164 diff.setShouldCacheNodes(true); 165 166 for (int i = 0; i < ns1.getLength(); i++) 167 { 168 Node n = ns1.elementAt(i); 169 170 if (!ns2.contains(n)) 171 diff.addElement(n); 172 } 173 174 return diff; 175 } 176 177 /** 178 * The set:distinct function returns a subset of the nodes contained in the node-set NS passed 179 * as the first argument. Specifically, it selects a node N if there is no node in NS that has 180 * the same string value as N, and that precedes N in document order. 181 * 182 * @param nl NodeList for the node-set. 183 * @return a NodeList with nodes from nl containing distinct string values. 184 * In other words, if more than one node in nl contains the same string value, 185 * only include the first such node found. 186 * 187 * @see <a href="http://www.exslt.org/">EXSLT</a> 188 */ 189 public static NodeList distinct(NodeList nl) 190 { 191 NodeSet dist = new NodeSet(); 192 dist.setShouldCacheNodes(true); 193 194 Map stringTable = new HashMap(); 195 196 for (int i = 0; i < nl.getLength(); i++) 197 { 198 Node currNode = nl.item(i); 199 String key = toString(currNode); 200 201 if (key == null) 202 dist.addElement(currNode); 203 else if (!stringTable.containsKey(key)) 204 { 205 stringTable.put(key, currNode); 206 dist.addElement(currNode); 207 } 208 } 209 210 return dist; 211 } 212 213 /** 214 * The set:has-same-node function returns true if the node set passed as the first argument shares 215 * any nodes with the node set passed as the second argument. If there are no nodes that are in both 216 * node sets, then it returns false. 217 * 218 * The Xalan extensions MethodResolver converts 'has-same-node' to 'hasSameNode'. 219 * 220 * Note: Not to be confused with hasSameNodes in the Xalan namespace, which returns true if 221 * the two node sets contain the exactly the same nodes (perhaps in a different order), 222 * otherwise false. 223 * 224 * @see <a href="http://www.exslt.org/">EXSLT</a> 225 */ 226 public static boolean hasSameNode(NodeList nl1, NodeList nl2) 227 { 228 229 NodeSet ns1 = new NodeSet(nl1); 230 NodeSet ns2 = new NodeSet(nl2); 231 232 for (int i = 0; i < ns1.getLength(); i++) 233 { 234 if (ns2.contains(ns1.elementAt(i))) 235 return true; 236 } 237 return false; 238 } 239 240 }