1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the  "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18/*
19 * $Id: ElemForEach.java 468643 2006-10-28 06:56:03Z minchau $
20 */
21package org.apache.xalan.templates;
22
23import java.util.Vector;
24
25import javax.xml.transform.TransformerException;
26
27import org.apache.xalan.transformer.NodeSorter;
28import org.apache.xalan.transformer.TransformerImpl;
29import org.apache.xml.dtm.DTM;
30import org.apache.xml.dtm.DTMIterator;
31import org.apache.xml.dtm.DTMManager;
32import org.apache.xml.utils.IntStack;
33import org.apache.xpath.Expression;
34import org.apache.xpath.ExpressionOwner;
35import org.apache.xpath.XPath;
36import org.apache.xpath.XPathContext;
37
38import java.io.ObjectInputStream;
39import java.io.IOException;
40
41/**
42 * Implement xsl:for-each.
43 * <pre>
44 * <!ELEMENT xsl:for-each
45 *  (#PCDATA
46 *   %instructions;
47 *   %result-elements;
48 *   | xsl:sort)
49 * >
50 *
51 * <!ATTLIST xsl:for-each
52 *   select %expr; #REQUIRED
53 *   %space-att;
54 * >
55 * </pre>
56 * @see <a href="http://www.w3.org/TR/xslt#for-each">for-each in XSLT Specification</a>
57 * @xsl.usage advanced
58 */
59public class ElemForEach extends ElemTemplateElement implements ExpressionOwner
60{
61    static final long serialVersionUID = 6018140636363583690L;
62  /** Set true to request some basic status reports */
63  static final boolean DEBUG = false;
64
65  /**
66   * This is set by an "xalan-doc-cache-off" pi, or the old "xalan:doc-cache-off" pi.
67   * The old form of the PI only works for XML parsers that are not namespace aware.
68   * It tells the engine that
69   * documents created in the location paths executed by this element
70   * will not be reparsed. It's set by StylesheetHandler during
71   * construction. Note that this feature applies _only_ to xsl:for-each
72   * elements in its current incarnation; a more general cache management
73   * solution is desperately needed.
74   */
75  public boolean m_doc_cache_off=false;
76
77  /**
78   * Construct a element representing xsl:for-each.
79   */
80  public ElemForEach(){}
81
82  /**
83   * The "select" expression.
84   * @serial
85   */
86  protected Expression m_selectExpression = null;
87
88
89  /**
90   * Used to fix bug#16889
91   * Store XPath away for later processing.
92   */
93  protected XPath m_xpath = null;
94
95  /**
96   * Set the "select" attribute.
97   *
98   * @param xpath The XPath expression for the "select" attribute.
99   */
100  public void setSelect(XPath xpath)
101  {
102    m_selectExpression = xpath.getExpression();
103
104    // The following line is part of the codes added to fix bug#16889
105    // Store xpath which will be needed when firing Selected Event
106    m_xpath = xpath;
107  }
108
109  /**
110   * Get the "select" attribute.
111   *
112   * @return The XPath expression for the "select" attribute.
113   */
114  public Expression getSelect()
115  {
116    return m_selectExpression;
117  }
118
119  /**
120   * This function is called after everything else has been
121   * recomposed, and allows the template to set remaining
122   * values that may be based on some other property that
123   * depends on recomposition.
124   *
125   * NEEDSDOC @param sroot
126   *
127   * @throws TransformerException
128   */
129  public void compose(StylesheetRoot sroot) throws TransformerException
130  {
131
132    super.compose(sroot);
133
134    int length = getSortElemCount();
135
136    for (int i = 0; i < length; i++)
137    {
138      getSortElem(i).compose(sroot);
139    }
140
141    java.util.Vector vnames = sroot.getComposeState().getVariableNames();
142
143    if (null != m_selectExpression)
144      m_selectExpression.fixupVariables(
145        vnames, sroot.getComposeState().getGlobalsSize());
146    else
147    {
148      m_selectExpression =
149        getStylesheetRoot().m_selectDefault.getExpression();
150    }
151  }
152
153  /**
154   * This after the template's children have been composed.
155   */
156  public void endCompose(StylesheetRoot sroot) throws TransformerException
157  {
158    int length = getSortElemCount();
159
160    for (int i = 0; i < length; i++)
161    {
162      getSortElem(i).endCompose(sroot);
163    }
164
165    super.endCompose(sroot);
166  }
167
168
169  //  /**
170  //   * This function is called after everything else has been
171  //   * recomposed, and allows the template to set remaining
172  //   * values that may be based on some other property that
173  //   * depends on recomposition.
174  //   *
175  //   * @throws TransformerException
176  //   */
177  //  public void compose() throws TransformerException
178  //  {
179  //
180  //    if (null == m_selectExpression)
181  //    {
182  //      m_selectExpression =
183  //        getStylesheetRoot().m_selectDefault.getExpression();
184  //    }
185  //  }
186
187  /**
188   * Vector containing the xsl:sort elements associated with this element.
189   *  @serial
190   */
191  protected Vector m_sortElems = null;
192
193  /**
194   * Get the count xsl:sort elements associated with this element.
195   * @return The number of xsl:sort elements.
196   */
197  public int getSortElemCount()
198  {
199    return (m_sortElems == null) ? 0 : m_sortElems.size();
200  }
201
202  /**
203   * Get a xsl:sort element associated with this element.
204   *
205   * @param i Index of xsl:sort element to get
206   *
207   * @return xsl:sort element at given index
208   */
209  public ElemSort getSortElem(int i)
210  {
211    return (ElemSort) m_sortElems.elementAt(i);
212  }
213
214  /**
215   * Set a xsl:sort element associated with this element.
216   *
217   * @param sortElem xsl:sort element to set
218   */
219  public void setSortElem(ElemSort sortElem)
220  {
221
222    if (null == m_sortElems)
223      m_sortElems = new Vector();
224
225    m_sortElems.addElement(sortElem);
226  }
227
228  /**
229   * Get an int constant identifying the type of element.
230   * @see org.apache.xalan.templates.Constants
231   *
232   * @return The token ID for this element
233   */
234  public int getXSLToken()
235  {
236    return Constants.ELEMNAME_FOREACH;
237  }
238
239  /**
240   * Return the node name.
241   *
242   * @return The element's name
243   */
244  public String getNodeName()
245  {
246    return Constants.ELEMNAME_FOREACH_STRING;
247  }
248
249  /**
250   * Execute the xsl:for-each transformation
251   *
252   * @param transformer non-null reference to the the current transform-time state.
253   *
254   * @throws TransformerException
255   */
256  public void execute(TransformerImpl transformer) throws TransformerException
257  {
258
259    transformer.pushCurrentTemplateRuleIsNull(true);
260    try
261    {
262      transformSelectedNodes(transformer);
263    }
264    finally
265    {
266      transformer.popCurrentTemplateRuleIsNull();
267    }
268  }
269
270  /**
271   * Get template element associated with this
272   *
273   *
274   * @return template element associated with this (itself)
275   */
276  protected ElemTemplateElement getTemplateMatch()
277  {
278    return this;
279  }
280
281  /**
282   * Sort given nodes
283   *
284   *
285   * @param xctxt The XPath runtime state for the sort.
286   * @param keys Vector of sort keyx
287   * @param sourceNodes Iterator of nodes to sort
288   *
289   * @return iterator of sorted nodes
290   *
291   * @throws TransformerException
292   */
293  public DTMIterator sortNodes(
294          XPathContext xctxt, Vector keys, DTMIterator sourceNodes)
295            throws TransformerException
296  {
297
298    NodeSorter sorter = new NodeSorter(xctxt);
299    sourceNodes.setShouldCacheNodes(true);
300    sourceNodes.runTo(-1);
301    xctxt.pushContextNodeList(sourceNodes);
302
303    try
304    {
305      sorter.sort(sourceNodes, keys, xctxt);
306      sourceNodes.setCurrentPos(0);
307    }
308    finally
309    {
310      xctxt.popContextNodeList();
311    }
312
313    return sourceNodes;
314  }
315
316  /**
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   *
321   * @throws TransformerException Thrown in a variety of circumstances.
322   * @xsl.usage advanced
323   */
324  public void transformSelectedNodes(TransformerImpl transformer)
325          throws TransformerException
326  {
327
328    final XPathContext xctxt = transformer.getXPathContext();
329    final int sourceNode = xctxt.getCurrentNode();
330    DTMIterator sourceNodes = m_selectExpression.asIterator(xctxt,
331            sourceNode);
332
333    try
334    {
335
336      final Vector keys = (m_sortElems == null)
337              ? null
338              : transformer.processSortKeys(this, sourceNode);
339
340      // Sort if we need to.
341      if (null != keys)
342        sourceNodes = sortNodes(xctxt, keys, sourceNodes);
343
344      xctxt.pushCurrentNode(DTM.NULL);
345
346      IntStack currentNodes = xctxt.getCurrentNodeStack();
347
348      xctxt.pushCurrentExpressionNode(DTM.NULL);
349
350      IntStack currentExpressionNodes = xctxt.getCurrentExpressionNodeStack();
351
352      xctxt.pushSAXLocatorNull();
353      xctxt.pushContextNodeList(sourceNodes);
354      transformer.pushElemTemplateElement(null);
355
356      // pushParams(transformer, xctxt);
357      // Should be able to get this from the iterator but there must be a bug.
358      DTM dtm = xctxt.getDTM(sourceNode);
359      int docID = sourceNode & DTMManager.IDENT_DTM_DEFAULT;
360      int child;
361
362      while (DTM.NULL != (child = sourceNodes.nextNode()))
363      {
364        currentNodes.setTop(child);
365        currentExpressionNodes.setTop(child);
366
367        if ((child & DTMManager.IDENT_DTM_DEFAULT) != docID)
368        {
369          dtm = xctxt.getDTM(child);
370          docID = child & DTMManager.IDENT_DTM_DEFAULT;
371        }
372
373        //final int exNodeType = dtm.getExpandedTypeID(child);
374        final int nodeType = dtm.getNodeType(child);
375
376        // And execute the child templates.
377        // Loop through the children of the template, calling execute on
378        // each of them.
379        for (ElemTemplateElement t = this.m_firstChild; t != null;
380             t = t.m_nextSibling)
381        {
382          xctxt.setSAXLocator(t);
383          transformer.setCurrentElement(t);
384          t.execute(transformer);
385        }
386
387        // KLUGE: Implement <?xalan:doc_cache_off?>
388	 	// ASSUMPTION: This will be set only when the XPath was indeed
389	 	// a call to the Document() function. Calling it in other
390	 	// situations is likely to fry Xalan.
391	 	//
392	 	// %REVIEW% We need a MUCH cleaner solution -- one that will
393	 	// handle cleaning up after document() and getDTM() in other
394		// contexts. The whole SourceTreeManager mechanism should probably
395	 	// be moved into DTMManager rather than being explicitly invoked in
396	 	// FuncDocument and here.
397	 	if(m_doc_cache_off)
398		{
399	 	  if(DEBUG)
400	 	    System.out.println("JJK***** CACHE RELEASE *****\n"+
401				       "\tdtm="+dtm.getDocumentBaseURI());
402	  	// NOTE: This will work because this is _NOT_ a shared DTM, and thus has
403	  	// only a single Document node. If it could ever be an RTF or other
404	 	// shared DTM, this would require substantial rework.
405	 	  xctxt.getSourceTreeManager().removeDocumentFromCache(dtm.getDocument());
406	 	  xctxt.release(dtm,false);
407	 	}
408      }
409    }
410    finally
411    {
412      xctxt.popSAXLocator();
413      xctxt.popContextNodeList();
414      transformer.popElemTemplateElement();
415      xctxt.popCurrentExpressionNode();
416      xctxt.popCurrentNode();
417      sourceNodes.detach();
418    }
419  }
420
421  /**
422   * Add a child to the child list.
423   * <!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
424   * <!ATTLIST xsl:apply-templates
425   *   select %expr; "node()"
426   *   mode %qname; #IMPLIED
427   * >
428   *
429   * @param newChild Child to add to child list
430   *
431   * @return Child just added to child list
432   */
433  public ElemTemplateElement appendChild(ElemTemplateElement newChild)
434  {
435
436    int type = ((ElemTemplateElement) newChild).getXSLToken();
437
438    if (Constants.ELEMNAME_SORT == type)
439    {
440      setSortElem((ElemSort) newChild);
441
442      return newChild;
443    }
444    else
445      return super.appendChild(newChild);
446  }
447
448  /**
449   * Call the children visitors.
450   * @param visitor The visitor whose appropriate method will be called.
451   */
452  public void callChildVisitors(XSLTVisitor visitor, boolean callAttributes)
453  {
454  	if(callAttributes && (null != m_selectExpression))
455  		m_selectExpression.callVisitors(this, visitor);
456
457    int length = getSortElemCount();
458
459    for (int i = 0; i < length; i++)
460    {
461      getSortElem(i).callVisitors(visitor);
462    }
463
464    super.callChildVisitors(visitor, callAttributes);
465  }
466
467  /**
468   * @see ExpressionOwner#getExpression()
469   */
470  public Expression getExpression()
471  {
472    return m_selectExpression;
473  }
474
475  /**
476   * @see ExpressionOwner#setExpression(Expression)
477   */
478  public void setExpression(Expression exp)
479  {
480  	exp.exprSetParent(this);
481  	m_selectExpression = exp;
482  }
483
484  /*
485   * to keep the binary compatibility, assign a default value for newly added
486   * globel varialbe m_xpath during deserialization of an object which was
487   * serialized using an older version
488   */
489   private void readObject(ObjectInputStream os) throws
490        IOException, ClassNotFoundException {
491           os.defaultReadObject();
492           m_xpath = null;
493   }
494}
495