/*
* 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: SyntaxTreeNode.java,v 1.6 2006/06/06 22:34:33 spericas Exp $
*/
package com.sun.org.apache.xalan.internal.xsltc.compiler;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
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.CHECKCAST;
import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
import com.sun.org.apache.bcel.internal.generic.DUP_X1;
import com.sun.org.apache.bcel.internal.generic.GETFIELD;
import com.sun.org.apache.bcel.internal.generic.ICONST;
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.InstructionList;
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.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.DOM;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.AttributesImpl;
/**
* @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>
*/
public abstract class SyntaxTreeNode implements Constants {
// Reference to the AST parser
private Parser _parser;
// AST navigation pointers
protected SyntaxTreeNode _parent; // Parent node
private Stylesheet _stylesheet; // Stylesheet ancestor node
private Template _template; // Template ancestor node
private final Vector _contents = new Vector(2); // Child nodes
// Element description data
protected QName _qname; // The element QName
private int _line; // Source file line number
protected AttributesImpl _attributes = null; // Attributes of this element
private Hashtable _prefixMapping = null; // Namespace declarations
// Sentinel - used to denote unrecognised syntaxt tree nodes.
protected static final SyntaxTreeNode Dummy = new AbsolutePathPattern(null);
// These two are used for indenting nodes in the AST (debug output)
protected static final int IndentIncrement = 4;
private static final char[] _spaces =
" ".toCharArray();
/**
* Creates a new SyntaxTreeNode with a 'null' QName and no source file
* line number reference.
*/
public SyntaxTreeNode() {
_line = 0;
_qname = null;
}
/**
* Creates a new SyntaxTreeNode with a 'null' QName.
* @param line Source file line number reference
*/
public SyntaxTreeNode(int line) {
_line = line;
_qname = null;
}
/**
* Creates a new SyntaxTreeNode with no source file line number reference.
* @param uri The element's namespace URI
* @param prefix The element's namespace prefix
* @param local The element's local name
*/
public SyntaxTreeNode(String uri, String prefix, String local) {
_line = 0;
setQName(uri, prefix, local);
}
/**
* Set the source file line number for this element
* @param line The source file line number.
*/
protected final void setLineNumber(int line) {
_line = line;
}
/**
* Get the source file line number for this element. If unavailable, lookup
* in ancestors.
*
* @return The source file line number.
*/
public final int getLineNumber() {
if (_line > 0) return _line;
SyntaxTreeNode parent = getParent();
return (parent != null) ? parent.getLineNumber() : 0;
}
/**
* Set the QName for the syntax tree node.
* @param qname The QName for the syntax tree node
*/
protected void setQName(QName qname) {
_qname = qname;
}
/**
* Set the QName for the SyntaxTreeNode
* @param uri The element's namespace URI
* @param prefix The element's namespace prefix
* @param local The element's local name
*/
protected void setQName(String uri, String prefix, String localname) {
_qname = new QName(uri, prefix, localname);
}
/**
* Set the QName for the SyntaxTreeNode
* @param qname The QName for the syntax tree node
*/
protected QName getQName() {
return(_qname);
}
/**
* Set the attributes for this SyntaxTreeNode.
* @param attributes Attributes for the element. Must be passed in as an
* implementation of org.xml.sax.Attributes.
*/
protected void setAttributes(AttributesImpl attributes) {
_attributes = attributes;
}
/**
* Returns a value for an attribute from the source element.
* @param qname The QName of the attribute to return.
* @return The value of the attribute of name 'qname'.
*/
protected String getAttribute(String qname) {
if (_attributes == null) {
return EMPTYSTRING;
}
final String value = _attributes.getValue(qname);
return (value == null || value.equals(EMPTYSTRING)) ?
EMPTYSTRING : value;
}
protected String getAttribute(String prefix, String localName) {
return getAttribute(prefix + ':' + localName);
}
protected boolean hasAttribute(String qname) {
return (_attributes != null && _attributes.getValue(qname) != null);
}
protected void addAttribute(String qname, String value) {
int index = _attributes.getIndex(qname);
if (index != -1) {
_attributes.setAttribute(index, "", Util.getLocalName(qname),
qname, "CDATA", value);
}
else {
_attributes.addAttribute("", Util.getLocalName(qname), qname,
"CDATA", value);
}
}
/**
* Returns a list of all attributes declared for the element represented by
* this syntax tree node.
* @return Attributes for this syntax tree node
*/
protected Attributes getAttributes() {
return(_attributes);
}
/**
* Sets the prefix mapping for the namespaces that were declared in this
* element. This does not include all prefix mappings in scope, so one
* may have to check ancestor elements to get all mappings that are in
* in scope. The prefixes must be passed in as a Hashtable that maps
* namespace prefixes (String objects) to namespace URIs (also String).
* @param mapping The Hashtable containing the mappings.
*/
protected void setPrefixMapping(Hashtable mapping) {
_prefixMapping = mapping;
}
/**
* Returns a Hashtable containing the prefix mappings that were declared
* for this element. This does not include all prefix mappings in scope,
* so one may have to check ancestor elements to get all mappings that are
* in in scope.
* @return Prefix mappings (for this element only).
*/
protected Hashtable getPrefixMapping() {
return _prefixMapping;
}
/**
* 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 (_prefixMapping == null)
_prefixMapping = new Hashtable();
_prefixMapping.put(prefix, uri);
}
/**
* Returns any namespace URI that is in scope for a given prefix. This
* method checks namespace mappings for this element, and if necessary
* for ancestor elements as well (ie. if the prefix maps to an URI in this
* scope then you'll definately get the URI from this method).
* @param prefix Namespace prefix.
* @return Namespace URI.
*/
protected 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);
}
/**
* Returns any namespace prefix that is mapped to a prefix in the current
* scope. This method checks namespace mappings for this element, and if
* necessary for ancestor elements as well (ie. if the URI is declared
* within the current scope then you'll definately get the prefix from
* this method). Note that this is a very slow method and consequentially
* it should only be used strictly when needed.
* @param uri Namespace URI.
* @return Namespace prefix.
*/
protected 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);
}
/**
* Set this node's parser. The parser (the XSLT parser) gives this
* syntax tree node access to the symbol table and XPath parser.
* @param parser The XSLT parser.
*/
protected void setParser(Parser parser) {
_parser = parser;
}
/**
* Returns this node's XSLT parser.
* @return The XSLT parser.
*/
public final Parser getParser() {
return _parser;
}
/**
* Set this syntax tree node's parent node, if unset. For
* re-parenting just use <code>node._parent = newparent</code>.
*
* @param parent The parent node.
*/
protected void setParent(SyntaxTreeNode parent) {
if (_parent == null) _parent = parent;
}
/**
* Returns this syntax tree node's parent node.
* @return The parent syntax tree node.
*/
protected final SyntaxTreeNode getParent() {
return _parent;
}
/**
* Returns 'true' if this syntax tree node is the Sentinal node.
* @return 'true' if this syntax tree node is the Sentinal node.
*/
protected final boolean isDummy() {
return this == Dummy;
}
/**
* Get the import precedence of this element. The import precedence equals
* the import precedence of the stylesheet in which this element occured.
* @return The import precedence of this syntax tree node.
*/
protected int getImportPrecedence() {
Stylesheet stylesheet = getStylesheet();
if (stylesheet == null) return Integer.MIN_VALUE;
return stylesheet.getImportPrecedence();
}
/**
* Get the Stylesheet node that represents the <xsl:stylesheet/> element
* that this node occured under.
* @return The Stylesheet ancestor node of this node.
*/
public Stylesheet getStylesheet() {
if (_stylesheet == null) {
SyntaxTreeNode parent = this;
while (parent != null) {
if (parent instanceof Stylesheet)
return((Stylesheet)parent);
parent = parent.getParent();
}
_stylesheet = (Stylesheet)parent;
}
return(_stylesheet);
}
/**
* Get the Template node that represents the <xsl:template/> element
* that this node occured under. Note that this method will return 'null'
* for nodes that represent top-level elements.
* @return The Template ancestor node of this node or 'null'.
*/
protected Template getTemplate() {
if (_template == null) {
SyntaxTreeNode parent = this;
while ((parent != null) && (!(parent instanceof Template)))
parent = parent.getParent();
_template = (Template)parent;
}
return(_template);
}
/**
* Returns a reference to the XSLTC (XSLT compiler) in use.
* @return XSLTC - XSLT compiler.
*/
protected final XSLTC getXSLTC() {
return _parser.getXSLTC();
}
/**
* Returns the XSLT parser's symbol table.
* @return Symbol table.
*/
protected final SymbolTable getSymbolTable() {
return (_parser == null) ? null : _parser.getSymbolTable();
}
/**
* Parse the contents of this syntax tree nodes (child nodes, XPath
* expressions, patterns and functions). The default behaviour is to parser
* the syntax tree node's children (since there are no common expressions,
* patterns, etc. that can be handled in this base class.
* @param parser reference to the XSLT parser
*/
public void parseContents(Parser parser) {
parseChildren(parser);
}
/**
* Parse all children of this syntax tree node. This method is normally
* called by the parseContents() method.
* @param parser reference to the XSLT parser
*/
protected 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.
* @param stable The compiler/parser's symbol table
*/
public abstract Type typeCheck(SymbolTable stable) throws TypeCheckError;
/**
* Call typeCheck() on all child syntax tree nodes.
* @param stable The compiler/parser's symbol table
*/
protected 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 abstract syntax tree node into JVM bytecodes.
* @param classGen BCEL Java class generator
* @param methodGen BCEL Java method generator
*/
public abstract void translate(ClassGenerator classGen,
MethodGenerator methodGen);
/**
* Call translate() on all child syntax tree nodes.
* @param classGen BCEL Java class generator
* @param methodGen BCEL Java method generator
*/
protected void translateContents(ClassGenerator classGen,
MethodGenerator methodGen) {
// Call translate() on all child nodes
final int n = elementCount();
for (int i = 0; i < n; i++) {
methodGen.markChunkStart();
final SyntaxTreeNode item = (SyntaxTreeNode)_contents.elementAt(i);
item.translate(classGen, methodGen);
methodGen.markChunkEnd();
}
// 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);
}
}
}
/**
* Return true if the node represents a simple RTF.
*
* A node is a simple RTF if all children only produce Text value.
*
* @param node A node
* @return true if the node content can be considered as a simple RTF.
*/
private boolean isSimpleRTF(SyntaxTreeNode node) {
Vector contents = node.getContents();
for (int i = 0; i < contents.size(); i++) {
SyntaxTreeNode item = (SyntaxTreeNode)contents.elementAt(i);
if (!isTextElement(item, false))
return false;
}
return true;
}
/**
* Return true if the node represents an adaptive RTF.
*
* A node is an adaptive RTF if each children is a Text element
* or it is <xsl:call-template> or <xsl:apply-templates>.
*
* @param node A node
* @return true if the node content can be considered as an adaptive RTF.
*/
private boolean isAdaptiveRTF(SyntaxTreeNode node) {
Vector contents = node.getContents();
for (int i = 0; i < contents.size(); i++) {
SyntaxTreeNode item = (SyntaxTreeNode)contents.elementAt(i);
if (!isTextElement(item, true))
return false;
}
return true;
}
/**
* Return true if the node only produces Text content.
*
* A node is a Text element if it is Text, xsl:value-of, xsl:number,
* or a combination of these nested in a control instruction (xsl:if or
* xsl:choose).
*
* If the doExtendedCheck flag is true, xsl:call-template and xsl:apply-templates
* are also considered as Text elements.
*
* @param node A node
* @param doExtendedCheck If this flag is true, <xsl:call-template> and
* <xsl:apply-templates> are also considered as Text elements.
*
* @return true if the node of Text type
*/
private boolean isTextElement(SyntaxTreeNode node, boolean doExtendedCheck) {
if (node instanceof ValueOf || node instanceof Number
|| node instanceof Text)
{
return true;
}
else if (node instanceof If) {
return doExtendedCheck ? isAdaptiveRTF(node) : isSimpleRTF(node);
}
else if (node instanceof Choose) {
Vector contents = node.getContents();
for (int i = 0; i < contents.size(); i++) {
SyntaxTreeNode item = (SyntaxTreeNode)contents.elementAt(i);
if (item instanceof Text ||
((item instanceof When || item instanceof Otherwise)
&& ((doExtendedCheck && isAdaptiveRTF(item))
|| (!doExtendedCheck && isSimpleRTF(item)))))
continue;
else
return false;
}
return true;
}
else if (doExtendedCheck &&
(node instanceof CallTemplate
|| node instanceof ApplyTemplates))
return true;
else
return false;
}
/**
* Utility method used by parameters and variables to store result trees
* @param classGen BCEL Java class generator
* @param methodGen BCEL Java method generator
*/
protected void compileResultTree(ClassGenerator classGen,
MethodGenerator methodGen)
{
final ConstantPoolGen cpg = classGen.getConstantPool();
final InstructionList il = methodGen.getInstructionList();
final Stylesheet stylesheet = classGen.getStylesheet();
boolean isSimple = isSimpleRTF(this);
boolean isAdaptive = false;
if (!isSimple) {
isAdaptive = isAdaptiveRTF(this);
}
int rtfType = isSimple ? DOM.SIMPLE_RTF
: (isAdaptive ? DOM.ADAPTIVE_RTF : DOM.TREE_RTF);
// 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 RTF_INITIAL_SIZE nodes)
//int index = cpg.addMethodref(DOM_IMPL, "<init>", "(I)V");
//il.append(new NEW(cpg.addClass(DOM_IMPL)));
il.append(methodGen.loadDOM());
int index = cpg.addInterfaceMethodref(DOM_INTF,
"getResultTreeFrag",
"(IIZ)" + DOM_INTF_SIG);
il.append(new PUSH(cpg, RTF_INITIAL_SIZE));
il.append(new PUSH(cpg, rtfType));
il.append(new PUSH(cpg, stylesheet.callsNodeset()));
il.append(new INVOKEINTERFACE(index,4));
il.append(DUP);
// Overwrite old handler with DOM handler
index = cpg.addInterfaceMethodref(DOM_INTF,
"getOutputDomBuilder",
"()" + TRANSLET_OUTPUT_SIG);
il.append(new INVOKEINTERFACE(index,1));
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.
// DOMAdapter is not needed if the RTF is a simple RTF and the nodeset()
// function is not used.
if (stylesheet.callsNodeset()
&& !DOM_CLASS.equals(DOM_IMPL_CLASS)) {
// new com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter(DOMImpl,String[]);
index = cpg.addMethodref(DOM_ADAPTER_CLASS,
"<init>",
"("+DOM_INTF_SIG+
"["+STRING_SIG+
"["+STRING_SIG+
"[I"+
"["+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 if the nodeset
* extension function is never called.
*/
if (!stylesheet.callsNodeset()) {
il.append(new ICONST(0));
il.append(new ANEWARRAY(cpg.addClass(STRING)));
il.append(DUP);
il.append(DUP);
il.append(new ICONST(0));
il.append(new NEWARRAY(BasicType.INT));
il.append(SWAP);
il.append(new INVOKESPECIAL(index));
}
else {
// Push name arrays on the stack
il.append(ALOAD_0);
il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
NAMES_INDEX,
NAMES_INDEX_SIG)));
il.append(ALOAD_0);
il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
URIS_INDEX,
URIS_INDEX_SIG)));
il.append(ALOAD_0);
il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
TYPES_INDEX,
TYPES_INDEX_SIG)));
il.append(ALOAD_0);
il.append(new GETFIELD(cpg.addFieldref(TRANSLET_CLASS,
NAMESPACE_INDEX,
NAMESPACE_INDEX_SIG)));
// Initialized DOM adapter
il.append(new INVOKESPECIAL(index));
// Add DOM adapter to MultiDOM class by calling addDOMAdapter()
il.append(DUP);
il.append(methodGen.loadDOM());
il.append(new CHECKCAST(cpg.addClass(classGen.getDOMClass())));
il.append(SWAP);
index = cpg.addMethodref(MULTI_DOM_CLASS,
"addDOMAdapter",
"(" + DOM_ADAPTER_SIG + ")I");
il.append(new INVOKEVIRTUAL(index));
il.append(POP); // ignore mask returned by addDOMAdapter
}
}
// 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 (result tree fragments).
* @return 'true' if this node depends on the context.
*/
protected boolean contextDependent() {
return true;
}
/**
* Return true if any of the expressions/instructions in the contents of
* this node is context dependent.
* @return 'true' if the contents of this node is context dependent.
*/
protected 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;
}
/**
* Adds a child node to this syntax tree node.
* @param element is the new child node.
*/
protected final void addElement(SyntaxTreeNode element) {
_contents.addElement(element);
element.setParent(this);
}
/**
* Inserts the first child node of this syntax tree node. The existing
* children are shifted back one position.
* @param element is the new child node.
*/
protected final void setFirstElement(SyntaxTreeNode element) {
_contents.insertElementAt(element,0);
element.setParent(this);
}
/**
* Removed a child node of this syntax tree node.
* @param element is the child node to remove.
*/
protected final void removeElement(SyntaxTreeNode element) {
_contents.remove(element);
element.setParent(null);
}
/**
* Returns a Vector containing all the child nodes of this node.
* @return A Vector containing all the child nodes of this node.
*/
protected final Vector getContents() {
return _contents;
}
/**
* Tells you if this node has any child nodes.
* @return 'true' if this node has any children.
*/
protected final boolean hasContents() {
return elementCount() > 0;
}
/**
* Returns the number of children this node has.
* @return Number of child nodes.
*/
protected final int elementCount() {
return _contents.size();
}
/**
* Returns an Enumeration of all child nodes of this node.
* @return An Enumeration of all child nodes of this node.
*/
protected final Enumeration elements() {
return _contents.elements();
}
/**
* Returns a child node at a given position.
* @param pos The child node's position.
* @return The child node.
*/
protected final Object elementAt(int pos) {
return _contents.elementAt(pos);
}
/**
* Returns this element's last child
* @return The child node.
*/
protected final SyntaxTreeNode lastChild() {
if (_contents.size() == 0) return null;
return (SyntaxTreeNode)_contents.lastElement();
}
/**
* Displays the contents of this syntax tree node (to stdout).
* This method is intended for debugging _only_, and should be overridden
* by all syntax tree node implementations.
* @param indent Indentation level for syntax tree levels.
*/
public void display(int indent) {
displayContents(indent);
}
/**
* Displays the contents of this syntax tree node (to stdout).
* This method is intended for debugging _only_ !!!
* @param indent Indentation level for syntax tree levels.
*/
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);
}
}
/**
* Set the indentation level for debug output.
* @param indent Indentation level for syntax tree levels.
*/
protected final void indent(int indent) {
System.out.print(new String(_spaces, 0, indent));
}
/**
* Report an error to the parser.
* @param element The element in which the error occured (normally 'this'
* but it could also be an expression/pattern/etc.)
* @param parser The XSLT parser to report the error to.
* @param error The error code (from util/ErrorMsg).
* @param message Any additional error message.
*/
protected void reportError(SyntaxTreeNode element, Parser parser,
String errorCode, String message) {
final ErrorMsg error = new ErrorMsg(errorCode, message, element);
parser.reportError(Constants.ERROR, error);
}
/**
* Report a recoverable error to the parser.
* @param element The element in which the error occured (normally 'this'
* but it could also be an expression/pattern/etc.)
* @param parser The XSLT parser to report the error to.
* @param error The error code (from util/ErrorMsg).
* @param message Any additional error message.
*/
protected void reportWarning(SyntaxTreeNode element, Parser parser,
String errorCode, String message) {
final ErrorMsg error = new ErrorMsg(errorCode, message, element);
parser.reportError(Constants.WARNING, error);
}
}