/*
* 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);
}
}