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