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.templates;
  58. //import org.w3c.dom.*;
  59. //import org.w3c.dom.traversal.NodeIterator;
  60. import org.apache.xml.dtm.DTM;
  61. import org.apache.xml.dtm.DTMIterator;
  62. import org.apache.xml.dtm.DTMManager;
  63. // Experemental
  64. import org.apache.xml.dtm.ref.ExpandedNameTable;
  65. import org.xml.sax.*;
  66. import org.apache.xpath.*;
  67. import org.apache.xpath.Expression;
  68. import org.apache.xpath.axes.ContextNodeList;
  69. import org.apache.xpath.objects.XObject;
  70. import java.util.Vector;
  71. import org.apache.xml.utils.QName;
  72. import org.apache.xml.utils.IntStack;
  73. import org.apache.xml.utils.PrefixResolver;
  74. import org.apache.xalan.res.XSLTErrorResources;
  75. import org.apache.xalan.transformer.TransformerImpl;
  76. import org.apache.xalan.transformer.NodeSorter;
  77. import org.apache.xalan.transformer.ResultTreeHandler;
  78. import org.apache.xalan.transformer.ClonerToResultTree;
  79. import javax.xml.transform.SourceLocator;
  80. import javax.xml.transform.TransformerException;
  81. import org.apache.xpath.ExpressionOwner;
  82. /**
  83. * <meta name="usage" content="advanced"/>
  84. * Implement xsl:for-each.
  85. * <pre>
  86. * <!ELEMENT xsl:for-each
  87. * (#PCDATA
  88. * %instructions;
  89. * %result-elements;
  90. * | xsl:sort)
  91. * >
  92. *
  93. * <!ATTLIST xsl:for-each
  94. * select %expr; #REQUIRED
  95. * %space-att;
  96. * >
  97. * </pre>
  98. * @see <a href="http://www.w3.org/TR/xslt#for-each">for-each in XSLT Specification</a>
  99. */
  100. public class ElemForEach extends ElemTemplateElement implements ExpressionOwner
  101. {
  102. /** Set true to request some basic status reports */
  103. static final boolean DEBUG = false;
  104. /**
  105. * This is set by an "xalan:doc-cache-off" pi. It tells the engine that
  106. * documents created in the location paths executed by this element
  107. * will not be reparsed. It's set by StylesheetHandler during
  108. * construction. Note that this feature applies _only_ to xsl:for-each
  109. * elements in its current incarnation; a more general cache management
  110. * solution is desperately needed.
  111. */
  112. public boolean m_doc_cache_off=false;
  113. /**
  114. * Construct a element representing xsl:for-each.
  115. */
  116. public ElemForEach(){}
  117. /**
  118. * The "select" expression.
  119. * @serial
  120. */
  121. protected Expression m_selectExpression = null;
  122. /**
  123. * Set the "select" attribute.
  124. *
  125. * @param xpath The XPath expression for the "select" attribute.
  126. */
  127. public void setSelect(XPath xpath)
  128. {
  129. m_selectExpression = xpath.getExpression();
  130. }
  131. /**
  132. * Get the "select" attribute.
  133. *
  134. * @return The XPath expression for the "select" attribute.
  135. */
  136. public Expression getSelect()
  137. {
  138. return m_selectExpression;
  139. }
  140. /**
  141. * This function is called after everything else has been
  142. * recomposed, and allows the template to set remaining
  143. * values that may be based on some other property that
  144. * depends on recomposition.
  145. *
  146. * NEEDSDOC @param sroot
  147. *
  148. * @throws TransformerException
  149. */
  150. public void compose(StylesheetRoot sroot) throws TransformerException
  151. {
  152. super.compose(sroot);
  153. int length = getSortElemCount();
  154. for (int i = 0; i < length; i++)
  155. {
  156. getSortElem(i).compose(sroot);
  157. }
  158. java.util.Vector vnames = sroot.getComposeState().getVariableNames();
  159. if (null != m_selectExpression)
  160. m_selectExpression.fixupVariables(
  161. vnames, sroot.getComposeState().getGlobalsSize());
  162. else
  163. {
  164. m_selectExpression =
  165. getStylesheetRoot().m_selectDefault.getExpression();
  166. }
  167. }
  168. /**
  169. * This after the template's children have been composed.
  170. */
  171. public void endCompose(StylesheetRoot sroot) throws TransformerException
  172. {
  173. int length = getSortElemCount();
  174. for (int i = 0; i < length; i++)
  175. {
  176. getSortElem(i).endCompose(sroot);
  177. }
  178. super.endCompose(sroot);
  179. }
  180. // /**
  181. // * This function is called after everything else has been
  182. // * recomposed, and allows the template to set remaining
  183. // * values that may be based on some other property that
  184. // * depends on recomposition.
  185. // *
  186. // * @throws TransformerException
  187. // */
  188. // public void compose() throws TransformerException
  189. // {
  190. //
  191. // if (null == m_selectExpression)
  192. // {
  193. // m_selectExpression =
  194. // getStylesheetRoot().m_selectDefault.getExpression();
  195. // }
  196. // }
  197. /**
  198. * Vector containing the xsl:sort elements associated with this element.
  199. * @serial
  200. */
  201. protected Vector m_sortElems = null;
  202. /**
  203. * Get the count xsl:sort elements associated with this element.
  204. * @return The number of xsl:sort elements.
  205. */
  206. public int getSortElemCount()
  207. {
  208. return (m_sortElems == null) ? 0 : m_sortElems.size();
  209. }
  210. /**
  211. * Get a xsl:sort element associated with this element.
  212. *
  213. * @param i Index of xsl:sort element to get
  214. *
  215. * @return xsl:sort element at given index
  216. */
  217. public ElemSort getSortElem(int i)
  218. {
  219. return (ElemSort) m_sortElems.elementAt(i);
  220. }
  221. /**
  222. * Set a xsl:sort element associated with this element.
  223. *
  224. * @param sortElem xsl:sort element to set
  225. */
  226. public void setSortElem(ElemSort sortElem)
  227. {
  228. if (null == m_sortElems)
  229. m_sortElems = new Vector();
  230. m_sortElems.addElement(sortElem);
  231. }
  232. /**
  233. * Get an int constant identifying the type of element.
  234. * @see org.apache.xalan.templates.Constants
  235. *
  236. * @return The token ID for this element
  237. */
  238. public int getXSLToken()
  239. {
  240. return Constants.ELEMNAME_FOREACH;
  241. }
  242. /**
  243. * Return the node name.
  244. *
  245. * @return The element's name
  246. */
  247. public String getNodeName()
  248. {
  249. return Constants.ELEMNAME_FOREACH_STRING;
  250. }
  251. /**
  252. * Execute the xsl:for-each transformation
  253. *
  254. * @param transformer non-null reference to the the current transform-time state.
  255. *
  256. * @throws TransformerException
  257. */
  258. public void execute(TransformerImpl transformer) throws TransformerException
  259. {
  260. transformer.pushCurrentTemplateRuleIsNull(true);
  261. if (TransformerImpl.S_DEBUG)
  262. transformer.getTraceManager().fireTraceEvent(this);
  263. try
  264. {
  265. transformSelectedNodes(transformer);
  266. }
  267. finally
  268. {
  269. if (TransformerImpl.S_DEBUG)
  270. transformer.getTraceManager().fireTraceEndEvent(this);
  271. transformer.popCurrentTemplateRuleIsNull();
  272. }
  273. }
  274. /**
  275. * Get template element associated with this
  276. *
  277. *
  278. * @return template element associated with this (itself)
  279. */
  280. protected ElemTemplateElement getTemplateMatch()
  281. {
  282. return this;
  283. }
  284. /**
  285. * Sort given nodes
  286. *
  287. *
  288. * @param xctxt The XPath runtime state for the sort.
  289. * @param keys Vector of sort keyx
  290. * @param sourceNodes Iterator of nodes to sort
  291. *
  292. * @return iterator of sorted nodes
  293. *
  294. * @throws TransformerException
  295. */
  296. public DTMIterator sortNodes(
  297. XPathContext xctxt, Vector keys, DTMIterator sourceNodes)
  298. throws TransformerException
  299. {
  300. NodeSorter sorter = new NodeSorter(xctxt);
  301. sourceNodes.setShouldCacheNodes(true);
  302. sourceNodes.runTo(-1);
  303. xctxt.pushContextNodeList(sourceNodes);
  304. try
  305. {
  306. sorter.sort(sourceNodes, keys, xctxt);
  307. sourceNodes.setCurrentPos(0);
  308. }
  309. finally
  310. {
  311. xctxt.popContextNodeList();
  312. }
  313. return sourceNodes;
  314. }
  315. /**
  316. * <meta name="usage" content="advanced"/>
  317. * Perform a query if needed, and call transformNode for each child.
  318. *
  319. * @param transformer non-null reference to the the current transform-time state.
  320. * @param template The owning template context.
  321. *
  322. * @throws TransformerException Thrown in a variety of circumstances.
  323. */
  324. public void transformSelectedNodes(TransformerImpl transformer)
  325. throws TransformerException
  326. {
  327. final XPathContext xctxt = transformer.getXPathContext();
  328. final int sourceNode = xctxt.getCurrentNode();
  329. DTMIterator sourceNodes = m_selectExpression.asIterator(xctxt,
  330. sourceNode);
  331. try
  332. {
  333. final Vector keys = (m_sortElems == null)
  334. ? null
  335. : transformer.processSortKeys(this, sourceNode);
  336. // Sort if we need to.
  337. if (null != keys)
  338. sourceNodes = sortNodes(xctxt, keys, sourceNodes);
  339. if (TransformerImpl.S_DEBUG)
  340. {
  341. transformer.getTraceManager().fireSelectedEvent(sourceNode, this,
  342. "select", new XPath(m_selectExpression),
  343. new org.apache.xpath.objects.XNodeSet(sourceNodes));
  344. }
  345. final ResultTreeHandler rth = transformer.getResultTreeHandler();
  346. ContentHandler chandler = rth.getContentHandler();
  347. xctxt.pushCurrentNode(DTM.NULL);
  348. IntStack currentNodes = xctxt.getCurrentNodeStack();
  349. xctxt.pushCurrentExpressionNode(DTM.NULL);
  350. IntStack currentExpressionNodes = xctxt.getCurrentExpressionNodeStack();
  351. xctxt.pushSAXLocatorNull();
  352. xctxt.pushContextNodeList(sourceNodes);
  353. transformer.pushElemTemplateElement(null);
  354. // pushParams(transformer, xctxt);
  355. // Should be able to get this from the iterator but there must be a bug.
  356. DTM dtm = xctxt.getDTM(sourceNode);
  357. int docID = sourceNode & DTMManager.IDENT_DTM_DEFAULT;
  358. int child;
  359. while (DTM.NULL != (child = sourceNodes.nextNode()))
  360. {
  361. currentNodes.setTop(child);
  362. currentExpressionNodes.setTop(child);
  363. if ((child & DTMManager.IDENT_DTM_DEFAULT) != docID)
  364. {
  365. dtm = xctxt.getDTM(child);
  366. docID = sourceNode & DTMManager.IDENT_DTM_DEFAULT;
  367. }
  368. //final int exNodeType = dtm.getExpandedTypeID(child);
  369. final int nodeType = dtm.getNodeType(child);
  370. // Fire a trace event for the template.
  371. if (TransformerImpl.S_DEBUG)
  372. {
  373. transformer.getTraceManager().fireTraceEvent(this);
  374. }
  375. // And execute the child templates.
  376. // Loop through the children of the template, calling execute on
  377. // each of them.
  378. for (ElemTemplateElement t = this.m_firstChild; t != null;
  379. t = t.m_nextSibling)
  380. {
  381. xctxt.setSAXLocator(t);
  382. transformer.setCurrentElement(t);
  383. t.execute(transformer);
  384. }
  385. if (TransformerImpl.S_DEBUG)
  386. {
  387. // We need to make sure an old current element is not
  388. // on the stack. See TransformerImpl#getElementCallstack.
  389. transformer.setCurrentElement(null);
  390. transformer.getTraceManager().fireTraceEndEvent(this);
  391. }
  392. // KLUGE: Implement <?xalan:doc_cache_off?>
  393. // ASSUMPTION: This will be set only when the XPath was indeed
  394. // a call to the Document() function. Calling it in other
  395. // situations is likely to fry Xalan.
  396. //
  397. // %REVIEW% We need a MUCH cleaner solution -- one that will
  398. // handle cleaning up after document() and getDTM() in other
  399. // contexts. The whole SourceTreeManager mechanism should probably
  400. // be moved into DTMManager rather than being explicitly invoked in
  401. // FuncDocument and here.
  402. if(m_doc_cache_off)
  403. {
  404. if(DEBUG)
  405. System.out.println("JJK***** CACHE RELEASE *****\n"+
  406. "\tdtm="+dtm.getDocumentBaseURI());
  407. // NOTE: This will work because this is _NOT_ a shared DTM, and thus has
  408. // only a single Document node. If it could ever be an RTF or other
  409. // shared DTM, this would require substantial rework.
  410. xctxt.getSourceTreeManager().removeDocumentFromCache(dtm.getDocument());
  411. xctxt.release(dtm,false);
  412. }
  413. }
  414. }
  415. finally
  416. {
  417. if (TransformerImpl.S_DEBUG)
  418. transformer.getTraceManager().fireSelectedEndEvent(sourceNode, this,
  419. "select", new XPath(m_selectExpression),
  420. new org.apache.xpath.objects.XNodeSet(sourceNodes));
  421. xctxt.popSAXLocator();
  422. xctxt.popContextNodeList();
  423. transformer.popElemTemplateElement();
  424. xctxt.popCurrentExpressionNode();
  425. xctxt.popCurrentNode();
  426. sourceNodes.detach();
  427. }
  428. }
  429. /**
  430. * Add a child to the child list.
  431. * <!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
  432. * <!ATTLIST xsl:apply-templates
  433. * select %expr; "node()"
  434. * mode %qname; #IMPLIED
  435. * >
  436. *
  437. * @param newChild Child to add to child list
  438. *
  439. * @return Child just added to child list
  440. */
  441. public ElemTemplateElement appendChild(ElemTemplateElement newChild)
  442. {
  443. int type = ((ElemTemplateElement) newChild).getXSLToken();
  444. if (Constants.ELEMNAME_SORT == type)
  445. {
  446. setSortElem((ElemSort) newChild);
  447. return newChild;
  448. }
  449. else
  450. return super.appendChild(newChild);
  451. }
  452. /**
  453. * Call the children visitors.
  454. * @param visitor The visitor whose appropriate method will be called.
  455. */
  456. public void callChildVisitors(XSLTVisitor visitor, boolean callAttributes)
  457. {
  458. if(callAttributes && (null != m_selectExpression))
  459. m_selectExpression.callVisitors(this, visitor);
  460. int length = getSortElemCount();
  461. for (int i = 0; i < length; i++)
  462. {
  463. getSortElem(i).callVisitors(visitor);
  464. }
  465. super.callChildVisitors(visitor, callAttributes);
  466. }
  467. /**
  468. * @see ExpressionOwner#getExpression()
  469. */
  470. public Expression getExpression()
  471. {
  472. return m_selectExpression;
  473. }
  474. /**
  475. * @see ExpressionOwner#setExpression(Expression)
  476. */
  477. public void setExpression(Expression exp)
  478. {
  479. exp.exprSetParent(this);
  480. m_selectExpression = exp;
  481. }
  482. }