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: ElemApplyTemplates.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.TransformerImpl; 28 import org.apache.xml.dtm.DTM; 29 import org.apache.xml.dtm.DTMIterator; 30 import org.apache.xml.serializer.SerializationHandler; 31 import org.apache.xml.utils.IntStack; 32 import org.apache.xml.utils.QName; 33 import org.apache.xpath.VariableStack; 34 import org.apache.xpath.XPath; 35 import org.apache.xpath.XPathContext; 36 import org.apache.xpath.objects.XObject; 37 import org.xml.sax.SAXException; 38 39 /** 40 * Implement xsl:apply-templates. 41 * <pre> 42 * &!ELEMENT xsl:apply-templates (xsl:sort|xsl:with-param)*> 43 * &!ATTLIST xsl:apply-templates 44 * select %expr; "node()" 45 * mode %qname; #IMPLIED 46 * & 47 * </pre> 48 * @see <a href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules in XSLT Specification</a> 49 * @xsl.usage advanced 50 */ 51 public class ElemApplyTemplates extends ElemCallTemplate 52 { 53 static final long serialVersionUID = 2903125371542621004L; 54 55 /** 56 * mode %qname; #IMPLIED 57 * @serial 58 */ 59 private QName m_mode = null; 60 61 /** 62 * Set the mode attribute for this element. 63 * 64 * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>. 65 */ 66 public void setMode(QName mode) 67 { 68 m_mode = mode; 69 } 70 71 /** 72 * Get the mode attribute for this element. 73 * 74 * @return The mode attribute for this element 75 */ 76 public QName getMode() 77 { 78 return m_mode; 79 } 80 81 /** 82 * Tells if this belongs to a default template, 83 * in which case it will act different with 84 * regard to processing modes. 85 * @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in XSLT Specification</a> 86 * @serial 87 */ 88 private boolean m_isDefaultTemplate = false; 89 90 // /** 91 // * List of namespace/localname IDs, for identification of xsl:with-param to 92 // * xsl:params. Initialized in the compose() method. 93 // */ 94 // private int[] m_paramIDs; 95 96 /** 97 * Set if this belongs to a default template, 98 * in which case it will act different with 99 * regard to processing modes. 100 * @see <a href="http://www.w3.org/TR/xslt#built-in-rule">built-in-rule in XSLT Specification</a> 101 * 102 * @param b boolean value to set. 103 */ 104 public void setIsDefaultTemplate(boolean b) 105 { 106 m_isDefaultTemplate = b; 107 } 108 109 /** 110 * Get an int constant identifying the type of element. 111 * @see org.apache.xalan.templates.Constants 112 * 113 * @return Token ID for this element types 114 */ 115 public int getXSLToken() 116 { 117 return Constants.ELEMNAME_APPLY_TEMPLATES; 118 } 119 120 /** 121 * This function is called after everything else has been 122 * recomposed, and allows the template to set remaining 123 * values that may be based on some other property that 124 * depends on recomposition. 125 */ 126 public void compose(StylesheetRoot sroot) throws TransformerException 127 { 128 super.compose(sroot); 129 } 130 131 /** 132 * Return the node name. 133 * 134 * @return Element name 135 */ 136 public String getNodeName() 137 { 138 return Constants.ELEMNAME_APPLY_TEMPLATES_STRING; 139 } 140 141 /** 142 * Apply the context node to the matching templates. 143 * @see <a href="http://www.w3.org/TR/xslt#section-Applying-Template-Rules">section-Applying-Template-Rules in XSLT Specification</a> 144 * 145 * @param transformer non-null reference to the the current transform-time state. 146 * 147 * @throws TransformerException 148 */ 149 public void execute(TransformerImpl transformer) throws TransformerException 150 { 151 152 transformer.pushCurrentTemplateRuleIsNull(false); 153 154 boolean pushMode = false; 155 156 try 157 { 158 // %REVIEW% Do we need this check?? 159 // if (null != sourceNode) 160 // { 161 // boolean needToTurnOffInfiniteLoopCheck = false; 162 QName mode = transformer.getMode(); 163 164 if (!m_isDefaultTemplate) 165 { 166 if (((null == mode) && (null != m_mode)) 167 || ((null != mode) &&!mode.equals(m_mode))) 168 { 169 pushMode = true; 170 171 transformer.pushMode(m_mode); 172 } 173 } 174 175 transformSelectedNodes(transformer); 176 } 177 finally 178 { 179 if (pushMode) 180 transformer.popMode(); 181 182 transformer.popCurrentTemplateRuleIsNull(); 183 } 184 } 185 186 187 /** 188 * Perform a query if needed, and call transformNode for each child. 189 * 190 * @param transformer non-null reference to the the current transform-time state. 191 * 192 * @throws TransformerException Thrown in a variety of circumstances. 193 * @xsl.usage advanced 194 */ 195 public void transformSelectedNodes(TransformerImpl transformer) 196 throws TransformerException 197 { 198 199 final XPathContext xctxt = transformer.getXPathContext(); 200 final int sourceNode = xctxt.getCurrentNode(); 201 DTMIterator sourceNodes = m_selectExpression.asIterator(xctxt, sourceNode); 202 VariableStack vars = xctxt.getVarStack(); 203 int nParams = getParamElemCount(); 204 int thisframe = vars.getStackFrame(); 205 206 boolean pushContextNodeListFlag = false; 207 208 try 209 { 210 211 xctxt.pushCurrentNode(DTM.NULL); 212 xctxt.pushCurrentExpressionNode(DTM.NULL); 213 xctxt.pushSAXLocatorNull(); 214 transformer.pushElemTemplateElement(null); 215 final Vector keys = (m_sortElems == null) 216 ? null 217 : transformer.processSortKeys(this, sourceNode); 218 219 // Sort if we need to. 220 if (null != keys) 221 sourceNodes = sortNodes(xctxt, keys, sourceNodes); 222 223 final SerializationHandler rth = transformer.getSerializationHandler(); 224 // ContentHandler chandler = rth.getContentHandler(); 225 final StylesheetRoot sroot = transformer.getStylesheet(); 226 final TemplateList tl = sroot.getTemplateListComposed(); 227 final boolean quiet = transformer.getQuietConflictWarnings(); 228 229 // Should be able to get this from the iterator but there must be a bug. 230 DTM dtm = xctxt.getDTM(sourceNode); 231 232 int argsFrame = -1; 233 if(nParams > 0) 234 { 235 // This code will create a section on the stack that is all the 236 // evaluated arguments. These will be copied into the real params 237 // section of each called template. 238 argsFrame = vars.link(nParams); 239 vars.setStackFrame(thisframe); 240 241 for (int i = 0; i < nParams; i++) 242 { 243 ElemWithParam ewp = m_paramElems[i]; 244 XObject obj = ewp.getValue(transformer, sourceNode); 245 246 vars.setLocalVariable(i, obj, argsFrame); 247 } 248 vars.setStackFrame(argsFrame); 249 } 250 251 xctxt.pushContextNodeList(sourceNodes); 252 pushContextNodeListFlag = true; 253 254 IntStack currentNodes = xctxt.getCurrentNodeStack(); 255 256 IntStack currentExpressionNodes = xctxt.getCurrentExpressionNodeStack(); 257 258 // pushParams(transformer, xctxt); 259 260 int child; 261 while (DTM.NULL != (child = sourceNodes.nextNode())) 262 { 263 currentNodes.setTop(child); 264 currentExpressionNodes.setTop(child); 265 266 if(xctxt.getDTM(child) != dtm) 267 { 268 dtm = xctxt.getDTM(child); 269 } 270 271 final int exNodeType = dtm.getExpandedTypeID(child); 272 273 final int nodeType = dtm.getNodeType(child); 274 275 final QName mode = transformer.getMode(); 276 277 ElemTemplate template = tl.getTemplateFast(xctxt, child, exNodeType, mode, 278 -1, quiet, dtm); 279 280 // If that didn't locate a node, fall back to a default template rule. 281 // See http://www.w3.org/TR/xslt#built-in-rule. 282 if (null == template) 283 { 284 switch (nodeType) 285 { 286 case DTM.DOCUMENT_FRAGMENT_NODE : 287 case DTM.ELEMENT_NODE : 288 template = sroot.getDefaultRule(); 289 // %OPT% direct faster? 290 break; 291 case DTM.ATTRIBUTE_NODE : 292 case DTM.CDATA_SECTION_NODE : 293 case DTM.TEXT_NODE : 294 // if(rth.m_elemIsPending || rth.m_docPending) 295 // rth.flushPending(true); 296 transformer.pushPairCurrentMatched(sroot.getDefaultTextRule(), child); 297 transformer.setCurrentElement(sroot.getDefaultTextRule()); 298 // dtm.dispatchCharactersEvents(child, chandler, false); 299 dtm.dispatchCharactersEvents(child, rth, false); 300 transformer.popCurrentMatched(); 301 continue; 302 case DTM.DOCUMENT_NODE : 303 template = sroot.getDefaultRootRule(); 304 break; 305 default : 306 307 // No default rules for processing instructions and the like. 308 continue; 309 } 310 } 311 else 312 { 313 transformer.setCurrentElement(template); 314 } 315 316 transformer.pushPairCurrentMatched(template, child); 317 318 int currentFrameBottom; // See comment with unlink, below 319 if(template.m_frameSize > 0) 320 { 321 xctxt.pushRTFContext(); 322 currentFrameBottom = vars.getStackFrame(); // See comment with unlink, below 323 vars.link(template.m_frameSize); 324 // You can't do the check for nParams here, otherwise the 325 // xsl:params might not be nulled. 326 if(/* nParams > 0 && */ template.m_inArgsSize > 0) 327 { 328 int paramIndex = 0; 329 for (ElemTemplateElement elem = template.getFirstChildElem(); 330 null != elem; elem = elem.getNextSiblingElem()) 331 { 332 if(Constants.ELEMNAME_PARAMVARIABLE == elem.getXSLToken()) 333 { 334 ElemParam ep = (ElemParam)elem; 335 336 int i; 337 for (i = 0; i < nParams; i++) 338 { 339 ElemWithParam ewp = m_paramElems[i]; 340 if(ewp.m_qnameID == ep.m_qnameID) 341 { 342 XObject obj = vars.getLocalVariable(i, argsFrame); 343 vars.setLocalVariable(paramIndex, obj); 344 break; 345 } 346 } 347 if(i == nParams) 348 vars.setLocalVariable(paramIndex, null); 349 } 350 else 351 break; 352 paramIndex++; 353 } 354 355 } 356 } 357 else 358 currentFrameBottom = 0; 359 360 // And execute the child templates. 361 // Loop through the children of the template, calling execute on 362 // each of them. 363 for (ElemTemplateElement t = template.m_firstChild; 364 t != null; t = t.m_nextSibling) 365 { 366 xctxt.setSAXLocator(t); 367 try 368 { 369 transformer.pushElemTemplateElement(t); 370 t.execute(transformer); 371 } 372 finally 373 { 374 transformer.popElemTemplateElement(); 375 } 376 } 377 378 if(template.m_frameSize > 0) 379 { 380 // See Frank Weiss bug around 03/19/2002 (no Bugzilla report yet). 381 // While unlink will restore to the proper place, the real position 382 // may have been changed for xsl:with-param, so that variables 383 // can be accessed. 384 // of right now. 385 // More: 386 // When we entered this function, the current 387 // frame buffer (cfb) index in the variable stack may 388 // have been manually set. If we just call 389 // unlink(), however, it will restore the cfb to the 390 // previous link index from the link stack, rather than 391 // the manually set cfb. So, 392 // the only safe solution is to restore it back 393 // to the same position it was on entry, since we're 394 // really not working in a stack context here. (Bug4218) 395 vars.unlink(currentFrameBottom); 396 xctxt.popRTFContext(); 397 } 398 399 transformer.popCurrentMatched(); 400 401 } // end while (DTM.NULL != (child = sourceNodes.nextNode())) 402 } 403 catch (SAXException se) 404 { 405 transformer.getErrorListener().fatalError(new TransformerException(se)); 406 } 407 finally 408 { 409 // Unlink to the original stack frame 410 if(nParams > 0) 411 vars.unlink(thisframe); 412 xctxt.popSAXLocator(); 413 if (pushContextNodeListFlag) xctxt.popContextNodeList(); 414 transformer.popElemTemplateElement(); 415 xctxt.popCurrentExpressionNode(); 416 xctxt.popCurrentNode(); 417 sourceNodes.detach(); 418 } 419 } 420 421 } 422