1 /*
   2  * Copyright (c) 2005, 2017, 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.DOM2Helper;
  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 (DOM2Helper.isNodeAfter(testNode, endNode)
  76           && !DOM2Helper.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 (DOM2Helper.isNodeAfter(startNode, testNode)
 111           && !DOM2Helper.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 }