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: Stylesheet.java 669373 2008-06-19 03:40:20Z zongaro $ 020 */ 021 022 package org.apache.xalan.xsltc.compiler; 023 024 import java.util.Vector; 025 import java.util.Enumeration; 026 import java.util.Hashtable; 027 import java.util.Iterator; 028 import java.util.Properties; 029 import java.util.StringTokenizer; 030 031 import org.apache.xml.utils.SystemIDResolver; 032 import org.apache.bcel.generic.ANEWARRAY; 033 import org.apache.bcel.generic.BasicType; 034 import org.apache.bcel.generic.ConstantPoolGen; 035 import org.apache.bcel.generic.FieldGen; 036 import org.apache.bcel.generic.GETFIELD; 037 import org.apache.bcel.generic.GETSTATIC; 038 import org.apache.bcel.generic.INVOKEINTERFACE; 039 import org.apache.bcel.generic.INVOKESPECIAL; 040 import org.apache.bcel.generic.INVOKEVIRTUAL; 041 import org.apache.bcel.generic.ISTORE; 042 import org.apache.bcel.generic.InstructionHandle; 043 import org.apache.bcel.generic.InstructionList; 044 import org.apache.bcel.generic.LocalVariableGen; 045 import org.apache.bcel.generic.NEW; 046 import org.apache.bcel.generic.NEWARRAY; 047 import org.apache.bcel.generic.PUSH; 048 import org.apache.bcel.generic.PUTFIELD; 049 import org.apache.bcel.generic.PUTSTATIC; 050 import org.apache.bcel.generic.TargetLostException; 051 import org.apache.bcel.util.InstructionFinder; 052 import org.apache.xalan.xsltc.compiler.util.ClassGenerator; 053 import org.apache.xalan.xsltc.compiler.util.ErrorMsg; 054 import org.apache.xalan.xsltc.compiler.util.MethodGenerator; 055 import org.apache.xalan.xsltc.compiler.util.Type; 056 import org.apache.xalan.xsltc.compiler.util.TypeCheckError; 057 import org.apache.xalan.xsltc.compiler.util.Util; 058 import org.apache.xalan.xsltc.runtime.AbstractTranslet; 059 import org.apache.xml.dtm.DTM; 060 061 public final class Stylesheet extends SyntaxTreeNode { 062 063 /** 064 * XSLT version defined in the stylesheet. 065 */ 066 private String _version; 067 068 /** 069 * Internal name of this stylesheet used as a key into the symbol table. 070 */ 071 private QName _name; 072 073 /** 074 * A URI that represents the system ID for this stylesheet. 075 */ 076 private String _systemId; 077 078 /** 079 * A reference to the parent stylesheet or null if topmost. 080 */ 081 private Stylesheet _parentStylesheet; 082 083 /** 084 * Contains global variables and parameters defined in the stylesheet. 085 */ 086 private Vector _globals = new Vector(); 087 088 /** 089 * Used to cache the result returned by <code>hasLocalParams()</code>. 090 */ 091 private Boolean _hasLocalParams = null; 092 093 /** 094 * The name of the class being generated. 095 */ 096 private String _className; 097 098 /** 099 * Contains all templates defined in this stylesheet 100 */ 101 private final Vector _templates = new Vector(); 102 103 /** 104 * Used to cache result of <code>getAllValidTemplates()</code>. Only 105 * set in top-level stylesheets that include/import other stylesheets. 106 */ 107 private Vector _allValidTemplates = null; 108 109 private Vector _elementsWithNamespacesUsedDynamically = null; 110 111 /** 112 * Counter to generate unique mode suffixes. 113 */ 114 private int _nextModeSerial = 1; 115 116 /** 117 * Mapping between mode names and Mode instances. 118 */ 119 private final Hashtable _modes = new Hashtable(); 120 121 /** 122 * A reference to the default Mode object. 123 */ 124 private Mode _defaultMode; 125 126 /** 127 * Mapping between extension URIs and their prefixes. 128 */ 129 private final Hashtable _extensions = new Hashtable(); 130 131 /** 132 * Reference to the stylesheet from which this stylesheet was 133 * imported (if any). 134 */ 135 public Stylesheet _importedFrom = null; 136 137 /** 138 * Reference to the stylesheet from which this stylesheet was 139 * included (if any). 140 */ 141 public Stylesheet _includedFrom = null; 142 143 /** 144 * Array of all the stylesheets imported or included from this one. 145 */ 146 private Vector _includedStylesheets = null; 147 148 /** 149 * Import precendence for this stylesheet. 150 */ 151 private int _importPrecedence = 1; 152 153 /** 154 * Minimum precendence of any descendant stylesheet by inclusion or 155 * importation. 156 */ 157 private int _minimumDescendantPrecedence = -1; 158 159 /** 160 * Mapping between key names and Key objects (needed by Key/IdPattern). 161 */ 162 private Hashtable _keys = new Hashtable(); 163 164 /** 165 * A reference to the SourceLoader set by the user (a URIResolver 166 * if the JAXP API is being used). 167 */ 168 private SourceLoader _loader = null; 169 170 /** 171 * Flag indicating if format-number() is called. 172 */ 173 private boolean _numberFormattingUsed = false; 174 175 /** 176 * Flag indicating if this is a simplified stylesheets. A template 177 * matching on "/" must be added in this case. 178 */ 179 private boolean _simplified = false; 180 181 /** 182 * Flag indicating if multi-document support is needed. 183 */ 184 private boolean _multiDocument = false; 185 186 /** 187 * Flag indicating if nodset() is called. 188 */ 189 private boolean _callsNodeset = false; 190 191 /** 192 * Flag indicating if id() is called. 193 */ 194 private boolean _hasIdCall = false; 195 196 /** 197 * Set to true to enable template inlining optimization. 198 * @see XSLTC#_templateInlining 199 */ 200 private boolean _templateInlining = false; 201 202 /** 203 * A reference to the last xsl:output object found in the styleshet. 204 */ 205 private Output _lastOutputElement = null; 206 207 /** 208 * Output properties for this stylesheet. 209 */ 210 private Properties _outputProperties = null; 211 212 /** 213 * Output method for this stylesheet (must be set to one of 214 * the constants defined below). 215 */ 216 private int _outputMethod = UNKNOWN_OUTPUT; 217 218 // Output method constants 219 public static final int UNKNOWN_OUTPUT = 0; 220 public static final int XML_OUTPUT = 1; 221 public static final int HTML_OUTPUT = 2; 222 public static final int TEXT_OUTPUT = 3; 223 224 /** 225 * Return the output method 226 */ 227 public int getOutputMethod() { 228 return _outputMethod; 229 } 230 231 /** 232 * Check and set the output method 233 */ 234 private void checkOutputMethod() { 235 if (_lastOutputElement != null) { 236 String method = _lastOutputElement.getOutputMethod(); 237 if (method != null) { 238 if (method.equals("xml")) 239 _outputMethod = XML_OUTPUT; 240 else if (method.equals("html")) 241 _outputMethod = HTML_OUTPUT; 242 else if (method.equals("text")) 243 _outputMethod = TEXT_OUTPUT; 244 } 245 } 246 } 247 248 public boolean getTemplateInlining() { 249 return _templateInlining; 250 } 251 252 public void setTemplateInlining(boolean flag) { 253 _templateInlining = flag; 254 } 255 256 public boolean isSimplified() { 257 return(_simplified); 258 } 259 260 public void setSimplified() { 261 _simplified = true; 262 } 263 264 public void setHasIdCall(boolean flag) { 265 _hasIdCall = flag; 266 } 267 268 public void setOutputProperty(String key, String value) { 269 if (_outputProperties == null) { 270 _outputProperties = new Properties(); 271 } 272 _outputProperties.setProperty(key, value); 273 } 274 275 public void setOutputProperties(Properties props) { 276 _outputProperties = props; 277 } 278 279 public Properties getOutputProperties() { 280 return _outputProperties; 281 } 282 283 public Output getLastOutputElement() { 284 return _lastOutputElement; 285 } 286 287 public void setMultiDocument(boolean flag) { 288 _multiDocument = flag; 289 } 290 291 public boolean isMultiDocument() { 292 return _multiDocument; 293 } 294 295 public void setCallsNodeset(boolean flag) { 296 if (flag) setMultiDocument(flag); 297 _callsNodeset = flag; 298 } 299 300 public boolean callsNodeset() { 301 return _callsNodeset; 302 } 303 304 public void numberFormattingUsed() { 305 _numberFormattingUsed = true; 306 /* 307 * Fix for bug 23046, if the stylesheet is included, set the 308 * numberFormattingUsed flag to the parent stylesheet too. 309 * AbstractTranslet.addDecimalFormat() will be inlined once for the 310 * outer most stylesheet. 311 */ 312 Stylesheet parent = getParentStylesheet(); 313 if (null != parent) parent.numberFormattingUsed(); 314 } 315 316 public void setImportPrecedence(final int precedence) { 317 // Set import precedence for this stylesheet 318 _importPrecedence = precedence; 319 320 // Set import precedence for all included stylesheets 321 final Enumeration elements = elements(); 322 while (elements.hasMoreElements()) { 323 SyntaxTreeNode child = (SyntaxTreeNode)elements.nextElement(); 324 if (child instanceof Include) { 325 Stylesheet included = ((Include)child).getIncludedStylesheet(); 326 if (included != null && included._includedFrom == this) { 327 included.setImportPrecedence(precedence); 328 } 329 } 330 } 331 332 // Set import precedence for the stylesheet that imported this one 333 if (_importedFrom != null) { 334 if (_importedFrom.getImportPrecedence() < precedence) { 335 final Parser parser = getParser(); 336 final int nextPrecedence = parser.getNextImportPrecedence(); 337 _importedFrom.setImportPrecedence(nextPrecedence); 338 } 339 } 340 // Set import precedence for the stylesheet that included this one 341 else if (_includedFrom != null) { 342 if (_includedFrom.getImportPrecedence() != precedence) 343 _includedFrom.setImportPrecedence(precedence); 344 } 345 } 346 347 public int getImportPrecedence() { 348 return _importPrecedence; 349 } 350 351 /** 352 * Get the minimum of the precedence of this stylesheet, any stylesheet 353 * imported by this stylesheet and any include/import descendant of this 354 * stylesheet. 355 */ 356 public int getMinimumDescendantPrecedence() { 357 if (_minimumDescendantPrecedence == -1) { 358 // Start with precedence of current stylesheet as a basis. 359 int min = getImportPrecedence(); 360 361 // Recursively examine all imported/included stylesheets. 362 final int inclImpCount = (_includedStylesheets != null) 363 ? _includedStylesheets.size() 364 : 0; 365 366 for (int i = 0; i < inclImpCount; i++) { 367 int prec = ((Stylesheet)_includedStylesheets.elementAt(i)) 368 .getMinimumDescendantPrecedence(); 369 370 if (prec < min) { 371 min = prec; 372 } 373 } 374 375 _minimumDescendantPrecedence = min; 376 } 377 return _minimumDescendantPrecedence; 378 } 379 380 public boolean checkForLoop(String systemId) { 381 // Return true if this stylesheet includes/imports itself 382 if (_systemId != null && _systemId.equals(systemId)) { 383 return true; 384 } 385 // Then check with any stylesheets that included/imported this one 386 if (_parentStylesheet != null) 387 return _parentStylesheet.checkForLoop(systemId); 388 // Otherwise OK 389 return false; 390 } 391 392 public void setParser(Parser parser) { 393 super.setParser(parser); 394 _name = makeStylesheetName("__stylesheet_"); 395 } 396 397 public void setParentStylesheet(Stylesheet parent) { 398 _parentStylesheet = parent; 399 } 400 401 public Stylesheet getParentStylesheet() { 402 return _parentStylesheet; 403 } 404 405 public void setImportingStylesheet(Stylesheet parent) { 406 _importedFrom = parent; 407 parent.addIncludedStylesheet(this); 408 } 409 410 public void setIncludingStylesheet(Stylesheet parent) { 411 _includedFrom = parent; 412 parent.addIncludedStylesheet(this); 413 } 414 415 public void addIncludedStylesheet(Stylesheet child) { 416 if (_includedStylesheets == null) { 417 _includedStylesheets = new Vector(); 418 } 419 _includedStylesheets.addElement(child); 420 } 421 422 public void setSystemId(String systemId) { 423 if (systemId != null) { 424 _systemId = SystemIDResolver.getAbsoluteURI(systemId); 425 } 426 } 427 428 public String getSystemId() { 429 return _systemId; 430 } 431 432 public void setSourceLoader(SourceLoader loader) { 433 _loader = loader; 434 } 435 436 public SourceLoader getSourceLoader() { 437 return _loader; 438 } 439 440 private QName makeStylesheetName(String prefix) { 441 return getParser().getQName(prefix+getXSLTC().nextStylesheetSerial()); 442 } 443 444 /** 445 * Returns true if this stylesheet has global vars or params. 446 */ 447 public boolean hasGlobals() { 448 return _globals.size() > 0; 449 } 450 451 /** 452 * Returns true if at least one template in the stylesheet has params 453 * defined. Uses the variable <code>_hasLocalParams</code> to cache the 454 * result. 455 */ 456 public boolean hasLocalParams() { 457 if (_hasLocalParams == null) { 458 Vector templates = getAllValidTemplates(); 459 final int n = templates.size(); 460 for (int i = 0; i < n; i++) { 461 final Template template = (Template)templates.elementAt(i); 462 if (template.hasParams()) { 463 _hasLocalParams = Boolean.TRUE; 464 return true; 465 } 466 } 467 _hasLocalParams = Boolean.FALSE; 468 return false; 469 } 470 else { 471 return _hasLocalParams.booleanValue(); 472 } 473 } 474 475 /** 476 * Adds a single prefix mapping to this syntax tree node. 477 * @param prefix Namespace prefix. 478 * @param uri Namespace URI. 479 */ 480 protected void addPrefixMapping(String prefix, String uri) { 481 if (prefix.equals(EMPTYSTRING) && uri.equals(XHTML_URI)) return; 482 super.addPrefixMapping(prefix, uri); 483 } 484 485 /** 486 * Store extension URIs 487 */ 488 private void extensionURI(String prefixes, SymbolTable stable) { 489 if (prefixes != null) { 490 StringTokenizer tokens = new StringTokenizer(prefixes); 491 while (tokens.hasMoreTokens()) { 492 final String prefix = tokens.nextToken(); 493 final String uri = lookupNamespace(prefix); 494 if (uri != null) { 495 _extensions.put(uri, prefix); 496 } 497 } 498 } 499 } 500 501 public boolean isExtension(String uri) { 502 return (_extensions.get(uri) != null); 503 } 504 505 public void declareExtensionPrefixes(Parser parser) { 506 final SymbolTable stable = parser.getSymbolTable(); 507 final String extensionPrefixes = getAttribute("extension-element-prefixes"); 508 extensionURI(extensionPrefixes, stable); 509 } 510 511 /** 512 * Parse the version and uri fields of the stylesheet and add an 513 * entry to the symbol table mapping the name <tt>__stylesheet_</tt> 514 * to an instance of this class. 515 */ 516 public void parseContents(Parser parser) { 517 final SymbolTable stable = parser.getSymbolTable(); 518 519 /* 520 // Make sure the XSL version set in this stylesheet 521 if ((_version == null) || (_version.equals(EMPTYSTRING))) { 522 reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR,"version"); 523 } 524 // Verify that the version is 1.0 and nothing else 525 else if (!_version.equals("1.0")) { 526 reportError(this, parser, ErrorMsg.XSL_VERSION_ERR, _version); 527 } 528 */ 529 530 // Add the implicit mapping of 'xml' to the XML namespace URI 531 addPrefixMapping("xml", "http://www.w3.org/XML/1998/namespace"); 532 533 // Report and error if more than one stylesheet defined 534 final Stylesheet sheet = stable.addStylesheet(_name, this); 535 if (sheet != null) { 536 // Error: more that one stylesheet defined 537 ErrorMsg err = new ErrorMsg(ErrorMsg.MULTIPLE_STYLESHEET_ERR,this); 538 parser.reportError(Constants.ERROR, err); 539 } 540 541 // If this is a simplified stylesheet we must create a template that 542 // grabs the root node of the input doc ( <xsl:template match="/"/> ). 543 // This template needs the current element (the one passed to this 544 // method) as its only child, so the Template class has a special 545 // method that handles this (parseSimplified()). 546 if (_simplified) { 547 stable.excludeURI(XSLT_URI); 548 Template template = new Template(); 549 template.parseSimplified(this, parser); 550 } 551 // Parse the children of this node 552 else { 553 parseOwnChildren(parser); 554 } 555 } 556 557 /** 558 * Parse all direct children of the <xsl:stylesheet/> element. 559 */ 560 public final void parseOwnChildren(Parser parser) { 561 final SymbolTable stable = parser.getSymbolTable(); 562 final String excludePrefixes = getAttribute("exclude-result-prefixes"); 563 final String extensionPrefixes = getAttribute("extension-element-prefixes"); 564 565 // Exclude XSLT uri 566 stable.pushExcludedNamespacesContext(); 567 stable.excludeURI(Constants.XSLT_URI); 568 stable.excludeNamespaces(excludePrefixes); 569 stable.excludeNamespaces(extensionPrefixes); 570 571 final Vector contents = getContents(); 572 final int count = contents.size(); 573 574 // We have to scan the stylesheet element's top-level elements for 575 // variables and/or parameters before we parse the other elements 576 for (int i = 0; i < count; i++) { 577 SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i); 578 if ((child instanceof VariableBase) || 579 (child instanceof NamespaceAlias)) { 580 parser.getSymbolTable().setCurrentNode(child); 581 child.parseContents(parser); 582 } 583 } 584 585 // Now go through all the other top-level elements... 586 for (int i = 0; i < count; i++) { 587 SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i); 588 if (!(child instanceof VariableBase) && 589 !(child instanceof NamespaceAlias)) { 590 parser.getSymbolTable().setCurrentNode(child); 591 child.parseContents(parser); 592 } 593 594 // All template code should be compiled as methods if the 595 // <xsl:apply-imports/> element was ever used in this stylesheet 596 if (!_templateInlining && (child instanceof Template)) { 597 Template template = (Template)child; 598 String name = "template$dot$" + template.getPosition(); 599 template.setName(parser.getQName(name)); 600 } 601 } 602 603 stable.popExcludedNamespacesContext(); 604 } 605 606 public void processModes() { 607 if (_defaultMode == null) 608 _defaultMode = new Mode(null, this, Constants.EMPTYSTRING); 609 _defaultMode.processPatterns(_keys); 610 final Enumeration modes = _modes.elements(); 611 while (modes.hasMoreElements()) { 612 final Mode mode = (Mode)modes.nextElement(); 613 mode.processPatterns(_keys); 614 } 615 } 616 617 private void compileModes(ClassGenerator classGen) { 618 _defaultMode.compileApplyTemplates(classGen); 619 final Enumeration modes = _modes.elements(); 620 while (modes.hasMoreElements()) { 621 final Mode mode = (Mode)modes.nextElement(); 622 mode.compileApplyTemplates(classGen); 623 } 624 } 625 626 public Mode getMode(QName modeName) { 627 if (modeName == null) { 628 if (_defaultMode == null) { 629 _defaultMode = new Mode(null, this, Constants.EMPTYSTRING); 630 } 631 return _defaultMode; 632 } 633 else { 634 Mode mode = (Mode)_modes.get(modeName); 635 if (mode == null) { 636 final String suffix = Integer.toString(_nextModeSerial++); 637 _modes.put(modeName, mode = new Mode(modeName, this, suffix)); 638 } 639 return mode; 640 } 641 } 642 643 /** 644 * Type check all the children of this node. 645 */ 646 public Type typeCheck(SymbolTable stable) throws TypeCheckError { 647 final int count = _globals.size(); 648 for (int i = 0; i < count; i++) { 649 final VariableBase var = (VariableBase)_globals.elementAt(i); 650 var.typeCheck(stable); 651 } 652 return typeCheckContents(stable); 653 } 654 655 /** 656 * Translate the stylesheet into JVM bytecodes. 657 */ 658 public void translate(ClassGenerator classGen, MethodGenerator methodGen) { 659 translate(); 660 } 661 662 private void addDOMField(ClassGenerator classGen) { 663 final FieldGen fgen = new FieldGen(ACC_PUBLIC, 664 Util.getJCRefType(DOM_INTF_SIG), 665 DOM_FIELD, 666 classGen.getConstantPool()); 667 classGen.addField(fgen.getField()); 668 } 669 670 /** 671 * Add a static field 672 */ 673 private void addStaticField(ClassGenerator classGen, String type, 674 String name) 675 { 676 final FieldGen fgen = new FieldGen(ACC_PROTECTED|ACC_STATIC, 677 Util.getJCRefType(type), 678 name, 679 classGen.getConstantPool()); 680 classGen.addField(fgen.getField()); 681 682 } 683 684 /** 685 * Translate the stylesheet into JVM bytecodes. 686 */ 687 public void translate() { 688 _className = getXSLTC().getClassName(); 689 690 // Define a new class by extending TRANSLET_CLASS 691 final ClassGenerator classGen = 692 new ClassGenerator(_className, 693 TRANSLET_CLASS, 694 Constants.EMPTYSTRING, 695 ACC_PUBLIC | ACC_SUPER, 696 null, this); 697 698 addDOMField(classGen); 699 700 // Compile transform() to initialize parameters, globals & output 701 // and run the transformation 702 compileTransform(classGen); 703 704 // Translate all non-template elements and filter out all templates 705 final Enumeration elements = elements(); 706 while (elements.hasMoreElements()) { 707 Object element = elements.nextElement(); 708 // xsl:template 709 if (element instanceof Template) { 710 // Separate templates by modes 711 final Template template = (Template)element; 712 //_templates.addElement(template); 713 getMode(template.getModeName()).addTemplate(template); 714 } 715 // xsl:attribute-set 716 else if (element instanceof AttributeSet) { 717 ((AttributeSet)element).translate(classGen, null); 718 } 719 else if (element instanceof Output) { 720 // save the element for later to pass to compileConstructor 721 Output output = (Output)element; 722 if (output.enabled()) _lastOutputElement = output; 723 } 724 else { 725 // Global variables and parameters are handled elsewhere. 726 // Other top-level non-template elements are ignored. Literal 727 // elements outside of templates will never be output. 728 } 729 } 730 731 checkOutputMethod(); 732 processModes(); 733 compileModes(classGen); 734 compileStaticInitializer(classGen); 735 compileConstructor(classGen, _lastOutputElement); 736 737 if (!getParser().errorsFound()) { 738 getXSLTC().dumpClass(classGen.getJavaClass()); 739 } 740 } 741 742 /** 743 * <p>Compile the namesArray, urisArray, typesArray, namespaceArray, 744 * namespaceAncestorsArray, prefixURIsIdxArray and prefixURIPairsArray into 745 * the static initializer. They are read-only from the 746 * translet. All translet instances can share a single 747 * copy of this informtion.</p> 748 * <p>The <code>namespaceAncestorsArray</code>, 749 * <code>prefixURIsIdxArray</code> and <code>prefixURIPairsArray</code> 750 * contain namespace information accessible from the stylesheet: 751 * <dl> 752 * <dt><code>namespaceAncestorsArray</code></dt> 753 * <dd>Array indexed by integer stylesheet node IDs containing node IDs of 754 * the nearest ancestor node in the stylesheet with namespace 755 * declarations or <code>-1</code> if there is no such ancestor. There 756 * can be more than one disjoint tree of nodes - one for each stylesheet 757 * module</dd> 758 * <dt><code>prefixURIsIdxArray</code></dt> 759 * <dd>Array indexed by integer stylesheet node IDs containing the index 760 * into <code>prefixURIPairsArray</code> of the first namespace prefix 761 * declared for the node. The values are stored in ascending order, so 762 * the next value in this array (if any) can be used to find the last such 763 * prefix-URI pair</dd> 764 * <dt>prefixURIPairsArray</dt> 765 * <dd>Array of pairs of namespace prefixes and URIs. A zero-length 766 * string represents the default namespace if it appears as a prefix and 767 * a namespace undeclaration if it appears as a URI.</dd> 768 * </dl> 769 * </p> 770 * <p>For this stylesheet 771 * <pre><code> 772 * <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 773 * <xsl:template match="/"> 774 * <xsl:for-each select="*" xmlns:foo="foouri"> 775 * <xsl:element name="{n}" xmlns:foo="baruri"> 776 * </xsl:for-each> 777 * <out xmlns="lumpit"/> 778 * <xsl:element name="{n}" xmlns="foouri"/> 779 * <xsl:element name="{n}" namespace="{ns}" xmlns="limpit"/gt; 780 * </xsl:template> 781 * </xsl:stylesheet> 782 * </code></pre> 783 * there will be four stylesheet nodes whose namespace information is 784 * needed, and 785 * <ul> 786 * <li><code>namespaceAncestorsArray</code> will have the value 787 * <code>[-1,0,1,0]</code>;</li> 788 * <li><code>prefixURIsIdxArray</code> will have the value 789 * <code>[0,4,6,8]</code>; and</li> 790 * <li><code>prefixURIPairsArray</code> will have the value 791 * <code>["xml","http://www.w3.org/XML/1998/namespace", 792 * "xsl","http://www.w3.org/1999/XSL/Transform" 793 * "foo","foouri","foo","baruri","","foouri"].</code></li> 794 * </ul> 795 * </p> 796 */ 797 private void compileStaticInitializer(ClassGenerator classGen) { 798 final ConstantPoolGen cpg = classGen.getConstantPool(); 799 final InstructionList il = new InstructionList(); 800 801 final MethodGenerator staticConst = 802 new MethodGenerator(ACC_PUBLIC|ACC_STATIC, 803 org.apache.bcel.generic.Type.VOID, 804 null, null, "<clinit>", 805 _className, il, cpg); 806 807 addStaticField(classGen, "[" + STRING_SIG, STATIC_NAMES_ARRAY_FIELD); 808 addStaticField(classGen, "[" + STRING_SIG, STATIC_URIS_ARRAY_FIELD); 809 addStaticField(classGen, "[I", STATIC_TYPES_ARRAY_FIELD); 810 addStaticField(classGen, "[" + STRING_SIG, STATIC_NAMESPACE_ARRAY_FIELD); 811 // Create fields of type char[] that will contain literal text from 812 // the stylesheet. 813 final int charDataFieldCount = getXSLTC().getCharacterDataCount(); 814 for (int i = 0; i < charDataFieldCount; i++) { 815 addStaticField(classGen, STATIC_CHAR_DATA_FIELD_SIG, 816 STATIC_CHAR_DATA_FIELD+i); 817 } 818 819 // Put the names array into the translet - used for dom/translet mapping 820 final Vector namesIndex = getXSLTC().getNamesIndex(); 821 int size = namesIndex.size(); 822 String[] namesArray = new String[size]; 823 String[] urisArray = new String[size]; 824 int[] typesArray = new int[size]; 825 826 int index; 827 for (int i = 0; i < size; i++) { 828 String encodedName = (String)namesIndex.elementAt(i); 829 if ((index = encodedName.lastIndexOf(':')) > -1) { 830 urisArray[i] = encodedName.substring(0, index); 831 } 832 833 index = index + 1; 834 if (encodedName.charAt(index) == '@') { 835 typesArray[i] = DTM.ATTRIBUTE_NODE; 836 index++; 837 } else if (encodedName.charAt(index) == '?') { 838 typesArray[i] = DTM.NAMESPACE_NODE; 839 index++; 840 } else { 841 typesArray[i] = DTM.ELEMENT_NODE; 842 } 843 844 if (index == 0) { 845 namesArray[i] = encodedName; 846 } 847 else { 848 namesArray[i] = encodedName.substring(index); 849 } 850 } 851 852 staticConst.markChunkStart(); 853 il.append(new PUSH(cpg, size)); 854 il.append(new ANEWARRAY(cpg.addClass(STRING))); 855 int namesArrayRef = cpg.addFieldref(_className, 856 STATIC_NAMES_ARRAY_FIELD, 857 NAMES_INDEX_SIG); 858 il.append(new PUTSTATIC(namesArrayRef)); 859 staticConst.markChunkEnd(); 860 861 for (int i = 0; i < size; i++) { 862 final String name = namesArray[i]; 863 staticConst.markChunkStart(); 864 il.append(new GETSTATIC(namesArrayRef)); 865 il.append(new PUSH(cpg, i)); 866 il.append(new PUSH(cpg, name)); 867 il.append(AASTORE); 868 staticConst.markChunkEnd(); 869 } 870 871 staticConst.markChunkStart(); 872 il.append(new PUSH(cpg, size)); 873 il.append(new ANEWARRAY(cpg.addClass(STRING))); 874 int urisArrayRef = cpg.addFieldref(_className, 875 STATIC_URIS_ARRAY_FIELD, 876 URIS_INDEX_SIG); 877 il.append(new PUTSTATIC(urisArrayRef)); 878 staticConst.markChunkEnd(); 879 880 for (int i = 0; i < size; i++) { 881 final String uri = urisArray[i]; 882 staticConst.markChunkStart(); 883 il.append(new GETSTATIC(urisArrayRef)); 884 il.append(new PUSH(cpg, i)); 885 il.append(new PUSH(cpg, uri)); 886 il.append(AASTORE); 887 staticConst.markChunkEnd(); 888 } 889 890 staticConst.markChunkStart(); 891 il.append(new PUSH(cpg, size)); 892 il.append(new NEWARRAY(BasicType.INT)); 893 int typesArrayRef = cpg.addFieldref(_className, 894 STATIC_TYPES_ARRAY_FIELD, 895 TYPES_INDEX_SIG); 896 il.append(new PUTSTATIC(typesArrayRef)); 897 staticConst.markChunkEnd(); 898 899 for (int i = 0; i < size; i++) { 900 final int nodeType = typesArray[i]; 901 staticConst.markChunkStart(); 902 il.append(new GETSTATIC(typesArrayRef)); 903 il.append(new PUSH(cpg, i)); 904 il.append(new PUSH(cpg, nodeType)); 905 il.append(IASTORE); 906 staticConst.markChunkEnd(); 907 } 908 909 // Put the namespace names array into the translet 910 final Vector namespaces = getXSLTC().getNamespaceIndex(); 911 staticConst.markChunkStart(); 912 il.append(new PUSH(cpg, namespaces.size())); 913 il.append(new ANEWARRAY(cpg.addClass(STRING))); 914 int namespaceArrayRef = cpg.addFieldref(_className, 915 STATIC_NAMESPACE_ARRAY_FIELD, 916 NAMESPACE_INDEX_SIG); 917 il.append(new PUTSTATIC(namespaceArrayRef)); 918 staticConst.markChunkEnd(); 919 920 for (int i = 0; i < namespaces.size(); i++) { 921 final String ns = (String)namespaces.elementAt(i); 922 staticConst.markChunkStart(); 923 il.append(new GETSTATIC(namespaceArrayRef)); 924 il.append(new PUSH(cpg, i)); 925 il.append(new PUSH(cpg, ns)); 926 il.append(AASTORE); 927 staticConst.markChunkEnd(); 928 } 929 930 // Put the tree of stylesheet namespace declarations into the translet 931 final Vector namespaceAncestors = getXSLTC().getNSAncestorPointers(); 932 if (namespaceAncestors != null && namespaceAncestors.size() != 0) { 933 addStaticField(classGen, NS_ANCESTORS_INDEX_SIG, 934 STATIC_NS_ANCESTORS_ARRAY_FIELD); 935 staticConst.markChunkStart(); 936 il.append(new PUSH(cpg, namespaceAncestors.size())); 937 il.append(new NEWARRAY(BasicType.INT)); 938 int namespaceAncestorsArrayRef = 939 cpg.addFieldref(_className, STATIC_NS_ANCESTORS_ARRAY_FIELD, 940 NS_ANCESTORS_INDEX_SIG); 941 il.append(new PUTSTATIC(namespaceAncestorsArrayRef)); 942 staticConst.markChunkEnd(); 943 for (int i = 0; i < namespaceAncestors.size(); i++) { 944 int ancestor = ((Integer) namespaceAncestors.get(i)).intValue(); 945 staticConst.markChunkStart(); 946 il.append(new GETSTATIC(namespaceAncestorsArrayRef)); 947 il.append(new PUSH(cpg, i)); 948 il.append(new PUSH(cpg, ancestor)); 949 il.append(IASTORE); 950 staticConst.markChunkEnd(); 951 } 952 } 953 // Put the array of indices into the namespace prefix/URI pairs array 954 // into the translet 955 final Vector prefixURIPairsIdx = getXSLTC().getPrefixURIPairsIdx(); 956 if (prefixURIPairsIdx != null && prefixURIPairsIdx.size() != 0) { 957 addStaticField(classGen, PREFIX_URIS_IDX_SIG, 958 STATIC_PREFIX_URIS_IDX_ARRAY_FIELD); 959 staticConst.markChunkStart(); 960 il.append(new PUSH(cpg, prefixURIPairsIdx.size())); 961 il.append(new NEWARRAY(BasicType.INT)); 962 int prefixURIPairsIdxArrayRef = 963 cpg.addFieldref(_className, 964 STATIC_PREFIX_URIS_IDX_ARRAY_FIELD, 965 PREFIX_URIS_IDX_SIG); 966 il.append(new PUTSTATIC(prefixURIPairsIdxArrayRef)); 967 staticConst.markChunkEnd(); 968 for (int i = 0; i < prefixURIPairsIdx.size(); i++) { 969 int idx = ((Integer) prefixURIPairsIdx.get(i)).intValue(); 970 staticConst.markChunkStart(); 971 il.append(new GETSTATIC(prefixURIPairsIdxArrayRef)); 972 il.append(new PUSH(cpg, i)); 973 il.append(new PUSH(cpg, idx)); 974 il.append(IASTORE); 975 staticConst.markChunkEnd(); 976 } 977 } 978 979 // Put the array of pairs of namespace prefixes and URIs into the 980 // translet 981 final Vector prefixURIPairs = getXSLTC().getPrefixURIPairs(); 982 if (prefixURIPairs != null && prefixURIPairs.size() != 0) { 983 addStaticField(classGen, PREFIX_URIS_ARRAY_SIG, 984 STATIC_PREFIX_URIS_ARRAY_FIELD); 985 986 staticConst.markChunkStart(); 987 il.append(new PUSH(cpg, prefixURIPairs.size())); 988 il.append(new ANEWARRAY(cpg.addClass(STRING))); 989 int prefixURIPairsRef = 990 cpg.addFieldref(_className, 991 STATIC_PREFIX_URIS_ARRAY_FIELD, 992 PREFIX_URIS_ARRAY_SIG); 993 il.append(new PUTSTATIC(prefixURIPairsRef)); 994 staticConst.markChunkEnd(); 995 for (int i = 0; i < prefixURIPairs.size(); i++) { 996 String prefixOrURI = (String) prefixURIPairs.get(i); 997 staticConst.markChunkStart(); 998 il.append(new GETSTATIC(prefixURIPairsRef)); 999 il.append(new PUSH(cpg, i)); 1000 il.append(new PUSH(cpg, prefixOrURI)); 1001 il.append(AASTORE); 1002 staticConst.markChunkEnd(); 1003 } 1004 } 1005 1006 // Grab all the literal text in the stylesheet and put it in a char[] 1007 final int charDataCount = getXSLTC().getCharacterDataCount(); 1008 final int toCharArray = cpg.addMethodref(STRING, "toCharArray", "()[C"); 1009 for (int i = 0; i < charDataCount; i++) { 1010 staticConst.markChunkStart(); 1011 il.append(new PUSH(cpg, getXSLTC().getCharacterData(i))); 1012 il.append(new INVOKEVIRTUAL(toCharArray)); 1013 il.append(new PUTSTATIC(cpg.addFieldref(_className, 1014 STATIC_CHAR_DATA_FIELD+i, 1015 STATIC_CHAR_DATA_FIELD_SIG))); 1016 staticConst.markChunkEnd(); 1017 } 1018 1019 il.append(RETURN); 1020 1021 classGen.addMethod(staticConst); 1022 1023 } 1024 1025 /** 1026 * Compile the translet's constructor 1027 */ 1028 private void compileConstructor(ClassGenerator classGen, Output output) { 1029 1030 final ConstantPoolGen cpg = classGen.getConstantPool(); 1031 final InstructionList il = new InstructionList(); 1032 1033 final MethodGenerator constructor = 1034 new MethodGenerator(ACC_PUBLIC, 1035 org.apache.bcel.generic.Type.VOID, 1036 null, null, "<init>", 1037 _className, il, cpg); 1038 1039 // Call the constructor in the AbstractTranslet superclass 1040 il.append(classGen.loadTranslet()); 1041 il.append(new INVOKESPECIAL(cpg.addMethodref(TRANSLET_CLASS, 1042 "<init>", "()V"))); 1043 1044 constructor.markChunkStart(); 1045 il.append(classGen.loadTranslet()); 1046 il.append(new GETSTATIC(cpg.addFieldref(_className, 1047 STATIC_NAMES_ARRAY_FIELD, 1048 NAMES_INDEX_SIG))); 1049 il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS, 1050 NAMES_INDEX, 1051 NAMES_INDEX_SIG))); 1052 constructor.markChunkEnd(); 1053 1054 constructor.markChunkStart(); 1055 il.append(classGen.loadTranslet()); 1056 il.append(new GETSTATIC(cpg.addFieldref(_className, 1057 STATIC_URIS_ARRAY_FIELD, 1058 URIS_INDEX_SIG))); 1059 il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS, 1060 URIS_INDEX, 1061 URIS_INDEX_SIG))); 1062 constructor.markChunkEnd(); 1063 1064 constructor.markChunkStart(); 1065 il.append(classGen.loadTranslet()); 1066 il.append(new GETSTATIC(cpg.addFieldref(_className, 1067 STATIC_TYPES_ARRAY_FIELD, 1068 TYPES_INDEX_SIG))); 1069 il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS, 1070 TYPES_INDEX, 1071 TYPES_INDEX_SIG))); 1072 constructor.markChunkEnd(); 1073 1074 constructor.markChunkStart(); 1075 il.append(classGen.loadTranslet()); 1076 il.append(new GETSTATIC(cpg.addFieldref(_className, 1077 STATIC_NAMESPACE_ARRAY_FIELD, 1078 NAMESPACE_INDEX_SIG))); 1079 il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS, 1080 NAMESPACE_INDEX, 1081 NAMESPACE_INDEX_SIG))); 1082 constructor.markChunkEnd(); 1083 1084 constructor.markChunkStart(); 1085 il.append(classGen.loadTranslet()); 1086 il.append(new PUSH(cpg, AbstractTranslet.CURRENT_TRANSLET_VERSION)); 1087 il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS, 1088 TRANSLET_VERSION_INDEX, 1089 TRANSLET_VERSION_INDEX_SIG))); 1090 constructor.markChunkEnd(); 1091 1092 if (_hasIdCall) { 1093 constructor.markChunkStart(); 1094 il.append(classGen.loadTranslet()); 1095 il.append(new PUSH(cpg, Boolean.TRUE)); 1096 il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS, 1097 HASIDCALL_INDEX, 1098 HASIDCALL_INDEX_SIG))); 1099 constructor.markChunkEnd(); 1100 } 1101 1102 // Compile in code to set the output configuration from <xsl:output> 1103 if (output != null) { 1104 // Set all the output settings files in the translet 1105 constructor.markChunkStart(); 1106 output.translate(classGen, constructor); 1107 constructor.markChunkEnd(); 1108 } 1109 1110 // Compile default decimal formatting symbols. 1111 // This is an implicit, nameless xsl:decimal-format top-level element. 1112 if (_numberFormattingUsed) { 1113 constructor.markChunkStart(); 1114 DecimalFormatting.translateDefaultDFS(classGen, constructor); 1115 constructor.markChunkEnd(); 1116 } 1117 1118 il.append(RETURN); 1119 1120 classGen.addMethod(constructor); 1121 } 1122 1123 /** 1124 * Compile a topLevel() method into the output class. This method is 1125 * called from transform() to handle all non-template top-level elements. 1126 * Returns the signature of the topLevel() method. 1127 * 1128 * Global variables/params and keys are first sorted to resolve 1129 * dependencies between them. The XSLT 1.0 spec does not allow a key 1130 * to depend on a variable. However, for compatibility with Xalan 1131 * interpretive, that type of dependency is allowed. Note also that 1132 * the buildKeys() method is still generated as it is used by the 1133 * LoadDocument class, but it no longer called from transform(). 1134 */ 1135 private String compileTopLevel(ClassGenerator classGen) { 1136 final ConstantPoolGen cpg = classGen.getConstantPool(); 1137 1138 final org.apache.bcel.generic.Type[] argTypes = { 1139 Util.getJCRefType(DOM_INTF_SIG), 1140 Util.getJCRefType(NODE_ITERATOR_SIG), 1141 Util.getJCRefType(TRANSLET_OUTPUT_SIG) 1142 }; 1143 1144 final String[] argNames = { 1145 DOCUMENT_PNAME, ITERATOR_PNAME, TRANSLET_OUTPUT_PNAME 1146 }; 1147 1148 final InstructionList il = new InstructionList(); 1149 1150 final MethodGenerator toplevel = 1151 new MethodGenerator(ACC_PUBLIC, 1152 org.apache.bcel.generic.Type.VOID, 1153 argTypes, argNames, 1154 "topLevel", _className, il, 1155 classGen.getConstantPool()); 1156 1157 toplevel.addException("org.apache.xalan.xsltc.TransletException"); 1158 1159 // Define and initialize 'current' variable with the root node 1160 final LocalVariableGen current = 1161 toplevel.addLocalVariable("current", 1162 org.apache.bcel.generic.Type.INT, 1163 null, null); 1164 1165 final int setFilter = cpg.addInterfaceMethodref(DOM_INTF, 1166 "setFilter", 1167 "(Lorg/apache/xalan/xsltc/StripFilter;)V"); 1168 1169 final int gitr = cpg.addInterfaceMethodref(DOM_INTF, 1170 "getIterator", 1171 "()"+NODE_ITERATOR_SIG); 1172 il.append(toplevel.loadDOM()); 1173 il.append(new INVOKEINTERFACE(gitr, 1)); 1174 il.append(toplevel.nextNode()); 1175 current.setStart(il.append(new ISTORE(current.getIndex()))); 1176 1177 // Create a new list containing variables/params + keys 1178 Vector varDepElements = new Vector(_globals); 1179 Enumeration elements = elements(); 1180 while (elements.hasMoreElements()) { 1181 final Object element = elements.nextElement(); 1182 if (element instanceof Key) { 1183 varDepElements.add(element); 1184 } 1185 } 1186 1187 // Determine a partial order for the variables/params and keys 1188 varDepElements = resolveDependencies(varDepElements); 1189 1190 // Translate vars/params and keys in the right order 1191 final int count = varDepElements.size(); 1192 for (int i = 0; i < count; i++) { 1193 final TopLevelElement tle = (TopLevelElement) varDepElements.elementAt(i); 1194 tle.translate(classGen, toplevel); 1195 if (tle instanceof Key) { 1196 final Key key = (Key) tle; 1197 _keys.put(key.getName(), key); 1198 } 1199 } 1200 1201 // Compile code for other top-level elements 1202 Vector whitespaceRules = new Vector(); 1203 elements = elements(); 1204 while (elements.hasMoreElements()) { 1205 final Object element = elements.nextElement(); 1206 // xsl:decimal-format 1207 if (element instanceof DecimalFormatting) { 1208 ((DecimalFormatting)element).translate(classGen,toplevel); 1209 } 1210 // xsl:strip/preserve-space 1211 else if (element instanceof Whitespace) { 1212 whitespaceRules.addAll(((Whitespace)element).getRules()); 1213 } 1214 } 1215 1216 // Translate all whitespace strip/preserve rules 1217 if (whitespaceRules.size() > 0) { 1218 Whitespace.translateRules(whitespaceRules,classGen); 1219 } 1220 1221 if (classGen.containsMethod(STRIP_SPACE, STRIP_SPACE_PARAMS) != null) { 1222 il.append(toplevel.loadDOM()); 1223 il.append(classGen.loadTranslet()); 1224 il.append(new INVOKEINTERFACE(setFilter, 2)); 1225 } 1226 1227 il.append(RETURN); 1228 1229 // Compute max locals + stack and add method to class 1230 classGen.addMethod(toplevel); 1231 1232 return("("+DOM_INTF_SIG+NODE_ITERATOR_SIG+TRANSLET_OUTPUT_SIG+")V"); 1233 } 1234 1235 /** 1236 * This method returns a vector with variables/params and keys in the 1237 * order in which they are to be compiled for initialization. The order 1238 * is determined by analyzing the dependencies between them. The XSLT 1.0 1239 * spec does not allow a key to depend on a variable. However, for 1240 * compatibility with Xalan interpretive, that type of dependency is 1241 * allowed and, therefore, consider to determine the partial order. 1242 */ 1243 private Vector resolveDependencies(Vector input) { 1244 /* DEBUG CODE - INGORE 1245 for (int i = 0; i < input.size(); i++) { 1246 final TopLevelElement e = (TopLevelElement) input.elementAt(i); 1247 System.out.println("e = " + e + " depends on:"); 1248 Vector dep = e.getDependencies(); 1249 for (int j = 0; j < (dep != null ? dep.size() : 0); j++) { 1250 System.out.println("\t" + dep.elementAt(j)); 1251 } 1252 } 1253 System.out.println("================================="); 1254 */ 1255 1256 Vector result = new Vector(); 1257 while (input.size() > 0) { 1258 boolean changed = false; 1259 for (int i = 0; i < input.size(); ) { 1260 final TopLevelElement vde = (TopLevelElement) input.elementAt(i); 1261 final Vector dep = vde.getDependencies(); 1262 if (dep == null || result.containsAll(dep)) { 1263 result.addElement(vde); 1264 input.remove(i); 1265 changed = true; 1266 } 1267 else { 1268 i++; 1269 } 1270 } 1271 1272 // If nothing was changed in this pass then we have a circular ref 1273 if (!changed) { 1274 ErrorMsg err = new ErrorMsg(ErrorMsg.CIRCULAR_VARIABLE_ERR, 1275 input.toString(), this); 1276 getParser().reportError(Constants.ERROR, err); 1277 return(result); 1278 } 1279 } 1280 1281 /* DEBUG CODE - INGORE 1282 System.out.println("================================="); 1283 for (int i = 0; i < result.size(); i++) { 1284 final TopLevelElement e = (TopLevelElement) result.elementAt(i); 1285 System.out.println("e = " + e); 1286 } 1287 */ 1288 1289 return result; 1290 } 1291 1292 /** 1293 * Compile a buildKeys() method into the output class. Note that keys 1294 * for the input document are created in topLevel(), not in this method. 1295 * However, we still need this method to create keys for documents loaded 1296 * via the XPath document() function. 1297 */ 1298 private String compileBuildKeys(ClassGenerator classGen) { 1299 final ConstantPoolGen cpg = classGen.getConstantPool(); 1300 1301 final org.apache.bcel.generic.Type[] argTypes = { 1302 Util.getJCRefType(DOM_INTF_SIG), 1303 Util.getJCRefType(NODE_ITERATOR_SIG), 1304 Util.getJCRefType(TRANSLET_OUTPUT_SIG), 1305 org.apache.bcel.generic.Type.INT 1306 }; 1307 1308 final String[] argNames = { 1309 DOCUMENT_PNAME, ITERATOR_PNAME, TRANSLET_OUTPUT_PNAME, "current" 1310 }; 1311 1312 final InstructionList il = new InstructionList(); 1313 1314 final MethodGenerator buildKeys = 1315 new MethodGenerator(ACC_PUBLIC, 1316 org.apache.bcel.generic.Type.VOID, 1317 argTypes, argNames, 1318 "buildKeys", _className, il, 1319 classGen.getConstantPool()); 1320 1321 buildKeys.addException("org.apache.xalan.xsltc.TransletException"); 1322 1323 final Enumeration elements = elements(); 1324 while (elements.hasMoreElements()) { 1325 // xsl:key 1326 final Object element = elements.nextElement(); 1327 if (element instanceof Key) { 1328 final Key key = (Key)element; 1329 key.translate(classGen, buildKeys); 1330 _keys.put(key.getName(),key); 1331 } 1332 } 1333 1334 il.append(RETURN); 1335 1336 // Add method to class 1337 classGen.addMethod(buildKeys); 1338 1339 return("("+DOM_INTF_SIG+NODE_ITERATOR_SIG+TRANSLET_OUTPUT_SIG+"I)V"); 1340 } 1341 1342 /** 1343 * Compile transform() into the output class. This method is used to 1344 * initialize global variables and global parameters. The current node 1345 * is set to be the document's root node. 1346 */ 1347 private void compileTransform(ClassGenerator classGen) { 1348 final ConstantPoolGen cpg = classGen.getConstantPool(); 1349 1350 /* 1351 * Define the the method transform with the following signature: 1352 * void transform(DOM, NodeIterator, HandlerBase) 1353 */ 1354 final org.apache.bcel.generic.Type[] argTypes = 1355 new org.apache.bcel.generic.Type[3]; 1356 argTypes[0] = Util.getJCRefType(DOM_INTF_SIG); 1357 argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG); 1358 argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG); 1359 1360 final String[] argNames = new String[3]; 1361 argNames[0] = DOCUMENT_PNAME; 1362 argNames[1] = ITERATOR_PNAME; 1363 argNames[2] = TRANSLET_OUTPUT_PNAME; 1364 1365 final InstructionList il = new InstructionList(); 1366 final MethodGenerator transf = 1367 new MethodGenerator(ACC_PUBLIC, 1368 org.apache.bcel.generic.Type.VOID, 1369 argTypes, argNames, 1370 "transform", 1371 _className, 1372 il, 1373 classGen.getConstantPool()); 1374 transf.addException("org.apache.xalan.xsltc.TransletException"); 1375 1376 // Define and initialize current with the root node 1377 final LocalVariableGen current = 1378 transf.addLocalVariable("current", 1379 org.apache.bcel.generic.Type.INT, 1380 null, null); 1381 final String applyTemplatesSig = classGen.getApplyTemplatesSig(); 1382 final int applyTemplates = cpg.addMethodref(getClassName(), 1383 "applyTemplates", 1384 applyTemplatesSig); 1385 final int domField = cpg.addFieldref(getClassName(), 1386 DOM_FIELD, 1387 DOM_INTF_SIG); 1388 1389 // push translet for PUTFIELD 1390 il.append(classGen.loadTranslet()); 1391 // prepare appropriate DOM implementation 1392 1393 if (isMultiDocument()) { 1394 il.append(new NEW(cpg.addClass(MULTI_DOM_CLASS))); 1395 il.append(DUP); 1396 } 1397 1398 il.append(classGen.loadTranslet()); 1399 il.append(transf.loadDOM()); 1400 il.append(new INVOKEVIRTUAL(cpg.addMethodref(TRANSLET_CLASS, 1401 "makeDOMAdapter", 1402 "("+DOM_INTF_SIG+")"+ 1403 DOM_ADAPTER_SIG))); 1404 // DOMAdapter is on the stack 1405 1406 if (isMultiDocument()) { 1407 final int init = cpg.addMethodref(MULTI_DOM_CLASS, 1408 "<init>", 1409 "("+DOM_INTF_SIG+")V"); 1410 il.append(new INVOKESPECIAL(init)); 1411 // MultiDOM is on the stack 1412 } 1413 1414 //store to _dom variable 1415 il.append(new PUTFIELD(domField)); 1416 1417 // continue with globals initialization 1418 final int gitr = cpg.addInterfaceMethodref(DOM_INTF, 1419 "getIterator", 1420 "()"+NODE_ITERATOR_SIG); 1421 il.append(transf.loadDOM()); 1422 il.append(new INVOKEINTERFACE(gitr, 1)); 1423 il.append(transf.nextNode()); 1424 current.setStart(il.append(new ISTORE(current.getIndex()))); 1425 1426 // Transfer the output settings to the output post-processor 1427 il.append(classGen.loadTranslet()); 1428 il.append(transf.loadHandler()); 1429 final int index = cpg.addMethodref(TRANSLET_CLASS, 1430 "transferOutputSettings", 1431 "("+OUTPUT_HANDLER_SIG+")V"); 1432 il.append(new INVOKEVIRTUAL(index)); 1433 1434 /* 1435 * Compile buildKeys() method. Note that this method is not 1436 * invoked here as keys for the input document are now created 1437 * in topLevel(). However, this method is still needed by the 1438 * LoadDocument class. 1439 */ 1440 final String keySig = compileBuildKeys(classGen); 1441 final int keyIdx = cpg.addMethodref(getClassName(), 1442 "buildKeys", keySig); 1443 1444 // Look for top-level elements that need handling 1445 final Enumeration toplevel = elements(); 1446 if (_globals.size() > 0 || toplevel.hasMoreElements()) { 1447 // Compile method for handling top-level elements 1448 final String topLevelSig = compileTopLevel(classGen); 1449 // Get a reference to that method 1450 final int topLevelIdx = cpg.addMethodref(getClassName(), 1451 "topLevel", 1452 topLevelSig); 1453 // Push all parameters on the stack and call topLevel() 1454 il.append(classGen.loadTranslet()); // The 'this' pointer 1455 il.append(classGen.loadTranslet()); 1456 il.append(new GETFIELD(domField)); // The DOM reference 1457 il.append(transf.loadIterator()); 1458 il.append(transf.loadHandler()); // The output handler 1459 il.append(new INVOKEVIRTUAL(topLevelIdx)); 1460 } 1461 1462 // start document 1463 il.append(transf.loadHandler()); 1464 il.append(transf.startDocument()); 1465 1466 // push first arg for applyTemplates 1467 il.append(classGen.loadTranslet()); 1468 // push translet for GETFIELD to get DOM arg 1469 il.append(classGen.loadTranslet()); 1470 il.append(new GETFIELD(domField)); 1471 // push remaining 2 args 1472 il.append(transf.loadIterator()); 1473 il.append(transf.loadHandler()); 1474 il.append(new INVOKEVIRTUAL(applyTemplates)); 1475 // endDocument 1476 il.append(transf.loadHandler()); 1477 il.append(transf.endDocument()); 1478 1479 il.append(RETURN); 1480 1481 // Compute max locals + stack and add method to class 1482 classGen.addMethod(transf); 1483 } 1484 1485 /** 1486 * Peephole optimization: Remove sequences of [ALOAD, POP]. 1487 */ 1488 private void peepHoleOptimization(MethodGenerator methodGen) { 1489 final String pattern = "`aload'`pop'`instruction'"; 1490 final InstructionList il = methodGen.getInstructionList(); 1491 final InstructionFinder find = new InstructionFinder(il); 1492 for(Iterator iter=find.search(pattern); iter.hasNext(); ) { 1493 InstructionHandle[] match = (InstructionHandle[])iter.next(); 1494 try { 1495 il.delete(match[0], match[1]); 1496 } 1497 catch (TargetLostException e) { 1498 // TODO: move target down into the list 1499 } 1500 } 1501 } 1502 1503 public int addParam(Param param) { 1504 _globals.addElement(param); 1505 return _globals.size() - 1; 1506 } 1507 1508 public int addVariable(Variable global) { 1509 _globals.addElement(global); 1510 return _globals.size() - 1; 1511 } 1512 1513 public void display(int indent) { 1514 indent(indent); 1515 Util.println("Stylesheet"); 1516 displayContents(indent + IndentIncrement); 1517 } 1518 1519 // do we need this wrapper ????? 1520 public String getNamespace(String prefix) { 1521 return lookupNamespace(prefix); 1522 } 1523 1524 public String getClassName() { 1525 return _className; 1526 } 1527 1528 public Vector getTemplates() { 1529 return _templates; 1530 } 1531 1532 public Vector getAllValidTemplates() { 1533 // Return templates if no imported/included stylesheets 1534 if (_includedStylesheets == null) { 1535 return _templates; 1536 } 1537 1538 // Is returned value cached? 1539 if (_allValidTemplates == null) { 1540 Vector templates = new Vector(); 1541 int size = _includedStylesheets.size(); 1542 for (int i = 0; i < size; i++) { 1543 Stylesheet included =(Stylesheet)_includedStylesheets.elementAt(i); 1544 templates.addAll(included.getAllValidTemplates()); 1545 } 1546 templates.addAll(_templates); 1547 1548 // Cache results in top-level stylesheet only 1549 if (_parentStylesheet != null) { 1550 return templates; 1551 } 1552 _allValidTemplates = templates; 1553 } 1554 1555 return _allValidTemplates; 1556 } 1557 1558 protected void addTemplate(Template template) { 1559 _templates.addElement(template); 1560 } 1561 }