/* * 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.59 2004/02/16 22:24:29 minchau Exp $ */ package com.sun.org.apache.xalan.internal.xsltc.compiler; import java.net.URL; import java.net.MalformedURLException; 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 com.sun.org.apache.xml.internal.utils.SystemIDResolver; import com.sun.org.apache.bcel.internal.generic.ANEWARRAY; import com.sun.org.apache.bcel.internal.generic.BasicType; import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen; import com.sun.org.apache.bcel.internal.generic.FieldGen; import com.sun.org.apache.bcel.internal.generic.GETFIELD; import com.sun.org.apache.bcel.internal.generic.GETSTATIC; import com.sun.org.apache.bcel.internal.generic.INVOKEINTERFACE; import com.sun.org.apache.bcel.internal.generic.INVOKESPECIAL; import com.sun.org.apache.bcel.internal.generic.INVOKEVIRTUAL; import com.sun.org.apache.bcel.internal.generic.ISTORE; import com.sun.org.apache.bcel.internal.generic.InstructionHandle; import com.sun.org.apache.bcel.internal.generic.InstructionList; import com.sun.org.apache.bcel.internal.generic.LocalVariableGen; import com.sun.org.apache.bcel.internal.generic.NEW; import com.sun.org.apache.bcel.internal.generic.NEWARRAY; import com.sun.org.apache.bcel.internal.generic.PUSH; import com.sun.org.apache.bcel.internal.generic.PUTFIELD; import com.sun.org.apache.bcel.internal.generic.PUTSTATIC; import com.sun.org.apache.bcel.internal.generic.TargetLostException; import com.sun.org.apache.bcel.internal.util.InstructionFinder; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError; import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.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 hasLocalParams(). */ 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 getAllValidTemplates(). 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. */ private boolean _templateInlining = true; /** * 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 _hasLocalParams 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 = new Boolean(true); return true; } } _hasLocalParams = new 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 excludeExtensionPrefixes(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.excludeURI(Constants.XSLT_URI); stable.excludeNamespaces(excludePrefixes); stable.excludeNamespaces(extensionPrefixes); extensionURI(extensionPrefixes, stable); } /** * Parse the version and uri fields of the stylesheet and add an * entry to the symbol table mapping the name __stylesheet_ * 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(Constants.ERROR, err); } // If this is a simplified stylesheet we must create a template that // grabs the root node of the input doc ( ). // 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 element. */ public final void parseOwnChildren(Parser parser) { 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 // 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)); } } } public void processModes() { if (_defaultMode == null) _defaultMode = new Mode(null, this, Constants.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, Constants.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, Constants.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, com.sun.org.apache.bcel.internal.generic.Type.VOID, null, null, "", _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); } } il.append(new PUSH(cpg, size)); il.append(new ANEWARRAY(cpg.addClass(STRING))); for (int i = 0; i < size; i++) { final String name = namesArray[i]; il.append(DUP); il.append(new PUSH(cpg, i)); il.append(new PUSH(cpg, name)); il.append(AASTORE); } il.append(new PUTSTATIC(cpg.addFieldref(_className, STATIC_NAMES_ARRAY_FIELD, NAMES_INDEX_SIG))); il.append(new PUSH(cpg, size)); il.append(new ANEWARRAY(cpg.addClass(STRING))); for (int i = 0; i < size; i++) { final String uri = urisArray[i]; il.append(DUP); il.append(new PUSH(cpg, i)); il.append(new PUSH(cpg, uri)); il.append(AASTORE); } il.append(new PUTSTATIC(cpg.addFieldref(_className, STATIC_URIS_ARRAY_FIELD, URIS_INDEX_SIG))); il.append(new PUSH(cpg, size)); il.append(new NEWARRAY(BasicType.INT)); for (int i = 0; i < size; i++) { final int nodeType = typesArray[i]; il.append(DUP); il.append(new PUSH(cpg, i)); il.append(new PUSH(cpg, nodeType)); il.append(IASTORE); } il.append(new PUTSTATIC(cpg.addFieldref(_className, STATIC_TYPES_ARRAY_FIELD, TYPES_INDEX_SIG))); // Put the namespace names array into the translet final Vector namespaces = getXSLTC().getNamespaceIndex(); il.append(new PUSH(cpg, namespaces.size())); il.append(new ANEWARRAY(cpg.addClass(STRING))); for (int i = 0; i < namespaces.size(); i++) { final String ns = (String)namespaces.elementAt(i); il.append(DUP); il.append(new PUSH(cpg, i)); il.append(new PUSH(cpg, ns)); il.append(AASTORE); } il.append(new PUTSTATIC(cpg.addFieldref(_className, STATIC_NAMESPACE_ARRAY_FIELD, NAMESPACE_INDEX_SIG))); // 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++) { 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))); } il.append(RETURN); staticConst.stripAttributes(true); staticConst.setMaxLocals(); staticConst.setMaxStack(); classGen.addMethod(staticConst.getMethod()); } /** * 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, com.sun.org.apache.bcel.internal.generic.Type.VOID, null, null, "", _className, il, cpg); // Call the constructor in the AbstractTranslet superclass il.append(classGen.loadTranslet()); il.append(new INVOKESPECIAL(cpg.addMethodref(TRANSLET_CLASS, "", "()V"))); 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))); 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))); 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))); 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))); if (_hasIdCall) { il.append(classGen.loadTranslet()); il.append(new PUSH(cpg, Boolean.TRUE)); il.append(new PUTFIELD(cpg.addFieldref(TRANSLET_CLASS, HASIDCALL_INDEX, HASIDCALL_INDEX_SIG))); } // Compile in code to set the output configuration from if (output != null) { // Set all the output settings files in the translet output.translate(classGen, constructor); } // Compile default decimal formatting symbols. // This is an implicit, nameless xsl:decimal-format top-level element. if (_numberFormattingUsed) DecimalFormatting.translateDefaultDFS(classGen, constructor); il.append(RETURN); constructor.stripAttributes(true); constructor.setMaxLocals(); constructor.setMaxStack(); classGen.addMethod(constructor.getMethod()); } /** * Compile a topLevel() method into the output class. This method is * called from transform() to handle all non-template top-level elemtents. * Returns the signature of the topLevel() method. */ private String compileTopLevel(ClassGenerator classGen, Enumeration elements) { final ConstantPoolGen cpg = classGen.getConstantPool(); final com.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, com.sun.org.apache.bcel.internal.generic.Type.VOID, argTypes, argNames, "topLevel", _className, il, classGen.getConstantPool()); toplevel.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException"); // Define and initialize 'current' variable with the root node final LocalVariableGen current = toplevel.addLocalVariable("current", com.sun.org.apache.bcel.internal.generic.Type.INT, il.getEnd(), null); final int setFilter = cpg.addInterfaceMethodref(DOM_INTF, "setFilter", "(Lcom/sun/org/apache/xalan/internal/xsltc/StripFilter;)V"); il.append(new PUSH(cpg, DTM.ROOT_NODE)); il.append(new ISTORE(current.getIndex())); // Resolve any forward referenes and translate global variables/params _globals = resolveReferences(_globals); final int count = _globals.size(); for (int i = 0; i < count; i++) { final VariableBase var = (VariableBase)_globals.elementAt(i); var.translate(classGen,toplevel); } // Compile code for other top-level elements Vector whitespaceRules = new Vector(); 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 toplevel.stripAttributes(true); toplevel.setMaxLocals(); toplevel.setMaxStack(); toplevel.removeNOPs(); classGen.addMethod(toplevel.getMethod()); return("("+DOM_INTF_SIG+NODE_ITERATOR_SIG+TRANSLET_OUTPUT_SIG+")V"); } /** * This method returns a vector with variables in the order in * which they are to be compiled. The order is determined by the * dependencies between them and the order in which they were defined * in the stylesheet. The first step is to close the input vector under * the dependence relation (this is usually needed when variables are * defined inside other variables in a RTF). */ private Vector resolveReferences(Vector input) { // Make sure that the vector 'input' is closed for (int i = 0; i < input.size(); i++) { final VariableBase var = (VariableBase) input.elementAt(i); final Vector dep = var.getDependencies(); final int depSize = (dep != null) ? dep.size() : 0; for (int j = 0; j < depSize; j++) { final VariableBase depVar = (VariableBase) dep.elementAt(j); if (!input.contains(depVar)) { input.addElement(depVar); } } } /* DEBUG CODE - INGORE for (int i = 0; i < input.size(); i++) { final VariableBase var = (VariableBase) input.elementAt(i); System.out.println("var = " + var); } System.out.println("================================="); */ Vector result = new Vector(); while (input.size() > 0) { boolean changed = false; for (int i = 0; i < input.size(); ) { final VariableBase var = (VariableBase)input.elementAt(i); final Vector dep = var.getDependencies(); if (dep == null || result.containsAll(dep)) { result.addElement(var); 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(Constants.ERROR, err); return(result); } } /* DEBUG CODE - INGORE System.out.println("================================="); for (int i = 0; i < result.size(); i++) { final VariableBase var = (VariableBase) result.elementAt(i); System.out.println("var = " + var); } */ return result; } /** * Compile a buildKeys() method into the output class. This method is * called from transform() to handle build all indexes needed by key(). */ private String compileBuildKeys(ClassGenerator classGen) { final ConstantPoolGen cpg = classGen.getConstantPool(); final com.sun.org.apache.bcel.internal.generic.Type[] argTypes = { Util.getJCRefType(DOM_INTF_SIG), Util.getJCRefType(NODE_ITERATOR_SIG), Util.getJCRefType(TRANSLET_OUTPUT_SIG), com.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, com.sun.org.apache.bcel.internal.generic.Type.VOID, argTypes, argNames, "buildKeys", _className, il, classGen.getConstantPool()); buildKeys.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException"); final Enumeration elements = elements(); // Compile code for other top-level 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 com.sun.org.apache.bcel.internal.generic.Type[] argTypes = new com.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, com.sun.org.apache.bcel.internal.generic.Type.VOID, argTypes, argNames, "transform", _className, il, classGen.getConstantPool()); transf.addException("com.sun.org.apache.xalan.internal.xsltc.TransletException"); // Define and initialize current with the root node final LocalVariableGen current = transf.addLocalVariable("current", com.sun.org.apache.bcel.internal.generic.Type.INT, il.getEnd(), 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, "", "("+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 il.append(new PUSH(cpg, DTM.ROOT_NODE)); 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 -- TODO: omit if not needed final String keySig = compileBuildKeys(classGen); final int keyIdx = cpg.addMethodref(getClassName(), "buildKeys", keySig); il.append(classGen.loadTranslet()); // The 'this' pointer il.append(classGen.loadTranslet()); il.append(new GETFIELD(domField)); // The DOM reference il.append(transf.loadIterator()); // Not really used, but... il.append(transf.loadHandler()); // The output handler il.append(new PUSH(cpg, DTM.ROOT_NODE)); // Start with the root node il.append(new INVOKEVIRTUAL(keyIdx)); // 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, toplevel); // 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 transf.stripAttributes(true); transf.setMaxLocals(); transf.setMaxStack(); transf.removeNOPs(); classGen.addMethod(transf.getMethod()); } /** * 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(); 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); } }