All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.xalan.xsltc.compiler.Stylesheet Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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 577935 2007-09-20 21:35:20Z minchau $
 */

package org.apache.xalan.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 org.apache.xml.utils.SystemIDResolver;
import org.apache.bcel.generic.ANEWARRAY;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldGen;
import org.apache.bcel.generic.GETFIELD;
import org.apache.bcel.generic.GETSTATIC;
import org.apache.bcel.generic.INVOKEINTERFACE;
import org.apache.bcel.generic.INVOKESPECIAL;
import org.apache.bcel.generic.INVOKEVIRTUAL;
import org.apache.bcel.generic.ISTORE;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.LocalVariableGen;
import org.apache.bcel.generic.NEW;
import org.apache.bcel.generic.NEWARRAY;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.PUTFIELD;
import org.apache.bcel.generic.PUTSTATIC;
import org.apache.bcel.generic.TargetLostException;
import org.apache.bcel.util.InstructionFinder;
import org.apache.xalan.xsltc.compiler.util.ClassGenerator;
import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
import org.apache.xalan.xsltc.compiler.util.MethodGenerator;
import org.apache.xalan.xsltc.compiler.util.Type;
import org.apache.xalan.xsltc.compiler.util.TypeCheckError;
import org.apache.xalan.xsltc.compiler.util.Util;
import org.apache.xalan.xsltc.runtime.AbstractTranslet;
import org.apache.xml.dtm.DTM;

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;

    private Vector _elementsWithNamespacesUsedDynamically = 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 _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 = 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 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, typesArray, namespaceArray, * namespaceAncestorsArray, prefixURIsIdxArray and prefixURIPairsArray into * the static initializer. They are read-only from the * translet. All translet instances can share a single * copy of this informtion.

*

The namespaceAncestorsArray, * prefixURIsIdxArray and prefixURIPairsArray * contain namespace information accessible from the stylesheet: *

*
namespaceAncestorsArray
*
Array indexed by integer stylesheet node IDs containing node IDs of * the nearest ancestor node in the stylesheet with namespace * declarations or -1 if there is no such ancestor. There * can be more than one disjoint tree of nodes - one for each stylesheet * module
*
prefixURIsIdxArray
*
Array indexed by integer stylesheet node IDs containing the index * into prefixURIPairsArray of the first namespace prefix * declared for the node. The values are stored in ascending order, so * the next value in this array (if any) can be used to find the last such * prefix-URI pair
*
prefixURIPairsArray
*
Array of pairs of namespace prefixes and URIs. A zero-length * string represents the default namespace if it appears as a prefix and * a namespace undeclaration if it appears as a URI.
*
*

*

For this stylesheet *


     * <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
     *   <xsl:template match="/">
     *     <xsl:for-each select="*" xmlns:foo="foouri">
     *       <xsl:element name="{n}" xmlns:foo="baruri">
     *     </xsl:for-each>
     *     <out xmlns="lumpit"/>
     *     <xsl:element name="{n}" xmlns="foouri"/>
     *     <xsl:element name="{n}" namespace="{ns}" xmlns="limpit"/gt;
     *   </xsl:template>
     * </xsl:stylesheet>
     * 
* there will be four stylesheet nodes whose namespace information is * needed, and *
    *
  • namespaceAncestorsArray will have the value * [-1,0,1,0];
  • *
  • prefixURIsIdxArray will have the value * [0,4,6,8]; and
  • *
  • prefixURIPairsArray will have the value * ["xml","http://www.w3.org/XML/1998/namespace", * "xsl","http://www.w3.org/1999/XSL/Transform" * "foo","foouri","foo","baruri","","foouri"].
  • *
*

