blob: 4054767fc1615e9590502f82a91e0c50b7764c24 [file] [log] [blame]
/*
* @(#)$Id$
*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2001, Sun
* Microsystems., http://www.sun.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* @author Jacek Ambroziak
* @author Santiago Pericas-Geertsen
* @author G. Todd Miller
* @author Morten Jorensen
* @author Erwin Bolwidt <ejb@klomp.org>
* @author John Howard <JohnH@schemasoft.com>
*
*/
package org.apache.xalan.xsltc.compiler;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Hashtable;
import java.net.URL;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.apache.xalan.xsltc.compiler.util.Type;
import org.apache.xalan.xsltc.compiler.util.ReferenceType;
import de.fub.bytecode.generic.*;
import org.apache.xalan.xsltc.compiler.util.*;
public abstract class SyntaxTreeNode implements Constants {
// Reference to the AST parser
private Parser _parser;
// This node's line number in the input file (not obtainable!!!!)
private final int _line;
// Reference to this node's parent node
protected SyntaxTreeNode _parent;
// Contains all child nodes of this node
private final Vector _contents = new Vector(2);
// The QName of this element (contains uri, prefix and localname)
protected QName _qname;
// The attributes (if any) of this element
protected Attributes _attributes = null;
// The namespace declarations (if any) of this element
private Hashtable _prefixMapping = null;
// Sentinel
public static final SyntaxTreeNode Dummy = new AbsolutePathPattern(null);
// These two are used for indenting nodes in the AST (debug output)
private static final char[] _spaces =
" ".toCharArray();
protected static final int IndentIncrement = 4;
public SyntaxTreeNode() {
_line = 0;
_qname = null;
}
public SyntaxTreeNode(int line) {
_line = line;
_qname = null;
}
public SyntaxTreeNode(String uri, String prefix, String localname) {
_line = 0;
setQName(uri, prefix, localname);
}
public void setQName(QName qname) {
_qname = qname;
}
public void setQName(String uri, String prefix, String localname) {
_qname = new QName(uri, prefix, localname);
}
public QName getQName() {
return(_qname);
}
public void setAttributes(Attributes attributes) {
_attributes = attributes;
}
public String getAttribute(String qname) {
if (_attributes == null)
return(Constants.EMPTYSTRING);
final String value = _attributes.getValue(qname);
if (value == null)
return(Constants.EMPTYSTRING);
else
return(value);
}
public Attributes getAttributes() {
return(_attributes);
}
public void setPrefixMapping(Hashtable mapping) {
_prefixMapping = mapping;
}
public Hashtable getPrefixMapping() {
return _prefixMapping;
}
public void addPrefixMapping(String prefix, String uri) {
if (_prefixMapping == null)
_prefixMapping = new Hashtable();
_prefixMapping.put(prefix, uri);
}
public String lookupNamespace(String prefix) {
// Initialise the output (default is 'null' for undefined)
String uri = null;
// First look up the prefix/uri mapping in our own hashtable...
if (_prefixMapping != null)
uri = (String)_prefixMapping.get(prefix);
// ... but if we can't find it there we ask our parent for the mapping
if ((uri == null) && (_parent != null)) {
uri = _parent.lookupNamespace(prefix);
if ((prefix == Constants.EMPTYSTRING) && (uri == null))
uri = Constants.EMPTYSTRING;
}
// ... and then we return whatever URI we've got.
return(uri);
}
public String lookupPrefix(String uri) {
// Initialise the output (default is 'null' for undefined)
String prefix = null;
// First look up the prefix/uri mapping in our own hashtable...
if ((_prefixMapping != null) &&
(_prefixMapping.contains(uri))) {
Enumeration prefixes = _prefixMapping.keys();
while (prefixes.hasMoreElements()) {
prefix = (String)prefixes.nextElement();
String mapsTo = (String)_prefixMapping.get(prefix);
if (mapsTo.equals(uri)) return(prefix);
}
}
// ... but if we can't find it there we ask our parent for the mapping
else if (_parent != null) {
prefix = _parent.lookupPrefix(uri);
if ((uri == Constants.EMPTYSTRING) && (prefix == null))
prefix = Constants.EMPTYSTRING;
}
return(prefix);
}
public void setParser(Parser parser) {
_parser = parser;
}
protected void setParent(SyntaxTreeNode parent) {
if (_parent == null)
_parent = parent;
}
public final SyntaxTreeNode getParent() {
return _parent;
}
public Stylesheet getStylesheet() {
SyntaxTreeNode parent = this;
while (parent != null) {
if (parent instanceof Stylesheet)
return((Stylesheet)parent);
parent = parent.getParent();
}
return((Stylesheet)parent);
}
public int getImportPrecedence() {
return getStylesheet().getImportPrecedence();
}
protected Template getTemplate() {
SyntaxTreeNode parent = this;
while ((parent != null) && (!(parent instanceof Template)))
parent = parent.getParent();
return((Template)parent);
}
public final boolean isDummy() {
return this == Dummy;
}
public final Parser getParser() {
return _parser;
}
public final XSLTC getXSLTC() {
return _parser.getXSLTC();
}
public final SymbolTable getSymbolTable() {
if (_parser != null)
return _parser.getSymbolTable();
else
return null;
}
/**
* To be overridden in nodes implemented by code outside of applyTemplates
* eg. in Predicates
*/
protected boolean isClosureBoundary() {
return false;
}
/**
* This method is normally overriden by subclasses.
* By default, it parses all the children of <tt>element</tt>.
*/
public void parseContents(Parser parser) {
parseChildren(parser);
}
/**
* Parse all the children of <tt>element</tt>.
* XSLT commands are recognized by the XSLT namespace
*/
public final void parseChildren(Parser parser) {
Vector locals = null; // only create when needed
final int count = _contents.size();
for (int i=0; i<count; i++) {
SyntaxTreeNode child = (SyntaxTreeNode)_contents.elementAt(i);
parser.getSymbolTable().setCurrentNode(child);
child.parseContents(parser);
// if variable or parameter, add it to scope
final QName varOrParamName = updateScope(parser, child);
if (varOrParamName != null) {
if (locals == null) {
locals = new Vector(2);
}
locals.addElement(varOrParamName);
}
}
parser.getSymbolTable().setCurrentNode(this);
// after the last element, remove any locals from scope
if (locals != null) {
final int nLocals = locals.size();
for (int i = 0; i < nLocals; i++) {
parser.removeVariable((QName)locals.elementAt(i));
}
}
}
/**
* Add a node to the current scope and return name of a variable or
* parameter if the node represents a variable or a parameter.
*/
protected QName updateScope(Parser parser, SyntaxTreeNode node) {
if (node instanceof Variable) {
final Variable var = (Variable)node;
parser.addVariable(var);
return var.getName();
}
else if (node instanceof Param) {
final Param param = (Param)node;
parser.addParameter(param);
return param.getName();
}
else {
return null;
}
}
/**
* Type check the children of this node. The type check phase may add
* coercions (CastExpr) to the AST.
*/
public abstract Type typeCheck(SymbolTable stable) throws TypeCheckError;
/**
* Call typeCheck() on every children of this node.
*/
public Type typeCheckContents(SymbolTable stable) throws TypeCheckError {
final int n = elementCount();
for (int i = 0; i < n; i++) {
SyntaxTreeNode item = (SyntaxTreeNode)_contents.elementAt(i);
item.typeCheck(stable);
}
return Type.Void;
}
/**
* Translate this node into JVM bytecodes.
*/
public abstract void translate(ClassGenerator classGen,
MethodGenerator methodGen);
/**
* Call translate() on every children of this node.
*/
public void translateContents(ClassGenerator classGen,
MethodGenerator methodGen) {
final int n = elementCount();
for (int i = 0; i < n; i++) {
final SyntaxTreeNode item = (SyntaxTreeNode)_contents.elementAt(i);
item.translate(classGen, methodGen);
}
/**
* After translation, unmap any registers for any variables/parameters
* that were declared in this scope. Performing this unmapping in the
* same AST scope as the declaration deals with the problems of
* references falling out-of-scope inside the for-each element.
* (the cause of which being 'lazy' register allocation for references)
*/
for (int i = 0; i < n; i++) {
if( _contents.elementAt(i) instanceof VariableBase) {
final VariableBase var = (VariableBase)_contents.elementAt(i);
var.unmapRegister(methodGen);
}
}
}
/**
* Utility method used by parameters and variables to store result trees
*/
public void compileResultTree(ClassGenerator classGen,
MethodGenerator methodGen) {
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
// Save the current handler base on the stack
il.append(methodGen.loadHandler());
final String DOM_CLASS = classGen.getDOMClass();
// Create new instance of DOM class (with 64 nodes)
int index = cpg.addMethodref(DOM_IMPL, "<init>", "(I)V");
il.append(new NEW(cpg.addClass(DOM_IMPL)));
il.append(DUP);
il.append(DUP);
il.append(new PUSH(cpg, 64));
il.append(new INVOKESPECIAL(index));
// Overwrite old handler with DOM handler
index = cpg.addMethodref(DOM_IMPL,
"getOutputDomBuilder",
"()" + TRANSLET_OUTPUT_SIG);
il.append(new INVOKEVIRTUAL(index));
il.append(DUP);
il.append(methodGen.storeHandler());
// Call startDocument on the new handler
il.append(methodGen.startDocument());
// Instantiate result tree fragment
translateContents(classGen, methodGen);
// Call endDocument on the new handler
il.append(methodGen.loadHandler());
il.append(methodGen.endDocument());
// Check if we need to wrap the DOMImpl object in a DOMAdapter object
if (!DOM_CLASS.equals(DOM_IMPL_CLASS)) {
// new org.apache.xalan.xsltc.dom.DOMAdapter(DOMImpl,String[]);
index = cpg.addMethodref(DOM_ADAPTER_CLASS,
"<init>",
"("+DOM_IMPL_SIG+
"["+STRING_SIG+
"["+STRING_SIG+")V");
il.append(new NEW(cpg.addClass(DOM_ADAPTER_CLASS)));
il.append(new DUP_X1());
il.append(SWAP);
// Give the DOM adapter an empty type mapping to start with.
// Type mapping is expensive and will only be done when casting
// a result tree fragment to a node-set.
il.append(new ICONST(0));
il.append(new ANEWARRAY(cpg.addClass(STRING)));
il.append(DUP);
il.append(new INVOKESPECIAL(index)); // leave DOMAdapter on stack
// Must we wrap the DOMAdapter object in a MultiDOM object?
/* Not needed with new design using DOM interface instead of
referencing DOM classes directly
if (DOM_CLASS.equals(MULTI_DOM_CLASS)) {
// new org.apache.xalan.xsltc.dom.MultiDOM(DOMAdapter);
index = cpg.addMethodref(MULTI_DOM_CLASS, "<init>",
"("+DOM_INTF_SIG+")V");
il.append(new NEW(cpg.addClass(MULTI_DOM_CLASS)));
il.append(new DUP_X1());
il.append(SWAP);
il.append(new INVOKESPECIAL(index)); // leave MultiDOM on stack
}
*/
}
// Restore old handler base from stack
il.append(SWAP);
il.append(methodGen.storeHandler());
}
/**
* Returns true if this expression/instruction depends on the context. By
* default, every expression/instruction depends on the context unless it
* overrides this method. Currently used to determine if result trees are
* compiled using procedures or little DOMs.
*/
public boolean contextDependent() {
return true;
}
/**
* Return true if any of the expressions/instructions in the contents of
* this node is context dependent.
*/
public boolean dependentContents() {
final int n = elementCount();
for (int i = 0; i < n; i++) {
final SyntaxTreeNode item = (SyntaxTreeNode)_contents.elementAt(i);
if (item.contextDependent()) {
return true;
}
}
return false;
}
public final void addElement(SyntaxTreeNode element) {
_contents.addElement(element);
element.setParent(this);
}
public final void setFirstElement(SyntaxTreeNode element) {
_contents.insertElementAt(element,0);
element.setParent(this);
}
public final void removeElement(SyntaxTreeNode element) {
_contents.remove(element);
element.setParent(null);
}
public final Vector getContents() {
return _contents;
}
public final boolean hasContents() {
return elementCount() > 0;
}
public final int elementCount() {
return _contents.size();
}
public final Enumeration elements() {
return _contents.elements();
}
public final Object elementAt(int i) {
return _contents.elementAt(i);
}
public void display(int indent) {
displayContents(indent);
}
protected void displayContents(int indent) {
final int n = elementCount();
for (int i = 0; i < n; i++) {
SyntaxTreeNode item = (SyntaxTreeNode)_contents.elementAt(i);
item.display(indent);
}
}
protected final void indent(int indent) {
System.out.print(new String(_spaces, 0, indent));
}
public final int getLineNumber() {
return _line;
}
protected void reportError(SyntaxTreeNode element, Parser parser,
int errorCode, String message) {
final ErrorMsg error = new ErrorMsg(errorCode, message, this);
parser.reportError(Constants.ERROR, error);
}
protected void reportWarning(SyntaxTreeNode element, Parser parser,
int errorCode, String message) {
final ErrorMsg error = new ErrorMsg(errorCode, message, this);
parser.reportError(Constants.WARNING, error);
}
}