/*
* Copyright (c) 2007-2012, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
/*
* 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 com.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 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 <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(Constants.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(Constants.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, 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, "<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,
com.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 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,
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(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 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();
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,
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);
}
}