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: ElemCallTemplate.java 468643 2006-10-28 06:56:03Z minchau $
     20  */
     21 package org.apache.xalan.templates;
     22 
     23 import javax.xml.transform.SourceLocator;
     24 import javax.xml.transform.TransformerException;
     25 
     26 import org.apache.xalan.res.XSLMessages;
     27 import org.apache.xalan.res.XSLTErrorResources;
     28 import org.apache.xalan.transformer.TransformerImpl;
     29 import org.apache.xml.utils.QName;
     30 import org.apache.xpath.VariableStack;
     31 import org.apache.xpath.XPathContext;
     32 import org.apache.xpath.objects.XObject;
     33 
     34 /**
     35  * Implement xsl:call-template.
     36  * <pre>
     37  * &amp;!ELEMENT xsl:call-template (xsl:with-param)*>
     38  * &amp;!ATTLIST xsl:call-template
     39  *   name %qname; #REQUIRED
     40  * &amp;
     41  * </pre>
     42  * @see <a href="http://www.w3.org/TR/xslt#named-templates">named-templates in XSLT Specification</a>
     43  * @xsl.usage advanced
     44  */
     45 public class ElemCallTemplate extends ElemForEach
     46 {
     47     static final long serialVersionUID = 5009634612916030591L;
     48 
     49   /**
     50    * An xsl:call-template element invokes a template by name;
     51    * it has a required name attribute that identifies the template to be invoked.
     52    * @serial
     53    */
     54   public QName m_templateName = null;
     55 
     56   /**
     57    * Set the "name" attribute.
     58    * An xsl:call-template element invokes a template by name;
     59    * it has a required name attribute that identifies the template to be invoked.
     60    *
     61    * @param name Name attribute to set
     62    */
     63   public void setName(QName name)
     64   {
     65     m_templateName = name;
     66   }
     67 
     68   /**
     69    * Get the "name" attribute.
     70    * An xsl:call-template element invokes a template by name;
     71    * it has a required name attribute that identifies the template to be invoked.
     72    *
     73    * @return Name attribute of this element
     74    */
     75   public QName getName()
     76   {
     77     return m_templateName;
     78   }
     79 
     80   /**
     81    * The template which is named by QName.
     82    * @serial
     83    */
     84   private ElemTemplate m_template = null;
     85 
     86   /**
     87    * Get an int constant identifying the type of element.
     88    * @see org.apache.xalan.templates.Constants
     89    *
     90    * @return The token ID for this element
     91    */
     92   public int getXSLToken()
     93   {
     94     return Constants.ELEMNAME_CALLTEMPLATE;
     95   }
     96 
     97   /**
     98    * Return the node name.
     99    *
    100    * @return The name of this element
    101    */
    102   public String getNodeName()
    103   {
    104     return Constants.ELEMNAME_CALLTEMPLATE_STRING;
    105   }
    106 
    107   /**
    108    * This function is called after everything else has been
    109    * recomposed, and allows the template to set remaining
    110    * values that may be based on some other property that
    111    * depends on recomposition.
    112    */
    113   public void compose(StylesheetRoot sroot) throws TransformerException
    114   {
    115     super.compose(sroot);
    116 
    117     // Call compose on each param no matter if this is apply-templates
    118     // or call templates.
    119     int length = getParamElemCount();
    120     for (int i = 0; i < length; i++)
    121     {
    122       ElemWithParam ewp = getParamElem(i);
    123       ewp.compose(sroot);
    124     }
    125 
    126 	if ((null != m_templateName) && (null == m_template)) {
    127 		m_template =
    128 			this.getStylesheetRoot().getTemplateComposed(m_templateName);
    129 
    130 		if (null == m_template) {
    131 			String themsg =
    132 				XSLMessages.createMessage(
    133 					XSLTErrorResources.ER_ELEMTEMPLATEELEM_ERR,
    134 					new Object[] { m_templateName });
    135 
    136 			throw new TransformerException(themsg, this);
    137 			//"Could not find template named: '"+templateName+"'");
    138 		}
    139 
    140       length = getParamElemCount();
    141       for (int i = 0; i < length; i++)
    142       {
    143         ElemWithParam ewp = getParamElem(i);
    144         ewp.m_index = -1;
    145         // Find the position of the param in the template being called,
    146         // and set the index of the param slot.
    147         int etePos = 0;
    148         for (ElemTemplateElement ete = m_template.getFirstChildElem();
    149              null != ete; ete = ete.getNextSiblingElem())
    150         {
    151           if(ete.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE)
    152           {
    153             ElemParam ep = (ElemParam)ete;
    154             if(ep.getName().equals(ewp.getName()))
    155             {
    156               ewp.m_index = etePos;
    157             }
    158           }
    159           else
    160             break;
    161           etePos++;
    162         }
    163 
    164       }
    165     }
    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 = getParamElemCount();
    174     for (int i = 0; i < length; i++)
    175     {
    176       ElemWithParam ewp = getParamElem(i);
    177       ewp.endCompose(sroot);
    178     }
    179 
    180     super.endCompose(sroot);
    181   }
    182 
    183   /**
    184    * Invoke a named template.
    185    * @see <a href="http://www.w3.org/TR/xslt#named-templates">named-templates in XSLT Specification</a>
    186    *
    187    * @param transformer non-null reference to the the current transform-time state.
    188    *
    189    * @throws TransformerException
    190    */
    191   public void execute(
    192           TransformerImpl transformer)
    193             throws TransformerException
    194   {
    195 
    196     if (null != m_template)
    197     {
    198       XPathContext xctxt = transformer.getXPathContext();
    199       VariableStack vars = xctxt.getVarStack();
    200 
    201       int thisframe = vars.getStackFrame();
    202       int nextFrame = vars.link(m_template.m_frameSize);
    203 
    204       // We have to clear the section of the stack frame that has params
    205       // so that the default param evaluation will work correctly.
    206       if(m_template.m_inArgsSize > 0)
    207       {
    208         vars.clearLocalSlots(0, m_template.m_inArgsSize);
    209 
    210         if(null != m_paramElems)
    211         {
    212           int currentNode = xctxt.getCurrentNode();
    213           vars.setStackFrame(thisframe);
    214           int size = m_paramElems.length;
    215 
    216           for (int i = 0; i < size; i++)
    217           {
    218             ElemWithParam ewp = m_paramElems[i];
    219             if(ewp.m_index >= 0)
    220             {
    221               XObject obj = ewp.getValue(transformer, currentNode);
    222 
    223               // Note here that the index for ElemWithParam must have been
    224               // statically made relative to the xsl:template being called,
    225               // NOT this xsl:template.
    226               vars.setLocalVariable(ewp.m_index, obj, nextFrame);
    227             }
    228           }
    229           vars.setStackFrame(nextFrame);
    230         }
    231       }
    232 
    233       SourceLocator savedLocator = xctxt.getSAXLocator();
    234 
    235       try
    236       {
    237         xctxt.setSAXLocator(m_template);
    238 
    239         // template.executeChildTemplates(transformer, sourceNode, mode, true);
    240         transformer.pushElemTemplateElement(m_template);
    241         m_template.execute(transformer);
    242       }
    243       finally
    244       {
    245         transformer.popElemTemplateElement();
    246         xctxt.setSAXLocator(savedLocator);
    247         // When we entered this function, the current
    248         // frame buffer (cfb) index in the variable stack may
    249         // have been manually set.  If we just call
    250         // unlink(), however, it will restore the cfb to the
    251         // previous link index from the link stack, rather than
    252         // the manually set cfb.  So,
    253         // the only safe solution is to restore it back
    254         // to the same position it was on entry, since we're
    255         // really not working in a stack context here. (Bug4218)
    256         vars.unlink(thisframe);
    257       }
    258     }
    259     else
    260     {
    261       transformer.getMsgMgr().error(this, XSLTErrorResources.ER_TEMPLATE_NOT_FOUND,
    262                                     new Object[]{ m_templateName });  //"Could not find template named: '"+templateName+"'");
    263     }
    264 
    265   }
    266 
    267   /** Vector of xsl:param elements associated with this element.
    268    *  @serial */
    269   protected ElemWithParam[] m_paramElems = null;
    270 
    271   /**
    272    * Get the count xsl:param elements associated with this element.
    273    * @return The number of xsl:param elements.
    274    */
    275   public int getParamElemCount()
    276   {
    277     return (m_paramElems == null) ? 0 : m_paramElems.length;
    278   }
    279 
    280   /**
    281    * Get a xsl:param element associated with this element.
    282    *
    283    * @param i Index of element to find
    284    *
    285    * @return xsl:param element at given index
    286    */
    287   public ElemWithParam getParamElem(int i)
    288   {
    289     return m_paramElems[i];
    290   }
    291 
    292   /**
    293    * Set a xsl:param element associated with this element.
    294    *
    295    * @param ParamElem xsl:param element to set.
    296    */
    297   public void setParamElem(ElemWithParam ParamElem)
    298   {
    299     if (null == m_paramElems)
    300     {
    301       m_paramElems = new ElemWithParam[1];
    302       m_paramElems[0] = ParamElem;
    303     }
    304     else
    305     {
    306       // Expensive 1 at a time growth, but this is done at build time, so
    307       // I think it's OK.
    308       int length = m_paramElems.length;
    309       ElemWithParam[] ewp = new ElemWithParam[length + 1];
    310       System.arraycopy(m_paramElems, 0, ewp, 0, length);
    311       m_paramElems = ewp;
    312       ewp[length] = ParamElem;
    313     }
    314   }
    315 
    316   /**
    317    * Add a child to the child list.
    318    * <!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*>
    319    * <!ATTLIST xsl:apply-templates
    320    *   select %expr; "node()"
    321    *   mode %qname; #IMPLIED
    322    * >
    323    *
    324    * @param newChild Child to add to this node's children list
    325    *
    326    * @return The child that was just added the children list
    327    *
    328    * @throws DOMException
    329    */
    330   public ElemTemplateElement appendChild(ElemTemplateElement newChild)
    331   {
    332 
    333     int type = ((ElemTemplateElement) newChild).getXSLToken();
    334 
    335     if (Constants.ELEMNAME_WITHPARAM == type)
    336     {
    337       setParamElem((ElemWithParam) newChild);
    338     }
    339 
    340     // You still have to append, because this element can
    341     // contain a for-each, and other elements.
    342     return super.appendChild(newChild);
    343   }
    344 
    345     /**
    346      * Call the children visitors.
    347      * @param visitor The visitor whose appropriate method will be called.
    348      */
    349     public void callChildVisitors(XSLTVisitor visitor, boolean callAttrs)
    350     {
    351 //      if (null != m_paramElems)
    352 //      {
    353 //        int size = m_paramElems.length;
    354 //
    355 //        for (int i = 0; i < size; i++)
    356 //        {
    357 //          ElemWithParam ewp = m_paramElems[i];
    358 //          ewp.callVisitors(visitor);
    359 //        }
    360 //      }
    361 
    362       super.callChildVisitors(visitor, callAttrs);
    363     }
    364 }
    365