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  */
21 package org.apache.xalan.templates;
22 
23 import java.util.Vector;
24 
25 import javax.xml.transform.TransformerException;
26 
27 import org.apache.xalan.transformer.NodeSorter;
28 import org.apache.xalan.transformer.TransformerImpl;
29 import org.apache.xml.dtm.DTM;
30 import org.apache.xml.dtm.DTMIterator;
31 import org.apache.xml.dtm.DTMManager;
32 import org.apache.xml.utils.IntStack;
33 import org.apache.xpath.Expression;
34 import org.apache.xpath.ExpressionOwner;
35 import org.apache.xpath.XPath;
36 import org.apache.xpath.XPathContext;
37 
38 import java.io.ObjectInputStream;
39 import 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  */
59 public 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    */
ElemForEach()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    */
setSelect(XPath xpath)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    */
getSelect()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    */
compose(StylesheetRoot sroot)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    */
endCompose(StylesheetRoot sroot)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    */
getSortElemCount()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    */
getSortElem(int i)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    */
setSortElem(ElemSort sortElem)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    */
getXSLToken()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    */
getNodeName()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    */
execute(TransformerImpl transformer)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    */
getTemplateMatch()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    */
sortNodes( XPathContext xctxt, Vector keys, DTMIterator sourceNodes)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    */
transformSelectedNodes(TransformerImpl transformer)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    */
appendChild(ElemTemplateElement newChild)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    */
callChildVisitors(XSLTVisitor visitor, boolean callAttributes)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    */
getExpression()470   public Expression getExpression()
471   {
472     return m_selectExpression;
473   }
474 
475   /**
476    * @see ExpressionOwner#setExpression(Expression)
477    */
setExpression(Expression exp)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    */
readObject(ObjectInputStream os)489    private void readObject(ObjectInputStream os) throws
490         IOException, ClassNotFoundException {
491            os.defaultReadObject();
492            m_xpath = null;
493    }
494 }
495