*/ private void compileStaticInitializer(ClassGenerator classGen) { final ConstantPoolGen cpg = classGen.getConstantPool(); final InstructionList il = new InstructionList(); final MethodGenerator staticConst = new MethodGenerator(ACC_PUBLIC|ACC_STATIC, org.apache.bcel.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); } } 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); staticConst.markChunkEnd(); } // 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(); } // Put the tree of stylesheet namespace declarations into the translet final Vector namespaceAncestors = getXSLTC().getNSAncestorPointers(); if (namespaceAncestors != null && namespaceAncestors.size() != 0) { addStaticField(classGen, NS_ANCESTORS_INDEX_SIG, STATIC_NS_ANCESTORS_ARRAY_FIELD); staticConst.markChunkStart(); il.append(new PUSH(cpg, namespaceAncestors.size())); il.append(new NEWARRAY(BasicType.INT)); int namespaceAncestorsArrayRef = cpg.addFieldref(_className, STATIC_NS_ANCESTORS_ARRAY_FIELD, NS_ANCESTORS_INDEX_SIG); il.append(new PUTSTATIC(namespaceAncestorsArrayRef)); staticConst.markChunkEnd(); for (int i = 0; i < namespaceAncestors.size(); i++) { int ancestor = ((Integer) namespaceAncestors.get(i)).intValue(); staticConst.markChunkStart(); il.append(new GETSTATIC(namespaceAncestorsArrayRef)); il.append(new PUSH(cpg, i)); il.append(new PUSH(cpg, ancestor)); il.append(IASTORE); staticConst.markChunkEnd(); } } // Put the array of indices into the namespace prefix/URI pairs array // into the translet final Vector prefixURIPairsIdx = getXSLTC().getPrefixURIPairsIdx(); if (prefixURIPairsIdx != null && prefixURIPairsIdx.size() != 0) { addStaticField(classGen, PREFIX_URIS_IDX_SIG, STATIC_PREFIX_URIS_IDX_ARRAY_FIELD); staticConst.markChunkStart(); il.append(new PUSH(cpg, prefixURIPairsIdx.size())); il.append(new NEWARRAY(BasicType.INT)); int prefixURIPairsIdxArrayRef = cpg.addFieldref(_className, STATIC_PREFIX_URIS_IDX_ARRAY_FIELD, PREFIX_URIS_IDX_SIG); il.append(new PUTSTATIC(prefixURIPairsIdxArrayRef)); staticConst.markChunkEnd(); for (int i = 0; i < prefixURIPairsIdx.size(); i++) { int idx = ((Integer) prefixURIPairsIdx.get(i)).intValue(); staticConst.markChunkStart(); il.append(new GETSTATIC(prefixURIPairsIdxArrayRef)); il.append(new PUSH(cpg, i)); il.append(new PUSH(cpg, idx)); il.append(IASTORE); staticConst.markChunkEnd(); } } // Put the array of pairs of namespace prefixes and URIs into the // translet final Vector prefixURIPairs = getXSLTC().getPrefixURIPairs(); if (prefixURIPairs != null && prefixURIPairs.size() != 0) { addStaticField(classGen, PREFIX_URIS_ARRAY_SIG, STATIC_PREFIX_URIS_ARRAY_FIELD); staticConst.markChunkStart(); il.append(new PUSH(cpg, prefixURIPairs.size())); il.append(new ANEWARRAY(cpg.addClass(STRING))); int prefixURIPairsRef = cpg.addFieldref(_className, STATIC_PREFIX_URIS_ARRAY_FIELD, PREFIX_URIS_ARRAY_SIG); il.append(new PUTSTATIC(prefixURIPairsRef)); staticConst.markChunkEnd(); for (int i = 0; i < prefixURIPairs.size(); i++) { String prefixOrURI = (String) prefixURIPairs.get(i); staticConst.markChunkStart(); il.append(new GETSTATIC(prefixURIPairsRef)); il.append(new PUSH(cpg, i)); il.append(new PUSH(cpg, prefixOrURI)); 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, org.apache.bcel.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"))); 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))); constructor.markChunkEnd(); constructor.markChunkStart(); 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 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 org.apache.bcel.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, org.apache.bcel.generic.Type.VOID, argTypes, argNames, "topLevel", _className, il, classGen.getConstantPool()); toplevel.addException("org.apache.xalan.xsltc.TransletException"); // Define and initialize 'current' variable with the root node final LocalVariableGen current = toplevel.addLocalVariable("current", org.apache.bcel.generic.Type.INT, null, null); final int setFilter = cpg.addInterfaceMethodref(DOM_INTF, "setFilter", "(Lorg/apache/xalan/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(Constants.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 org.apache.bcel.generic.Type[] argTypes = { Util.getJCRefType(DOM_INTF_SIG), Util.getJCRefType(NODE_ITERATOR_SIG), Util.getJCRefType(TRANSLET_OUTPUT_SIG), org.apache.bcel.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, org.apache.bcel.generic.Type.VOID, argTypes, argNames, "buildKeys", _className, il, classGen.getConstantPool()); buildKeys.addException("org.apache.xalan.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); // Add method to class classGen.addMethod(buildKeys); 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 org.apache.bcel.generic.Type[] argTypes = new org.apache.bcel.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, org.apache.bcel.generic.Type.VOID, argTypes, argNames, "transform", _className, il, classGen.getConstantPool()); transf.addException("org.apache.xalan.xsltc.TransletException"); // Define and initialize current with the root node final LocalVariableGen current = transf.addLocalVariable("current", org.apache.bcel.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, "", "("+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(); 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); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy