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.*;
  59. import org.apache.xpath.objects.XObject;
  60. import org.apache.xpath.objects.XBoolean;
  61. import org.apache.xpath.objects.XNumber;
  62. import org.apache.xpath.objects.XNodeSet;
  63. import org.apache.xpath.XPath;
  64. import org.apache.xpath.XPathContext;
  65. import org.apache.xpath.NodeSet;
  66. import org.apache.xpath.NodeSetDTM;
  67. import org.xml.sax.SAXNotSupportedException;
  68. import org.apache.xalan.extensions.ExpressionContext;
  69. import org.apache.xalan.res.XSLMessages;
  70. import org.apache.xalan.res.XSLTErrorResources;
  71. import javax.xml.transform.*;
  72. import javax.xml.parsers.*;
  73. /**
  74. * <meta name="usage" content="general"/>
  75. * This class contains EXSLT dynamic extension functions.
  76. *
  77. * It is accessed by specifying a namespace URI as follows:
  78. * <pre>
  79. * xmlns:math="http://exslt.org/dynamic"
  80. * </pre>
  81. * The documentation for each function has been copied from the relevant
  82. * EXSLT Implementer page.
  83. *
  84. * @see <a href="http://www.exslt.org/">EXSLT</a>
  85. */
  86. public class ExsltDynamic extends ExsltBase
  87. {
  88. public static final String EXSL_URI = "http://exslt.org/common";
  89. /**
  90. * The dyn:max function calculates the maximum value for the nodes passed as
  91. * the first argument, where the value of each node is calculated dynamically
  92. * using an XPath expression passed as a string as the second argument.
  93. * <p>
  94. * The expressions are evaluated relative to the nodes passed as the first argument.
  95. * In other words, the value for each node is calculated by evaluating the XPath
  96. * expression with all context information being the same as that for the call to
  97. * the dyn:max function itself, except for the following:
  98. * <p>
  99. * <ul>
  100. * <li>the context node is the node whose value is being calculated.</li>
  101. * <li>the context position is the position of the node within the node set passed as
  102. * the first argument to the dyn:max function, arranged in document order.</li>
  103. * <li>the context size is the number of nodes passed as the first argument to the
  104. * dyn:max function.</li>
  105. * </ul>
  106. * <p>
  107. * The dyn:max function returns the maximum of these values, calculated in exactly
  108. * the same way as for math:max.
  109. * <p>
  110. * If the expression string passed as the second argument is an invalid XPath
  111. * expression (including an empty string), this function returns NaN.
  112. * <p>
  113. * This function must take a second argument. To calculate the maximum of a set of
  114. * nodes based on their string values, you should use the math:max function.
  115. *
  116. * @param myContext The ExpressionContext passed by the extension processor
  117. * @param nl The node set
  118. * @param expr The expression string
  119. *
  120. * @return The maximum evaluation value
  121. */
  122. public static double max(ExpressionContext myContext, NodeList nl, String expr)
  123. throws SAXNotSupportedException
  124. {
  125. XPathContext xctxt = null;
  126. if (myContext instanceof XPathContext.XPathExpressionContext)
  127. xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
  128. else
  129. throw new SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object[]{myContext }));
  130. if (expr == null || expr.length() == 0)
  131. return Double.NaN;
  132. NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
  133. xctxt.pushContextNodeList(contextNodes);
  134. double maxValue = Double.MIN_VALUE;
  135. for (int i = 0; i < contextNodes.getLength(); i++)
  136. {
  137. int contextNode = contextNodes.item(i);
  138. xctxt.pushCurrentNode(contextNode);
  139. double result = 0;
  140. try
  141. {
  142. XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
  143. xctxt.getNamespaceContext(),
  144. XPath.SELECT);
  145. result = dynamicXPath.execute(xctxt, contextNode, xctxt.getNamespaceContext()).num();
  146. }
  147. catch (TransformerException e)
  148. {
  149. xctxt.popCurrentNode();
  150. xctxt.popContextNodeList();
  151. return Double.NaN;
  152. }
  153. xctxt.popCurrentNode();
  154. if (result > maxValue)
  155. maxValue = result;
  156. }
  157. xctxt.popContextNodeList();
  158. return maxValue;
  159. }
  160. /**
  161. * The dyn:min function calculates the minimum value for the nodes passed as the
  162. * first argument, where the value of each node is calculated dynamically using
  163. * an XPath expression passed as a string as the second argument.
  164. * <p>
  165. * The expressions are evaluated relative to the nodes passed as the first argument.
  166. * In other words, the value for each node is calculated by evaluating the XPath
  167. * expression with all context information being the same as that for the call to
  168. * the dyn:min function itself, except for the following:
  169. * <p>
  170. * <ul>
  171. * <li>the context node is the node whose value is being calculated.</li>
  172. * <li>the context position is the position of the node within the node set passed
  173. * as the first argument to the dyn:min function, arranged in document order.</li>
  174. * <li>the context size is the number of nodes passed as the first argument to the
  175. * dyn:min function.</li>
  176. * </ul>
  177. * <p>
  178. * The dyn:min function returns the minimum of these values, calculated in exactly
  179. * the same way as for math:min.
  180. * <p>
  181. * If the expression string passed as the second argument is an invalid XPath expression
  182. * (including an empty string), this function returns NaN.
  183. * <p>
  184. * This function must take a second argument. To calculate the minimum of a set of
  185. * nodes based on their string values, you should use the math:min function.
  186. *
  187. * @param myContext The ExpressionContext passed by the extension processor
  188. * @param nl The node set
  189. * @param expr The expression string
  190. *
  191. * @return The minimum evaluation value
  192. */
  193. public static double min(ExpressionContext myContext, NodeList nl, String expr)
  194. throws SAXNotSupportedException
  195. {
  196. XPathContext xctxt = null;
  197. if (myContext instanceof XPathContext.XPathExpressionContext)
  198. xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
  199. else
  200. throw new SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object[]{myContext }));
  201. if (expr == null || expr.length() == 0)
  202. return Double.NaN;
  203. NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
  204. xctxt.pushContextNodeList(contextNodes);
  205. double minValue = Double.MAX_VALUE;
  206. for (int i = 0; i < nl.getLength(); i++)
  207. {
  208. int contextNode = contextNodes.item(i);
  209. xctxt.pushCurrentNode(contextNode);
  210. double result = 0;
  211. try
  212. {
  213. XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
  214. xctxt.getNamespaceContext(),
  215. XPath.SELECT);
  216. result = dynamicXPath.execute(xctxt, contextNode, xctxt.getNamespaceContext()).num();
  217. }
  218. catch (TransformerException e)
  219. {
  220. xctxt.popCurrentNode();
  221. xctxt.popContextNodeList();
  222. return Double.NaN;
  223. }
  224. xctxt.popCurrentNode();
  225. if (result < minValue)
  226. minValue = result;
  227. }
  228. xctxt.popContextNodeList();
  229. return minValue;
  230. }
  231. /**
  232. * The dyn:sum function calculates the sum for the nodes passed as the first argument,
  233. * where the value of each node is calculated dynamically using an XPath expression
  234. * passed as a string as the second argument.
  235. * <p>
  236. * The expressions are evaluated relative to the nodes passed as the first argument.
  237. * In other words, the value for each node is calculated by evaluating the XPath
  238. * expression with all context information being the same as that for the call to
  239. * the dyn:sum function itself, except for the following:
  240. * <p>
  241. * <ul>
  242. * <li>the context node is the node whose value is being calculated.</li>
  243. * <li>the context position is the position of the node within the node set passed as
  244. * the first argument to the dyn:sum function, arranged in document order.</li>
  245. * <li>the context size is the number of nodes passed as the first argument to the
  246. * dyn:sum function.</li>
  247. * </ul>
  248. * <p>
  249. * The dyn:sum function returns the sumimum of these values, calculated in exactly
  250. * the same way as for sum.
  251. * <p>
  252. * If the expression string passed as the second argument is an invalid XPath
  253. * expression (including an empty string), this function returns NaN.
  254. * <p>
  255. * This function must take a second argument. To calculate the sumimum of a set of
  256. * nodes based on their string values, you should use the sum function.
  257. *
  258. * @param myContext The ExpressionContext passed by the extension processor
  259. * @param nl The node set
  260. * @param expr The expression string
  261. *
  262. * @return The sum of the evaluation value on each node
  263. */
  264. public static double sum(ExpressionContext myContext, NodeList nl, String expr)
  265. throws SAXNotSupportedException
  266. {
  267. XPathContext xctxt = null;
  268. if (myContext instanceof XPathContext.XPathExpressionContext)
  269. xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
  270. else
  271. throw new SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object[]{myContext }));
  272. if (expr == null || expr.length() == 0)
  273. return Double.NaN;
  274. NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
  275. xctxt.pushContextNodeList(contextNodes);
  276. double sum = 0;
  277. for (int i = 0; i < nl.getLength(); i++)
  278. {
  279. int contextNode = contextNodes.item(i);
  280. xctxt.pushCurrentNode(contextNode);
  281. double result = 0;
  282. try
  283. {
  284. XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
  285. xctxt.getNamespaceContext(),
  286. XPath.SELECT);
  287. result = dynamicXPath.execute(xctxt, contextNode, xctxt.getNamespaceContext()).num();
  288. }
  289. catch (TransformerException e)
  290. {
  291. xctxt.popCurrentNode();
  292. xctxt.popContextNodeList();
  293. return Double.NaN;
  294. }
  295. xctxt.popCurrentNode();
  296. sum = sum + result;
  297. }
  298. xctxt.popContextNodeList();
  299. return sum;
  300. }
  301. /**
  302. * The dyn:map function evaluates the expression passed as the second argument for
  303. * each of the nodes passed as the first argument, and returns a node set of those values.
  304. * <p>
  305. * The expressions are evaluated relative to the nodes passed as the first argument.
  306. * In other words, the value for each node is calculated by evaluating the XPath
  307. * expression with all context information being the same as that for the call to
  308. * the dyn:map function itself, except for the following:
  309. * <p>
  310. * <ul>
  311. * <li>The context node is the node whose value is being calculated.</li>
  312. * <li>the context position is the position of the node within the node set passed
  313. * as the first argument to the dyn:map function, arranged in document order.</li>
  314. * <li>the context size is the number of nodes passed as the first argument to the
  315. * dyn:map function.</li>
  316. * </ul>
  317. * <p>
  318. * If the expression string passed as the second argument is an invalid XPath
  319. * expression (including an empty string), this function returns an empty node set.
  320. * <p>
  321. * If the XPath expression evaluates as a node set, the dyn:map function returns
  322. * the union of the node sets returned by evaluating the expression for each of the
  323. * nodes in the first argument. Note that this may mean that the node set resulting
  324. * from the call to the dyn:map function contains a different number of nodes from
  325. * the number in the node set passed as the first argument to the function.
  326. * <p>
  327. * If the XPath expression evaluates as a number, the dyn:map function returns a
  328. * node set containing one exsl:number element (namespace http://exslt.org/common)
  329. * for each node in the node set passed as the first argument to the dyn:map function,
  330. * in document order. The string value of each exsl:number element is the same as
  331. * the result of converting the number resulting from evaluating the expression to
  332. * a string as with the number function, with the exception that Infinity results
  333. * in an exsl:number holding the highest number the implementation can store, and
  334. * -Infinity results in an exsl:number holding the lowest number the implementation
  335. * can store.
  336. * <p>
  337. * If the XPath expression evaluates as a boolean, the dyn:map function returns a
  338. * node set containing one exsl:boolean element (namespace http://exslt.org/common)
  339. * for each node in the node set passed as the first argument to the dyn:map function,
  340. * in document order. The string value of each exsl:boolean element is 'true' if the
  341. * expression evaluates as true for the node, and '' if the expression evaluates as
  342. * false.
  343. * <p>
  344. * Otherwise, the dyn:map function returns a node set containing one exsl:string
  345. * element (namespace http://exslt.org/common) for each node in the node set passed
  346. * as the first argument to the dyn:map function, in document order. The string
  347. * value of each exsl:string element is the same as the result of converting the
  348. * result of evaluating the expression for the relevant node to a string as with
  349. * the string function.
  350. *
  351. * @param myContext The ExpressionContext passed by the extension processor
  352. * @param nl The node set
  353. * @param expr The expression string
  354. *
  355. * @return The node set after evaluation
  356. */
  357. public static NodeList map(ExpressionContext myContext, NodeList nl, String expr)
  358. throws SAXNotSupportedException
  359. {
  360. XPathContext xctxt = null;
  361. if (myContext instanceof XPathContext.XPathExpressionContext)
  362. xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
  363. else
  364. throw new SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object[]{myContext }));
  365. if (expr == null || expr.length() == 0)
  366. return new NodeSet();
  367. NodeSetDTM contextNodes = new NodeSetDTM(nl, xctxt);
  368. xctxt.pushContextNodeList(contextNodes);
  369. NodeSet resultSet = new NodeSet();
  370. resultSet.setShouldCacheNodes(true);
  371. for (int i = 0; i < nl.getLength(); i++)
  372. {
  373. int contextNode = contextNodes.item(i);
  374. xctxt.pushCurrentNode(contextNode);
  375. XObject object = null;
  376. try
  377. {
  378. XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
  379. xctxt.getNamespaceContext(),
  380. XPath.SELECT);
  381. object = dynamicXPath.execute(xctxt, contextNode, xctxt.getNamespaceContext());
  382. if (object instanceof XNodeSet)
  383. {
  384. NodeList nodelist = null;
  385. nodelist = ((XNodeSet)object).nodelist();
  386. for (int k = 0; k < nodelist.getLength(); k++)
  387. {
  388. Node n = nodelist.item(k);
  389. if (!resultSet.contains(n))
  390. resultSet.addNode(n);
  391. }
  392. }
  393. else
  394. {
  395. Document lDoc = null;
  396. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  397. dbf.setNamespaceAware(true);
  398. DocumentBuilder db = dbf.newDocumentBuilder();
  399. lDoc = db.newDocument();
  400. Element element = null;
  401. if (object instanceof XNumber)
  402. element = lDoc.createElementNS(EXSL_URI, "exsl:number");
  403. else if (object instanceof XBoolean)
  404. element = lDoc.createElementNS(EXSL_URI, "exsl:boolean");
  405. else
  406. element = lDoc.createElementNS(EXSL_URI, "exsl:string");
  407. Text textNode = lDoc.createTextNode(object.str());
  408. element.appendChild(textNode);
  409. resultSet.addNode(element);
  410. }
  411. }
  412. catch (Exception e)
  413. {
  414. xctxt.popCurrentNode();
  415. xctxt.popContextNodeList();
  416. return new NodeSet();
  417. }
  418. xctxt.popCurrentNode();
  419. }
  420. xctxt.popContextNodeList();
  421. return resultSet;
  422. }
  423. /**
  424. * The dyn:evaluate function evaluates a string as an XPath expression and returns
  425. * the resulting value, which might be a boolean, number, string, node set, result
  426. * tree fragment or external object. The sole argument is the string to be evaluated.
  427. * <p>
  428. * If the expression string passed as the second argument is an invalid XPath
  429. * expression (including an empty string), this function returns an empty node set.
  430. * <p>
  431. * You should only use this function if the expression must be constructed dynamically,
  432. * otherwise it is much more efficient to use the expression literally.
  433. *
  434. * @param myContext The ExpressionContext passed by the extension processor
  435. * @param xpathExpr The XPath expression string
  436. *
  437. * @return The evaluation result
  438. */
  439. public static XObject evaluate(ExpressionContext myContext, String xpathExpr)
  440. throws SAXNotSupportedException
  441. {
  442. if (myContext instanceof XPathContext.XPathExpressionContext)
  443. {
  444. XPathContext xctxt = null;
  445. try
  446. {
  447. xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
  448. XPath dynamicXPath = new XPath(xpathExpr, xctxt.getSAXLocator(),
  449. xctxt.getNamespaceContext(),
  450. XPath.SELECT);
  451. return dynamicXPath.execute(xctxt, myContext.getContextNode(),
  452. xctxt.getNamespaceContext());
  453. }
  454. catch (TransformerException e)
  455. {
  456. return new XNodeSet(xctxt.getDTMManager());
  457. }
  458. }
  459. else
  460. throw new SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object[]{myContext })); //"Invalid context passed to evaluate "
  461. }
  462. /**
  463. * The dyn:closure function creates a node set resulting from transitive closure of
  464. * evaluating the expression passed as the second argument on each of the nodes passed
  465. * as the first argument, then on the node set resulting from that and so on until no
  466. * more nodes are found. For example:
  467. * <pre>
  468. * dyn:closure(., '*')
  469. * </pre>
  470. * returns all the descendant elements of the node (its element children, their
  471. * children, their children's children and so on).
  472. * <p>
  473. * The expression is thus evaluated several times, each with a different node set
  474. * acting as the context of the expression. The first time the expression is
  475. * evaluated, the context node set is the first argument passed to the dyn:closure
  476. * function. In other words, the node set for each node is calculated by evaluating
  477. * the XPath expression with all context information being the same as that for
  478. * the call to the dyn:closure function itself, except for the following:
  479. * <p>
  480. * <ul>
  481. * <li>the context node is the node whose value is being calculated.</li>
  482. * <li>the context position is the position of the node within the node set passed
  483. * as the first argument to the dyn:closure function, arranged in document order.</li>
  484. * <li>the context size is the number of nodes passed as the first argument to the
  485. * dyn:closure function.</li>
  486. * <li>the current node is the node whose value is being calculated.</li>
  487. * </ul>
  488. * <p>
  489. * The result for a particular iteration is the union of the node sets resulting
  490. * from evaluting the expression for each of the nodes in the source node set for
  491. * that iteration. This result is then used as the source node set for the next
  492. * iteration, and so on. The result of the function as a whole is the union of
  493. * the node sets generated by each iteration.
  494. * <p>
  495. * If the expression string passed as the second argument is an invalid XPath
  496. * expression (including an empty string) or an expression that does not return a
  497. * node set, this function returns an empty node set.
  498. *
  499. * @param myContext The ExpressionContext passed by the extension processor
  500. * @param nl The node set
  501. * @param expr The expression string
  502. *
  503. * @return The node set after evaluation
  504. */
  505. public static NodeList closure(ExpressionContext myContext, NodeList nl, String expr)
  506. throws SAXNotSupportedException
  507. {
  508. XPathContext xctxt = null;
  509. if (myContext instanceof XPathContext.XPathExpressionContext)
  510. xctxt = ((XPathContext.XPathExpressionContext) myContext).getXPathContext();
  511. else
  512. throw new SAXNotSupportedException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_CONTEXT_PASSED, new Object[]{myContext }));
  513. if (expr == null || expr.length() == 0)
  514. return new NodeSet();
  515. NodeSet closureSet = new NodeSet();
  516. closureSet.setShouldCacheNodes(true);
  517. NodeList iterationList = nl;
  518. do
  519. {
  520. NodeSet iterationSet = new NodeSet();
  521. NodeSetDTM contextNodes = new NodeSetDTM(iterationList, xctxt);
  522. xctxt.pushContextNodeList(contextNodes);
  523. for (int i = 0; i < iterationList.getLength(); i++)
  524. {
  525. int contextNode = contextNodes.item(i);
  526. xctxt.pushCurrentNode(contextNode);
  527. XObject object = null;
  528. try
  529. {
  530. XPath dynamicXPath = new XPath(expr, xctxt.getSAXLocator(),
  531. xctxt.getNamespaceContext(),
  532. XPath.SELECT);
  533. object = dynamicXPath.execute(xctxt, contextNode, xctxt.getNamespaceContext());
  534. if (object instanceof XNodeSet)
  535. {
  536. NodeList nodelist = null;
  537. nodelist = ((XNodeSet)object).nodelist();
  538. for (int k = 0; k < nodelist.getLength(); k++)
  539. {
  540. Node n = nodelist.item(k);
  541. if (!iterationSet.contains(n))
  542. iterationSet.addNode(n);
  543. }
  544. }
  545. else
  546. {
  547. xctxt.popCurrentNode();
  548. xctxt.popContextNodeList();
  549. return new NodeSet();
  550. }
  551. }
  552. catch (TransformerException e)
  553. {
  554. xctxt.popCurrentNode();
  555. xctxt.popContextNodeList();
  556. return new NodeSet();
  557. }
  558. xctxt.popCurrentNode();
  559. }
  560. xctxt.popContextNodeList();
  561. iterationList = iterationSet;
  562. for (int i = 0; i < iterationList.getLength(); i++)
  563. {
  564. Node n = iterationList.item(i);
  565. if (!closureSet.contains(n))
  566. closureSet.addNode(n);
  567. }
  568. } while(iterationList.getLength() > 0);
  569. return closureSet;
  570. }
  571. }