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: StylesheetRoot.java 476466 2006-11-18 08:22:31Z minchau $ 020 */ 021 package org.apache.xalan.templates; 022 023 import java.text.DecimalFormatSymbols; 024 import java.util.ArrayList; 025 import java.util.HashMap; 026 import java.util.Hashtable; 027 import java.util.Properties; 028 import java.util.Vector; 029 030 import javax.xml.transform.ErrorListener; 031 import javax.xml.transform.Templates; 032 import javax.xml.transform.Transformer; 033 import javax.xml.transform.TransformerConfigurationException; 034 import javax.xml.transform.TransformerException; 035 036 import org.apache.xalan.extensions.ExtensionNamespacesManager; 037 import org.apache.xalan.processor.XSLTSchema; 038 import org.apache.xalan.res.XSLMessages; 039 import org.apache.xalan.res.XSLTErrorResources; 040 041 import org.apache.xalan.transformer.TransformerImpl; 042 import org.apache.xml.dtm.DTM; 043 import org.apache.xml.dtm.ref.ExpandedNameTable; 044 import org.apache.xml.utils.IntStack; 045 import org.apache.xml.utils.QName; 046 import org.apache.xpath.XPath; 047 import org.apache.xpath.XPathContext; 048 049 /** 050 * This class represents the root object of the stylesheet tree. 051 * @xsl.usage general 052 */ 053 public class StylesheetRoot extends StylesheetComposed 054 implements java.io.Serializable, Templates 055 { 056 static final long serialVersionUID = 3875353123529147855L; 057 058 /** 059 * The flag for the setting of the optimize feature; 060 */ 061 private boolean m_optimizer = true; 062 063 /** 064 * The flag for the setting of the incremental feature; 065 */ 066 private boolean m_incremental = false; 067 068 /** 069 * The flag for the setting of the source_location feature; 070 */ 071 private boolean m_source_location = false; 072 073 /** 074 * State of the secure processing feature. 075 */ 076 private boolean m_isSecureProcessing = false; 077 078 /** 079 * Uses an XSL stylesheet document. 080 * @throws TransformerConfigurationException if the baseIdentifier can not be resolved to a URL. 081 */ 082 public StylesheetRoot(ErrorListener errorListener) throws TransformerConfigurationException 083 { 084 085 super(null); 086 087 setStylesheetRoot(this); 088 089 try 090 { 091 m_selectDefault = new XPath("node()", this, this, XPath.SELECT, errorListener); 092 093 initDefaultRule(errorListener); 094 } 095 catch (TransformerException se) 096 { 097 throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_INIT_DEFAULT_TEMPLATES, null), se); //"Can't init default templates!", se); 098 } 099 } 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 */ 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 */ 126 public boolean isRoot() 127 { 128 return true; 129 } 130 131 /** 132 * Set the state of the secure processing feature. 133 */ 134 public void setSecureProcessing(boolean flag) 135 { 136 m_isSecureProcessing = flag; 137 } 138 139 /** 140 * Return the state of the secure processing feature. 141 */ 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 */ 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 */ 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 */ 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 */ 198 public Transformer newTransformer() 199 { 200 return new TransformerImpl(this); 201 } 202 203 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 1208 ComposeState getComposeState() 1209 { 1210 return m_composeState; 1211 } 1212 1213 /** 1214 * Clear the compose state. 1215 */ 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 */ 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 */ 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 { 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 */ 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 */ 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 1301 void resetStackFrameSize() 1302 { 1303 m_maxStackFrameSize = 0; 1304 } 1305 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 */ 1316 int getCurrentStackFrameSize() 1317 { 1318 return m_variableNames.size(); 1319 } 1320 1321 /** 1322 * Set the current size of the stack frame. 1323 */ 1324 void setCurrentStackFrameSize(int sz) 1325 { 1326 m_variableNames.setSize(sz); 1327 } 1328 1329 int getGlobalsSize() 1330 { 1331 return m_variables.size(); 1332 } 1333 1334 IntStack m_marks = new IntStack(); 1335 1336 void pushStackMark() 1337 { 1338 m_marks.push(getCurrentStackFrameSize()); 1339 } 1340 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 */ 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 */ 1366 public boolean getOptimizer() { 1367 return m_optimizer; 1368 } 1369 1370 /** 1371 * @param b Optimization flag 1372 */ 1373 public void setOptimizer(boolean b) { 1374 m_optimizer = b; 1375 } 1376 1377 /** 1378 * @return Incremental flag 1379 */ 1380 public boolean getIncremental() { 1381 return m_incremental; 1382 } 1383 1384 /** 1385 * @return source location flag 1386 */ 1387 public boolean getSource_location() { 1388 return m_source_location; 1389 } 1390 1391 /** 1392 * @param b Incremental flag 1393 */ 1394 public void setIncremental(boolean b) { 1395 m_incremental = b; 1396 } 1397 1398 /** 1399 * @param b Source location flag 1400 */ 1401 public void setSource_location(boolean b) { 1402 m_source_location = b; 1403 } 1404 1405 }