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