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