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: StylesheetRoot.java 476466 2006-11-18 08:22:31Z minchau $ 20 */ 21 package org.apache.xalan.templates; 22 23 import java.text.DecimalFormatSymbols; 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 import java.util.Hashtable; 27 import java.util.Properties; 28 import java.util.Vector; 29 30 import javax.xml.transform.ErrorListener; 31 import javax.xml.transform.Templates; 32 import javax.xml.transform.Transformer; 33 import javax.xml.transform.TransformerConfigurationException; 34 import javax.xml.transform.TransformerException; 35 36 import org.apache.xalan.extensions.ExtensionNamespacesManager; 37 import org.apache.xalan.processor.XSLTSchema; 38 import org.apache.xalan.res.XSLMessages; 39 import org.apache.xalan.res.XSLTErrorResources; 40 41 import org.apache.xalan.transformer.TransformerImpl; 42 import org.apache.xml.dtm.DTM; 43 import org.apache.xml.dtm.ref.ExpandedNameTable; 44 import org.apache.xml.utils.IntStack; 45 import org.apache.xml.utils.QName; 46 import org.apache.xpath.XPath; 47 import org.apache.xpath.XPathContext; 48 49 /** 50 * This class represents the root object of the stylesheet tree. 51 * @xsl.usage general 52 */ 53 public class StylesheetRoot extends StylesheetComposed 54 implements java.io.Serializable, Templates 55 { 56 static final long serialVersionUID = 3875353123529147855L; 57 58 /** 59 * The flag for the setting of the optimize feature; 60 */ 61 private boolean m_optimizer = true; 62 63 /** 64 * The flag for the setting of the incremental feature; 65 */ 66 private boolean m_incremental = false; 67 68 /** 69 * The flag for the setting of the source_location feature; 70 */ 71 private boolean m_source_location = false; 72 73 /** 74 * State of the secure processing feature. 75 */ 76 private boolean m_isSecureProcessing = false; 77 78 /** 79 * Uses an XSL stylesheet document. 80 * @throws TransformerConfigurationException if the baseIdentifier can not be resolved to a URL. 81 */ StylesheetRoot(ErrorListener errorListener)82 public StylesheetRoot(ErrorListener errorListener) throws TransformerConfigurationException 83 { 84 85 super(null); 86 87 setStylesheetRoot(this); 88 89 try 90 { 91 m_selectDefault = new XPath("node()", this, this, XPath.SELECT, errorListener); 92 93 initDefaultRule(errorListener); 94 } 95 catch (TransformerException se) 96 { 97 throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_INIT_DEFAULT_TEMPLATES, null), se); //"Can't init default templates!", se); 98 } 99 } 100 101 /** 102 * The schema used when creating this StylesheetRoot 103 * @serial 104 */ 105 private HashMap m_availElems; 106 107 /** 108 * Creates a StylesheetRoot and retains a pointer to the schema used to create this 109 * StylesheetRoot. The schema may be needed later for an element-available() function call. 110 * 111 * @param schema The schema used to create this stylesheet 112 * @throws TransformerConfigurationException if the baseIdentifier can not be resolved to a URL. 113 */ StylesheetRoot(XSLTSchema schema, ErrorListener listener)114 public StylesheetRoot(XSLTSchema schema, ErrorListener listener) throws TransformerConfigurationException 115 { 116 117 this(listener); 118 m_availElems = schema.getElemsAvailable(); 119 } 120 121 /** 122 * Tell if this is the root of the stylesheet tree. 123 * 124 * @return True since this is the root of the stylesheet tree. 125 */ isRoot()126 public boolean isRoot() 127 { 128 return true; 129 } 130 131 /** 132 * Set the state of the secure processing feature. 133 */ setSecureProcessing(boolean flag)134 public void setSecureProcessing(boolean flag) 135 { 136 m_isSecureProcessing = flag; 137 } 138 139 /** 140 * Return the state of the secure processing feature. 141 */ isSecureProcessing()142 public boolean isSecureProcessing() 143 { 144 return m_isSecureProcessing; 145 } 146 147 /** 148 * Get the hashtable of available elements. 149 * 150 * @return table of available elements, keyed by qualified names, and with 151 * values of the same qualified names. 152 */ getAvailableElements()153 public HashMap getAvailableElements() 154 { 155 return m_availElems; 156 } 157 158 private transient ExtensionNamespacesManager m_extNsMgr = null; 159 160 /** 161 * Only instantiate an ExtensionNamespacesManager if one is called for 162 * (i.e., if the stylesheet contains extension functions and/or elements). 163 */ getExtensionNamespacesManager()164 public ExtensionNamespacesManager getExtensionNamespacesManager() 165 { 166 if (m_extNsMgr == null) 167 m_extNsMgr = new ExtensionNamespacesManager(); 168 return m_extNsMgr; 169 } 170 171 /** 172 * Get the vector of extension namespaces. Used to provide 173 * the extensions table access to a list of extension 174 * namespaces encountered during composition of a stylesheet. 175 */ getExtensions()176 public Vector getExtensions() 177 { 178 return m_extNsMgr != null ? m_extNsMgr.getExtensions() : null; 179 } 180 181 /* 182 public void runtimeInit(TransformerImpl transformer) throws TransformerException 183 { 184 System.out.println("StylesheetRoot.runtimeInit()"); 185 186 // try{throw new Exception("StylesheetRoot.runtimeInit()");} catch(Exception e){e.printStackTrace();} 187 188 } 189 */ 190 191 //============== Templates Interface ================ 192 193 /** 194 * Create a new transformation context for this Templates object. 195 * 196 * @return A Transformer instance, never null. 197 */ newTransformer()198 public Transformer newTransformer() 199 { 200 return new TransformerImpl(this); 201 } 202 203 getDefaultOutputProps()204 public Properties getDefaultOutputProps() 205 { 206 return m_outputProperties.getProperties(); 207 } 208 209 /** 210 * Get the static properties for xsl:output. The object returned will 211 * be a clone of the internal values, and thus it can be mutated 212 * without mutating the Templates object, and then handed in to 213 * the process method. 214 * 215 * <p>For XSLT, Attribute Value Templates attribute values will 216 * be returned unexpanded (since there is no context at this point).</p> 217 * 218 * @return A Properties object, not null. 219 */ getOutputProperties()220 public Properties getOutputProperties() 221 { 222 return (Properties)getDefaultOutputProps().clone(); 223 } 224 225 //============== End Templates Interface ================ 226 227 /** 228 * Recompose the values of all "composed" properties, meaning 229 * properties that need to be combined or calculated from 230 * the combination of imported and included stylesheets. This 231 * method determines the proper import precedence of all imported 232 * stylesheets. It then iterates through all of the elements and 233 * properties in the proper order and triggers the individual recompose 234 * methods. 235 * 236 * @throws TransformerException 237 */ recompose()238 public void recompose() throws TransformerException 239 { 240 // Now we make a Vector that is going to hold all of the recomposable elements 241 242 Vector recomposableElements = new Vector(); 243 244 // First, we build the global import tree. 245 246 if (null == m_globalImportList) 247 { 248 249 Vector importList = new Vector(); 250 251 addImports(this, true, importList); 252 253 // Now we create an array and reverse the order of the importList vector. 254 // We built the importList vector backwards so that we could use addElement 255 // to append to the end of the vector instead of constantly pushing new 256 // stylesheets onto the front of the vector and having to shift the rest 257 // of the vector each time. 258 259 m_globalImportList = new StylesheetComposed[importList.size()]; 260 261 for (int i = 0, j= importList.size() -1; i < importList.size(); i++) 262 { 263 m_globalImportList[j] = (StylesheetComposed) importList.elementAt(i); 264 // Build the global include list for this stylesheet. 265 // This needs to be done ahead of the recomposeImports 266 // because we need the info from the composed includes. 267 m_globalImportList[j].recomposeIncludes(m_globalImportList[j]); 268 // Calculate the number of this import. 269 m_globalImportList[j--].recomposeImports(); 270 } 271 } 272 // Next, we walk the import tree and add all of the recomposable elements to the vector. 273 int n = getGlobalImportCount(); 274 275 for (int i = 0; i < n; i++) 276 { 277 StylesheetComposed imported = getGlobalImport(i); 278 imported.recompose(recomposableElements); 279 } 280 281 // We sort the elements into ascending order. 282 283 QuickSort2(recomposableElements, 0, recomposableElements.size() - 1); 284 285 // We set up the global variables that will hold the recomposed information. 286 287 288 m_outputProperties = new OutputProperties(org.apache.xml.serializer.Method.UNKNOWN); 289 // m_outputProperties = new OutputProperties(Method.XML); 290 291 m_attrSets = new HashMap(); 292 m_decimalFormatSymbols = new Hashtable(); 293 m_keyDecls = new Vector(); 294 m_namespaceAliasComposed = new Hashtable(); 295 m_templateList = new TemplateList(); 296 m_variables = new Vector(); 297 298 // Now we sequence through the sorted elements, 299 // calling the recompose() function on each one. This will call back into the 300 // appropriate routine here to actually do the recomposition. 301 // Note that we're going backwards, encountering the highest precedence items first. 302 for (int i = recomposableElements.size() - 1; i >= 0; i--) 303 ((ElemTemplateElement) recomposableElements.elementAt(i)).recompose(this); 304 305 /* 306 * Backing out REE again, as it seems to cause some new failures 307 * which need to be investigated. -is 308 */ 309 // This has to be done before the initialization of the compose state, because 310 // eleminateRedundentGlobals will add variables to the m_variables vector, which 311 // it then copied in the ComposeState constructor. 312 313 // if(true && org.apache.xalan.processor.TransformerFactoryImpl.m_optimize) 314 // { 315 // RedundentExprEliminator ree = new RedundentExprEliminator(); 316 // callVisitors(ree); 317 // ree.eleminateRedundentGlobals(this); 318 // } 319 320 initComposeState(); 321 322 // Need final composition of TemplateList. This adds the wild cards onto the chains. 323 m_templateList.compose(this); 324 325 // Need to clear check for properties at the same import level. 326 m_outputProperties.compose(this); 327 m_outputProperties.endCompose(this); 328 329 // Now call the compose() method on every element to give it a chance to adjust 330 // based on composed values. 331 332 n = getGlobalImportCount(); 333 334 for (int i = 0; i < n; i++) 335 { 336 StylesheetComposed imported = this.getGlobalImport(i); 337 int includedCount = imported.getIncludeCountComposed(); 338 for (int j = -1; j < includedCount; j++) 339 { 340 Stylesheet included = imported.getIncludeComposed(j); 341 composeTemplates(included); 342 } 343 } 344 // Attempt to register any remaining unregistered extension namespaces. 345 if (m_extNsMgr != null) 346 m_extNsMgr.registerUnregisteredNamespaces(); 347 348 clearComposeState(); 349 } 350 351 /** 352 * Call the compose function for each ElemTemplateElement. 353 * 354 * @param templ non-null reference to template element that will have 355 * the composed method called on it, and will have it's children's composed 356 * methods called. 357 */ composeTemplates(ElemTemplateElement templ)358 void composeTemplates(ElemTemplateElement templ) throws TransformerException 359 { 360 361 templ.compose(this); 362 363 for (ElemTemplateElement child = templ.getFirstChildElem(); 364 child != null; child = child.getNextSiblingElem()) 365 { 366 composeTemplates(child); 367 } 368 369 templ.endCompose(this); 370 } 371 372 /** 373 * The combined list of imports. The stylesheet with the highest 374 * import precedence will be at element 0. The one with the lowest 375 * import precedence will be at element length - 1. 376 * @serial 377 */ 378 private StylesheetComposed[] m_globalImportList; 379 380 /** 381 * Add the imports in the given sheet to the working importList vector. 382 * The will be added from highest import precedence to 383 * least import precedence. This is a post-order traversal of the 384 * import tree as described in <a href="http://www.w3.org/TR/xslt.html#import">the 385 * XSLT Recommendation</a>. 386 * <p>For example, suppose</p> 387 * <p>stylesheet A imports stylesheets B and C in that order;</p> 388 * <p>stylesheet B imports stylesheet D;</p> 389 * <p>stylesheet C imports stylesheet E.</p> 390 * <p>Then the order of import precedence (highest first) is 391 * A, C, E, B, D.</p> 392 * 393 * @param stylesheet Stylesheet to examine for imports. 394 * @param addToList <code>true</code> if this template should be added to the import list 395 * @param importList The working import list. Templates are added here in the reverse 396 * order of priority. When we're all done, we'll reverse this to the correct 397 * priority in an array. 398 */ addImports(Stylesheet stylesheet, boolean addToList, Vector importList)399 protected void addImports(Stylesheet stylesheet, boolean addToList, Vector importList) 400 { 401 402 // Get the direct imports of this sheet. 403 404 int n = stylesheet.getImportCount(); 405 406 if (n > 0) 407 { 408 for (int i = 0; i < n; i++) 409 { 410 Stylesheet imported = stylesheet.getImport(i); 411 412 addImports(imported, true, importList); 413 } 414 } 415 416 n = stylesheet.getIncludeCount(); 417 418 if (n > 0) 419 { 420 for (int i = 0; i < n; i++) 421 { 422 Stylesheet included = stylesheet.getInclude(i); 423 424 addImports(included, false, importList); 425 } 426 } 427 428 if (addToList) 429 importList.addElement(stylesheet); 430 431 } 432 433 /** 434 * Get a stylesheet from the global import list. 435 * TODO: JKESS PROPOSES SPECIAL-CASE FOR NO IMPORT LIST, TO MATCH COUNT. 436 * 437 * @param i Index of stylesheet to get from global import list 438 * 439 * @return The stylesheet at the given index 440 */ getGlobalImport(int i)441 public StylesheetComposed getGlobalImport(int i) 442 { 443 return m_globalImportList[i]; 444 } 445 446 /** 447 * Get the total number of imports in the global import list. 448 * 449 * @return The total number of imported stylesheets, including 450 * the root stylesheet, thus the number will always be 1 or 451 * greater. 452 * TODO: JKESS PROPOSES SPECIAL-CASE FOR NO IMPORT LIST, TO MATCH DESCRIPTION. 453 */ getGlobalImportCount()454 public int getGlobalImportCount() 455 { 456 return (m_globalImportList!=null) 457 ? m_globalImportList.length 458 : 1; 459 } 460 461 /** 462 * Given a stylesheet, return the number of the stylesheet 463 * in the global import list. 464 * @param sheet The stylesheet which will be located in the 465 * global import list. 466 * @return The index into the global import list of the given stylesheet, 467 * or -1 if it is not found (which should never happen). 468 */ getImportNumber(StylesheetComposed sheet)469 public int getImportNumber(StylesheetComposed sheet) 470 { 471 472 if (this == sheet) 473 return 0; 474 475 int n = getGlobalImportCount(); 476 477 for (int i = 0; i < n; i++) 478 { 479 if (sheet == getGlobalImport(i)) 480 return i; 481 } 482 483 return -1; 484 } 485 486 /** 487 * This will be set up with the default values, and then the values 488 * will be set as stylesheets are encountered. 489 * @serial 490 */ 491 private OutputProperties m_outputProperties; 492 493 /** 494 * Recompose the output format object from the included elements. 495 * 496 * @param oprops non-null reference to xsl:output properties representation. 497 */ recomposeOutput(OutputProperties oprops)498 void recomposeOutput(OutputProperties oprops) 499 throws TransformerException 500 { 501 502 m_outputProperties.copyFrom(oprops); 503 } 504 505 /** 506 * Get the combined "xsl:output" property with the properties 507 * combined from the included stylesheets. If a xsl:output 508 * is not declared in this stylesheet or an included stylesheet, 509 * look in the imports. 510 * Please note that this returns a reference to the OutputProperties 511 * object, not a cloned object, like getOutputProperties does. 512 * @see <a href="http://www.w3.org/TR/xslt#output">output in XSLT Specification</a> 513 * 514 * @return non-null reference to composed output properties object. 515 */ getOutputComposed()516 public OutputProperties getOutputComposed() 517 { 518 519 // System.out.println("getOutputComposed.getIndent: "+m_outputProperties.getIndent()); 520 // System.out.println("getOutputComposed.getIndenting: "+m_outputProperties.getIndenting()); 521 return m_outputProperties; 522 } 523 524 /** Flag indicating whether an output method has been set by the user. 525 * @serial */ 526 private boolean m_outputMethodSet = false; 527 528 /** 529 * Find out if an output method has been set by the user. 530 * 531 * @return Value indicating whether an output method has been set by the user 532 * @xsl.usage internal 533 */ isOutputMethodSet()534 public boolean isOutputMethodSet() 535 { 536 return m_outputMethodSet; 537 } 538 539 /** 540 * Composed set of all included and imported attribute set properties. 541 * Each entry is a vector of ElemAttributeSet objects. 542 * @serial 543 */ 544 private HashMap m_attrSets; 545 546 /** 547 * Recompose the attribute-set declarations. 548 * 549 * @param attrSet An attribute-set to add to the hashtable of attribute sets. 550 */ recomposeAttributeSets(ElemAttributeSet attrSet)551 void recomposeAttributeSets(ElemAttributeSet attrSet) 552 { 553 ArrayList attrSetList = (ArrayList) m_attrSets.get(attrSet.getName()); 554 555 if (null == attrSetList) 556 { 557 attrSetList = new ArrayList(); 558 559 m_attrSets.put(attrSet.getName(), attrSetList); 560 } 561 562 attrSetList.add(attrSet); 563 } 564 565 /** 566 * Get a list "xsl:attribute-set" properties that match the qname. 567 * @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in XSLT Specification</a> 568 * 569 * @param name Qualified name of attribute set properties to get 570 * 571 * @return A vector of attribute sets matching the given name 572 * 573 * @throws ArrayIndexOutOfBoundsException 574 */ getAttributeSetComposed(QName name)575 public ArrayList getAttributeSetComposed(QName name) 576 throws ArrayIndexOutOfBoundsException 577 { 578 return (ArrayList) m_attrSets.get(name); 579 } 580 581 /** 582 * Table of DecimalFormatSymbols, keyed by QName. 583 * @serial 584 */ 585 private Hashtable m_decimalFormatSymbols; 586 587 /** 588 * Recompose the decimal-format declarations. 589 * 590 * @param dfp A DecimalFormatProperties to add to the hashtable of decimal formats. 591 */ recomposeDecimalFormats(DecimalFormatProperties dfp)592 void recomposeDecimalFormats(DecimalFormatProperties dfp) 593 { 594 DecimalFormatSymbols oldDfs = 595 (DecimalFormatSymbols) m_decimalFormatSymbols.get(dfp.getName()); 596 if (null == oldDfs) 597 { 598 m_decimalFormatSymbols.put(dfp.getName(), dfp.getDecimalFormatSymbols()); 599 } 600 else if (!dfp.getDecimalFormatSymbols().equals(oldDfs)) 601 { 602 String themsg; 603 if (dfp.getName().equals(new QName(""))) 604 { 605 // "Only one default xsl:decimal-format declaration is allowed." 606 themsg = XSLMessages.createWarning( 607 XSLTErrorResources.WG_ONE_DEFAULT_XSLDECIMALFORMAT_ALLOWED, 608 new Object[0]); 609 } 610 else 611 { 612 // "xsl:decimal-format names must be unique. Name {0} has been duplicated." 613 themsg = XSLMessages.createWarning( 614 XSLTErrorResources.WG_XSLDECIMALFORMAT_NAMES_MUST_BE_UNIQUE, 615 new Object[] {dfp.getName()}); 616 } 617 618 error(themsg); // Should we throw TransformerException instead? 619 } 620 621 } 622 623 /** 624 * Given a valid element decimal-format name, return the 625 * decimalFormatSymbols with that name. 626 * <p>It is an error to declare either the default decimal-format or 627 * a decimal-format with a given name more than once (even with 628 * different import precedence), unless it is declared every 629 * time with the same value for all attributes (taking into 630 * account any default values).</p> 631 * <p>Which means, as far as I can tell, the decimal-format 632 * properties are not additive.</p> 633 * 634 * @param name Qualified name of the decimal format to find 635 * @return DecimalFormatSymbols object matching the given name or 636 * null if name is not found. 637 */ getDecimalFormatComposed(QName name)638 public DecimalFormatSymbols getDecimalFormatComposed(QName name) 639 { 640 return (DecimalFormatSymbols) m_decimalFormatSymbols.get(name); 641 } 642 643 /** 644 * A list of all key declarations visible from this stylesheet and all 645 * lesser stylesheets. 646 * @serial 647 */ 648 private Vector m_keyDecls; 649 650 /** 651 * Recompose the key declarations. 652 * 653 * @param keyDecl A KeyDeclaration to be added to the vector of key declarations. 654 */ recomposeKeys(KeyDeclaration keyDecl)655 void recomposeKeys(KeyDeclaration keyDecl) 656 { 657 m_keyDecls.addElement(keyDecl); 658 } 659 660 /** 661 * Get the composed "xsl:key" properties. 662 * @see <a href="http://www.w3.org/TR/xslt#key">key in XSLT Specification</a> 663 * 664 * @return A vector of the composed "xsl:key" properties. 665 */ getKeysComposed()666 public Vector getKeysComposed() 667 { 668 return m_keyDecls; 669 } 670 671 /** 672 * Composed set of all namespace aliases. 673 * @serial 674 */ 675 private Hashtable m_namespaceAliasComposed; 676 677 /** 678 * Recompose the namespace-alias declarations. 679 * 680 * @param nsAlias A NamespaceAlias object to add to the hashtable of namespace aliases. 681 */ recomposeNamespaceAliases(NamespaceAlias nsAlias)682 void recomposeNamespaceAliases(NamespaceAlias nsAlias) 683 { 684 m_namespaceAliasComposed.put(nsAlias.getStylesheetNamespace(), 685 nsAlias); 686 } 687 688 /** 689 * Get the "xsl:namespace-alias" property. 690 * Return the NamespaceAlias for a given namespace uri. 691 * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a> 692 * 693 * @param uri non-null reference to namespace that is to be aliased. 694 * 695 * @return NamespaceAlias that matches uri, or null if no match. 696 */ getNamespaceAliasComposed(String uri)697 public NamespaceAlias getNamespaceAliasComposed(String uri) 698 { 699 return (NamespaceAlias) ((null == m_namespaceAliasComposed) 700 ? null : m_namespaceAliasComposed.get(uri)); 701 } 702 703 /** 704 * The "xsl:template" properties. 705 * @serial 706 */ 707 private TemplateList m_templateList; 708 709 /** 710 * Recompose the template declarations. 711 * 712 * @param template An ElemTemplate object to add to the template list. 713 */ recomposeTemplates(ElemTemplate template)714 void recomposeTemplates(ElemTemplate template) 715 { 716 m_templateList.setTemplate(template); 717 } 718 719 /** 720 * Accessor method to retrieve the <code>TemplateList</code> associated with 721 * this StylesheetRoot. 722 * 723 * @return The composed <code>TemplateList</code>. 724 */ getTemplateListComposed()725 public final TemplateList getTemplateListComposed() 726 { 727 return m_templateList; 728 } 729 730 /** 731 * Mutator method to set the <code>TemplateList</code> associated with this 732 * StylesheetRoot. This method should only be used by the compiler. Normally, 733 * the template list is built during the recompose process and should not be 734 * altered by the user. 735 * @param templateList The new <code>TemplateList</code> for this StylesheetRoot. 736 */ setTemplateListComposed(TemplateList templateList)737 public final void setTemplateListComposed(TemplateList templateList) 738 { 739 m_templateList = templateList; 740 } 741 742 /** 743 * Get an "xsl:template" property by node match. This looks in the imports as 744 * well as this stylesheet. 745 * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a> 746 * 747 * @param xctxt non-null reference to XPath runtime execution context. 748 * @param targetNode non-null reference of node that the template must match. 749 * @param mode qualified name of the node, or null. 750 * @param quietConflictWarnings true if conflict warnings should not be reported. 751 * 752 * @return reference to ElemTemplate that is the best match for targetNode, or 753 * null if no match could be made. 754 * 755 * @throws TransformerException 756 */ getTemplateComposed(XPathContext xctxt, int targetNode, QName mode, boolean quietConflictWarnings, DTM dtm)757 public ElemTemplate getTemplateComposed(XPathContext xctxt, 758 int targetNode, 759 QName mode, 760 boolean quietConflictWarnings, 761 DTM dtm) 762 throws TransformerException 763 { 764 return m_templateList.getTemplate(xctxt, targetNode, mode, 765 quietConflictWarnings, 766 dtm); 767 } 768 769 /** 770 * Get an "xsl:template" property by node match. This looks in the imports as 771 * well as this stylesheet. 772 * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a> 773 * 774 * @param xctxt non-null reference to XPath runtime execution context. 775 * @param targetNode non-null reference of node that the template must match. 776 * @param mode qualified name of the node, or null. 777 * @param maxImportLevel The maximum importCountComposed that we should consider or -1 778 * if we should consider all import levels. This is used by apply-imports to 779 * access templates that have been overridden. 780 * @param endImportLevel The count of composed imports 781 * @param quietConflictWarnings true if conflict warnings should not be reported. 782 * 783 * @return reference to ElemTemplate that is the best match for targetNode, or 784 * null if no match could be made. 785 * 786 * @throws TransformerException 787 */ getTemplateComposed(XPathContext xctxt, int targetNode, QName mode, int maxImportLevel, int endImportLevel, boolean quietConflictWarnings, DTM dtm)788 public ElemTemplate getTemplateComposed(XPathContext xctxt, 789 int targetNode, 790 QName mode, 791 int maxImportLevel, int endImportLevel, 792 boolean quietConflictWarnings, 793 DTM dtm) 794 throws TransformerException 795 { 796 return m_templateList.getTemplate(xctxt, targetNode, mode, 797 maxImportLevel, endImportLevel, 798 quietConflictWarnings, 799 dtm); 800 } 801 802 /** 803 * Get an "xsl:template" property. This looks in the imports as 804 * well as this stylesheet. 805 * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a> 806 * 807 * @param qname non-null reference to qualified name of template. 808 * 809 * @return reference to named template, or null if not found. 810 */ getTemplateComposed(QName qname)811 public ElemTemplate getTemplateComposed(QName qname) 812 { 813 return m_templateList.getTemplate(qname); 814 } 815 816 /** 817 * Composed set of all variables and params. 818 * @serial 819 */ 820 private Vector m_variables; 821 822 /** 823 * Recompose the top level variable and parameter declarations. 824 * 825 * @param elemVar A top level variable or parameter to be added to the Vector. 826 */ recomposeVariables(ElemVariable elemVar)827 void recomposeVariables(ElemVariable elemVar) 828 { 829 // Don't overide higher priority variable 830 if (getVariableOrParamComposed(elemVar.getName()) == null) 831 { 832 elemVar.setIsTopLevel(true); // Mark as a top-level variable or param 833 elemVar.setIndex(m_variables.size()); 834 m_variables.addElement(elemVar); 835 } 836 } 837 838 /** 839 * Get an "xsl:variable" property. 840 * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a> 841 * 842 * @param qname Qualified name of variable or param 843 * 844 * @return The ElemVariable with the given qualified name 845 */ getVariableOrParamComposed(QName qname)846 public ElemVariable getVariableOrParamComposed(QName qname) 847 { 848 if (null != m_variables) 849 { 850 int n = m_variables.size(); 851 852 for (int i = 0; i < n; i++) 853 { 854 ElemVariable var = (ElemVariable)m_variables.elementAt(i); 855 if(var.getName().equals(qname)) 856 return var; 857 } 858 } 859 860 return null; 861 } 862 863 /** 864 * Get all global "xsl:variable" properties in scope for this stylesheet. 865 * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a> 866 * 867 * @return Vector of all variables and params in scope 868 */ getVariablesAndParamsComposed()869 public Vector getVariablesAndParamsComposed() 870 { 871 return m_variables; 872 } 873 874 /** 875 * A list of properties that specify how to do space 876 * stripping. This uses the same exact mechanism as Templates. 877 * @serial 878 */ 879 private TemplateList m_whiteSpaceInfoList; 880 881 /** 882 * Recompose the strip-space and preserve-space declarations. 883 * 884 * @param wsi A WhiteSpaceInfo element to add to the list of WhiteSpaceInfo elements. 885 */ recomposeWhiteSpaceInfo(WhiteSpaceInfo wsi)886 void recomposeWhiteSpaceInfo(WhiteSpaceInfo wsi) 887 { 888 if (null == m_whiteSpaceInfoList) 889 m_whiteSpaceInfoList = new TemplateList(); 890 891 m_whiteSpaceInfoList.setTemplate(wsi); 892 } 893 894 /** 895 * Check to see if the caller should bother with check for 896 * whitespace nodes. 897 * 898 * @return Whether the caller should bother with check for 899 * whitespace nodes. 900 */ shouldCheckWhitespace()901 public boolean shouldCheckWhitespace() 902 { 903 return null != m_whiteSpaceInfoList; 904 } 905 906 /** 907 * Get information about whether or not an element should strip whitespace. 908 * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a> 909 * 910 * @param support The XPath runtime state. 911 * @param targetElement Element to check 912 * 913 * @return WhiteSpaceInfo for the given element 914 * 915 * @throws TransformerException 916 */ getWhiteSpaceInfo( XPathContext support, int targetElement, DTM dtm)917 public WhiteSpaceInfo getWhiteSpaceInfo( 918 XPathContext support, int targetElement, DTM dtm) throws TransformerException 919 { 920 921 if (null != m_whiteSpaceInfoList) 922 return (WhiteSpaceInfo) m_whiteSpaceInfoList.getTemplate(support, 923 targetElement, null, false, dtm); 924 else 925 return null; 926 } 927 928 /** 929 * Get information about whether or not an element should strip whitespace. 930 * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a> 931 * 932 * @param support The XPath runtime state. 933 * @param targetElement Element to check 934 * 935 * @return true if the whitespace should be stripped. 936 * 937 * @throws TransformerException 938 */ shouldStripWhiteSpace( XPathContext support, int targetElement)939 public boolean shouldStripWhiteSpace( 940 XPathContext support, int targetElement) throws TransformerException 941 { 942 if (null != m_whiteSpaceInfoList) 943 { 944 while(DTM.NULL != targetElement) 945 { 946 DTM dtm = support.getDTM(targetElement); 947 WhiteSpaceInfo info = (WhiteSpaceInfo) m_whiteSpaceInfoList.getTemplate(support, 948 targetElement, null, false, dtm); 949 if(null != info) 950 return info.getShouldStripSpace(); 951 952 int parent = dtm.getParent(targetElement); 953 if(DTM.NULL != parent && DTM.ELEMENT_NODE == dtm.getNodeType(parent)) 954 targetElement = parent; 955 else 956 targetElement = DTM.NULL; 957 } 958 } 959 return false; 960 } 961 962 /** 963 * Get information about whether or not whitespace can be stripped. 964 * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a> 965 * 966 * @return true if the whitespace can be stripped. 967 */ canStripWhiteSpace()968 public boolean canStripWhiteSpace() 969 { 970 return (null != m_whiteSpaceInfoList); 971 } 972 973 974 975 /** 976 * The default template to use for text nodes if we don't find 977 * anything else. This is initialized in initDefaultRule(). 978 * @serial 979 * @xsl.usage advanced 980 */ 981 private ElemTemplate m_defaultTextRule; 982 983 /** 984 * Get the default template for text. 985 * 986 * @return the default template for text. 987 * @xsl.usage advanced 988 */ getDefaultTextRule()989 public final ElemTemplate getDefaultTextRule() 990 { 991 return m_defaultTextRule; 992 } 993 994 /** 995 * The default template to use if we don't find anything 996 * else. This is initialized in initDefaultRule(). 997 * @serial 998 * @xsl.usage advanced 999 */ 1000 private ElemTemplate m_defaultRule; 1001 1002 /** 1003 * Get the default template for elements. 1004 * 1005 * @return the default template for elements. 1006 * @xsl.usage advanced 1007 */ getDefaultRule()1008 public final ElemTemplate getDefaultRule() 1009 { 1010 return m_defaultRule; 1011 } 1012 1013 /** 1014 * The default template to use for the root if we don't find 1015 * anything else. This is initialized in initDefaultRule(). 1016 * We kind of need this because the defaultRule isn't good 1017 * enough because it doesn't supply a document context. 1018 * For now, I default the root document element to "HTML". 1019 * Don't know if this is really a good idea or not. 1020 * I suspect it is not. 1021 * @serial 1022 * @xsl.usage advanced 1023 */ 1024 private ElemTemplate m_defaultRootRule; 1025 1026 /** 1027 * Get the default template for a root node. 1028 * 1029 * @return The default template for a root node. 1030 * @xsl.usage advanced 1031 */ getDefaultRootRule()1032 public final ElemTemplate getDefaultRootRule() 1033 { 1034 return m_defaultRootRule; 1035 } 1036 1037 /** 1038 * The start rule to kick off the transformation. 1039 * @serial 1040 * @xsl.usage advanced 1041 */ 1042 private ElemTemplate m_startRule; 1043 1044 /** 1045 * Get the default template for a root node. 1046 * 1047 * @return The default template for a root node. 1048 * @xsl.usage advanced 1049 */ getStartRule()1050 public final ElemTemplate getStartRule() 1051 { 1052 return m_startRule; 1053 } 1054 1055 1056 /** 1057 * Used for default selection. 1058 * @serial 1059 */ 1060 XPath m_selectDefault; 1061 1062 /** 1063 * Create the default rule if needed. 1064 * 1065 * @throws TransformerException 1066 */ initDefaultRule(ErrorListener errorListener)1067 private void initDefaultRule(ErrorListener errorListener) throws TransformerException 1068 { 1069 1070 // Then manufacture a default 1071 m_defaultRule = new ElemTemplate(); 1072 1073 m_defaultRule.setStylesheet(this); 1074 1075 XPath defMatch = new XPath("*", this, this, XPath.MATCH, errorListener); 1076 1077 m_defaultRule.setMatch(defMatch); 1078 1079 ElemApplyTemplates childrenElement = new ElemApplyTemplates(); 1080 1081 childrenElement.setIsDefaultTemplate(true); 1082 childrenElement.setSelect(m_selectDefault); 1083 m_defaultRule.appendChild(childrenElement); 1084 1085 m_startRule = m_defaultRule; 1086 1087 // ----------------------------- 1088 m_defaultTextRule = new ElemTemplate(); 1089 1090 m_defaultTextRule.setStylesheet(this); 1091 1092 defMatch = new XPath("text() | @*", this, this, XPath.MATCH, errorListener); 1093 1094 m_defaultTextRule.setMatch(defMatch); 1095 1096 ElemValueOf elemValueOf = new ElemValueOf(); 1097 1098 m_defaultTextRule.appendChild(elemValueOf); 1099 1100 XPath selectPattern = new XPath(".", this, this, XPath.SELECT, errorListener); 1101 1102 elemValueOf.setSelect(selectPattern); 1103 1104 //-------------------------------- 1105 m_defaultRootRule = new ElemTemplate(); 1106 1107 m_defaultRootRule.setStylesheet(this); 1108 1109 defMatch = new XPath("/", this, this, XPath.MATCH, errorListener); 1110 1111 m_defaultRootRule.setMatch(defMatch); 1112 1113 childrenElement = new ElemApplyTemplates(); 1114 1115 childrenElement.setIsDefaultTemplate(true); 1116 m_defaultRootRule.appendChild(childrenElement); 1117 childrenElement.setSelect(m_selectDefault); 1118 } 1119 1120 /** 1121 * This is a generic version of C.A.R Hoare's Quick Sort 1122 * algorithm. This will handle arrays that are already 1123 * sorted, and arrays with duplicate keys. It was lifted from 1124 * the NodeSorter class but should probably be eliminated and replaced 1125 * with a call to Collections.sort when we migrate to Java2.<BR> 1126 * 1127 * If you think of a one dimensional array as going from 1128 * the lowest index on the left to the highest index on the right 1129 * then the parameters to this function are lowest index or 1130 * left and highest index or right. The first time you call 1131 * this function it will be with the parameters 0, a.length - 1. 1132 * 1133 * @param v a vector of ElemTemplateElement elements 1134 * @param lo0 left boundary of partition 1135 * @param hi0 right boundary of partition 1136 * 1137 */ 1138 QuickSort2(Vector v, int lo0, int hi0)1139 private void QuickSort2(Vector v, int lo0, int hi0) 1140 { 1141 int lo = lo0; 1142 int hi = hi0; 1143 1144 if ( hi0 > lo0) 1145 { 1146 // Arbitrarily establishing partition element as the midpoint of 1147 // the array. 1148 ElemTemplateElement midNode = (ElemTemplateElement) v.elementAt( ( lo0 + hi0 ) / 2 ); 1149 1150 // loop through the array until indices cross 1151 while( lo <= hi ) 1152 { 1153 // find the first element that is greater than or equal to 1154 // the partition element starting from the left Index. 1155 while( (lo < hi0) && (((ElemTemplateElement) v.elementAt(lo)).compareTo(midNode) < 0) ) 1156 { 1157 ++lo; 1158 } // end while 1159 1160 // find an element that is smaller than or equal to 1161 // the partition element starting from the right Index. 1162 while( (hi > lo0) && (((ElemTemplateElement) v.elementAt(hi)).compareTo(midNode) > 0) ) { 1163 --hi; 1164 } 1165 1166 // if the indexes have not crossed, swap 1167 if( lo <= hi ) 1168 { 1169 ElemTemplateElement node = (ElemTemplateElement) v.elementAt(lo); 1170 v.setElementAt(v.elementAt(hi), lo); 1171 v.setElementAt(node, hi); 1172 1173 ++lo; 1174 --hi; 1175 } 1176 } 1177 1178 // If the right index has not reached the left side of array 1179 // must now sort the left partition. 1180 if( lo0 < hi ) 1181 { 1182 QuickSort2( v, lo0, hi ); 1183 } 1184 1185 // If the left index has not reached the right side of array 1186 // must now sort the right partition. 1187 if( lo < hi0 ) 1188 { 1189 QuickSort2( v, lo, hi0 ); 1190 } 1191 } 1192 } // end QuickSort2 */ 1193 1194 private transient ComposeState m_composeState; 1195 1196 /** 1197 * Initialize a new ComposeState. 1198 */ initComposeState()1199 void initComposeState() 1200 { 1201 m_composeState = new ComposeState(); 1202 } 1203 1204 /** 1205 * Return class to track state global state during the compose() operation. 1206 * @return ComposeState reference, or null if endCompose has been called. 1207 */ getComposeState()1208 ComposeState getComposeState() 1209 { 1210 return m_composeState; 1211 } 1212 1213 /** 1214 * Clear the compose state. 1215 */ clearComposeState()1216 private void clearComposeState() 1217 { 1218 m_composeState = null; 1219 } 1220 1221 private String m_extensionHandlerClass = 1222 "org.apache.xalan.extensions.ExtensionHandlerExsltFunction"; 1223 1224 /** 1225 * This internal method allows the setting of the java class 1226 * to handle the extension function (if other than the default one). 1227 * 1228 * @xsl.usage internal 1229 */ setExtensionHandlerClass(String handlerClassName)1230 public String setExtensionHandlerClass(String handlerClassName) { 1231 String oldvalue = m_extensionHandlerClass; 1232 m_extensionHandlerClass = handlerClassName; 1233 return oldvalue; 1234 } 1235 /** 1236 * 1237 * @xsl.usage internal 1238 */ getExtensionHandlerClass()1239 public String getExtensionHandlerClass() { 1240 return m_extensionHandlerClass; 1241 } 1242 1243 /** 1244 * Class to track state global state during the compose() operation. 1245 */ 1246 class ComposeState 1247 { ComposeState()1248 ComposeState() 1249 { 1250 int size = m_variables.size(); 1251 for (int i = 0; i < size; i++) 1252 { 1253 ElemVariable ev = (ElemVariable)m_variables.elementAt(i); 1254 m_variableNames.addElement(ev.getName()); 1255 } 1256 1257 } 1258 1259 private ExpandedNameTable m_ent = new ExpandedNameTable(); 1260 1261 /** 1262 * Given a qualified name, return an integer ID that can be 1263 * quickly compared. 1264 * 1265 * @param qname a qualified name object, must not be null. 1266 * 1267 * @return the expanded-name id of the qualified name. 1268 */ getQNameID(QName qname)1269 public int getQNameID(QName qname) 1270 { 1271 1272 return m_ent.getExpandedTypeID(qname.getNamespace(), 1273 qname.getLocalName(), 1274 // The type doesn't matter for our 1275 // purposes. 1276 org.apache.xml.dtm.DTM.ELEMENT_NODE); 1277 } 1278 1279 /** 1280 * A Vector of the current params and QNames within the current template. 1281 * Set by ElemTemplate and used by ProcessorVariable. 1282 */ 1283 private java.util.Vector m_variableNames = new java.util.Vector(); 1284 1285 /** 1286 * Add the name of a qualified name within the template. The position in 1287 * the vector is its ID. 1288 * @param qname A qualified name of a param or variable, should be non-null. 1289 * @return the index where the variable was added. 1290 */ addVariableName(final org.apache.xml.utils.QName qname)1291 int addVariableName(final org.apache.xml.utils.QName qname) 1292 { 1293 int pos = m_variableNames.size(); 1294 m_variableNames.addElement(qname); 1295 int frameSize = m_variableNames.size() - getGlobalsSize(); 1296 if(frameSize > m_maxStackFrameSize) 1297 m_maxStackFrameSize++; 1298 return pos; 1299 } 1300 resetStackFrameSize()1301 void resetStackFrameSize() 1302 { 1303 m_maxStackFrameSize = 0; 1304 } 1305 getFrameSize()1306 int getFrameSize() 1307 { 1308 return m_maxStackFrameSize; 1309 } 1310 1311 /** 1312 * Get the current size of the stack frame. Use this to record the position 1313 * in a template element at startElement, so that it can be popped 1314 * at endElement. 1315 */ getCurrentStackFrameSize()1316 int getCurrentStackFrameSize() 1317 { 1318 return m_variableNames.size(); 1319 } 1320 1321 /** 1322 * Set the current size of the stack frame. 1323 */ setCurrentStackFrameSize(int sz)1324 void setCurrentStackFrameSize(int sz) 1325 { 1326 m_variableNames.setSize(sz); 1327 } 1328 getGlobalsSize()1329 int getGlobalsSize() 1330 { 1331 return m_variables.size(); 1332 } 1333 1334 IntStack m_marks = new IntStack(); 1335 pushStackMark()1336 void pushStackMark() 1337 { 1338 m_marks.push(getCurrentStackFrameSize()); 1339 } 1340 popStackMark()1341 void popStackMark() 1342 { 1343 int mark = m_marks.pop(); 1344 setCurrentStackFrameSize(mark); 1345 } 1346 1347 /** 1348 * Get the Vector of the current params and QNames to be collected 1349 * within the current template. 1350 * @return A reference to the vector of variable names. The reference 1351 * returned is owned by this class, and so should not really be mutated, or 1352 * stored anywhere. 1353 */ getVariableNames()1354 java.util.Vector getVariableNames() 1355 { 1356 return m_variableNames; 1357 } 1358 1359 private int m_maxStackFrameSize; 1360 1361 } 1362 1363 /** 1364 * @return Optimization flag 1365 */ getOptimizer()1366 public boolean getOptimizer() { 1367 return m_optimizer; 1368 } 1369 1370 /** 1371 * @param b Optimization flag 1372 */ setOptimizer(boolean b)1373 public void setOptimizer(boolean b) { 1374 m_optimizer = b; 1375 } 1376 1377 /** 1378 * @return Incremental flag 1379 */ getIncremental()1380 public boolean getIncremental() { 1381 return m_incremental; 1382 } 1383 1384 /** 1385 * @return source location flag 1386 */ getSource_location()1387 public boolean getSource_location() { 1388 return m_source_location; 1389 } 1390 1391 /** 1392 * @param b Incremental flag 1393 */ setIncremental(boolean b)1394 public void setIncremental(boolean b) { 1395 m_incremental = b; 1396 } 1397 1398 /** 1399 * @param b Source location flag 1400 */ setSource_location(boolean b)1401 public void setSource_location(boolean b) { 1402 m_source_location = b; 1403 } 1404 1405 } 1406