1 /*
2  * Copyright (c) 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 package com.sun.org.apache.xalan.internal.lib;
22 
23 import java.util.StringTokenizer;
24 
25 import com.sun.org.apache.xalan.internal.extensions.ExpressionContext;
26 import com.sun.org.apache.xpath.internal.NodeSet;
27 import com.sun.org.apache.xpath.internal.objects.XBoolean;
28 import com.sun.org.apache.xpath.internal.objects.XNumber;
29 import com.sun.org.apache.xpath.internal.objects.XObject;
30 import jdk.xml.internal.JdkXmlUtils;
31 
32 import org.w3c.dom.Document;
33 import org.w3c.dom.DocumentFragment;
34 import org.w3c.dom.Node;
35 import org.w3c.dom.NodeList;
36 import org.w3c.dom.Text;
37 import org.w3c.dom.traversal.NodeIterator;
38 
39 import org.xml.sax.SAXNotSupportedException;
40 
41 /**
42  * This class contains many of the Xalan-supplied extensions.
43  * It is accessed by specifying a namespace URI as follows:
44  * <pre>
45  *    xmlns:xalan="http://xml.apache.org/xalan"
46  * </pre>
47  * @xsl.usage general
48  */
49 public class Extensions
50 {
51   /**
52    * Constructor Extensions
53    *
54    */
Extensions()55   private Extensions(){}  // Make sure class cannot be instantiated
56 
57   /**
58    * This method is an extension that implements as a Xalan extension
59    * the node-set function also found in xt and saxon.
60    * If the argument is a Result Tree Fragment, then <code>nodeset</code>
61    * returns a node-set consisting of a single root node as described in
62    * section 11.1 of the XSLT 1.0 Recommendation.  If the argument is a
63    * node-set, <code>nodeset</code> returns a node-set.  If the argument
64    * is a string, number, or boolean, then <code>nodeset</code> returns
65    * a node-set consisting of a single root node with a single text node
66    * child that is the result of calling the XPath string() function on the
67    * passed parameter.  If the argument is anything else, then a node-set
68    * is returned consisting of a single root node with a single text node
69    * child that is the result of calling the java <code>toString()</code>
70    * method on the passed argument.
71    * Most of the
72    * actual work here is done in <code>MethodResolver</code> and
73    * <code>XRTreeFrag</code>.
74    * @param myProcessor Context passed by the extension processor
75    * @param rtf Argument in the stylesheet to the nodeset extension function
76    *
77    * NEEDSDOC ($objectName$) @return
78    */
nodeset(ExpressionContext myProcessor, Object rtf)79   public static NodeSet nodeset(ExpressionContext myProcessor, Object rtf)
80   {
81 
82     String textNodeValue;
83 
84     if (rtf instanceof NodeIterator)
85     {
86       return new NodeSet((NodeIterator) rtf);
87     }
88     else
89     {
90       if (rtf instanceof String)
91       {
92         textNodeValue = (String) rtf;
93       }
94       else if (rtf instanceof Boolean)
95       {
96         textNodeValue = new XBoolean(((Boolean) rtf).booleanValue()).str();
97       }
98       else if (rtf instanceof Double)
99       {
100         textNodeValue = new XNumber(((Double) rtf).doubleValue()).str();
101       }
102       else
103       {
104         textNodeValue = rtf.toString();
105       }
106 
107       // This no longer will work right since the DTM.
108       // Document myDoc = myProcessor.getContextNode().getOwnerDocument();
109       Document myDoc = JdkXmlUtils.getDOMDocument();
110 
111         Text textNode = myDoc.createTextNode(textNodeValue);
112         DocumentFragment docFrag = myDoc.createDocumentFragment();
113 
114         docFrag.appendChild(textNode);
115 
116       return new NodeSet(docFrag);
117     }
118   }
119 
120   /**
121    * Returns the intersection of two node-sets.
122    *
123    * @param nl1 NodeList for first node-set
124    * @param nl2 NodeList for second node-set
125    * @return a NodeList containing the nodes in nl1 that are also in nl2
126    *
127    * Note: The usage of this extension function in the xalan namespace
128    * is deprecated. Please use the same function in the EXSLT sets extension
129    * (http://exslt.org/sets).
130    */
intersection(NodeList nl1, NodeList nl2)131   public static NodeList intersection(NodeList nl1, NodeList nl2)
132   {
133     return ExsltSets.intersection(nl1, nl2);
134   }
135 
136   /**
137    * Returns the difference between two node-sets.
138    *
139    * @param nl1 NodeList for first node-set
140    * @param nl2 NodeList for second node-set
141    * @return a NodeList containing the nodes in nl1 that are not in nl2
142    *
143    * Note: The usage of this extension function in the xalan namespace
144    * is deprecated. Please use the same function in the EXSLT sets extension
145    * (http://exslt.org/sets).
146    */
difference(NodeList nl1, NodeList nl2)147   public static NodeList difference(NodeList nl1, NodeList nl2)
148   {
149     return ExsltSets.difference(nl1, nl2);
150   }
151 
152   /**
153    * Returns node-set containing distinct string values.
154    *
155    * @param nl NodeList for node-set
156    * @return a NodeList with nodes from nl containing distinct string values.
157    * In other words, if more than one node in nl contains the same string value,
158    * only include the first such node found.
159    *
160    * Note: The usage of this extension function in the xalan namespace
161    * is deprecated. Please use the same function in the EXSLT sets extension
162    * (http://exslt.org/sets).
163    */
distinct(NodeList nl)164   public static NodeList distinct(NodeList nl)
165   {
166     return ExsltSets.distinct(nl);
167   }
168 
169   /**
170    * Returns true if both node-sets contain the same set of nodes.
171    *
172    * @param nl1 NodeList for first node-set
173    * @param nl2 NodeList for second node-set
174    * @return true if nl1 and nl2 contain exactly the same set of nodes.
175    */
hasSameNodes(NodeList nl1, NodeList nl2)176   public static boolean hasSameNodes(NodeList nl1, NodeList nl2)
177   {
178 
179     NodeSet ns1 = new NodeSet(nl1);
180     NodeSet ns2 = new NodeSet(nl2);
181 
182     if (ns1.getLength() != ns2.getLength())
183       return false;
184 
185     for (int i = 0; i < ns1.getLength(); i++)
186     {
187       Node n = ns1.elementAt(i);
188 
189       if (!ns2.contains(n))
190         return false;
191     }
192 
193     return true;
194   }
195 
196   /**
197    * Returns the result of evaluating the argument as a string containing
198    * an XPath expression.  Used where the XPath expression is not known until
199    * run-time.  The expression is evaluated as if the run-time value of the
200    * argument appeared in place of the evaluate function call at compile time.
201    *
202    * @param myContext an <code>ExpressionContext</code> passed in by the
203    *                  extension mechanism.  This must be an XPathContext.
204    * @param xpathExpr The XPath expression to be evaluated.
205    * @return the XObject resulting from evaluating the XPath
206    *
207    * @throws SAXNotSupportedException
208    *
209    * Note: The usage of this extension function in the xalan namespace
210    * is deprecated. Please use the same function in the EXSLT dynamic extension
211    * (http://exslt.org/dynamic).
212    */
evaluate(ExpressionContext myContext, String xpathExpr)213   public static XObject evaluate(ExpressionContext myContext, String xpathExpr)
214          throws SAXNotSupportedException
215   {
216     return ExsltDynamic.evaluate(myContext, xpathExpr);
217   }
218 
219   /**
220    * Returns a NodeSet containing one text node for each token in the first argument.
221    * Delimiters are specified in the second argument.
222    * Tokens are determined by a call to <code>StringTokenizer</code>.
223    * If the first argument is an empty string or contains only delimiters, the result
224    * will be an empty NodeSet.
225    *
226    * Contributed to XalanJ1 by <a href="mailto:benoit.cerrina@writeme.com">Benoit Cerrina</a>.
227    *
228    * @param toTokenize The string to be split into text tokens.
229    * @param delims The delimiters to use.
230    * @return a NodeSet as described above.
231    */
tokenize(String toTokenize, String delims)232   public static NodeList tokenize(String toTokenize, String delims)
233   {
234 
235     Document doc = JdkXmlUtils.getDOMDocument();
236 
237     StringTokenizer lTokenizer = new StringTokenizer(toTokenize, delims);
238     NodeSet resultSet = new NodeSet();
239 
240     synchronized (doc)
241     {
242       while (lTokenizer.hasMoreTokens())
243       {
244         resultSet.addNode(doc.createTextNode(lTokenizer.nextToken()));
245       }
246     }
247 
248     return resultSet;
249   }
250 
251   /**
252    * Returns a NodeSet containing one text node for each token in the first argument.
253    * Delimiters are whitespace.  That is, the delimiters that are used are tab (&#x09),
254    * linefeed (&#x0A), return (&#x0D), and space (&#x20).
255    * Tokens are determined by a call to <code>StringTokenizer</code>.
256    * If the first argument is an empty string or contains only delimiters, the result
257    * will be an empty NodeSet.
258    *
259    * Contributed to XalanJ1 by <a href="mailto:benoit.cerrina@writeme.com">Benoit Cerrina</a>.
260    *
261    * @param toTokenize The string to be split into text tokens.
262    * @return a NodeSet as described above.
263    */
tokenize(String toTokenize)264   public static NodeList tokenize(String toTokenize)
265   {
266     return tokenize(toTokenize, " \t\n\r");
267   }
268 }
269