001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 /* 019 * $Id: ElemApplyTemplates.java 468643 2006-10-28 06:56:03Z minchau $ 020 */ 021 package org.apache.xalan.templates; 022 023 import java.util.Vector; 024 025 import javax.xml.transform.TransformerException; 026 027 import org.apache.xalan.transformer.StackGuard; 028 import org.apache.xalan.transformer.TransformerImpl; 029 import org.apache.xml.dtm.DTM; 030 import org.apache.xml.dtm.DTMIterator; 031 import org.apache.xml.serializer.SerializationHandler; 032 import org.apache.xml.utils.IntStack; 033 import org.apache.xml.utils.QName; 034 import org.apache.xpath.VariableStack; 035 import org.apache.xpath.XPath; 036 import org.apache.xpath.XPathContext; 037 import org.apache.xpath.objects.XObject; 038 import org.xml.sax.SAXException; 039 040 /** 041 * Implement xsl:apply-templates. 042 * <pre> 043 * &!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*> 044 * &!ATTLIST xsl:apply-templates 045 * select %expr; "node()" 046 * mode %qname; #IMPLIED 047 * & 048 * </pre> 049 * @see <a href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules in XSLT Specification</a> 050 * @xsl.usage advanced 051 */ 052 public class ElemApplyTemplates extends ElemCallTemplate 053 { 054 static final long serialVersionUID = 2903125371542621004L; 055 056 /** 057 * mode %qname; #IMPLIED 058 * @serial 059 */ 060 private QName m_mode = null; 061 062 /** 063 * Set the mode attribute for this element. 064 * 065 * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>. 066 */ 067 public void setMode(QName mode) 068 { 069 m_mode = mode; 070 } 071 072 /** 073 * Get the mode attribute for this element. 074 * 075 * @return The mode attribute for this element 076 */ 077 public QName getMode() 078 { 079 return m_mode; 080 } 081 082 /** 083 * Tells if this belongs to a default template, 084 * in which case it will act different with 085 * regard to processing modes. 086 * @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in XSLT Specification</a> 087 * @serial 088 */ 089 private boolean m_isDefaultTemplate = false; 090 091 // /** 092 // * List of namespace/localname IDs, for identification of xsl:with-param to 093 // * xsl:params. Initialized in the compose() method. 094 // */ 095 // private int[] m_paramIDs; 096 097 /** 098 * Set if this belongs to a default template, 099 * in which case it will act different with 100 * regard to processing modes. 101 * @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in XSLT Specification</a> 102 * 103 * @param b boolean value to set. 104 */ 105 public void setIsDefaultTemplate(boolean b) 106 { 107 m_isDefaultTemplate = b; 108 } 109 110 /** 111 * Get an int constant identifying the type of element. 112 * @see org.apache.xalan.templates.Constants 113 * 114 * @return Token ID for this element types 115 */ 116 public int getXSLToken() 117 { 118 return Constants.ELEMNAME_APPLY_TEMPLATES; 119 } 120 121 /** 122 * This function is called after everything else has been 123 * recomposed, and allows the template to set remaining 124 * values that may be based on some other property that 125 * depends on recomposition. 126 */ 127 public void compose(StylesheetRoot sroot) throws TransformerException 128 { 129 super.compose(sroot); 130 } 131 132 /** 133 * Return the node name. 134 * 135 * @return Element name 136 */ 137 public String getNodeName() 138 { 139 return Constants.ELEMNAME_APPLY_TEMPLATES_STRING; 140 } 141 142 /** 143 * Apply the context node to the matching templates. 144 * @see <a href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules in XSLT Specification</a> 145 * 146 * @param transformer non-null reference to the the current transform-time state. 147 * 148 * @throws TransformerException 149 */ 150 public void execute(TransformerImpl transformer) throws TransformerException 151 { 152 153 transformer.pushCurrentTemplateRuleIsNull(false); 154 155 boolean pushMode = false; 156 157 try 158 { 159 // %REVIEW% Do we need this check?? 160 // if (null != sourceNode) 161 // { 162 // boolean needToTurnOffInfiniteLoopCheck = false; 163 QName mode = transformer.getMode(); 164 165 if (!m_isDefaultTemplate) 166 { 167 if (((null == mode) && (null != m_mode)) 168 || ((null != mode) &&!mode.equals(m_mode))) 169 { 170 pushMode = true; 171 172 transformer.pushMode(m_mode); 173 } 174 } 175 if (transformer.getDebug()) 176 transformer.getTraceManager().fireTraceEvent(this); 177 178 transformSelectedNodes(transformer); 179 } 180 finally 181 { 182 if (transformer.getDebug()) 183 transformer.getTraceManager().fireTraceEndEvent(this); 184 185 if (pushMode) 186 transformer.popMode(); 187 188 transformer.popCurrentTemplateRuleIsNull(); 189 } 190 } 191 192 193 /** 194 * Perform a query if needed, and call transformNode for each child. 195 * 196 * @param transformer non-null reference to the the current transform-time state. 197 * 198 * @throws TransformerException Thrown in a variety of circumstances. 199 * @xsl.usage advanced 200 */ 201 public void transformSelectedNodes(TransformerImpl transformer) 202 throws TransformerException 203 { 204 205 final XPathContext xctxt = transformer.getXPathContext(); 206 final int sourceNode = xctxt.getCurrentNode(); 207 DTMIterator sourceNodes = m_selectExpression.asIterator(xctxt, sourceNode); 208 VariableStack vars = xctxt.getVarStack(); 209 int nParams = getParamElemCount(); 210 int thisframe = vars.getStackFrame(); 211 StackGuard guard = transformer.getStackGuard(); 212 boolean check = (guard.getRecursionLimit() > -1) ? true : false; 213 214 boolean pushContextNodeListFlag = false; 215 216 try 217 { 218 219 xctxt.pushCurrentNode(DTM.NULL); 220 xctxt.pushCurrentExpressionNode(DTM.NULL); 221 xctxt.pushSAXLocatorNull(); 222 transformer.pushElemTemplateElement(null); 223 final Vector keys = (m_sortElems == null) 224 ? null 225 : transformer.processSortKeys(this, sourceNode); 226 227 // Sort if we need to. 228 if (null != keys) 229 sourceNodes = sortNodes(xctxt, keys, sourceNodes); 230 231 if (transformer.getDebug()) 232 { 233 transformer.getTraceManager().fireSelectedEvent(sourceNode, this, 234 "select", new XPath(m_selectExpression), 235 new org.apache.xpath.objects.XNodeSet(sourceNodes)); 236 } 237 238 final SerializationHandler rth = transformer.getSerializationHandler(); 239 // ContentHandler chandler = rth.getContentHandler(); 240 final StylesheetRoot sroot = transformer.getStylesheet(); 241 final TemplateList tl = sroot.getTemplateListComposed(); 242 final boolean quiet = transformer.getQuietConflictWarnings(); 243 244 // Should be able to get this from the iterator but there must be a bug. 245 DTM dtm = xctxt.getDTM(sourceNode); 246 247 int argsFrame = -1; 248 if(nParams > 0) 249 { 250 // This code will create a section on the stack that is all the 251 // evaluated arguments. These will be copied into the real params 252 // section of each called template. 253 argsFrame = vars.link(nParams); 254 vars.setStackFrame(thisframe); 255 256 for (int i = 0; i < nParams; i++) 257 { 258 ElemWithParam ewp = m_paramElems[i]; 259 if (transformer.getDebug()) 260 transformer.getTraceManager().fireTraceEvent(ewp); 261 XObject obj = ewp.getValue(transformer, sourceNode); 262 if (transformer.getDebug()) 263 transformer.getTraceManager().fireTraceEndEvent(ewp); 264 265 vars.setLocalVariable(i, obj, argsFrame); 266 } 267 vars.setStackFrame(argsFrame); 268 } 269 270 xctxt.pushContextNodeList(sourceNodes); 271 pushContextNodeListFlag = true; 272 273 IntStack currentNodes = xctxt.getCurrentNodeStack(); 274 275 IntStack currentExpressionNodes = xctxt.getCurrentExpressionNodeStack(); 276 277 // pushParams(transformer, xctxt); 278 279 int child; 280 while (DTM.NULL != (child = sourceNodes.nextNode())) 281 { 282 currentNodes.setTop(child); 283 currentExpressionNodes.setTop(child); 284 285 if(xctxt.getDTM(child) != dtm) 286 { 287 dtm = xctxt.getDTM(child); 288 } 289 290 final int exNodeType = dtm.getExpandedTypeID(child); 291 292 final int nodeType = dtm.getNodeType(child); 293 294 final QName mode = transformer.getMode(); 295 296 ElemTemplate template = tl.getTemplateFast(xctxt, child, exNodeType, mode, 297 -1, quiet, dtm); 298 299 // If that didn't locate a node, fall back to a default template rule. 300 // See http://www.w3.org/TR/xslt#built-in-rule. 301 if (null == template) 302 { 303 switch (nodeType) 304 { 305 case DTM.DOCUMENT_FRAGMENT_NODE : 306 case DTM.ELEMENT_NODE : 307 template = sroot.getDefaultRule(); 308 // %OPT% direct faster? 309 break; 310 case DTM.ATTRIBUTE_NODE : 311 case DTM.CDATA_SECTION_NODE : 312 case DTM.TEXT_NODE : 313 // if(rth.m_elemIsPending || rth.m_docPending) 314 // rth.flushPending(true); 315 transformer.pushPairCurrentMatched(sroot.getDefaultTextRule(), child); 316 transformer.setCurrentElement(sroot.getDefaultTextRule()); 317 // dtm.dispatchCharactersEvents(child, chandler, false); 318 dtm.dispatchCharactersEvents(child, rth, false); 319 transformer.popCurrentMatched(); 320 continue; 321 case DTM.DOCUMENT_NODE : 322 template = sroot.getDefaultRootRule(); 323 break; 324 default : 325 326 // No default rules for processing instructions and the like. 327 continue; 328 } 329 } 330 else 331 { 332 transformer.setCurrentElement(template); 333 } 334 335 transformer.pushPairCurrentMatched(template, child); 336 if (check) 337 guard.checkForInfinateLoop(); 338 339 int currentFrameBottom; // See comment with unlink, below 340 if(template.m_frameSize > 0) 341 { 342 xctxt.pushRTFContext(); 343 currentFrameBottom = vars.getStackFrame(); // See comment with unlink, below 344 vars.link(template.m_frameSize); 345 // You can't do the check for nParams here, otherwise the 346 // xsl:params might not be nulled. 347 if(/* nParams > 0 && */ template.m_inArgsSize > 0) 348 { 349 int paramIndex = 0; 350 for (ElemTemplateElement elem = template.getFirstChildElem(); 351 null != elem; elem = elem.getNextSiblingElem()) 352 { 353 if(Constants.ELEMNAME_PARAMVARIABLE == elem.getXSLToken()) 354 { 355 ElemParam ep = (ElemParam)elem; 356 357 int i; 358 for (i = 0; i < nParams; i++) 359 { 360 ElemWithParam ewp = m_paramElems[i]; 361 if(ewp.m_qnameID == ep.m_qnameID) 362 { 363 XObject obj = vars.getLocalVariable(i, argsFrame); 364 vars.setLocalVariable(paramIndex, obj); 365 break; 366 } 367 } 368 if(i == nParams) 369 vars.setLocalVariable(paramIndex, null); 370 } 371 else 372 break; 373 paramIndex++; 374 } 375 376 } 377 } 378 else 379 currentFrameBottom = 0; 380 381 // Fire a trace event for the template. 382 if (transformer.getDebug()) 383 transformer.getTraceManager().fireTraceEvent(template); 384 385 // And execute the child templates. 386 // Loop through the children of the template, calling execute on 387 // each of them. 388 for (ElemTemplateElement t = template.m_firstChild; 389 t != null; t = t.m_nextSibling) 390 { 391 xctxt.setSAXLocator(t); 392 try 393 { 394 transformer.pushElemTemplateElement(t); 395 t.execute(transformer); 396 } 397 finally 398 { 399 transformer.popElemTemplateElement(); 400 } 401 } 402 403 if (transformer.getDebug()) 404 transformer.getTraceManager().fireTraceEndEvent(template); 405 406 if(template.m_frameSize > 0) 407 { 408 // See Frank Weiss bug around 03/19/2002 (no Bugzilla report yet). 409 // While unlink will restore to the proper place, the real position 410 // may have been changed for xsl:with-param, so that variables 411 // can be accessed. 412 // of right now. 413 // More: 414 // When we entered this function, the current 415 // frame buffer (cfb) index in the variable stack may 416 // have been manually set. If we just call 417 // unlink(), however, it will restore the cfb to the 418 // previous link index from the link stack, rather than 419 // the manually set cfb. So, 420 // the only safe solution is to restore it back 421 // to the same position it was on entry, since we're 422 // really not working in a stack context here. (Bug4218) 423 vars.unlink(currentFrameBottom); 424 xctxt.popRTFContext(); 425 } 426 427 transformer.popCurrentMatched(); 428 429 } // end while (DTM.NULL != (child = sourceNodes.nextNode())) 430 } 431 catch (SAXException se) 432 { 433 transformer.getErrorListener().fatalError(new TransformerException(se)); 434 } 435 finally 436 { 437 if (transformer.getDebug()) 438 transformer.getTraceManager().fireSelectedEndEvent(sourceNode, this, 439 "select", new XPath(m_selectExpression), 440 new org.apache.xpath.objects.XNodeSet(sourceNodes)); 441 442 // Unlink to the original stack frame 443 if(nParams > 0) 444 vars.unlink(thisframe); 445 xctxt.popSAXLocator(); 446 if (pushContextNodeListFlag) xctxt.popContextNodeList(); 447 transformer.popElemTemplateElement(); 448 xctxt.popCurrentExpressionNode(); 449 xctxt.popCurrentNode(); 450 sourceNodes.detach(); 451 } 452 } 453 454 }