1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xalan" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 1999, Lotus
  53. * Development Corporation., http://www.lotus.com. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package org.apache.xalan.lib;
  58. import org.w3c.dom.Node;
  59. import org.w3c.dom.Document;
  60. import org.w3c.dom.DocumentFragment;
  61. import org.w3c.dom.NodeList;
  62. import org.w3c.dom.Text;
  63. import org.w3c.dom.traversal.NodeIterator;
  64. import org.apache.xpath.NodeSet;
  65. import org.apache.xpath.objects.XObject;
  66. import org.apache.xpath.objects.XBoolean;
  67. import org.apache.xpath.objects.XNumber;
  68. import org.xml.sax.SAXNotSupportedException;
  69. import java.util.Hashtable;
  70. import java.util.StringTokenizer;
  71. import org.apache.xalan.extensions.ExpressionContext;
  72. import org.apache.xalan.res.XSLMessages;
  73. import org.apache.xalan.res.XSLTErrorResources;
  74. // Note: we should consider loading EnvironmentCheck at runtime
  75. // to simplify inter-package dependencies Sep-01 -sc
  76. import org.apache.xalan.xslt.EnvironmentCheck;
  77. import javax.xml.transform.TransformerException;
  78. import javax.xml.parsers.*;
  79. /**
  80. * <meta name="usage" content="general"/>
  81. * This class contains many of the Xalan-supplied extensions.
  82. * It is accessed by specifying a namespace URI as follows:
  83. * <pre>
  84. * xmlns:xalan="http://xml.apache.org/xalan"
  85. * </pre>
  86. */
  87. public class Extensions
  88. {
  89. // Reuse the Document object to reduce memory usage.
  90. private static Document lDoc = null;
  91. /**
  92. * Constructor Extensions
  93. *
  94. */
  95. private Extensions(){} // Make sure class cannot be instantiated
  96. /**
  97. * This method is an extension that implements as a Xalan extension
  98. * the node-set function also found in xt and saxon.
  99. * If the argument is a Result Tree Fragment, then <code>nodeset</code>
  100. * returns a node-set consisting of a single root node as described in
  101. * section 11.1 of the XSLT 1.0 Recommendation. If the argument is a
  102. * node-set, <code>nodeset</code> returns a node-set. If the argument
  103. * is a string, number, or boolean, then <code>nodeset</code> returns
  104. * a node-set consisting of a single root node with a single text node
  105. * child that is the result of calling the XPath string() function on the
  106. * passed parameter. If the argument is anything else, then a node-set
  107. * is returned consisting of a single root node with a single text node
  108. * child that is the result of calling the java <code>toString()</code>
  109. * method on the passed argument.
  110. * Most of the
  111. * actual work here is done in <code>MethodResolver</code> and
  112. * <code>XRTreeFrag</code>.
  113. * @param myProcessor Context passed by the extension processor
  114. * @param rtf Argument in the stylesheet to the nodeset extension function
  115. *
  116. * NEEDSDOC ($objectName$) @return
  117. */
  118. public static NodeSet nodeset(ExpressionContext myProcessor, Object rtf)
  119. {
  120. String textNodeValue;
  121. if (rtf instanceof NodeIterator)
  122. {
  123. return new NodeSet((NodeIterator) rtf);
  124. }
  125. else
  126. {
  127. if (rtf instanceof String)
  128. {
  129. textNodeValue = (String) rtf;
  130. }
  131. else if (rtf instanceof Boolean)
  132. {
  133. textNodeValue = new XBoolean(((Boolean) rtf).booleanValue()).str();
  134. }
  135. else if (rtf instanceof Double)
  136. {
  137. textNodeValue = new XNumber(((Double) rtf).doubleValue()).str();
  138. }
  139. else
  140. {
  141. textNodeValue = rtf.toString();
  142. }
  143. // This no longer will work right since the DTM.
  144. // Document myDoc = myProcessor.getContextNode().getOwnerDocument();
  145. try
  146. {
  147. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  148. DocumentBuilder db = dbf.newDocumentBuilder();
  149. Document myDoc = db.newDocument();
  150. Text textNode = myDoc.createTextNode(textNodeValue);
  151. DocumentFragment docFrag = myDoc.createDocumentFragment();
  152. docFrag.appendChild(textNode);
  153. return new NodeSet(docFrag);
  154. }
  155. catch(ParserConfigurationException pce)
  156. {
  157. throw new org.apache.xml.utils.WrappedRuntimeException(pce);
  158. }
  159. }
  160. }
  161. /**
  162. * Returns the intersection of two node-sets.
  163. *
  164. * @param nl1 NodeList for first node-set
  165. * @param nl2 NodeList for second node-set
  166. * @return a NodeList containing the nodes in nl1 that are also in nl2
  167. *
  168. * Note: The usage of this extension function in the xalan namespace
  169. * is deprecated. Please use the same function in the EXSLT sets extension
  170. * (http://exslt.org/sets).
  171. */
  172. public static NodeList intersection(NodeList nl1, NodeList nl2)
  173. {
  174. return ExsltSets.intersection(nl1, nl2);
  175. }
  176. /**
  177. * Returns the difference between two node-sets.
  178. *
  179. * @param nl1 NodeList for first node-set
  180. * @param nl2 NodeList for second node-set
  181. * @return a NodeList containing the nodes in nl1 that are not in nl2
  182. *
  183. * Note: The usage of this extension function in the xalan namespace
  184. * is deprecated. Please use the same function in the EXSLT sets extension
  185. * (http://exslt.org/sets).
  186. */
  187. public static NodeList difference(NodeList nl1, NodeList nl2)
  188. {
  189. return ExsltSets.difference(nl1, nl2);
  190. }
  191. /**
  192. * Returns node-set containing distinct string values.
  193. *
  194. * @param nl NodeList for node-set
  195. * @return a NodeList with nodes from nl containing distinct string values.
  196. * In other words, if more than one node in nl contains the same string value,
  197. * only include the first such node found.
  198. *
  199. * Note: The usage of this extension function in the xalan namespace
  200. * is deprecated. Please use the same function in the EXSLT sets extension
  201. * (http://exslt.org/sets).
  202. */
  203. public static NodeList distinct(NodeList nl)
  204. {
  205. return ExsltSets.distinct(nl);
  206. }
  207. /**
  208. * Returns true if both node-sets contain the same set of nodes.
  209. *
  210. * @param nl1 NodeList for first node-set
  211. * @param nl2 NodeList for second node-set
  212. * @return true if nl1 and nl2 contain exactly the same set of nodes.
  213. */
  214. public static boolean hasSameNodes(NodeList nl1, NodeList nl2)
  215. {
  216. NodeSet ns1 = new NodeSet(nl1);
  217. NodeSet ns2 = new NodeSet(nl2);
  218. if (ns1.getLength() != ns2.getLength())
  219. return false;
  220. for (int i = 0; i < ns1.getLength(); i++)
  221. {
  222. Node n = ns1.elementAt(i);
  223. if (!ns2.contains(n))
  224. return false;
  225. }
  226. return true;
  227. }
  228. /**
  229. * Returns the result of evaluating the argument as a string containing
  230. * an XPath expression. Used where the XPath expression is not known until
  231. * run-time. The expression is evaluated as if the run-time value of the
  232. * argument appeared in place of the evaluate function call at compile time.
  233. *
  234. * @param myContext an <code>ExpressionContext</code> passed in by the
  235. * extension mechanism. This must be an XPathContext.
  236. * @param xpathExtr The XPath expression to be evaluated.
  237. * @return the XObject resulting from evaluating the XPath
  238. *
  239. * @throws SAXNotSupportedException
  240. *
  241. * Note: The usage of this extension function in the xalan namespace
  242. * is deprecated. Please use the same function in the EXSLT dynamic extension
  243. * (http://exslt.org/dynamic).
  244. */
  245. public static XObject evaluate(ExpressionContext myContext, String xpathExpr)
  246. throws SAXNotSupportedException
  247. {
  248. return ExsltDynamic.evaluate(myContext, xpathExpr);
  249. }
  250. /**
  251. * Returns a NodeSet containing one text node for each token in the first argument.
  252. * Delimiters are specified in the second argument.
  253. * Tokens are determined by a call to <code>StringTokenizer</code>.
  254. * If the first argument is an empty string or contains only delimiters, the result
  255. * will be an empty NodeSet.
  256. *
  257. * Contributed to XalanJ1 by <a href="mailto:benoit.cerrina@writeme.com">Benoit Cerrina</a>.
  258. *
  259. * @param myContext an <code>ExpressionContext</code> passed in by the
  260. * extension mechanism. This must be an XPathContext.
  261. * @param toTokenize The string to be split into text tokens.
  262. * @param delims The delimiters to use.
  263. * @return a NodeSet as described above.
  264. */
  265. public static NodeList tokenize(String toTokenize, String delims)
  266. {
  267. try
  268. {
  269. if (lDoc == null)
  270. lDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
  271. }
  272. catch(ParserConfigurationException pce)
  273. {
  274. throw new org.apache.xml.utils.WrappedRuntimeException(pce);
  275. }
  276. StringTokenizer lTokenizer = new StringTokenizer(toTokenize, delims);
  277. NodeSet resultSet = new NodeSet();
  278. while (lTokenizer.hasMoreTokens())
  279. {
  280. resultSet.addNode(lDoc.createTextNode(lTokenizer.nextToken()));
  281. }
  282. return resultSet;
  283. }
  284. /**
  285. * Returns a NodeSet containing one text node for each token in the first argument.
  286. * Delimiters are whitespace. That is, the delimiters that are used are tab ( ),
  287. * linefeed ( ), return ( ), and space ( ).
  288. * Tokens are determined by a call to <code>StringTokenizer</code>.
  289. * If the first argument is an empty string or contains only delimiters, the result
  290. * will be an empty NodeSet.
  291. *
  292. * Contributed to XalanJ1 by <a href="mailto:benoit.cerrina@writeme.com">Benoit Cerrina</a>.
  293. *
  294. * @param myContext an <code>ExpressionContext</code> passed in by the
  295. * extension mechanism. This must be an XPathContext.
  296. * @param toTokenize The string to be split into text tokens.
  297. * @return a NodeSet as described above.
  298. */
  299. public static NodeList tokenize(String toTokenize)
  300. {
  301. return tokenize(toTokenize, " \t\n\r");
  302. }
  303. /**
  304. * Return a Node of basic debugging information from the
  305. * EnvironmentCheck utility about the Java environment.
  306. *
  307. * <p>Simply calls the {@link org.apache.xalan.xslt.EnvironmentCheck}
  308. * utility to grab info about the Java environment and CLASSPATH,
  309. * etc., and then returns the resulting Node. Stylesheets can
  310. * then maniuplate this data or simply xsl:copy-of the Node. Note
  311. * that we first attempt to load the more advanced
  312. * org.apache.env.Which utility by reflection; only if that fails
  313. * to we still use the internal version. Which is available from
  314. * <a href="http://xml.apache.org/commons/">http://xml.apache.org/commons/</a>.</p>
  315. *
  316. * <p>We throw a WrappedRuntimeException in the unlikely case
  317. * that reading information from the environment throws us an
  318. * exception. (Is this really the best thing to do?)</p>
  319. *
  320. * @param myContext an <code>ExpressionContext</code> passed in by the
  321. * extension mechanism. This must be an XPathContext.
  322. * @return a Node as described above.
  323. */
  324. public static Node checkEnvironment(ExpressionContext myContext)
  325. {
  326. Document factoryDocument;
  327. try
  328. {
  329. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  330. DocumentBuilder db = dbf.newDocumentBuilder();
  331. factoryDocument = db.newDocument();
  332. }
  333. catch(ParserConfigurationException pce)
  334. {
  335. throw new org.apache.xml.utils.WrappedRuntimeException(pce);
  336. }
  337. Node resultNode = null;
  338. try
  339. {
  340. // First use reflection to try to load Which, which is a
  341. // better version of EnvironmentCheck
  342. resultNode = checkEnvironmentUsingWhich(myContext, factoryDocument);
  343. if (null != resultNode)
  344. return resultNode;
  345. // If reflection failed, fallback to our internal EnvironmentCheck
  346. EnvironmentCheck envChecker = new EnvironmentCheck();
  347. Hashtable h = envChecker.getEnvironmentHash();
  348. resultNode = factoryDocument.createElement("checkEnvironmentExtension");
  349. envChecker.appendEnvironmentReport(resultNode, factoryDocument, h);
  350. envChecker = null;
  351. }
  352. catch(Exception e)
  353. {
  354. throw new org.apache.xml.utils.WrappedRuntimeException(e);
  355. }
  356. return resultNode;
  357. }
  358. /**
  359. * Private worker method to attempt to use org.apache.env.Which.
  360. *
  361. * @param myContext an <code>ExpressionContext</code> passed in by the
  362. * extension mechanism. This must be an XPathContext.
  363. * @param factoryDocument providing createElement services, etc.
  364. * @return a Node with environment info; null if any error
  365. */
  366. private static Node checkEnvironmentUsingWhich(ExpressionContext myContext,
  367. Document factoryDocument)
  368. {
  369. final String WHICH_CLASSNAME = "org.apache.env.Which";
  370. final String WHICH_METHODNAME = "which";
  371. final Class WHICH_METHOD_ARGS[] = { java.util.Hashtable.class,
  372. java.lang.String.class,
  373. java.lang.String.class };
  374. try
  375. {
  376. // Use reflection to try to find xml-commons utility 'Which'
  377. // Classloader note: if anyone really cares, we could try to
  378. // use the context classloader instead
  379. Class clazz = Class.forName(WHICH_CLASSNAME);
  380. if (null == clazz)
  381. return null;
  382. // Fully qualify names since this is the only method they're used in
  383. java.lang.reflect.Method method = clazz.getMethod(WHICH_METHODNAME, WHICH_METHOD_ARGS);
  384. Hashtable report = new Hashtable();
  385. // Call the method with our Hashtable, common options, and ignore return value
  386. Object[] methodArgs = { report, "XmlCommons;Xalan;Xerces;Crimson;Ant", "" };
  387. Object returnValue = method.invoke(null, methodArgs);
  388. // Create a parent to hold the report and append hash to it
  389. Node resultNode = factoryDocument.createElement("checkEnvironmentExtension");
  390. org.apache.xml.utils.Hashtree2Node.appendHashToNode(report, "whichReport",
  391. resultNode, factoryDocument);
  392. return resultNode;
  393. }
  394. catch (Throwable t)
  395. {
  396. // Simply return null; no need to report error
  397. return null;
  398. }
  399. }
  400. }