content: /*
   *  This file is part of AndroidIDE.
   *
   *  AndroidIDE is free software: you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation, either version 3 of the License, or
   *  (at your option) any later version.
   *
   *  AndroidIDE is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *   along with AndroidIDE.  If not, see <https://www.gnu.org/licenses/>.
   */
  
  /*
   * reserved comment block
   * DO NOT REMOVE OR ALTER!
   */
  /*
   * Copyright 2001-2004 The Apache Software Foundation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  /*
   * $Id: Stylesheet.java,v 1.5 2005/09/28 13:48:16 pvedula Exp $
   */
  
  package jaxp.sun.org.apache.xalan.internal.xsltc.compiler;
  
  import java.util.Vector;
  import java.util.Enumeration;
  import java.util.Hashtable;
  import java.util.Iterator;
  import java.util.Properties;
  import java.util.StringTokenizer;
  
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  import jaxp.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  import jaxp.sun.org.apache.xml.internal.utils.SystemIDResolver;
  import jaxp.sun.org.apache.bcel.internal.generic.ANEWARRAY;
  import jaxp.sun.org.apache.bcel.internal.generic.BasicType;
  import jaxp.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  import jaxp.sun.org.apache.bcel.internal.generic.FieldGen;
  import jaxp.sun.org.apache.bcel.internal.generic.GETFIELD;
  import jaxp.sun.org.apache.bcel.internal.generic.GETSTATIC;
  import jaxp.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE;
  import jaxp.sun.org.apache.bcel.internal.generic.INVOKESPECIAL;
  import jaxp.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL;
  import jaxp.sun.org.apache.bcel.internal.generic.ISTORE;
  import jaxp.sun.org.apache.bcel.internal.generic.InstructionHandle;
  import jaxp.sun.org.apache.bcel.internal.generic.InstructionList;
  import jaxp.sun.org.apache.bcel.internal.generic.LocalVariableGen;
  import jaxp.sun.org.apache.bcel.internal.generic.NEW;
  import jaxp.sun.org.apache.bcel.internal.generic.NEWARRAY;
  import jaxp.sun.org.apache.bcel.internal.generic.PUSH;
  import jaxp.sun.org.apache.bcel.internal.generic.PUTFIELD;
  import jaxp.sun.org.apache.bcel.internal.generic.PUTSTATIC;
  import jaxp.sun.org.apache.bcel.internal.generic.TargetLostException;
  import jaxp.sun.org.apache.bcel.internal.util.InstructionFinder;
  import jaxp.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  import jaxp.sun.org.apache.xml.internal.dtm.DTM;
  
  /**
   * @author Jacek Ambroziak
   * @author Santiago Pericas-Geertsen
   * @author Morten Jorgensen
   */
  public final class Stylesheet extends SyntaxTreeNode {
  
      /**
       * XSLT version defined in the stylesheet.
       */
      private String _version;
  
      /**
       * Internal name of this stylesheet used as a key into the symbol table.
       */
      private QName _name;
  
      /**
       * A URI that represents the system ID for this stylesheet.
       */
      private String _systemId;
  
      /**
       * A reference to the parent stylesheet or null if topmost.
       */
      private Stylesheet _parentStylesheet;
  
      /**
       * Contains global variables and parameters defined in the stylesheet.
       */
      private Vector _globals = new Vector();
  
      /**
       * Used to cache the result returned by <code>hasLocalParams()</code>.
       */
      private Boolean _hasLocalParams = null;
  
      /**
       * The name of the class being generated.
       */
      private String _className;
  
      /**
        * Contains all templates defined in this stylesheet
        */
      private final Vector _templates = new Vector();
  
      /**
       * Used to cache result of <code>getAllValidTemplates()</code>. Only
       * set in top-level stylesheets that include/import other stylesheets.
       */
      private Vector _allValidTemplates = null;
  
      /**
       * Counter to generate unique mode suffixes.
       */
      private int _nextModeSerial = 1;
  
      /**
       * Mapping between mode names and Mode instances.
       */
      private final Hashtable _modes = new Hashtable();
  
      /**
       * A reference to the default Mode object.
       */
      private Mode _defaultMode;
  
      /**
       * Mapping between extension URIs and their prefixes.
       */
      private final Hashtable _extensions = new Hashtable();
  
      /**
       * Reference to the stylesheet from which this stylesheet was
       * imported (if any).
       */
      public Stylesheet _importedFrom = null;
  
      /**
       * Reference to the stylesheet from which this stylesheet was
       * included (if any).
       */
      public Stylesheet _includedFrom = null;
  
      /**
       * Array of all the stylesheets imported or included from this one.
       */
      private Vector _includedStylesheets = null;
  
      /**
       * Import precendence for this stylesheet.
       */
      private int _importPrecedence = 1;
  
      /**
       * Minimum precendence of any descendant stylesheet by inclusion or
       * importation.
       */
      private int _minimumDescendantPrecedence = -1;
  
      /**
       * Mapping between key names and Key objects (needed by Key/IdPattern).
       */
      private Hashtable _keys = new Hashtable();
  
      /**
       * A reference to the SourceLoader set by the user (a URIResolver
       * if the JAXP API is being used).
       */
      private SourceLoader _loader = null;
  
      /**
       * Flag indicating if format-number() is called.
       */
      private boolean _numberFormattingUsed = false;
  
      /**
       * Flag indicating if this is a simplified stylesheets. A template
       * matching on "/" must be added in this case.
       */
      private boolean _simplified = false;
  
      /**
       * Flag indicating if multi-document support is needed.
       */
      private boolean _multiDocument = false;
  
      /**
       * Flag indicating if nodset() is called.
       */
      private boolean _callsNodeset = false;
  
      /**
       * Flag indicating if id() is called.
       */
      private boolean _hasIdCall = false;
  
      /**
       * Set to true to enable template inlining optimization.
       * @see XSLTC#_templateInlining
       */
      private boolean _templateInlining = false;
  
      /**
       * A reference to the last xsl:output object found in the styleshet.
       */
      private Output  _lastOutputElement = null;
  
      /**
       * Output properties for this stylesheet.
       */
      private Properties _outputProperties = null;
  
      /**
       * Output method for this stylesheet (must be set to one of
       * the constants defined below).
       */
      private int _outputMethod = UNKNOWN_OUTPUT;
  
      // Output method constants
      public static final int UNKNOWN_OUTPUT = 0;
      public static final int XML_OUTPUT     = 1;
      public static final int HTML_OUTPUT    = 2;
      public static final int TEXT_OUTPUT    = 3;
  
      /**
       * Return the output method
       */
      public int getOutputMethod() {
          return _outputMethod;
      }
  
      /**
       * Check and set the output method
       */
      private void checkOutputMethod() {
          if (_lastOutputElement != null) {
              String method = _lastOutputElement.getOutputMethod();
              if (method != null) {
                  if (method.equals("xml"))
                      _outputMethod = XML_OUTPUT;
                  else if (method.equals("html"))
                      _outputMethod = HTML_OUTPUT;
                  else if (method.equals("text"))
                      _outputMethod = TEXT_OUTPUT;
              }
          }
      }
  
      public boolean getTemplateInlining() {
          return _templateInlining;
      }
  
      public void setTemplateInlining(boolean flag) {
          _templateInlining = flag;
      }
  
      public boolean isSimplified() {
          return(_simplified);
      }
  
      public void setSimplified() {
          _simplified = true;
      }
  
      public void setHasIdCall(boolean flag) {
          _hasIdCall = flag;
      }
  
      public void setOutputProperty(String key, String value) {
          if (_outputProperties == null) {
              _outputProperties = new Properties();
          }
          _outputProperties.setProperty(key, value);
      }
  
      public void setOutputProperties(Properties props) {
          _outputProperties = props;
      }
  
      public Properties getOutputProperties() {
          return _outputProperties;
      }
  
      public Output getLastOutputElement() {
          return _lastOutputElement;
      }
  
      public void setMultiDocument(boolean flag) {
          _multiDocument = flag;
      }
  
      public boolean isMultiDocument() {
          return _multiDocument;
      }
  
      public void setCallsNodeset(boolean flag) {
          if (flag) setMultiDocument(flag);
          _callsNodeset = flag;
      }
  
      public boolean callsNodeset() {
          return _callsNodeset;
      }
  
      public void numberFormattingUsed() {
          _numberFormattingUsed = true;
          /*
           * Fix for bug 23046, if the stylesheet is included, set the
           * numberFormattingUsed flag to the parent stylesheet too.
           * AbstractTranslet.addDecimalFormat() will be inlined once for the
           * outer most stylesheet.
           */
          Stylesheet parent = getParentStylesheet();
          if (null != parent) parent.numberFormattingUsed();
      }
  
      public void setImportPrecedence(final int precedence) {
          // Set import precedence for this stylesheet
          _importPrecedence = precedence;
  
          // Set import precedence for all included stylesheets
          final Enumeration elements = elements();
          while (elements.hasMoreElements()) {
              SyntaxTreeNode child = (SyntaxTreeNode)elements.nextElement();
              if (child instanceof Include) {
                  Stylesheet included = ((Include)child).getIncludedStylesheet();
                  if (included != null && included._includedFrom == this) {
                      included.setImportPrecedence(precedence);
                  }
              }
          }
  
          // Set import precedence for the stylesheet that imported this one
          if (_importedFrom != null) {
              if (_importedFrom.getImportPrecedence() < precedence) {
                  final Parser parser = getParser();
                  final int nextPrecedence = parser.getNextImportPrecedence();
                  _importedFrom.setImportPrecedence(nextPrecedence);
              }
          }
          // Set import precedence for the stylesheet that included this one
          else if (_includedFrom != null) {
              if (_includedFrom.getImportPrecedence() != precedence)
                  _includedFrom.setImportPrecedence(precedence);
          }
      }
  
      public int getImportPrecedence() {
          return _importPrecedence;
      }
  
      /**
       * Get the minimum of the precedence of this stylesheet, any stylesheet
       * imported by this stylesheet and any include/import descendant of this
       * stylesheet.
       */
      public int getMinimumDescendantPrecedence() {
          if (_minimumDescendantPrecedence == -1) {
              // Start with precedence of current stylesheet as a basis.
              int min = getImportPrecedence();
  
              // Recursively examine all imported/included stylesheets.
              final int inclImpCount = (_includedStylesheets != null)
                                            ? _includedStylesheets.size()
                                            : 0;
  
              for (int i = 0; i < inclImpCount; i++) {
                  int prec = ((Stylesheet)_includedStylesheets.elementAt(i))
                                                .getMinimumDescendantPrecedence();
  
                  if (prec < min) {
                      min = prec;
                  }
              }
  
              _minimumDescendantPrecedence = min;
          }
          return _minimumDescendantPrecedence;
      }
  
      public boolean checkForLoop(String systemId) {
          // Return true if this stylesheet includes/imports itself
          if (_systemId != null && _systemId.equals(systemId)) {
              return true;
          }
          // Then check with any stylesheets that included/imported this one
          if (_parentStylesheet != null)
              return _parentStylesheet.checkForLoop(systemId);
          // Otherwise OK
          return false;
      }
  
      public void setParser(Parser parser) {
          super.setParser(parser);
          _name = makeStylesheetName("__stylesheet_");
      }
  
      public void setParentStylesheet(Stylesheet parent) {
          _parentStylesheet = parent;
      }
  
      public Stylesheet getParentStylesheet() {
          return _parentStylesheet;
      }
  
      public void setImportingStylesheet(Stylesheet parent) {
          _importedFrom = parent;
          parent.addIncludedStylesheet(this);
      }
  
      public void setIncludingStylesheet(Stylesheet parent) {
          _includedFrom = parent;
          parent.addIncludedStylesheet(this);
      }
  
      public void addIncludedStylesheet(Stylesheet child) {
          if (_includedStylesheets == null) {
              _includedStylesheets = new Vector();
          }
          _includedStylesheets.addElement(child);
      }
  
      public void setSystemId(String systemId) {
          if (systemId != null) {
              _systemId = SystemIDResolver.getAbsoluteURI(systemId);
          }
      }
  
      public String getSystemId() {
          return _systemId;
      }
  
      public void setSourceLoader(SourceLoader loader) {
          _loader = loader;
      }
  
      public SourceLoader getSourceLoader() {
          return _loader;
      }
  
      private QName makeStylesheetName(String prefix) {
          return getParser().getQName(prefix+getXSLTC().nextStylesheetSerial());
      }
  
      /**
       * Returns true if this stylesheet has global vars or params.
       */
      public boolean hasGlobals() {
          return _globals.size() > 0;
      }
  
      /**
       * Returns true if at least one template in the stylesheet has params
       * defined. Uses the variable <code>_hasLocalParams</code> to cache the
       * result.
       */
      public boolean hasLocalParams() {
          if (_hasLocalParams == null) {
              Vector templates = getAllValidTemplates();
              final int n = templates.size();
              for (int i = 0; i < n; i++) {
                  final Template template = (Template)templates.elementAt(i);
                  if (template.hasParams()) {
                      _hasLocalParams = Boolean.TRUE;
                      return true;
                  }
              }
              _hasLocalParams = Boolean.FALSE;
              return false;
          }
          else {
              return _hasLocalParams.booleanValue();
          }
      }
  
      /**
       * Adds a single prefix mapping to this syntax tree node.
       * @param prefix Namespace prefix.
       * @param uri Namespace URI.
       */
      protected void addPrefixMapping(String prefix, String uri) {
          if (prefix.equals(EMPTYSTRING) && uri.equals(XHTML_URI)) return;
          super.addPrefixMapping(prefix, uri);
      }
  
      /**
       * Store extension URIs
       */
      private void extensionURI(String prefixes, SymbolTable stable) {
          if (prefixes != null) {
              StringTokenizer tokens = new StringTokenizer(prefixes);
              while (tokens.hasMoreTokens()) {
                  final String prefix = tokens.nextToken();
                  final String uri = lookupNamespace(prefix);
                  if (uri != null) {
                      _extensions.put(uri, prefix);
                  }
              }
          }
      }
  
      public boolean isExtension(String uri) {
          return (_extensions.get(uri) != null);
      }
  
      public void declareExtensionPrefixes(Parser parser) {
          final SymbolTable stable = parser.getSymbolTable();
          final String extensionPrefixes = getAttribute("extension-element-prefixes");
          extensionURI(extensionPrefixes, stable);
      }
  
      /**
       * Parse the version and uri fields of the stylesheet and add an
       * entry to the symbol table mapping the name <tt>__stylesheet_</tt>
       * to an instance of this class.
       */
      public void parseContents(Parser parser) {
          final SymbolTable stable = parser.getSymbolTable();
  
          /*
          // Make sure the XSL version set in this stylesheet
          if ((_version == null) || (_version.equals(EMPTYSTRING))) {
              reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR,"version");
          }
          // Verify that the version is 1.0 and nothing else
          else if (!_version.equals("1.0")) {
              reportError(this, parser, ErrorMsg.XSL_VERSION_ERR, _version);
          }
          */
  
          // Add the implicit mapping of 'xml' to the XML namespace URI
          addPrefixMapping("xml", "http://www.w3.org/XML/1998/namespace");
  
          // Report and error if more than one stylesheet defined
          final Stylesheet sheet = stable.addStylesheet(_name, this);
          if (sheet != null) {
              // Error: more that one stylesheet defined
              ErrorMsg err = new ErrorMsg(ErrorMsg.MULTIPLE_STYLESHEET_ERR,this);
              parser.reportError(ERROR, err);
          }
  
          // If this is a simplified stylesheet we must create a template that
          // grabs the root node of the input doc ( <xsl:template match="/"/> ).
          // This template needs the current element (the one passed to this
          // method) as its only child, so the Template class has a special
          // method that handles this (parseSimplified()).
          if (_simplified) {
              stable.excludeURI(XSLT_URI);
              Template template = new Template();
              template.parseSimplified(this, parser);
          }
          // Parse the children of this node
          else {
              parseOwnChildren(parser);
          }
      }
  
      /**
       * Parse all direct children of the <xsl:stylesheet/> element.
       */
      public final void parseOwnChildren(Parser parser) {
          final SymbolTable stable = parser.getSymbolTable();
          final String excludePrefixes = getAttribute("exclude-result-prefixes");
          final String extensionPrefixes = getAttribute("extension-element-prefixes");
  
          // Exclude XSLT uri
          stable.pushExcludedNamespacesContext();
          stable.excludeURI(XSLT_URI);
          stable.excludeNamespaces(excludePrefixes);
          stable.excludeNamespaces(extensionPrefixes);
  
          final Vector contents = getContents();
          final int count = contents.size();
  
          // We have to scan the stylesheet element's top-level elements for
          // variables and/or parameters before we parse the other elements
          for (int i = 0; i < count; i++) {
              SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i);
              if ((child instanceof VariableBase) ||
                  (child instanceof NamespaceAlias)) {
                  parser.getSymbolTable().setCurrentNode(child);
                  child.parseContents(parser);
              }
          }
  
          // Now go through all the other top-level elements...
          for (int i = 0; i < count; i++) {
              SyntaxTreeNode child = (SyntaxTreeNode)contents.elementAt(i);
              if (!(child instanceof VariableBase) &&
                  !(child instanceof NamespaceAlias)) {
                  parser.getSymbolTable().setCurrentNode(child);
                  child.parseContents(parser);
              }
  
              // All template code should be compiled as methods if the
              // <xsl:apply-imports/> element was ever used in this stylesheet
              if (!_templateInlining && (child instanceof Template)) {
                  Template template = (Template)child;
                  String name = "template$dot$" + template.getPosition();
                  template.setName(parser.getQName(name));
              }
          }
  
          stable.popExcludedNamespacesContext();
      }
  
      public void processModes() {
          if (_defaultMode == null)
              _defaultMode = new Mode(null, this, EMPTYSTRING);
          _defaultMode.processPatterns(_keys);
          final Enumeration modes = _modes.elements();
          while (modes.hasMoreElements()) {
              final Mode mode = (Mode)modes.nextElement();
              mode.processPatterns(_keys);
          }
      }
  
      private void compileModes(ClassGenerator classGen) {
          _defaultMode.compileApplyTemplates(classGen);
          final Enumeration modes = _modes.elements();
          while (modes.hasMoreElements()) {
              final Mode mode = (Mode)modes.nextElement();
              mode.compileApplyTemplates(classGen);
          }
      }
  
      public Mode getMode(QName modeName) {
          if (modeName == null) {
              if (_defaultMode == null) {
                  _defaultMode = new Mode(null, this, EMPTYSTRING);
              }
              return _defaultMode;
          }
          else {
              Mode mode = (Mode)_modes.get(modeName);
              if (mode == null) {
                  final String suffix = Integer.toString(_nextModeSerial++);
                  _modes.put(modeName, mode = new Mode(modeName, this, suffix));
              }
              return mode;
          }
      }
  
      /**
       * Type check all the children of this node.
       */
      public Type typeCheck(SymbolTable stable) throws TypeCheckError {
          final int count = _globals.size();
          for (int i = 0; i < count; i++) {
              final VariableBase var = (VariableBase)_globals.elementAt(i);
              var.typeCheck(stable);
          }
          return typeCheckContents(stable);
      }
  
      /**
       * Translate the stylesheet into JVM bytecodes.
       */
      public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
          translate();
      }
  
      private void addDOMField(ClassGenerator classGen) {
          final FieldGen fgen = new FieldGen(ACC_PUBLIC,
                                             Util.getJCRefType(DOM_INTF_SIG),
                                             DOM_FIELD,
                                             classGen.getConstantPool());
          classGen.addField(fgen.getField());
      }
  
      /**
       * Add a static field
       */
      private void addStaticField(ClassGenerator classGen, String type,
                                  String name)
      {
          final FieldGen fgen = new FieldGen(ACC_PROTECTED|ACC_STATIC,
                                             Util.getJCRefType(type),
                                             name,
                                             classGen.getConstantPool());
          classGen.addField(fgen.getField());
  
      }
  
      /**
       * Translate the stylesheet into JVM bytecodes.
       */
      public void translate() {
          _className = getXSLTC().getClassName();
  
          // Define a new class by extending TRANSLET_CLASS
          final ClassGenerator classGen =
              new ClassGenerator(_className,
                                 TRANSLET_CLASS,
                EMPTYSTRING,
                                 ACC_PUBLIC | ACC_SUPER,
                                 null, this);
  
          addDOMField(classGen);
  
          // Compile transform() to initialize parameters, globals & output
          // and run the transformation
          compileTransform(classGen);
  
          // Translate all non-template elements and filter out all templates
          final Enumeration elements = elements();
          while (elements.hasMoreElements()) {
              Object element = elements.nextElement();
              // xsl:template
              if (element instanceof Template) {
                  // Separate templates by modes
                  final Template template = (Template)element;
                  //_templates.addElement(template);
                  getMode(template.getModeName()).addTemplate(template);
              }
              // xsl:attribute-set
              else if (element instanceof AttributeSet) {
                  ((AttributeSet)element).translate(classGen, null);
              }
              else if (element instanceof Output) {
                  // save the element for later to pass to compileConstructor
                  Output output = (Output)element;
                  if (output.enabled()) _lastOutputElement = output;
              }
              else {
                  // Global variables and parameters are handled elsewhere.
                  // Other top-level non-template elements are ignored. Literal
                  // elements outside of templates will never be output.
              }
          }
  
          checkOutputMethod();
          processModes();
          compileModes(classGen);
          compileStaticInitializer(classGen);
          compileConstructor(classGen, _lastOutputElement);
  
          if (!getParser().errorsFound()) {
              getXSLTC().dumpClass(classGen.getJavaClass());
          }
      }
  
      /**
       * Compile the namesArray, urisArray and typesArray into
       * the static initializer. They are read-only from the
       * translet. All translet instances can share a single
       * copy of this informtion.
       */
      private void compileStaticInitializer(ClassGenerator classGen) {
          final ConstantPoolGen cpg = classGen.getConstantPool();
          final InstructionList il = new InstructionList();
  
          final MethodGenerator staticConst =
              new MethodGenerator(ACC_PUBLIC|ACC_STATIC,
                                  jaxp.sun.org.apache.bcel.internal.generic.Type.VOID,
                                  null, null, "<clinit>",
                                  _className, il, cpg);
  
          addStaticField(classGen, "[" + STRING_SIG, STATIC_NAMES_ARRAY_FIELD);
          addStaticField(classGen, "[" + STRING_SIG, STATIC_URIS_ARRAY_FIELD);
          addStaticField(classGen, "[I", STATIC_TYPES_ARRAY_FIELD);
          addStaticField(classGen, "[" + STRING_SIG, STATIC_NAMESPACE_ARRAY_FIELD);
          // Create fields of type char[] that will contain literal text from
          // the stylesheet.
          final int charDataFieldCount = getXSLTC().getCharacterDataCount();
          for (int i = 0; i < charDataFieldCount; i++) {
              addStaticField(classGen, STATIC_CHAR_DATA_FIELD_SIG,
                             STATIC_CHAR_DATA_FIELD+i);
          }
  
          // Put the names array into the translet - used for dom/translet mapping
          final Vector namesIndex = getXSLTC().getNamesIndex();
          int size = namesIndex.size();
          String[] namesArray = new String[size];
          String[] urisArray = new String[size];
          int[] typesArray = new int[size];
  
          int index;
          for (int i = 0; i < size; i++) {
              String encodedName = (String)namesIndex.elementAt(i);
              if ((index = encodedName.lastIndexOf(':')) > -1) {
                  urisArray[i] = encodedName.substring(0, index);
              }
  
              index = index + 1;
              if (encodedName.charAt(index) == '@') {
                  typesArray[i] = DTM.ATTRIBUTE_NODE;
                  index++;
              } else if (encodedName.charAt(index) == '?') {
                  typesArray[i] = DTM.NAMESPACE_NODE;
                  index++;
              } else {
                  typesArray[i] = DTM.ELEMENT_NODE;
              }
  
              if (index == 0) {
                  namesArray[i] = encodedName;
              }
              else {
                  namesArray[i] = encodedName.substring(index);
              }
          }
  
          staticConst.markChunkStart();
          il.append(new PUSH(cpg, size));
          il.append(new ANEWARRAY(cpg.addClass(STRING)));
          int namesArrayRef = cpg.addFieldref(_className,
                                              STATIC_NAMES_ARRAY_FIELD,
                                              NAMES_INDEX_SIG);
          il.append(new PUTSTATIC(namesArrayRef));
          staticConst.markChunkEnd();
  
          for (int i = 0; i < size; i++) {
              final String name = namesArray[i];
              staticConst.markChunkStart();
              il.append(new GETSTATIC(namesArrayRef));
              il.append(new PUSH(cpg, i));
              il.append(new PUSH(cpg, name));
              il.append(AASTORE);
              staticConst.markChunkEnd();
          }
  
          staticConst.markChunkStart();
          il.append(new PUSH(cpg, size));
          il.append(new ANEWARRAY(cpg.addClass(STRING)));
          int urisArrayRef = cpg.addFieldref(_className,
                                             STATIC_URIS_ARRAY_FIELD,
                                             URIS_INDEX_SIG);
          il.append(new PUTSTATIC(urisArrayRef));
          staticConst.markChunkEnd();
  
          for (int i = 0; i < size; i++) {
              final String uri = urisArray[i];
              staticConst.markChunkStart();
              il.append(new GETSTATIC(urisArrayRef));
              il.append(new PUSH(cpg, i));
              il.append(new PUSH(cpg, uri));
              il.append(AASTORE);
              staticConst.markChunkEnd();
          }
  
          staticConst.markChunkStart();
          il.append(new PUSH(cpg, size));
          il.append(new NEWARRAY(BasicType.INT));
          int typesArrayRef = cpg.addFieldref(_className,
                                              STATIC_TYPES_ARRAY_FIELD,
                                              TYPES_INDEX_SIG);
          il.append(new PUTSTATIC(typesArrayRef));
          staticConst.markChunkEnd();
  
          for (int i = 0; i < size; i++) {
              final int nodeType = typesArray[i];
              staticConst.markChunkStart();
              il.append(new GETSTATIC(typesArrayRef));
              il.append(new PUSH(cpg, i));
              il.append(new PUSH(cpg, nodeType));
              il.append(IASTORE);
          }
  
          // Put the namespace names array into the translet
          final Vector namespaces = getXSLTC().getNamespaceIndex();
          staticConst.markChunkStart();
          il.append(new PUSH(cpg, namespaces.size()));
          il.append(new ANEWARRAY(cpg.addClass(STRING)));
          int namespaceArrayRef = cpg.addFieldref(_className,
                                                  STATIC_NAMESPACE_ARRAY_FIELD,
                                                  NAMESPACE_INDEX_SIG);
          il.append(new PUTSTATIC(namespaceArrayRef));
          staticConst.markChunkEnd();
  
          for (int i = 0; i < namespaces.size(); i++) {
              final String ns = (String)namespaces.elementAt(i);
              staticConst.markChunkStart();
              il.append(new GETSTATIC(namespaceArrayRef));
              il.append(new PUSH(cpg, i));
              il.append(new PUSH(cpg, ns));
              il.append(AASTORE);
              staticConst.markChunkEnd();
          }
  
          // Grab all the literal text in the stylesheet and put it in a char[]
          final int charDataCount = getXSLTC().getCharacterDataCount();
          final int toCharArray = cpg.addMethodref(STRING, "toCharArray", "()[C");
          for (int i = 0; i < charDataCount; i++) {
              staticConst.markChunkStart();
              il.append(new PUSH(cpg, getXSLTC().getCharacterData(i)));
              il.append(new INVOKEVIRTUAL(toCharArray));
              il.append(new PUTSTATIC(cpg.addFieldref(_className,
                                                 STATIC_CHAR_DATA_FIELD+i,
                                                 STATIC_CHAR_DATA_FIELD_SIG)));
              staticConst.markChunkEnd();
          }
  
          il.append(RETURN);
  
          classGen.addMethod(staticConst);
  
      }
  
      /**
       * Compile the translet's constructor
       */
      private void compileConstructor(ClassGenerator classGen, Output output) {
  
          final ConstantPoolGen cpg = classGen.getConstantPool();
          final InstructionList il = new InstructionList();
  
          final MethodGenerator constructor =
              new MethodGenerator(ACC_PUBLIC,
                                  jaxp.sun.org.apache.bcel.internal.generic.Type.VOID,
                                  null, null, "<init>",
                                  _className, il, cpg);
  
          // Call the constructor in the AbstractTranslet superclass
          il.append(classGen.loadTranslet());
          il.append(new INVOKESPECIAL(cpg.addMethodref(TRANSLET_CLASS,
                                                       "<init>", "()V")));
  
          constructor.markChunkStart();
          il.append(classGen.loadTranslet());
          il.append(new GETSTATIC(cpg.addFieldref(_className,
                                                  STATIC_NAMES_ARRAY_FIELD,
                                                  NAMES_INDEX_SIG)));
          il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
                                                 NAMES_INDEX,
                                                 NAMES_INDEX_SIG)));
  
          il.append(classGen.loadTranslet());
          il.append(new GETSTATIC(cpg.addFieldref(_className,
                                                  STATIC_URIS_ARRAY_FIELD,
                                                  URIS_INDEX_SIG)));
          il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
                                                 URIS_INDEX,
                                                 URIS_INDEX_SIG)));
          constructor.markChunkEnd();
  
          constructor.markChunkStart();
          il.append(classGen.loadTranslet());
          il.append(new GETSTATIC(cpg.addFieldref(_className,
                                                  STATIC_TYPES_ARRAY_FIELD,
                                                  TYPES_INDEX_SIG)));
          il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
                                                 TYPES_INDEX,
                                                 TYPES_INDEX_SIG)));
          constructor.markChunkEnd();
  
          constructor.markChunkStart();
          il.append(classGen.loadTranslet());
          il.append(new GETSTATIC(cpg.addFieldref(_className,
                                                  STATIC_NAMESPACE_ARRAY_FIELD,
                                                  NAMESPACE_INDEX_SIG)));
          il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
                                                 NAMESPACE_INDEX,
                                                 NAMESPACE_INDEX_SIG)));
          constructor.markChunkEnd();
  
          constructor.markChunkStart();
          il.append(classGen.loadTranslet());
          il.append(new PUSH(cpg, AbstractTranslet.CURRENT_TRANSLET_VERSION));
          il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
                                                 TRANSLET_VERSION_INDEX,
                                                 TRANSLET_VERSION_INDEX_SIG)));
          constructor.markChunkEnd();
  
          if (_hasIdCall) {
              constructor.markChunkStart();
              il.append(classGen.loadTranslet());
              il.append(new PUSH(cpg, Boolean.TRUE));
              il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS,
                                                     HASIDCALL_INDEX,
                                                     HASIDCALL_INDEX_SIG)));
              constructor.markChunkEnd();
          }
  
          // Compile in code to set the output configuration from <xsl:output>
          if (output != null) {
              // Set all the output settings files in the translet
              constructor.markChunkStart();
              output.translate(classGen, constructor);
              constructor.markChunkEnd();
          }
  
          // Compile default decimal formatting symbols.
          // This is an implicit, nameless xsl:decimal-format top-level element.
          if (_numberFormattingUsed) {
              constructor.markChunkStart();
              DecimalFormatting.translateDefaultDFS(classGen, constructor);
              constructor.markChunkEnd();
          }
  
          il.append(RETURN);
  
          classGen.addMethod(constructor);
      }
  
      /**
       * Compile a topLevel() method into the output class. This method is
       * called from transform() to handle all non-template top-level elements.
       * Returns the signature of the topLevel() method.
       *
       * Global variables/params and keys are first sorted to resolve
       * dependencies between them. The XSLT 1.0 spec does not allow a key
       * to depend on a variable. However, for compatibility with Xalan
       * interpretive, that type of dependency is allowed. Note also that
       * the buildKeys() method is still generated as it is used by the
       * LoadDocument class, but it no longer called from transform().
       */
      private String compileTopLevel(ClassGenerator classGen) {
  
          final ConstantPoolGen cpg = classGen.getConstantPool();
  
          final jaxp.sun.org.apache.bcel.internal.generic.Type[] argTypes = {
              Util.getJCRefType(DOM_INTF_SIG),
              Util.getJCRefType(NODE_ITERATOR_SIG),
              Util.getJCRefType(TRANSLET_OUTPUT_SIG)
          };
  
          final String[] argNames = {
              DOCUMENT_PNAME, ITERATOR_PNAME, TRANSLET_OUTPUT_PNAME
          };
  
          final InstructionList il = new InstructionList();
  
          final MethodGenerator toplevel =
              new MethodGenerator(ACC_PUBLIC,
                                  jaxp.sun.org.apache.bcel.internal.generic.Type.VOID,
                                  argTypes, argNames,
                                  "topLevel", _className, il,
                                  classGen.getConstantPool());
  
          toplevel.addException("jaxp.sun.org.apache.xalan.internal.xsltc.TransletException");
  
          // Define and initialize 'current' variable with the root node
          final LocalVariableGen current =
              toplevel.addLocalVariable("current",
                                        jaxp.sun.org.apache.bcel.internal.generic.Type.INT,
                                        null, null);
  
          final int setFilter = cpg.addInterfaceMethodref(DOM_INTF,
                                 "setFilter",
                                 "(Lcom/sun/org/apache/xalan/internal/xsltc/StripFilter;)V");
  
          final int gitr = cpg.addInterfaceMethodref(DOM_INTF,
                                                          "getIterator",
                                                          "()"+NODE_ITERATOR_SIG);
          il.append(toplevel.loadDOM());
          il.append(new INVOKEINTERFACE(gitr, 1));
          il.append(toplevel.nextNode());
          current.setStart(il.append(new ISTORE(current.getIndex())));
  
          // Create a new list containing variables/params + keys
          Vector varDepElements = new Vector(_globals);
          Enumeration elements = elements();
          while (elements.hasMoreElements()) {
              final Object element = elements.nextElement();
              if (element instanceof Key) {
                  varDepElements.add(element);
              }
          }
  
          // Determine a partial order for the variables/params and keys
          varDepElements = resolveDependencies(varDepElements);
  
          // Translate vars/params and keys in the right order
          final int count = varDepElements.size();
          for (int i = 0; i < count; i++) {
              final TopLevelElement tle = (TopLevelElement) varDepElements.elementAt(i);
              tle.translate(classGen, toplevel);
              if (tle instanceof Key) {
                  final Key key = (Key) tle;
                  _keys.put(key.getName(), key);
              }
          }
  
          // Compile code for other top-level elements
          Vector whitespaceRules = new Vector();
          elements = elements();
          while (elements.hasMoreElements()) {
              final Object element = elements.nextElement();
              // xsl:decimal-format
              if (element instanceof DecimalFormatting) {
                  ((DecimalFormatting)element).translate(classGen,toplevel);
              }
              // xsl:strip/preserve-space
              else if (element instanceof Whitespace) {
                  whitespaceRules.addAll(((Whitespace)element).getRules());
              }
          }
  
          // Translate all whitespace strip/preserve rules
          if (whitespaceRules.size() > 0) {
              Whitespace.translateRules(whitespaceRules,classGen);
          }
  
          if (classGen.containsMethod(STRIP_SPACE, STRIP_SPACE_PARAMS) != null) {
              il.append(toplevel.loadDOM());
              il.append(classGen.loadTranslet());
              il.append(new INVOKEINTERFACE(setFilter, 2));
          }
  
          il.append(RETURN);
  
          // Compute max locals + stack and add method to class
          classGen.addMethod(toplevel);
  
          return("("+DOM_INTF_SIG+NODE_ITERATOR_SIG+TRANSLET_OUTPUT_SIG+")V");
      }
  
      /**
       * This method returns a vector with variables/params and keys in the
       * order in which they are to be compiled for initialization. The order
       * is determined by analyzing the dependencies between them. The XSLT 1.0
       * spec does not allow a key to depend on a variable. However, for
       * compatibility with Xalan interpretive, that type of dependency is
       * allowed and, therefore, consider to determine the partial order.
       */
      private Vector resolveDependencies(Vector input) {
          /* DEBUG CODE - INGORE
          for (int i = 0; i < input.size(); i++) {
              final TopLevelElement e = (TopLevelElement) input.elementAt(i);
              System.out.println("e = " + e + " depends on:");
              Vector dep = e.getDependencies();
              for (int j = 0; j < (dep != null ? dep.size() : 0); j++) {
                  System.out.println("\t" + dep.elementAt(j));
              }
          }
          System.out.println("=================================");
          */
  
          Vector result = new Vector();
          while (input.size() > 0) {
              boolean changed = false;
              for (int i = 0; i < input.size(); ) {
                  final TopLevelElement vde = (TopLevelElement) input.elementAt(i);
                  final Vector dep = vde.getDependencies();
                  if (dep == null || result.containsAll(dep)) {
                      result.addElement(vde);
                      input.remove(i);
                      changed = true;
                  }
                  else {
                      i++;
                  }
              }
  
              // If nothing was changed in this pass then we have a circular ref
              if (!changed) {
                  ErrorMsg err = new ErrorMsg(ErrorMsg.CIRCULAR_VARIABLE_ERR,
                                              input.toString(), this);
                  getParser().reportError(ERROR, err);
                  return(result);
              }
          }
  
          /* DEBUG CODE - INGORE
          System.out.println("=================================");
          for (int i = 0; i < result.size(); i++) {
              final TopLevelElement e = (TopLevelElement) result.elementAt(i);
              System.out.println("e = " + e);
          }
          */
  
          return result;
      }
  
      /**
       * Compile a buildKeys() method into the output class. Note that keys
       * for the input document are created in topLevel(), not in this method.
       * However, we still need this method to create keys for documents loaded
       * via the XPath document() function.
       */
      private String compileBuildKeys(ClassGenerator classGen) {
          final ConstantPoolGen cpg = classGen.getConstantPool();
  
          final jaxp.sun.org.apache.bcel.internal.generic.Type[] argTypes = {
              Util.getJCRefType(DOM_INTF_SIG),
              Util.getJCRefType(NODE_ITERATOR_SIG),
              Util.getJCRefType(TRANSLET_OUTPUT_SIG),
              jaxp.sun.org.apache.bcel.internal.generic.Type.INT
          };
  
          final String[] argNames = {
              DOCUMENT_PNAME, ITERATOR_PNAME, TRANSLET_OUTPUT_PNAME, "current"
          };
  
          final InstructionList il = new InstructionList();
  
          final MethodGenerator buildKeys =
              new MethodGenerator(ACC_PUBLIC,
                                  jaxp.sun.org.apache.bcel.internal.generic.Type.VOID,
                                  argTypes, argNames,
                                  "buildKeys", _className, il,
                                  classGen.getConstantPool());
  
          buildKeys.addException("jaxp.sun.org.apache.xalan.internal.xsltc.TransletException");
  
          final Enumeration elements = elements();
          while (elements.hasMoreElements()) {
              // xsl:key
              final Object element = elements.nextElement();
              if (element instanceof Key) {
                  final Key key = (Key)element;
                  key.translate(classGen, buildKeys);
                  _keys.put(key.getName(),key);
              }
          }
  
          il.append(RETURN);
  
          // Compute max locals + stack and add method to class
          buildKeys.stripAttributes(true);
          buildKeys.setMaxLocals();
          buildKeys.setMaxStack();
          buildKeys.removeNOPs();
  
          classGen.addMethod(buildKeys.getMethod());
  
          return("("+DOM_INTF_SIG+NODE_ITERATOR_SIG+TRANSLET_OUTPUT_SIG+"I)V");
      }
  
      /**
       * Compile transform() into the output class. This method is used to
       * initialize global variables and global parameters. The current node
       * is set to be the document's root node.
       */
      private void compileTransform(ClassGenerator classGen) {
          final ConstantPoolGen cpg = classGen.getConstantPool();
  
          /*
           * Define the the method transform with the following signature:
           * void transform(DOM, NodeIterator, HandlerBase)
           */
          final jaxp.sun.org.apache.bcel.internal.generic.Type[] argTypes =
              new jaxp.sun.org.apache.bcel.internal.generic.Type[3];
          argTypes[0] = Util.getJCRefType(DOM_INTF_SIG);
          argTypes[1] = Util.getJCRefType(NODE_ITERATOR_SIG);
          argTypes[2] = Util.getJCRefType(TRANSLET_OUTPUT_SIG);
  
          final String[] argNames = new String[3];
          argNames[0] = DOCUMENT_PNAME;
          argNames[1] = ITERATOR_PNAME;
          argNames[2] = TRANSLET_OUTPUT_PNAME;
  
          final InstructionList il = new InstructionList();
          final MethodGenerator transf =
              new MethodGenerator(ACC_PUBLIC,
                                  jaxp.sun.org.apache.bcel.internal.generic.Type.VOID,
                                  argTypes, argNames,
                                  "transform",
                                  _className,
                                  il,
                                  classGen.getConstantPool());
          transf.addException("jaxp.sun.org.apache.xalan.internal.xsltc.TransletException");
  
          // Define and initialize current with the root node
          final LocalVariableGen current =
              transf.addLocalVariable("current",
                                      jaxp.sun.org.apache.bcel.internal.generic.Type.INT,
                                      null, null);
          final String applyTemplatesSig = classGen.getApplyTemplatesSig();
          final int applyTemplates = cpg.addMethodref(getClassName(),
                                                      "applyTemplates",
                                                      applyTemplatesSig);
          final int domField = cpg.addFieldref(getClassName(),
                                               DOM_FIELD,
                                               DOM_INTF_SIG);
  
          // push translet for PUTFIELD
          il.append(classGen.loadTranslet());
          // prepare appropriate DOM implementation
  
          if (isMultiDocument()) {
              il.append(new NEW(cpg.addClass(MULTI_DOM_CLASS)));
              il.append(DUP);
          }
  
          il.append(classGen.loadTranslet());
          il.append(transf.loadDOM());
          il.append(new INVOKEVIRTUAL(cpg.addMethodref(TRANSLET_CLASS,
                                                       "makeDOMAdapter",
                                                       "("+DOM_INTF_SIG+")"+
                                                       DOM_ADAPTER_SIG)));
          // DOMAdapter is on the stack
  
          if (isMultiDocument()) {
              final int init = cpg.addMethodref(MULTI_DOM_CLASS,
                                                "<init>",
                                                "("+DOM_INTF_SIG+")V");
              il.append(new INVOKESPECIAL(init));
              // MultiDOM is on the stack
          }
  
          //store to _dom variable
          il.append(new PUTFIELD(domField));
  
          // continue with globals initialization
          final int gitr = cpg.addInterfaceMethodref(DOM_INTF,
                                                          "getIterator",
                                                          "()"+NODE_ITERATOR_SIG);
          il.append(transf.loadDOM());
          il.append(new INVOKEINTERFACE(gitr, 1));
          il.append(transf.nextNode());
          current.setStart(il.append(new ISTORE(current.getIndex())));
  
          // Transfer the output settings to the output post-processor
          il.append(classGen.loadTranslet());
          il.append(transf.loadHandler());
          final int index = cpg.addMethodref(TRANSLET_CLASS,
                                             "transferOutputSettings",
                                             "("+OUTPUT_HANDLER_SIG+")V");
          il.append(new INVOKEVIRTUAL(index));
  
          /*
           * Compile buildKeys() method. Note that this method is not
           * invoked here as keys for the input document are now created
           * in topLevel(). However, this method is still needed by the
           * LoadDocument class.
           */
          final String keySig = compileBuildKeys(classGen);
          final int keyIdx = cpg.addMethodref(getClassName(),
                                                 "buildKeys", keySig);
  
          // Look for top-level elements that need handling
          final Enumeration toplevel = elements();
          if (_globals.size() > 0 || toplevel.hasMoreElements()) {
              // Compile method for handling top-level elements
              final String topLevelSig = compileTopLevel(classGen);
              // Get a reference to that method
              final int topLevelIdx = cpg.addMethodref(getClassName(),
                                                       "topLevel",
                                                       topLevelSig);
              // Push all parameters on the stack and call topLevel()
              il.append(classGen.loadTranslet()); // The 'this' pointer
              il.append(classGen.loadTranslet());
              il.append(new GETFIELD(domField));  // The DOM reference
              il.append(transf.loadIterator());
              il.append(transf.loadHandler());    // The output handler
              il.append(new INVOKEVIRTUAL(topLevelIdx));
          }
  
          // start document
          il.append(transf.loadHandler());
          il.append(transf.startDocument());
  
          // push first arg for applyTemplates
          il.append(classGen.loadTranslet());
          // push translet for GETFIELD to get DOM arg
          il.append(classGen.loadTranslet());
          il.append(new GETFIELD(domField));
          // push remaining 2 args
          il.append(transf.loadIterator());
          il.append(transf.loadHandler());
          il.append(new INVOKEVIRTUAL(applyTemplates));
          // endDocument
          il.append(transf.loadHandler());
          il.append(transf.endDocument());
  
          il.append(RETURN);
  
          // Compute max locals + stack and add method to class
          classGen.addMethod(transf);
  
      }
  
      /**
       * Peephole optimization: Remove sequences of [ALOAD, POP].
       */
      private void peepHoleOptimization(MethodGenerator methodGen) {
          final String pattern = "`aload'`pop'`instruction'";
          final InstructionList il = methodGen.getInstructionList();
          final InstructionFinder find = new InstructionFinder(il);
          for(Iterator iter=find.search(pattern); iter.hasNext(); ) {
              InstructionHandle[] match = (InstructionHandle[])iter.next();
              try {
                  il.delete(match[0], match[1]);
              }
              catch (TargetLostException e) {
                  // TODO: move target down into the list
              }
          }
      }
  
      public int addParam(Param param) {
          _globals.addElement(param);
          return _globals.size() - 1;
      }
  
      public int addVariable(Variable global) {
          _globals.addElement(global);
          return _globals.size() - 1;
      }
  
      public void display(int indent) {
          indent(indent);
          Util.println("Stylesheet");
          displayContents(indent + IndentIncrement);
      }
  
      // do we need this wrapper ?????
      public String getNamespace(String prefix) {
          return lookupNamespace(prefix);
      }
  
      public String getClassName() {
          return _className;
      }
  
      public Vector getTemplates() {
          return _templates;
      }
  
      public Vector getAllValidTemplates() {
          // Return templates if no imported/included stylesheets
          if (_includedStylesheets == null) {
              return _templates;
          }
  
          // Is returned value cached?
          if (_allValidTemplates == null) {
             Vector templates = new Vector();
             templates.addAll(_templates);
              int size = _includedStylesheets.size();
              for (int i = 0; i < size; i++) {
                  Stylesheet included =(Stylesheet)_includedStylesheets.elementAt(i);
                  templates.addAll(included.getAllValidTemplates());
              }
              //templates.addAll(_templates);
  
              // Cache results in top-level stylesheet only
              if (_parentStylesheet != null) {
                  return templates;
              }
              _allValidTemplates = templates;
           }
  
          return _allValidTemplates;
      }
  
      protected void addTemplate(Template template) {
          _templates.addElement(template);
      }
  }
  