Home | History | Annotate | Download | only in templates
      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    */
     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