blob: 8721aab343146ca271e27e061b5bbbe1216f5b50 [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 Morten Jorgensen
* @author Douglas Sellers <douglasjsellers@hotmail.com>
*
*/
package org.apache.xalan.xsltc.dom;
import java.io.Externalizable;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.IOException;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Stack;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.DOMException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Document;
import org.xml.sax.*;
import org.xml.sax.ext.*;
import org.xml.sax.helpers.AttributesImpl;
import org.apache.xalan.xsltc.*;
import org.apache.xalan.xsltc.util.IntegerArray;
import org.apache.xalan.xsltc.runtime.BasisLibrary;
import org.apache.xalan.xsltc.runtime.SAXAdapter;
import org.apache.xalan.xsltc.runtime.Hashtable;
public final class DOMImpl implements DOM, Externalizable {
// empty String for null attribute values
private final static String EMPTYSTRING = "";
// empty iterator to be returned when there are no children
private final static NodeIterator EMPTYITERATOR = new NodeIterator() {
public NodeIterator reset() { return this; }
public NodeIterator setStartNode(int node) { return this; }
public int next() { return NULL; }
public void setMark() {}
public void gotoMark() {}
public int getLast() { return 0; }
public int getPosition() { return 0; }
public NodeIterator cloneIterator() { return this; }
public boolean isReverse() { return false; }
public NodeIterator resetOnce() { return this; }
public NodeIterator includeSelf() { return this; }
public void setRestartable(boolean isRestartable) { }
};
// Contains the number of nodes and attribute nodes in the tree
private int _treeNodeLimit;
private int _firstAttributeNode;
// Node-to-type, type-to-name, and name-to-type mappings
private short[] _type;
private Hashtable _types = null;
private String[] _namesArray;
// Tree navigation arrays
private int[] _parent;
private int[] _nextSibling;
private int[] _offsetOrChild; // Serves two purposes !!!
private int[] _lengthOrAttr; // Serves two purposes !!!
// Holds contents of text/comment nodes and attribute values
private char[] _text;
// Namespace related stuff
private String[] _uriArray;
private String[] _prefixArray;
private short[] _namespace;
private short[] _prefix;
private Hashtable _nsIndex = new Hashtable();
// Tracks which textnodes are whitespaces and which are not
private BitArray _whitespace; // takes xml:space into acc.
// Tracks which textnodes are not escaped
private BitArray _dontEscape = null;
// The URI to this document
private String _documentURI = null;
static private int _documentURIIndex = 0;
// Support for access/navigation through org.w3c.dom API
private Node[] _nodes;
private NodeList[] _nodeLists;
private static NodeList EmptyNodeList;
private static NamedNodeMap EmptyNamedNodeMap;
private final static String XML_LANG_ATTRIBUTE =
"http://www.w3.org/XML/1998/namespace:@lang";
/**
* Define the origin of the document from which the tree was built
*/
public void setDocumentURI(String uri) {
_documentURI = uri;
}
/**
* Returns the origin of the document from which the tree was built
*/
public String getDocumentURI() {
return (_documentURI != null) ? _documentURI : "rtf" + _documentURIIndex++;
}
public String getDocumentURI(int node) {
return getDocumentURI();
}
public void setupMapping(String[] names, String[] namespaces) {
// This method only has a function in DOM adapters
}
/**
* Lookup a namespace URI from a prefix starting at node. This method
* is used in the execution of xsl:element when the prefix is not known
* at compile time.
*/
public String lookupNamespace(int node, String prefix)
throws TransletException
{
int anode, nsnode;
final AncestorIterator ancestors = new AncestorIterator();
if (isElement(node)) {
ancestors.includeSelf();
}
ancestors.setStartNode(node);
while ((anode = ancestors.next()) != NULL) {
final NodeIterator namespaces =
new NamespaceIterator().setStartNode(anode);
while ((nsnode = namespaces.next()) != NULL) {
if (_prefixArray[_prefix[nsnode]].equals(prefix)) {
return getNodeValue(nsnode);
}
}
}
// TODO: Internationalization?
throw new TransletException("Namespace prefix '" + prefix + "' is undeclared.");
}
/**
* Returns 'true' if a specific node is an element (of any type)
*/
public boolean isElement(final int node) {
final int type = _type[node];
return ((node < _firstAttributeNode) && (type >= NTYPES));
}
/**
* Returns 'true' if a specific node is an element (of any type)
*/
public boolean isAttribute(final int node) {
final int type = _type[node];
return ((node >= _firstAttributeNode) && (type >= NTYPES));
}
/**
* Returns the number of nodes in the tree (used for indexing)
*/
public int getSize() {
return(_type.length);
}
/**
* Part of the DOM interface - no function here.
*/
public void setFilter(StripFilter filter) { }
/**
* Returns true if node1 comes before node2 in document order
*/
public boolean lessThan(int node1, int node2) {
// Hack for ordering attribute nodes
if (node1 >= _firstAttributeNode) node1 = _parent[node1];
if (node2 >= _firstAttributeNode) node2 = _parent[node2];
if ((node2 < _treeNodeLimit) && (node1 < node2))
return(true);
else
return(false);
}
/**
* Create an org.w3c.dom.Node from a node in the tree
*/
public Node makeNode(int index) {
if (_nodes == null) {
_nodes = new Node[_type.length];
}
return _nodes[index] != null
? _nodes[index]
: (_nodes[index] = new NodeImpl(index));
}
/**
* Create an org.w3c.dom.Node from a node in an iterator
* The iterator most be started before this method is called
*/
public Node makeNode(NodeIterator iter) {
return makeNode(iter.next());
}
/**
* Create an org.w3c.dom.NodeList from a node in the tree
*/
public NodeList makeNodeList(int index) {
if (_nodeLists == null) {
_nodeLists = new NodeList[_type.length];
}
return _nodeLists[index] != null
? _nodeLists[index]
: (_nodeLists[index] = new NodeListImpl(index));
}
/**
* Create an org.w3c.dom.NodeList from a node iterator
* The iterator most be started before this method is called
*/
public NodeList makeNodeList(NodeIterator iter) {
return new NodeListImpl(iter);
}
/**
* Create an empty org.w3c.dom.NodeList
*/
private NodeList getEmptyNodeList() {
return EmptyNodeList != null
? EmptyNodeList
: (EmptyNodeList = new NodeListImpl(new int[0]));
}
/**
* Create an empty org.w3c.dom.NamedNodeMap
*/
private NamedNodeMap getEmptyNamedNodeMap() {
return EmptyNamedNodeMap != null
? EmptyNamedNodeMap
: (EmptyNamedNodeMap = new NamedNodeMapImpl(new int[0]));
}
/**
* Exception thrown by methods in inner classes implementing
* various org.w3c.dom interfaces (below)
*/
private final class NotSupportedException extends DOMException {
public NotSupportedException() {
super(NOT_SUPPORTED_ERR, "modification not supported");
}
}
/**************************************************************
* Implementation of org.w3c.dom.NodeList
*/
private final class NodeListImpl implements NodeList {
private final int[] _nodes;
public NodeListImpl(int node) {
_nodes = new int[1];
_nodes[0] = node;
}
public NodeListImpl(int[] nodes) {
_nodes = nodes;
}
public NodeListImpl(NodeIterator iter) {
final IntegerArray list = new IntegerArray();
int node;
while ((node = iter.next()) != NodeIterator.END) {
list.add(node);
}
_nodes = list.toIntArray();
}
public int getLength() {
return _nodes.length;
}
public Node item(int index) {
return makeNode(_nodes[index]);
}
}
/**************************************************************
* Implementation of org.w3c.dom.NamedNodeMap
*/
private final class NamedNodeMapImpl implements NamedNodeMap {
private final int[] _nodes;
public NamedNodeMapImpl(int[] nodes) {
_nodes = nodes;
}
public int getLength() {
return _nodes.length;
}
public Node getNamedItem(String name) {
for (int i = 0; i < _nodes.length; i++) {
if (name.equals(getNodeName(_nodes[i]))) {
return makeNode(_nodes[i]);
}
}
return null;
}
public Node item(int index) {
return makeNode(_nodes[index]);
}
public Node removeNamedItem(String name) {
throw new NotSupportedException();
}
public Node setNamedItem(Node node) {
throw new NotSupportedException();
}
public Node getNamedItemNS(String uri, String local) {
return(getNamedItem(uri+':'+local));
}
public Node setNamedItemNS(Node node) {
throw new NotSupportedException();
}
public Node removeNamedItemNS(String uri, String local) {
throw new NotSupportedException();
}
}
/**************************************************************
* Implementation of org.w3c.dom.Node
*/
private final class NodeImpl implements Node {
private final int _index;
public NodeImpl(int index) {
_index = index;
}
public short getNodeType() {
switch (_type[_index]) {
case ROOT:
return Node.DOCUMENT_NODE;
case TEXT:
return Node.TEXT_NODE;
case PROCESSING_INSTRUCTION:
return Node.PROCESSING_INSTRUCTION_NODE;
case COMMENT:
return Node.COMMENT_NODE;
default:
return _index < _firstAttributeNode
? Node.ELEMENT_NODE : Node.ATTRIBUTE_NODE;
}
}
public Node getParentNode() {
final int parent = getParent(_index);
return parent > NULL ? makeNode(parent) : null;
}
public Node appendChild(Node node) throws DOMException {
throw new NotSupportedException();
}
public Node cloneNode(boolean deep) {
throw new NotSupportedException();
}
public NamedNodeMap getAttributes() {
if (getNodeType() == Node.ELEMENT_NODE) {
int attribute = _lengthOrAttr[_index];
// Skip attribute nodes
while (_type[attribute] == NAMESPACE) {
attribute = _nextSibling[attribute];
}
if (attribute != NULL) {
final IntegerArray attributes = new IntegerArray(4);
do {
attributes.add(attribute);
}
while ((attribute = _nextSibling[attribute]) != 0);
return new NamedNodeMapImpl(attributes.toIntArray());
}
else {
return getEmptyNamedNodeMap();
}
}
else {
return null;
}
}
public NodeList getChildNodes() {
if (hasChildNodes()) {
final IntegerArray children = new IntegerArray(8);
int child = _offsetOrChild[_index];
do {
children.add(child);
}
while ((child = _nextSibling[child]) != 0);
return new NodeListImpl(children.toIntArray());
}
else {
return getEmptyNodeList();
}
}
public Node getFirstChild() {
return hasChildNodes()
? makeNode(_offsetOrChild[_index])
: null;
}
public Node getLastChild() {
return hasChildNodes()
? makeNode(lastChild(_index))
: null;
}
public Node getNextSibling() {
final int next = _nextSibling[_index];
return next != 0 ? makeNode(next) : null;
}
public String getNodeName() {
switch (_type[_index]) {
case ROOT:
return "#document";
case TEXT:
return "#text";
case PROCESSING_INSTRUCTION:
return "#pi";
case COMMENT:
return "#comment";
default:
return DOMImpl.this.getNodeName(_index);
}
}
public String getNodeValue() throws DOMException {
return DOMImpl.this.getNodeValue(_index);
}
public Document getOwnerDocument() {
return null;
}
//??? how does it work with attributes
public Node getPreviousSibling() {
int node = _parent[_index];
if (node > NULL) {
int prev = -1;
node = _offsetOrChild[node];
while (node != _index) {
node = _nextSibling[prev = node];
}
if (prev != -1) {
return makeNode(prev);
}
}
return null;
}
public boolean hasChildNodes() {
switch (getNodeType()) {
case Node.ELEMENT_NODE:
case Node.DOCUMENT_NODE:
return _offsetOrChild[_index] != 0;
default:
return false;
}
}
public Node insertBefore(Node n1, Node n2) throws DOMException {
throw new NotSupportedException();
}
public Node removeChild(Node n) throws DOMException {
throw new NotSupportedException();
}
public Node replaceChild(Node n1, Node n2) throws DOMException {
throw new NotSupportedException();
}
public void setNodeValue(String s) throws DOMException {
throw new NotSupportedException();
}
public void normalize() {
throw new NotSupportedException();
}
public boolean isSupported(String feature, String version) {
return false;
}
public String getNamespaceURI() {
return _uriArray[_namespace[_type[_index] - NTYPES]];
}
public String getPrefix() {
return _prefixArray[_prefix[_index]];
}
public void setPrefix(String prefix) {
throw new NotSupportedException();
}
public String getLocalName() {
return DOMImpl.this.getLocalName(_index);
}
public boolean hasAttributes() {
int attribute = _lengthOrAttr[_index];
while (_type[attribute] == NAMESPACE) {
attribute = _nextSibling[attribute];
}
return (attribute != NULL);
}
}
// A single copy (cache) of ElementFilter
private Filter _elementFilter;
/**
* Returns a filter that lets only element nodes through
*/
private Filter getElementFilter() {
if (_elementFilter == null) {
_elementFilter = new Filter() {
public boolean test(int node) {
return isElement(node);
}
};
}
return _elementFilter;
}
/**
* Implementation of a filter that only returns nodes of a
* certain type (instanciate through getTypeFilter()).
*/
private final class TypeFilter implements Filter {
private final int _nodeType;
public TypeFilter(int type) {
_nodeType = type;
}
public boolean test(int node) {
return _type[node] == _nodeType;
}
}
/**
* Returns a node type filter (implementation of Filter)
*/
public Filter getTypeFilter(int type) {
return new TypeFilter(type);
}
/**************************************************************
* Iterator that returns all children of a given node
*/
private final class ChildrenIterator extends NodeIteratorBase {
// child to return next
private int _currentChild;
private int _last = -1;
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
if (node >= _firstAttributeNode) node = NULL;
if (node != _startNode) _last = -1;
_startNode = node;
if (_includeSelf) {
_currentChild = -1;
}
else {
if (hasChildren(node))
_currentChild = _offsetOrChild[node];
else
_currentChild = END;
}
return resetPosition();
}
return this;
}
public int next() {
int node = _currentChild;
if (_includeSelf) {
if (node == -1) {
node = _startNode;
if (hasChildren(node))
_currentChild = _offsetOrChild[node];
else
_currentChild = END;
// IMPORTANT: The start node (parent of all children) is
// returned, but the node position counter (_position)
// should not be increased, so returnNode() is not called
return node;
}
}
_currentChild = _nextSibling[node];
return returnNode(node);
}
public void setMark() {
_markedNode = _currentChild;
}
public void gotoMark() {
_currentChild = _markedNode;
}
public int getLast() {
if (_last == -1) {
_last = 1;
int node = _offsetOrChild[_startNode];
while ((node = _nextSibling[node]) != END) _last++;
}
return(_last);
}
} // end of ChildrenIterator
/**************************************************************
* Iterator that returns the parent of a given node
*/
private final class ParentIterator extends NodeIteratorBase {
// candidate parent node
private int _node;
private int _nodeType = -1;
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
_node = _parent[_startNode = node];
return resetPosition();
}
return this;
}
public NodeIterator setNodeType(final int type) {
_nodeType = type;
return this;
}
public int next() {
int result = _node;
if ((_nodeType != -1) && (_type[_node] != _nodeType))
result = END;
else
result = _node;
_node = END;
return returnNode(result);
}
public void setMark() {
_markedNode = _node;
}
public void gotoMark() {
_node = _markedNode;
}
} // end of ParentIterator
/**************************************************************
* Iterator that returns children of a given type for a given node.
* The functionality chould be achieved by putting a filter on top
* of a basic child iterator, but a specialised iterator is used
* for efficiency (both speed and size of translet).
*/
private final class TypedChildrenIterator extends NodeIteratorBase {
private int _nodeType;
// node to consider next
private int _currentChild;
public TypedChildrenIterator(int nodeType) {
_nodeType = nodeType;
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
if (node >= _firstAttributeNode) node = NULL;
_currentChild = hasChildren(node)
? _offsetOrChild[_startNode = node] : END;
return resetPosition();
}
return this;
}
public NodeIterator cloneIterator() {
try {
final TypedChildrenIterator clone =
(TypedChildrenIterator)super.clone();
clone._nodeType = _nodeType;
clone.setRestartable(false);
return clone.reset();
}
catch (CloneNotSupportedException e) {
BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
e.toString());
return null;
}
}
public NodeIterator reset() {
if (hasChildren(_startNode))
_currentChild = _offsetOrChild[_startNode];
else
_currentChild = END;
return resetPosition();
}
public int next() {
for (int node = _currentChild; node != END;
node = _nextSibling[node]) {
if (_type[node] == _nodeType) {
_currentChild = _nextSibling[node];
return returnNode(node);
}
}
return END;
}
public void setMark() {
_markedNode = _currentChild;
}
public void gotoMark() {
_currentChild = _markedNode;
}
} // end of TypedChildrenIterator
/**************************************************************
* Iterator that returns children within a given namespace for a
* given node. The functionality chould be achieved by putting a
* filter on top of a basic child iterator, but a specialised
* iterator is used for efficiency (both speed and size of translet).
*/
private final class NamespaceChildrenIterator extends NodeIteratorBase {
private final int _nsType;
private int _currentChild;
public NamespaceChildrenIterator(final int type) {
_nsType = type;
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
if (node >= _firstAttributeNode) node = NULL;
_currentChild = hasChildren(node)
? _offsetOrChild[_startNode = node] : END;
return resetPosition();
}
return this;
}
public int next() {
for (int node = _currentChild; node != END;
node = _nextSibling[node]) {
if (getNamespaceType(node) == _nsType) {
_currentChild = _nextSibling[node];
return returnNode(node);
}
}
return END;
}
public void setMark() {
_markedNode = _currentChild;
}
public void gotoMark() {
_currentChild = _markedNode;
}
} // end of TypedChildrenIterator
/**************************************************************
* Iterator that returns attributes within a given namespace for a node.
*/
private final class NamespaceAttributeIterator extends NodeIteratorBase {
private final int _nsType;
private int _attribute;
public NamespaceAttributeIterator(int nsType) {
super();
_nsType = nsType;
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
for (node = _lengthOrAttr[_startNode = node];
node != NULL && getNamespaceType(node) != _nsType;
node = _nextSibling[node]);
_attribute = node;
return resetPosition();
}
return this;
}
public int next() {
final int save = _attribute;
int node = save;
do {
_attribute = _nextSibling[_attribute];
} while(_type[_attribute] == NAMESPACE);
for (node = _lengthOrAttr[_startNode = node];
node != NULL && getNamespaceType(node) != _nsType;
node = _nextSibling[node]);
_attribute = node;
return returnNode(save);
}
public void setMark() {
_markedNode = _attribute;
}
public void gotoMark() {
_attribute = _markedNode;
}
} // end of TypedChildrenIterator
/**************************************************************
* Iterator that returns all siblings of a given node.
*/
private class FollowingSiblingIterator extends NodeIteratorBase {
private int _node;
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
if (node >= _firstAttributeNode) node = NULL;
_node = _startNode = node;
return resetPosition();
}
return this;
}
public int next() {
return returnNode(_node = _nextSibling[_node]);
}
public void setMark() {
_markedNode = _node;
}
public void gotoMark() {
_node = _markedNode;
}
} // end of FollowingSiblingIterator
/**************************************************************
* Iterator that returns all following siblings of a given node.
*/
private final class TypedFollowingSiblingIterator
extends FollowingSiblingIterator {
private final int _nodeType;
public TypedFollowingSiblingIterator(int type) {
_nodeType = type;
}
public int next() {
int node;
while ((node = super.next()) != NULL) {
if (_type[node] == _nodeType) return(node);
_position--;
}
return END;
}
} // end of TypedFollowingSiblingIterator
/**************************************************************
* Iterator that returns attribute nodes (of what nodes?)
*/
private final class AttributeIterator extends NodeIteratorBase {
private int _attribute;
// assumes caller will pass element nodes
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
if (isElement(node)) {
_attribute = _lengthOrAttr[_startNode = node];
// Skip namespace nodes
while (_type[_attribute] == NAMESPACE) {
_attribute = _nextSibling[_attribute];
}
}
else {
_attribute = NULL;
}
return resetPosition();
}
return this;
}
public int next() {
final int node = _attribute;
_attribute = _nextSibling[_attribute];
return returnNode(node);
}
public void setMark() {
_markedNode = _attribute;
}
public void gotoMark() {
_attribute = _markedNode;
}
} // end of AttributeIterator
/**************************************************************
* Iterator that returns attribute nodes of a given type
*/
private final class TypedAttributeIterator extends NodeIteratorBase {
private final int _nodeType;
private int _attribute;
public TypedAttributeIterator(int nodeType) {
_nodeType = nodeType;
}
// assumes caller will pass element nodes
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
for (node = _lengthOrAttr[_startNode = node];
node != NULL && _type[node] != _nodeType;
node = _nextSibling[node]);
_attribute = node;
return resetPosition();
}
return this;
}
public NodeIterator reset() {
int node = _startNode;
for (node = _lengthOrAttr[node];
node != NULL && _type[node] != _nodeType;
node = _nextSibling[node]);
_attribute = node;
return resetPosition();
}
public int next() {
final int node = _attribute;
_attribute = NULL; // singleton iterator
return returnNode(node);
}
public void setMark() {
_markedNode = _attribute;
}
public void gotoMark() {
_attribute = _markedNode;
}
} // end of TypedAttributeIterator
/**************************************************************
* Iterator that returns namespace nodes
*/
private class NamespaceIterator extends NodeIteratorBase {
protected int _node;
protected int _ns;
// assumes caller will pass element nodes
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
if (isElement(node)) {
_startNode = _node = node;
_ns = _lengthOrAttr[_node];
while ((_ns != DOM.NULL) && (_type[_ns] != NAMESPACE)) {
_ns = _nextSibling[_ns];
}
}
else {
_ns = DOM.NULL;
}
return resetPosition();
}
return this;
}
public int next() {
while (_node != NULL) {
final int node = _ns;
_ns = _nextSibling[_ns];
while ((_ns == DOM.NULL) && (_node != DOM.NULL)) {
_node = _parent[_node];
_ns = _lengthOrAttr[_node];
while ((_ns != DOM.NULL) && (_type[_ns] != NAMESPACE)) {
_ns = _nextSibling[_ns];
}
}
if (_type[node] == NAMESPACE)
return returnNode(node);
}
return NULL;
}
public void setMark() {
_markedNode = _ns;
}
public void gotoMark() {
_ns = _markedNode;
}
} // end of NamespaceIterator
/**************************************************************
* Iterator that returns namespace nodes
*/
private final class TypedNamespaceIterator extends NamespaceIterator {
final int _uriType;
public TypedNamespaceIterator(int type) {
_uriType = type;
}
public int next() {
int node;
while ((node = _ns) != DOM.NULL) {
_ns = _nextSibling[_ns];
while ((_ns == DOM.NULL) && (_node != DOM.NULL)) {
_node = _parent[_node];
_ns = _lengthOrAttr[_node];
while ((_ns != DOM.NULL) && (_type[_ns] != NAMESPACE)) {
_ns = _nextSibling[_ns];
}
}
if (_prefix[node] == _uriType) return returnNode(node);
}
return DOM.NULL;
}
} // end of AttributeIterator
/**************************************************************
* Iterator that returns preceding siblings of a given node
*/
private class PrecedingSiblingIterator extends NodeIteratorBase {
private int _node;
private int _mom;
public boolean isReverse() {
return true;
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
if (node >= _firstAttributeNode) node = NULL;
int tmp = NULL;
_startNode = node;
_mom = _parent[node];
_node = _offsetOrChild[_mom];
while ((_node != node) && (_node != NULL)) {
tmp = _node;
_node = _nextSibling[_node];
}
_node = tmp;
return resetPosition();
}
return this;
}
public int next() {
// Return NULL if end already reached
if (_node == NULL) return NULL;
int current = _offsetOrChild[_mom];
// Otherwise find the next preceeding sibling
int last = NULL;
while ((current != _node) && (current != NULL)) {
last = current;
current = _nextSibling[current];
}
current = _node;
_node = last;
return returnNode(current);
}
public void setMark() {
_markedNode = _node;
}
public void gotoMark() {
_node = _markedNode;
}
} // end of PrecedingSiblingIterator
/**************************************************************
* Iterator that returns preceding siblings of a given type for
* a given node
*/
private final class TypedPrecedingSiblingIterator
extends PrecedingSiblingIterator {
private final int _nodeType;
public TypedPrecedingSiblingIterator(int type) {
_nodeType = type;
}
public int next() {
int node;
while ((node = super.next()) != NULL && _type[node] != _nodeType)
_position--;
return(node);
}
} // end of PrecedingSiblingIterator
/**************************************************************
* Iterator that returns preceding nodes of a given node.
* This includes the node set {root+1, start-1}, but excludes
* all ancestors.
*/
private class PrecedingIterator extends NodeIteratorBase {
private int _node = 0;
private int _mom = 0;
public boolean isReverse() {
return true;
}
public NodeIterator cloneIterator() {
try {
final PrecedingIterator clone =
(PrecedingIterator)super.clone();
clone.setRestartable(false);
return clone.reset();
}
catch (CloneNotSupportedException e) {
BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
e.toString());
return null;
}
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
if (node >= _firstAttributeNode) node = _parent[node];
_node = _startNode = node;
_mom = _parent[_startNode];
return resetPosition();
}
return this;
}
public int next() {
while (--_node > ROOTNODE) {
if (_node < _mom) _mom = _parent[_mom];
if (_node != _mom) return returnNode(_node);
}
return(NULL);
}
// redefine NodeIteratorBase's reset
public NodeIterator reset() {
_node = _startNode;
_mom = _parent[_startNode];
return resetPosition();
}
public void setMark() {
_markedNode = _node;
}
public void gotoMark() {
_node = _markedNode;
}
} // end of PrecedingIterator
/**************************************************************
* Iterator that returns preceding nodes of agiven type for a
* given node. This includes the node set {root+1, start-1}, but
* excludes all ancestors.
*/
private final class TypedPrecedingIterator extends PrecedingIterator {
private final int _nodeType;
public TypedPrecedingIterator(int type) {
_nodeType = type;
}
public int next() {
int node;
while ((node = super.next()) != NULL && _type[node] != _nodeType)
_position--;
return node;
}
} // end of TypedPrecedingIterator
/**************************************************************
* Iterator that returns following nodes of for a given node.
*/
private class FollowingIterator extends NodeIteratorBase {
// _node precedes search for next
protected int _node;
public NodeIterator setStartNode(int node) {
int skip = 0;
if (_isRestartable) {
if (node >= _firstAttributeNode) {
skip = 1;
node = _parent[node];
int child = _offsetOrChild[node];
if (child != NULL) node = child;
}
_startNode = node;
// find rightmost descendant (or self)
int current;
while ((node = lastChild(current = node)) != NULL) { }
_node = current - skip;
// _node precedes possible following(node) nodes
return resetPosition();
}
return this;
}
public int next() {
final int node = _node + 1;
return node < _firstAttributeNode ? returnNode(_node = node) : NULL;
}
public void setMark() {
_markedNode = _node;
}
public void gotoMark() {
_node = _markedNode;
}
} // end of FollowingIterator
/**************************************************************
* Iterator that returns following nodes of a given type for a given node.
*/
private final class TypedFollowingIterator extends FollowingIterator {
private final int _nodeType;
public TypedFollowingIterator(int type) {
_nodeType = type;
}
public int next() {
int node;
while ((node = super.next()) != NULL) {
if (_type[node] == _nodeType) return(node);
_position--;
}
return END;
}
} // end of TypedFollowingIterator
/**************************************************************
* Iterator that returns the ancestors of a given node.
* The nodes are returned in reverse document order, so you
* get the context node (or its parent node) first, and the
* root node in the very, very end.
*/
private class AncestorIterator extends NodeIteratorBase {
protected int _index;
protected int _last = -1;
public final boolean isReverse() {
return true;
}
public int getLast() {
if (_last > -1) return _last;
int count = 1;
int node = _startNode;
while ((node = _parent[node]) != ROOT) count++;
_last = count;
return(count);
}
public NodeIterator cloneIterator() {
try {
final AncestorIterator clone = (AncestorIterator)super.clone();
clone.setRestartable(false); // must set to false for any clone
clone._startNode = _startNode;
return clone.reset();
}
catch (CloneNotSupportedException e) {
BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
e.toString());
return null;
}
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
_last = -1;
if (node >= _firstAttributeNode)
_startNode = node = _parent[node];
else if (_includeSelf)
_startNode = node;
else
_startNode = _parent[node];
_index = _startNode;
return resetPosition();
}
return this;
}
public NodeIterator reset() {
_index = _startNode;
return resetPosition();
}
public int next() {
if (_index >= 0) {
int bob = _index;
if (_index == 0)
_index = -1;
else
_index = _parent[_index];
return returnNode(bob);
}
return(NULL);
}
public void setMark() {
_markedNode = _index;
}
public void gotoMark() {
_index = _markedNode;
}
} // end of AncestorIterator
/**************************************************************
* Typed iterator that returns the ancestors of a given node.
*/
private final class TypedAncestorIterator extends AncestorIterator {
private final int _nodeType;
public TypedAncestorIterator(int type) {
_nodeType = type;
}
public int next() {
int node;
while ((node = super.next()) != NULL) {
if (_type[node] == _nodeType) return(node);
_position--;
}
return(NULL);
}
public int getLast() {
if (_last > -1) return _last;
int count = 1;
int node = _startNode;
do {
if (_type[node] == _nodeType) count++;
} while ((node = _parent[node]) != ROOT);
_last = count;
return(count);
}
} // end of TypedAncestorIterator
/**************************************************************
* Iterator that returns the descendants of a given node.
*/
private class DescendantIterator extends NodeIteratorBase {
// _node precedes search for next
protected int _node;
// first node outside descendant range
protected int _limit;
public NodeIterator setStartNode(int node) {
_startNode = node;
if (_isRestartable) {
_node = _startNode = _includeSelf ? node - 1 : node;
// no descendents if no children
if (hasChildren(node) == false) {
// set _limit to match next()'s criteria for end
_limit = node + 1;
}
// find leftmost descendant of next sibling
else if ((node = _nextSibling[node]) == 0) {
// no next sibling, array end is the limit
_limit = _treeNodeLimit;
}
else {
_limit = leftmostDescendant(node);
}
return resetPosition();
}
return this;
}
public int next() {
while (++_node < _limit) {
if (_type[_node] > TEXT) {
return(returnNode(_node));
}
}
return(NULL);
}
public void setMark() {
_markedNode = _node;
}
public void gotoMark() {
_node = _markedNode;
}
} // end of DescendantIterator
/**************************************************************
* Typed iterator that returns the descendants of a given node.
*/
private final class TypedDescendantIterator extends DescendantIterator {
private final int _nodeType;
public TypedDescendantIterator(int nodeType) {
_nodeType = nodeType;
}
public int next() {
final int limit = _limit;
final int type = _nodeType;
int node = _node + 1; // start search w/ next
// while condition == which nodes to skip
// iteration stops when at end or node w/ desired type
while (node < limit && _type[node] != type) {
++node;
}
return node < limit ? returnNode(_node = node) : NULL;
}
} // end of TypedDescendantIterator
/**************************************************************
* Iterator that returns the descendants of a given node.
*/
private class NthDescendantIterator extends DescendantIterator {
final NodeIterator _source;
final int _pos;
final int _ourtype;
public NthDescendantIterator(NodeIterator source, int pos, int type) {
_source = source;
_ourtype = type;
_pos = pos;
}
public void setRestartable(boolean isRestartable) {
_isRestartable = isRestartable;
_source.setRestartable(isRestartable);
}
// The start node of this iterator is always the root!!!
public NodeIterator setStartNode(int node) {
_source.setStartNode(node);
return this;
}
public int next() {
int node;
while ((node = _source.next()) != END) {
int parent = _parent[node];
int child = _offsetOrChild[parent];
int pos = 0;
if (_ourtype != -1) {
do {
if (isElement(child) && _type[child] == _ourtype) pos++;
} while ((pos<_pos) && (child = _nextSibling[child]) != 0);
}
else {
do {
if (isElement(child)) pos++;
} while ((pos<_pos) && (child = _nextSibling[child]) != 0);
}
if (node == child) return node;
}
return(END);
}
public NodeIterator reset() {
_source.reset();
return this;
}
} // end of NthDescendantIterator
/**************************************************************
* Iterator that returns a given node only if it is of a given type.
*/
private final class TypedSingletonIterator extends SingletonIterator {
private final int _nodeType;
public TypedSingletonIterator(int nodeType) {
_nodeType = nodeType;
}
public int next() {
final int result = super.next();
return _type[result] == _nodeType ? result : NULL;
}
} // end of TypedSingletonIterator
/**************************************************************
* Iterator to put on top of other iterators. It will take the
* nodes from the underlaying iterator and return all but
* whitespace text nodes. The iterator needs to be a supplied
* with a filter that tells it what nodes are WS text.
*/
private final class StrippingIterator extends NodeIteratorBase {
private static final int USE_PREDICATE = 0;
private static final int STRIP_SPACE = 1;
private static final int PRESERVE_SPACE = 2;
private StripFilter _filter = null;
private short[] _mapping = null;
private final NodeIterator _source;
private boolean _children = false;
private int _action = USE_PREDICATE;
private int _last = -1;
public StrippingIterator(NodeIterator source,
short[] mapping,
StripFilter filter) {
_filter = filter;
_mapping = mapping;
_source = source;
if (_source instanceof ChildrenIterator ||
_source instanceof TypedChildrenIterator) {
_children = true;
}
}
public void setRestartable(boolean isRestartable) {
_isRestartable = isRestartable;
_source.setRestartable(isRestartable);
}
public NodeIterator setStartNode(int node) {
if (_children) {
if (_filter.stripSpace((DOM)DOMImpl.this, node,
_mapping[_type[node]]))
_action = STRIP_SPACE;
else
_action = PRESERVE_SPACE;
}
_source.setStartNode(node);
//return resetPosition();
return(this);
}
public int next() {
int node;
while ((node = _source.next()) != END) {
switch(_action) {
case STRIP_SPACE:
if (_whitespace.getBit(node)) continue;
// fall through...
case PRESERVE_SPACE:
return returnNode(node);
case USE_PREDICATE:
default:
if (_whitespace.getBit(node) &&
_filter.stripSpace((DOM)DOMImpl.this, node,
_mapping[_type[_parent[node]]]))
continue;
return returnNode(node);
}
}
return END;
}
public NodeIterator reset() {
_source.reset();
return this;
}
public void setMark() {
_source.setMark();
}
public void gotoMark() {
_source.gotoMark();
}
public int getLast() {
// Return chached value (if we have it)
if (_last != -1) return _last;
int count = getPosition();
int node;
_source.setMark();
while ((node = _source.next()) != END) {
switch(_action) {
case STRIP_SPACE:
if (_whitespace.getBit(node))
continue;
// fall through...
case PRESERVE_SPACE:
count++;
break;
case USE_PREDICATE:
default:
if (_whitespace.getBit(node) &&
_filter.stripSpace((DOM)DOMImpl.this, node,
_mapping[_type[_parent[node]]]))
continue;
else
count++;
}
}
_source.gotoMark();
_last = count;
return(count);
}
} // end of StrippingIterator
public NodeIterator strippingIterator(NodeIterator iterator,
short[] mapping,
StripFilter filter) {
return(new StrippingIterator(iterator, mapping, filter));
}
/**************************************************************
* This is a specialised iterator for predicates comparing node or
* attribute values to variable or parameter values.
*/
private final class NodeValueIterator extends NodeIteratorBase {
private NodeIterator _source;
private String _value;
private boolean _op;
private final boolean _isReverse;
private int _returnType = RETURN_PARENT;
private int _pos;
public NodeValueIterator(NodeIterator source, int returnType,
String value, boolean op) {
_source = source;
_returnType = returnType;
_value = value;
_op = op;
_isReverse = source.isReverse();
}
public boolean isReverse() {
return _isReverse;
}
public void setRestartable(boolean isRestartable) {
_isRestartable = isRestartable;
_source.setRestartable(isRestartable);
}
public NodeIterator cloneIterator() {
try {
NodeValueIterator clone = (NodeValueIterator)super.clone();
clone._source = _source.cloneIterator();
clone.setRestartable(false);
return clone.reset();
}
catch (CloneNotSupportedException e) {
BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
e.toString());
return null;
}
}
public NodeIterator reset() {
_source.reset();
return resetPosition();
}
public int next() {
int node;
while ((node = _source.next()) != END) {
String val = getNodeValue(node);
if (_value.equals(val) == _op) {
if (_returnType == RETURN_CURRENT)
return returnNode(node);
else
return returnNode(_parent[node]);
}
}
return END;
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
_source.setStartNode(_startNode = node);
return resetPosition();
}
return this;
}
public void setMark() {
_source.setMark();
_pos = _position;
}
public void gotoMark() {
_source.gotoMark();
_position = _pos;
}
}
public NodeIterator getNodeValueIterator(NodeIterator iterator, int type,
String value, boolean op) {
return(new NodeValueIterator(iterator, type, value, op));
}
/**
* Encapsulates an iterator in an OrderedIterator to ensure node order
*/
public NodeIterator orderNodes(NodeIterator source, int node) {
return new DupFilterIterator(source);
}
/**
* Returns the leftmost descendant of a node (bottom left in sub-tree)
*/
private int leftmostDescendant(int node) {
int current;
while (_type[current = node] >= NTYPES
&& (node = _offsetOrChild[node]) != NULL) {
}
return current;
}
/**
* Returns index of last child or 0 if no children
*/
private int lastChild(int node) {
if (isElement(node) || node == ROOTNODE) {
int child;
if ((child = _offsetOrChild[node]) != NULL) {
while ((child = _nextSibling[node = child]) != NULL) {
}
return node;
}
}
return 0;
}
/**
* Returns the parent of a node
*/
public int getParent(final int node) {
return _parent[node];
}
public int getElementPosition(int node) {
// Initialize with the first sbiling of the current node
int match = 0;
int curr = _offsetOrChild[_parent[node]];
if (isElement(curr)) match++;
// Then traverse all other siblings up until the current node
while (curr != node) {
curr = _nextSibling[curr];
if (isElement(curr)) match++;
}
// And finally return number of matches
return match;
}
public int getAttributePosition(int attr) {
// Initialize with the first sbiling of the current node
int match = 1;
int curr = _lengthOrAttr[_parent[attr]];
// Then traverse all other siblings up until the current node
while (curr != attr) {
curr = _nextSibling[curr];
match++;
}
// And finally return number of matches
return match;
}
/**
* Returns a node's position amongst other nodes of the same type
*/
public int getTypedPosition(int type, int node) {
// Just return the basic position if no type is specified
switch(type) {
case ELEMENT:
return getElementPosition(node);
case ATTRIBUTE:
return getAttributePosition(node);
case -1:
type = _type[node];
}
// Initialize with the first sbiling of the current node
int match = 0;
int curr = _offsetOrChild[_parent[node]];
if (_type[curr] == type) match++;
// Then traverse all other siblings up until the current node
while (curr != node) {
curr = _nextSibling[curr];
if (_type[curr] == type) match++;
}
// And finally return number of matches
return match;
}
/**
* Returns an iterator's last node of a given type
*/
public int getTypedLast(int type, int node) {
// Just return the basic position if no type is specified
if (type == -1) type = _type[node];
// Initialize with the first sbiling of the current node
int match = 0;
int curr = _offsetOrChild[_parent[node]];
if (_type[curr] == type) match++;
// Then traverse all other siblings up until the very last one
while (curr != NULL) {
curr = _nextSibling[curr];
if (_type[curr] == type) match++;
}
return match;
}
/**
* Returns singleton iterator containg the document root
* Works for them main document (mark == 0)
*/
public NodeIterator getIterator() {
return new SingletonIterator(ROOTNODE);
}
/**
* Returns the type of a specific node
*/
public int getType(final int node) {
if (node >= _type.length)
return(0);
else
return _type[node];
}
/**
* Returns the namespace type of a specific node
*/
public int getNamespaceType(final int node) {
final int type = _type[node];
if (type >= NTYPES)
return(_namespace[type-NTYPES]);
else
return(0); // default namespace
}
/**
* Returns the node-to-type mapping array
*/
public short[] getTypeArray() {
return _type;
}
/**
* Returns the (String) value of any node in the tree
*/
public String getNodeValue(final int node) {
// NS prefix = _prefixArray[_prefix[node]]
if ((node == NULL) || (node > _treeNodeLimit)) return EMPTYSTRING;
switch(_type[node]) {
case ROOT:
return getNodeValue(_offsetOrChild[node]);
case TEXT:
// GTM - add escapign code here too.
case COMMENT:
return makeStringValue(node);
case PROCESSING_INSTRUCTION:
final String pistr = makeStringValue(node);
final int col = pistr.indexOf(' ');
if (col > 0)
return pistr.substring(col+1);
else
return pistr;
default:
if (node < _firstAttributeNode)
return getElementValue(node); // element string value
else
return makeStringValue(node); // attribute value
}
}
private String getLocalName(int node) {
final int type = _type[node] - NTYPES;
final String qname = _namesArray[type];
final String uri = _uriArray[_namespace[type]];
if (uri != null) {
final int len = uri.length();
if (len > 0) return qname.substring(len+1);
}
return qname;
}
/**
* Sets up a translet-to-dom type mapping table
*/
private Hashtable setupMapping(String[] namesArray) {
final int nNames = namesArray.length;
final Hashtable types = new Hashtable(nNames);
for (int i = 0; i < nNames; i++) {
types.put(namesArray[i], new Integer(i + NTYPES));
}
return types;
}
/**
* Returns the internal type associated with an expaneded QName
*/
public int getGeneralizedType(final String name) {
final Integer type = (Integer)_types.get(name);
if (type == null) {
// memorize default type
final int code = name.charAt(0) == '@' ? ATTRIBUTE : ELEMENT;
_types.put(name, new Integer(code));
return code;
}
else {
return type.intValue();
}
}
/**
* Get mapping from DOM element/attribute types to external types
*/
public short[] getMapping(String[] names) {
int i;
final int namesLength = names.length;
final int mappingLength = _namesArray.length + NTYPES;
final short[] result = new short[mappingLength];
// primitive types map to themselves
for (i = 0; i < NTYPES; i++)
result[i] = (short)i;
// extended types initialized to "beyond caller's types"
// unknown element or Attr
for (i = NTYPES; i < mappingLength; i++) {
final int type = i - NTYPES;
final String name = _namesArray[type];
final String uri = _uriArray[_namespace[type]];
int len = 0;
if (uri != null) {
len = uri.length();
if (len > 0) len++;
}
if ((name.length() > 0) && (name.charAt(len) == '@'))
result[i] = (short)ATTRIBUTE;
else
result[i] = (short)ELEMENT;
}
// actual mapping of caller requested names
for (i = 0; i < namesLength; i++) {
result[getGeneralizedType(names[i])] = (short)(i + NTYPES);
}
return(result);
}
/**
* Get mapping from external element/attribute types to DOM types
*/
public short[] getReverseMapping(String[] names) {
int i;
final short[] result = new short[names.length + NTYPES];
// primitive types map to themselves
for (i = 0; i < NTYPES; i++) {
result[i] = (short)i;
}
// caller's types map into appropriate dom types
for (i = 0; i < names.length; i++) {
result[i + NTYPES] = (short)getGeneralizedType(names[i]);
if (result[i + NTYPES] == ELEMENT)
result[i + NTYPES] = NO_TYPE;
}
return(result);
}
/**
* Get mapping from DOM namespace types to external namespace types
*/
public short[] getNamespaceMapping(String[] namespaces) {
int i;
final int nsLength = namespaces.length;
final int mappingLength = _uriArray.length;
final short[] result = new short[mappingLength];
// Initialize all entries to -1
for (i=0; i<mappingLength; i++)
result[i] = (-1);
for (i=0; i<nsLength; i++) {
Integer type = (Integer)_nsIndex.get(namespaces[i]);
if (type != null) {
result[type.intValue()] = (short)i;
}
}
return(result);
}
/**
* Get mapping from external namespace types to DOM namespace types
*/
public short[] getReverseNamespaceMapping(String[] namespaces) {
int i;
final int length = namespaces.length;
final short[] result = new short[length];
for (i=0; i<length; i++) {
Integer type = (Integer)_nsIndex.get(namespaces[i]);
if (type == null)
result[i] = -1;
else
result[i] = type.shortValue();
}
return(result);
}
/**
* Dump the whole tree to a file (serialized)
*/
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(_treeNodeLimit); // number of nodes in DOM
out.writeInt(_firstAttributeNode); // index of first attribute node
out.writeObject(_documentURI); // URI of original document
out.writeObject(_type); // type of every node in DOM
out.writeObject(_namespace); // namespace URI of each type
out.writeObject(_prefix); // prefix type of every node in DOM
out.writeObject(_parent); // parent of every node in DOM
out.writeObject(_nextSibling); // next sibling of every node in DOM
out.writeObject(_offsetOrChild); // first child of every node in DOM
out.writeObject(_lengthOrAttr); // first attr of every node in DOM
out.writeObject(_text); // all text in DOM (text, PIs, etc)
out.writeObject(_namesArray); // names of all element/attr types
out.writeObject(_uriArray); // name of all URIs
out.writeObject(_prefixArray); // name of all prefixes
out.writeObject(_whitespace);
if (_dontEscape != null) {
out.writeObject(_dontEscape);
}
else {
out.writeObject(new BitArray(0));
}
out.flush();
}
/**
* Read the whole tree from a file (serialized)
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
_treeNodeLimit = in.readInt();
_firstAttributeNode = in.readInt();
_documentURI = (String)in.readObject();
_type = (short[])in.readObject();
_namespace = (short[])in.readObject();
_prefix = (short[])in.readObject();
_parent = (int[])in.readObject();
_nextSibling = (int[])in.readObject();
_offsetOrChild = (int[])in.readObject();
_lengthOrAttr = (int[])in.readObject();
_text = (char[])in.readObject();
_namesArray = (String[])in.readObject();
_uriArray = (String[])in.readObject();
_prefixArray = (String[])in.readObject();
_whitespace = (BitArray)in.readObject();
_dontEscape = (BitArray)in.readObject();
if (_dontEscape.size() == 0) {
_dontEscape = null;
}
_types = setupMapping(_namesArray);
}
/**
* Constructor - defaults to 32K nodes
*/
public DOMImpl() {
//this(32*1024);
this(8*1024);
}
/**
* Constructor - defines initial size
*/
public DOMImpl(int size) {
_type = new short[size];
_parent = new int[size];
_nextSibling = new int[size];
_offsetOrChild = new int[size];
_lengthOrAttr = new int[size];
_text = new char[size * 10];
_whitespace = new BitArray(size);
_prefix = new short[size];
// _namesArray[] and _uriArray[] are allocated in endDocument
}
/**
* Prints the whole tree to standard output
*/
public void print(int node, int level) {
switch(_type[node]) {
case ROOT:
print(_offsetOrChild[node], level);
break;
case TEXT:
case COMMENT:
case PROCESSING_INSTRUCTION:
System.out.print(makeStringValue(node));
break;
default: // element
final String name = getNodeName(node);
System.out.print("<" + name);
for (int a = _lengthOrAttr[node]; a != NULL; a = _nextSibling[a]) {
System.out.print("\n" + getNodeName(a) +
"=\"" + makeStringValue(a) + "\"");
}
System.out.print('>');
for (int child = _offsetOrChild[node]; child != NULL;
child = _nextSibling[child]) {
print(child, level + 1);
}
System.out.println("</" + name + '>');
break;
}
}
/**
* Returns the name of a node (attribute or element).
*/
public String getNodeName(final int node) {
// Get the node type and make sure that it is within limits
final short type = _type[node];
switch(type) {
case DOM.ROOT:
case DOM.TEXT:
case DOM.ELEMENT:
case DOM.ATTRIBUTE:
case DOM.COMMENT:
return EMPTYSTRING;
case DOM.NAMESPACE:
final int index = _prefix[node];
if (index < _prefixArray.length)
return _prefixArray[index];
else
return EMPTYSTRING;
case DOM.PROCESSING_INSTRUCTION:
final String pistr = makeStringValue(node);
final int col = pistr.indexOf(' ');
if (col > -1)
return(pistr.substring(0,col));
else
return pistr;
default:
// Construct the local part (omit '@' for attributes)
String name = getLocalName(node);
if (node >= _firstAttributeNode)
name = name.substring(1);
final int pi = _prefix[node];
if (pi > 0) {
final String prefix = _prefixArray[pi];
if (prefix != EMPTYSTRING)
name = prefix+':'+name;
}
return name;
}
}
/**
* Returns the namespace URI to which a node belongs
*/
public String getNamespaceName(final int node) {
if (_type[node] == NAMESPACE) {
return(EMPTYSTRING); //return getNodeValue(node);
}
else {
final int type = getNamespaceType(node);
final String name = _uriArray[type];
if (name == null)
return(EMPTYSTRING);
else
return(name);
}
}
/**
* Returns the string value of a single text/comment node or
* attribute value (they are all stored in the same array).
*/
private String makeStringValue(final int node) {
return new String(_text, _offsetOrChild[node], _lengthOrAttr[node]);
}
/**
* Returns the attribute node of a given type (if any) for an element
*/
public int getAttributeNode(final int type, final int element) {
for (int attr = _lengthOrAttr[element];
attr != NULL;
attr = _nextSibling[attr]) {
if (_type[attr] == type) return attr;
}
return NULL;
}
/**
* Returns the value of a given attribute type of a given element
*/
public String getAttributeValue(final int type, final int element) {
final int attr = getAttributeNode(type, element);
if (attr != NULL)
return makeStringValue(attr);
else
return EMPTYSTRING;
}
/**
* Returns true if a given element has an attribute of a given type
*/
public boolean hasAttribute(final int type, final int node) {
if (getAttributeNode(type, node) != NULL)
return true;
else
return false;
}
/**
* This method is for testing/debugging only
*/
public String getAttributeValue(final String name, final int element) {
return getAttributeValue(getGeneralizedType(name), element);
}
/**
* Returns true if the given element has any children
*/
private boolean hasChildren(final int node) {
if (node < _firstAttributeNode) {
final int type = _type[node];
return(((type >= NTYPES) || (type == ROOT)) &&
(_offsetOrChild[node] != 0));
}
return(false);
}
/**
* Returns an iterator with all the children of a given node
*/
public NodeIterator getChildren(final int node) {
if (hasChildren(node))
return(new ChildrenIterator());
else
return(EMPTYITERATOR);
}
/**
* Returns an iterator with all children of a specific type
* for a given node (element)
*/
public NodeIterator getTypedChildren(final int type) {
return(new TypedChildrenIterator(type));
}
/**
* This is a shortcut to the iterators that implement the
* supported XPath axes (only namespace::) is not supported.
* Returns a bare-bones iterator that must be initialized
* with a start node (using iterator.setStartNode()).
*/
public NodeIterator getAxisIterator(final int axis) {
NodeIterator iterator = null;
switch (axis) {
case Axis.SELF:
iterator = new SingletonIterator();
break;
case Axis.CHILD:
iterator = new ChildrenIterator();
break;
case Axis.PARENT:
return(new ParentIterator());
case Axis.ANCESTOR:
return(new AncestorIterator());
case Axis.ANCESTORORSELF:
return((new AncestorIterator()).includeSelf());
case Axis.ATTRIBUTE:
return(new AttributeIterator());
case Axis.DESCENDANT:
iterator = new DescendantIterator();
break;
case Axis.DESCENDANTORSELF:
iterator = (new DescendantIterator()).includeSelf();
break;
case Axis.FOLLOWING:
iterator = new FollowingIterator();
break;
case Axis.PRECEDING:
iterator = new PrecedingIterator();
break;
case Axis.FOLLOWINGSIBLING:
iterator = new FollowingSiblingIterator();
break;
case Axis.PRECEDINGSIBLING:
iterator = new PrecedingSiblingIterator();
break;
case Axis.NAMESPACE:
iterator = new NamespaceIterator();
break;
default:
BasisLibrary.runTimeError(BasisLibrary.AXIS_SUPPORT_ERR,
Axis.names[axis]);
}
return(iterator);
}
/**
* Similar to getAxisIterator, but this one returns an iterator
* containing nodes of a typed axis (ex.: child::foo)
*/
public NodeIterator getTypedAxisIterator(int axis, int type) {
NodeIterator iterator = null;
/* This causes an error when using patterns for elements that
do not exist in the DOM (translet types which do not correspond
to a DOM type are mapped to the DOM.ELEMENT type).
*/
if (type == NO_TYPE) {
return(EMPTYITERATOR);
}
else if ((type == ELEMENT) && (axis != Axis.NAMESPACE)) {
iterator = new FilterIterator(getAxisIterator(axis),
getElementFilter());
}
else {
switch (axis) {
case Axis.SELF:
iterator = new TypedSingletonIterator(type);
break;
case Axis.CHILD:
iterator = new TypedChildrenIterator(type);
break;
case Axis.PARENT:
return(new ParentIterator().setNodeType(type));
case Axis.ANCESTOR:
return(new TypedAncestorIterator(type));
case Axis.ANCESTORORSELF:
return((new TypedAncestorIterator(type)).includeSelf());
case Axis.ATTRIBUTE:
return(new TypedAttributeIterator(type));
case Axis.DESCENDANT:
iterator = new TypedDescendantIterator(type);
break;
case Axis.DESCENDANTORSELF:
iterator = (new TypedDescendantIterator(type)).includeSelf();
break;
case Axis.FOLLOWING:
iterator = new TypedFollowingIterator(type);
break;
case Axis.PRECEDING:
iterator = new TypedPrecedingIterator(type);
break;
case Axis.FOLLOWINGSIBLING:
iterator = new TypedFollowingSiblingIterator(type);
break;
case Axis.PRECEDINGSIBLING:
iterator = new TypedPrecedingSiblingIterator(type);
break;
case Axis.NAMESPACE:
if (type == ELEMENT)
iterator = new NamespaceIterator();
else
iterator = new TypedNamespaceIterator(type);
break;
default:
BasisLibrary.runTimeError(BasisLibrary.TYPED_AXIS_SUPPORT_ERR,
Axis.names[axis]);
}
}
return(iterator);
}
/**
* Do not thing that this returns an iterator for the namespace axis.
* It returns an iterator with nodes that belong in a certain namespace,
* such as with <xsl:apply-templates select="blob/foo:*"/>
* The 'axis' specifies the axis for the base iterator from which the
* nodes are taken, while 'ns' specifies the namespace URI type.
*/
public NodeIterator getNamespaceAxisIterator(int axis, int ns) {
NodeIterator iterator = null;
if (ns == NO_TYPE) {
return(EMPTYITERATOR);
}
else {
switch (axis) {
case Axis.CHILD:
iterator = new NamespaceChildrenIterator(ns);
break;
case Axis.ATTRIBUTE:
iterator = new NamespaceAttributeIterator(ns);
break;
default:
BasisLibrary.runTimeError(BasisLibrary.TYPED_AXIS_SUPPORT_ERR,
Axis.names[axis]);
}
}
return(iterator);
}
/**
* Returns an iterator with all descendants of a node that are of
* a given type.
*/
public NodeIterator getTypedDescendantIterator(int type) {
NodeIterator iterator;
if (type == ELEMENT)
iterator = new FilterIterator(new DescendantIterator(),
getElementFilter());
else
iterator = new TypedDescendantIterator(type);
return(iterator);
}
/**
* Returns the nth descendant of a node
*/
public NodeIterator getNthDescendant(int type, int n, boolean includeself) {
NodeIterator source;
if (type == ELEMENT)
source = new FilterIterator(new DescendantIterator(),
getElementFilter());
else
source = new TypedDescendantIterator(type);
if (includeself) ((NodeIteratorBase)source).includeSelf();
return(new NthDescendantIterator(source, n, type));
}
/**
* Copy the contents of a text-node to an output handler
*/
public void characters(final int textNode, TransletOutputHandler handler)
throws TransletException {
handler.characters(_text,
_offsetOrChild[textNode],
_lengthOrAttr[textNode]);
}
/**
* Copy a node-set to an output handler
*/
public void copy(NodeIterator nodes, TransletOutputHandler handler)
throws TransletException {
int node;
while ((node = nodes.next()) != NULL) {
copy(node, handler);
}
}
/**
* Copy the whole tree to an output handler
*/
public void copy(TransletOutputHandler handler) throws TransletException {
copy(ROOTNODE, handler);
}
/**
* Performs a deep copy (ref. XSLs copy-of())
*
* TODO: Copy namespace declarations. Can't be done until we
* add namespace nodes and keep track of NS prefixes
* TODO: Copy comment nodes
*/
public void copy(final int node, TransletOutputHandler handler)
throws TransletException {
final int type = _type[node];
switch(type) {
case ROOT:
for (int c=_offsetOrChild[node]; c!=NULL; c=_nextSibling[c])
copy(c, handler);
break;
case PROCESSING_INSTRUCTION:
copyPI(node, handler);
break;
case COMMENT:
handler.comment(new String(_text,
_offsetOrChild[node],
_lengthOrAttr[node]));
break;
case TEXT:
boolean last = false;
boolean escapeBit = false;
if (_dontEscape != null) {
escapeBit = _dontEscape.getBit(node);
if (escapeBit) {
last = handler.setEscaping(false);
}
}
handler.characters(_text,
_offsetOrChild[node],
_lengthOrAttr[node]);
if (_dontEscape != null && escapeBit) {
handler.setEscaping(last);
}
break;
case ATTRIBUTE:
shallowCopy(node, handler);
break;
case NAMESPACE:
shallowCopy(node, handler);
break;
default:
if (isElement(node)) {
// Start element definition
final String name = copyElement(node, type, handler);
// Copy element attribute
for (int a=_lengthOrAttr[node]; a!=NULL; a=_nextSibling[a]) {
if (_type[a] != NAMESPACE) {
final String uri = getNamespaceName(a);
if (uri != EMPTYSTRING) {
final String prefix = _prefixArray[_prefix[a]];
handler.namespace(prefix, uri);
}
handler.attribute(getNodeName(a), makeStringValue(a));
}
else {
handler.namespace(_prefixArray[_prefix[a]],
makeStringValue(a));
}
}
// Copy element children
for (int c=_offsetOrChild[node]; c!=NULL; c=_nextSibling[c])
copy(c, handler);
// Close element definition
handler.endElement(name);
}
// Shallow copy of attribute to output handler
else {
final String uri = getNamespaceName(node);
if (uri != EMPTYSTRING) {
final String prefix = _prefixArray[_prefix[node]];
handler.namespace(prefix, uri);
}
handler.attribute(getNodeName(node), makeStringValue(node));
}
break;
}
}
/**
* Copies a processing instruction node to an output handler
*/
private void copyPI(final int node, TransletOutputHandler handler)
throws TransletException {
final char[] text = _text;
final int start = _offsetOrChild[node];
final int length = _lengthOrAttr[node];
// Target and Value are separated by a whitespace - find it!
int i = start;
while (text[i] != ' ') i++;
final int len = i - start;
final String target = new String(text, start, len);
final String value = new String(text, i + 1, length - len - 1);
handler.processingInstruction(target, value);
}
/**
* Performs a shallow copy (ref. XSLs copy())
*/
public String shallowCopy(final int node, TransletOutputHandler handler)
throws TransletException {
final int type = _type[node];
switch(type) {
case ROOT: // do nothing
return EMPTYSTRING;
case TEXT:
handler.characters(_text,
_offsetOrChild[node],
_lengthOrAttr[node]);
return null;
case PROCESSING_INSTRUCTION:
copyPI(node, handler);
return null;
case COMMENT:
final String comment = new String(_text,
_offsetOrChild[node],
_lengthOrAttr[node]);
handler.comment(comment);
return null;
case NAMESPACE:
handler.namespace(_prefixArray[_prefix[node]],
makeStringValue(node));
return null;
default:
if (isElement(node)) {
return(copyElement(node, type, handler));
}
else {
final String uri = getNamespaceName(node);
if (uri != EMPTYSTRING) {
final String prefix = _prefixArray[_prefix[node]];
handler.namespace(prefix, uri);
}
handler.attribute(getNodeName(node), makeStringValue(node));
return null;
}
}
}
private String copyElement(int node, int type,
TransletOutputHandler handler)
throws TransletException {
type = type - NTYPES;
String name = _namesArray[type];
final int pi = _prefix[node];
final int ui = _namespace[type];
if (pi > 0) {
final String prefix = _prefixArray[pi];
final String uri = _uriArray[ui];
final String local = getLocalName(node);
if (prefix.equals(EMPTYSTRING))
name = local;
else
name = prefix+':'+local;
handler.startElement(name);
handler.namespace(prefix, uri);
}
else {
if (ui > 0) {
handler.startElement(getLocalName(node));
handler.namespace(EMPTYSTRING, _uriArray[ui]);
}
else {
handler.startElement(name);
}
}
return name;
}
/**
* Returns the string value of the entire tree
*/
public String getStringValue() {
return getElementValue(ROOTNODE);
}
/**
* Returns the string value of any element
*/
public String getElementValue(final int element) {
// optimization: only create StringBuffer if > 1 child
final int child = _offsetOrChild[element];
if (child == NULL)
return EMPTYSTRING;
if ((_type[child] == TEXT) && (_nextSibling[child] == NULL))
return makeStringValue(child);
else
return stringValueAux(new StringBuffer(), element).toString();
}
/**
* Helper to getElementValue() above
*/
private StringBuffer stringValueAux(StringBuffer buffer, final int element) {
for (int child = _offsetOrChild[element];
child != NULL;
child = _nextSibling[child]) {
switch (_type[child]) {
case TEXT:
buffer.append(_text,
_offsetOrChild[child],
_lengthOrAttr[child]);
break;
case PROCESSING_INSTRUCTION:
case COMMENT:
// This method should not return anything for PIs and comments
break;
default:
stringValueAux(buffer, child);
}
}
return buffer;
}
public String getTreeString() {
StringBuffer buf = new StringBuffer();
buf = getElementString(buf, ROOTNODE);
return buf.toString();
}
/**
* Helper to getTreeString() above
*/
private StringBuffer getElementString(StringBuffer buffer, int element) {
String name = null;
if (isElement(element)) {
if ((name = getNodeName(element)) != null) {
buffer.append('<');
buffer.append(name);
if (_offsetOrChild[element] == NULL) {
buffer.append("/>");
return buffer;
}
buffer.append('>');
}
}
for (int child = _offsetOrChild[element];
child != NULL;
child = _nextSibling[child]) {
switch (_type[child]) {
case COMMENT:
buffer.append("<!--");
buffer.append(_text,
_offsetOrChild[child],
_lengthOrAttr[child]);
buffer.append("-->");
break;
case TEXT:
buffer.append(_text,
_offsetOrChild[child],
_lengthOrAttr[child]);
break;
case PROCESSING_INSTRUCTION:
buffer.append("<?");
buffer.append(_text,
_offsetOrChild[child],
_lengthOrAttr[child]);
buffer.append("?>");
break;
default:
getElementString(buffer, child);
}
}
if (isElement(element) && name != null) {
buffer.append("</");
buffer.append(name);
buffer.append(">");
}
return buffer;
}
/**
* Returns a node' defined language for a node (if any)
*/
public String getLanguage(int node) {
final Integer langType = (Integer)_types.get(XML_LANG_ATTRIBUTE);
if (langType != null) {
final int type = langType.intValue();
while (node != DOM.NULL) {
int attr = _lengthOrAttr[node];
while (attr != DOM.NULL) {
if (_type[attr] == type)
return(getNodeValue(attr));
attr = _nextSibling[attr];
}
node = getParent(node);
}
}
return(null);
}
/**
* Returns an instance of the DOMBuilder inner class
* This class will consume the input document through a SAX2
* interface and populate the tree.
*/
public DOMBuilder getBuilder() {
return new DOMBuilderImpl();
}
/**
* Returns a DOMBuilder class wrapped in a SAX adapter.
* I am not sure if we need this one anymore now that the
* DOM builder's interface is pure SAX2 (must investigate)
*/
public TransletOutputHandler getOutputDomBuilder() {
return new SAXAdapter(new DOMBuilderImpl());
}
/**
* Returns true if a character is an XML whitespace character.
* Order of tests is important for performance ([space] first).
*/
private static final boolean isWhitespaceChar(char c) {
return c == 0x20 || c == 0x0A || c == 0x0D || c == 0x09;
}
/****************************************************************/
/* DOM builder class definition */
/****************************************************************/
private final class DOMBuilderImpl implements DOMBuilder {
private final static int ATTR_ARRAY_SIZE = 32;
private final static int REUSABLE_TEXT_SIZE = 32;
private final static int INIT_STACK_LENGTH = 64;
private Hashtable _shortTexts = null;
private Hashtable _names = null;
private int _nextNameCode = NTYPES;
private int _parentStackLength = INIT_STACK_LENGTH;
private int[] _parentStack = new int[INIT_STACK_LENGTH];
private int[] _previousSiblingStack = new int[INIT_STACK_LENGTH];
private int _sp;
private int _baseOffset = 0;
private int _currentOffset = 0;
private int _currentNode = 0;
// Temporary structures for attribute nodes
private int _currentAttributeNode = 1;
private short[] _type2 = new short[ATTR_ARRAY_SIZE];
private short[] _prefix2 = new short[ATTR_ARRAY_SIZE];
private int[] _parent2 = new int[ATTR_ARRAY_SIZE];
private int[] _nextSibling2 = new int[ATTR_ARRAY_SIZE];
private int[] _offset = new int[ATTR_ARRAY_SIZE];
private int[] _length = new int[ATTR_ARRAY_SIZE];
// Namespace prefix-to-uri mapping stuff
private Hashtable _nsPrefixes = new Hashtable();
private int _uriCount = 0;
private int _prefixCount = 0;
private int _nextNamespace = DOM.NULL;
private int _lastNamespace = DOM.NULL;
// Stack used to keep track of what whitespace text nodes are protected
// by xml:space="preserve" attributes and which nodes that are not.
private int[] _xmlSpaceStack = new int[64];
private int _idx = 1;
private boolean _preserve = false;
private static final String XML_STRING = "xml:";
private static final String XMLSPACE_STRING = "xml:space";
private static final String PRESERVE_STRING = "preserve";
private static final String XML_PREFIX = "xml";
private static final String XMLNS_PREFIX = "xmlns";
private boolean _escaping = true;
private boolean _disableEscaping = false;
/**
* Default constructor for the DOMBuiler class
*/
public DOMBuilderImpl() {
_xmlSpaceStack[0] = DOM.ROOTNODE;
}
/**
* Returns the namespace URI that a prefix currently maps to
*/
private String getNamespaceURI(String prefix) {
// Get the stack associated with this namespace prefix
final Stack stack = (Stack)_nsPrefixes.get(prefix);
if ((stack != null) && (!stack.empty())) {
return((String)stack.peek());
}
else
return(EMPTYSTRING);
}
/**
* Call this when an xml:space attribute is encountered to
* define the whitespace strip/preserve settings.
*/
private void xmlSpaceDefine(String val, final int node) {
final boolean setting = val.equals(PRESERVE_STRING);
if (setting != _preserve) {
_xmlSpaceStack[_idx++] = node;
_preserve = setting;
}
}
/**
* Call this from endElement() to revert strip/preserve setting
* to whatever it was before the corresponding startElement()
*/
private void xmlSpaceRevert(final int node) {
if (node == _xmlSpaceStack[_idx - 1]) {
_idx--;
_preserve = !_preserve;
}
}
/**
* Returns the next available node. Increases the various arrays
* that constitute the node if necessary.
*/
private int nextNode() {
final int index = _currentNode++;
if (index == _type.length) {
resizeArrays(_type.length * 2, index);
}
return index;
}
/**
* Returns the next available attribute node. Increases the
* various arrays that constitute the attribute if necessary
*/
private int nextAttributeNode() {
final int index = _currentAttributeNode++;
if (index == _type2.length) {
resizeArrays2(_type2.length * 2, index);
}
return index;
}
/**
* Resize the character array that holds the contents of
* all text nodes, comments and attribute values
*/
private void resizeTextArray(final int newSize) {
final char[] newText = new char[newSize];
System.arraycopy(_text, 0, newText, 0, _currentOffset);
_text = newText;
}
/**
* Links together the children of a node. Child nodes are linked
* through the _nextSibling array
*/
private void linkChildren(final int node) {
_parent[node] = _parentStack[_sp];
if (_previousSiblingStack[_sp] != 0) { // current not first child
_nextSibling[_previousSiblingStack[_sp]] = node;
}
else {
_offsetOrChild[_parentStack[_sp]] = node;
}
_previousSiblingStack[_sp] = node;
}
/**
* Sets the current parent
*/
private void linkParent(final int node) {
if (++_sp >= _parentStackLength) {
int length = _parentStackLength;
_parentStackLength = length + INIT_STACK_LENGTH;
final int newParent[] = new int[_parentStackLength];
System.arraycopy(_parentStack,0,newParent,0,length);
_parentStack = newParent;
final int newSibling[] = new int[_parentStackLength];
System.arraycopy(_previousSiblingStack,0,newSibling,0,length);
_previousSiblingStack = newSibling;
}
_parentStack[_sp] = node;
}
/**
* Generate the internal type for an element's expanded QName
*/
private short makeElementNode(String uri, String localname)
throws SAXException {
final String name;
if (uri != EMPTYSTRING)
name = uri + ':' + localname;
else
name = localname;
// Stuff the QName into the names vector & hashtable
Integer obj = (Integer)_names.get(name);
if (obj == null) {
_names.put(name, obj = new Integer(_nextNameCode++));
}
return (short)obj.intValue();
}
/**
* Generate the internal type for an element's expanded QName
*/
private short makeElementNode(String name, int col)
throws SAXException {
// Expand prefix:localname to full QName
if (col > -1) {
final String uri = getNamespaceURI(name.substring(0, col));
name = uri + name.substring(col);
}
// Append default namespace with the name has no prefix
else {
final String uri = getNamespaceURI(EMPTYSTRING);
if (!uri.equals(EMPTYSTRING)) name = uri + ':' + name;
}
// Stuff the QName into the names vector & hashtable
Integer obj = (Integer)_names.get(name);
if (obj == null) {
_names.put(name, obj = new Integer(_nextNameCode++));
}
return (short)obj.intValue();
}
/**
*
*/
private short registerPrefix(String prefix) {
Stack stack = (Stack)_nsPrefixes.get(prefix);
if (stack != null) {
Integer obj = (Integer)stack.elementAt(0);
return (short)obj.intValue();
}
return 0;
}
/*
* This method will check if the current text node contains text that
* is already in the text array. If the text is found in the array
* then this method returns the offset of the previous instance of the
* string. Otherwise the text is inserted in the array, and the
* offset of the new instance is inserted.
* Updates the globals _baseOffset and _currentOffset
*/
private int maybeReuseText(final int length) {
final int base = _baseOffset;
if (length <= REUSABLE_TEXT_SIZE) {
// Use a char array instead of string for performance benefit
char[] chars = new char[length];
System.arraycopy(_text, base, chars, 0, length);
final Integer offsetObj = (Integer)_shortTexts.get(chars);
if (offsetObj != null) {
_currentOffset = base; // step back current
return offsetObj.intValue(); // reuse previous string
}
else {
_shortTexts.put(chars, new Integer(base));
}
}
_baseOffset = _currentOffset; // advance base to current
return base;
}
/**
* Links a text reference (an occurance of a sequence of characters
* in the _text[] array) to a specific node index.
*/
private void storeTextRef(final int node) {
final int length = _currentOffset - _baseOffset;
_offsetOrChild[node] = maybeReuseText(length);
_lengthOrAttr[node] = length;
}
/**
* Creates a text-node and checks if it is a whitespace node.
*/
private int makeTextNode(boolean isWhitespace) {
if (_currentOffset > _baseOffset) {
final int node = nextNode();
final int limit = _currentOffset;
// Tag as whitespace node if the parser tells us that it is...
if (isWhitespace) {
_whitespace.setBit(node);
}
// ...otherwise we check if this is a whitespace node, unless
// the node is protected by an xml:space="preserve" attribute.
else if (!_preserve) {
int i = _baseOffset;
while (isWhitespaceChar(_text[i++]) && i < limit) ;
if ((i == limit) && isWhitespaceChar(_text[i-1])) {
_whitespace.setBit(node);
}
}
_type[node] = TEXT;
linkChildren(node);
storeTextRef(node);
if (_disableEscaping) {
if (_dontEscape == null) {
_dontEscape = new BitArray(_whitespace.size());
}
_dontEscape.setBit(node);
_disableEscaping = false;
}
return node;
}
return -1;
}
/**
* Links an attribute value (an occurance of a sequence of characters
* in the _text[] array) to a specific attribute node index.
*/
private void storeAttrValRef(final int attributeNode) {
final int length = _currentOffset - _baseOffset;
_offset[attributeNode] = maybeReuseText(length);
_length[attributeNode] = length;
}
private int makeNamespaceNode(String prefix, String uri)
throws SAXException {
final int node = nextAttributeNode();
_type2[node] = NAMESPACE;
characters(uri);
storeAttrValRef(node);
return node;
}
/**
* Creates an attribute node
*/
private int makeAttributeNode(int parent, Attributes attList, int i)
throws SAXException
{
final int node = nextAttributeNode();
final String qname = attList.getQName(i);
String localName = attList.getLocalName(i);
final String value = attList.getValue(i);
StringBuffer namebuf = new StringBuffer(EMPTYSTRING);
if (qname.startsWith(XMLSPACE_STRING)) {
xmlSpaceDefine(attList.getValue(i), parent);
}
// If local name is null set it to the empty string
if (localName == null) {
localName = EMPTYSTRING;
}
// Create the internal attribute node name (uri+@+localname)
final String uri = attList.getURI(i);
if (uri != null && !uri.equals(EMPTYSTRING)) {
namebuf.append(uri);
namebuf.append(':');
}
namebuf.append('@');
namebuf.append(localName.length() > 0 ? localName : qname);
String name = namebuf.toString();
// Get the index of the attribute node name (create new if non-ex).
Integer obj = (Integer)_names.get(name);
if (obj == null) {
_type2[node] = (short)_nextNameCode;
_names.put(name, obj = new Integer(_nextNameCode++));
}
else {
_type2[node] = (short)obj.intValue();
}
final int col = qname.lastIndexOf(':');
if (col > 0) {
_prefix2[node] = registerPrefix(qname.substring(0, col));
}
characters(attList.getValue(i));
storeAttrValRef(node);
return node;
}
/****************************************************************/
/* SAX Interface Starts Here */
/****************************************************************/
/**
* SAX2: Receive notification of character data.
*/
public void characters(char[] ch, int start, int length) {
if (_currentOffset + length > _text.length) {
// GTM resizeTextArray(_text.length * 2);
// bug fix 6189, contributed by Mirko Seifert
resizeTextArray(
Math.max(_text.length * 2, _currentOffset + length));
}
System.arraycopy(ch, start, _text, _currentOffset, length);
_currentOffset += length;
_disableEscaping = !_escaping;
}
/**
* SAX2: Receive notification of the beginning of a document.
*/
public void startDocument() throws SAXException {
_shortTexts = new Hashtable();
_names = new Hashtable();
_sp = 0;
_parentStack[0] = ROOTNODE; // root
_currentNode = ROOTNODE + 1;
_currentAttributeNode = 1;
_type2[0] = NAMESPACE;
startPrefixMapping(EMPTYSTRING, EMPTYSTRING);
startPrefixMapping(XML_PREFIX, "http://www.w3.org/XML/1998/namespace");
_lengthOrAttr[ROOTNODE] = _nextNamespace;
_parent2[_nextNamespace] = ROOTNODE;
_nextNamespace = DOM.NULL;
}
/**
* SAX2: Receive notification of the end of a document.
*/
public void endDocument() {
makeTextNode(false);
_shortTexts = null;
final int namesSize = _nextNameCode - NTYPES;
// Fill the _namesArray[] array
_namesArray = new String[namesSize];
Enumeration keys = _names.keys();
while (keys.hasMoreElements()) {
final String name = (String)keys.nextElement();
final Integer idx = (Integer)_names.get(name);
_namesArray[idx.intValue() - NTYPES] = name;
}
_names = null;
_types = setupMapping(_namesArray);
// trim arrays' sizes
resizeTextArray(_currentOffset);
_firstAttributeNode = _currentNode;
shiftAttributes(_currentNode);
resizeArrays(_currentNode + _currentAttributeNode, _currentNode);
appendAttributes();
_treeNodeLimit = _currentNode + _currentAttributeNode;
// Fill the _namespace[] and _uriArray[] array
_namespace = new short[namesSize];
_uriArray = new String[_uriCount];
for (int i = 0; i<namesSize; i++) {
final String qname = _namesArray[i];
final int col = _namesArray[i].lastIndexOf(':');
// Elements/attributes with the xml prefix are not in a NS
if ((!qname.startsWith(XML_STRING)) && (col > -1)) {
final String uri = _namesArray[i].substring(0, col);
final Integer idx = (Integer)_nsIndex.get(uri);
if (idx != null) {
_namespace[i] = idx.shortValue();
_uriArray[idx.intValue()] = uri;
}
}
}
_prefixArray = new String[_prefixCount];
Enumeration p = _nsPrefixes.keys();
while (p.hasMoreElements()) {
final String prefix = (String)p.nextElement();
final Stack stack = (Stack)_nsPrefixes.get(prefix);
final Integer I = (Integer)stack.elementAt(0);
_prefixArray[I.shortValue()] = prefix;
}
}
/**
* SAX2: Receive notification of the beginning of an element.
*/
public void startElement(String uri, String localName,
String qname, Attributes attributes)
throws SAXException {
makeTextNode(false);
// Get node index and setup parent/child references
final int node = nextNode();
linkChildren(node);
linkParent(node);
_lengthOrAttr[node] = DOM.NULL;
int last = -1;
final int count = attributes.getLength();
// Append any namespace nodes
if (_nextNamespace != DOM.NULL) {
_lengthOrAttr[node] = _nextNamespace;
while (_nextNamespace != DOM.NULL) {
_parent2[_nextNamespace] = node;
_nextNamespace = _nextSibling2[last = _nextNamespace];
// Chain last namespace node to following attribute node(s)
if (_nextNamespace == DOM.NULL && count > 0) {
_nextSibling2[last] = _currentAttributeNode;
}
}
}
// If local name is null set it to the empty string
if (localName == null) {
localName = EMPTYSTRING;
}
// Append any attribute nodes
boolean attrsAdded = false;
if (count > 0) {
int attr = _currentAttributeNode;
if (_lengthOrAttr[node] == DOM.NULL) {
_lengthOrAttr[node] = attr;
}
for (int i = 0; i < count; i++) {
if (!attributes.getQName(i).startsWith(XMLNS_PREFIX)) {
attr = makeAttributeNode(node, attributes, i);
_parent2[attr] = node;
_nextSibling2[attr] = attr + 1;
attrsAdded = true;
}
}
// Did we append namespace nodes only?
if (!attrsAdded && last != -1) {
_nextSibling2[last] = DOM.NULL;
}
else {
_nextSibling2[attr] = DOM.NULL;
}
}
final int col = qname.lastIndexOf(':');
// Assign an internal type to this element (may exist)
if (uri != null && localName.length() > 0) {
_type[node] = makeElementNode(uri, localName);
}
else {
_type[node] = makeElementNode(qname, col);
}
// Assign an internal type to the element's prefix (may exist)
if (col > -1) {
_prefix[node] = registerPrefix(qname.substring(0, col));
}
}
/**
* SAX2: Receive notification of the end of an element.
*/
public void endElement(String uri, String localName,
String qname) {
makeTextNode(false);
// Revert to strip/preserve-space setting from before this element
xmlSpaceRevert(_parentStack[_sp]);
_previousSiblingStack[_sp--] = 0;
}
/**
* SAX2: Receive notification of a processing instruction.
*/
public void processingInstruction(String target, String data)
throws SAXException {
makeTextNode(false);
final int node = nextNode();
_type[node] = PROCESSING_INSTRUCTION;
linkChildren(node);
characters(target);
characters(" ");
characters(data);
storeTextRef(node);
}
/**
* SAX2: Receive notification of ignorable whitespace in element
* content. Similar to characters(char[], int, int).
*/
public void ignorableWhitespace(char[] ch, int start, int length) {
if (_currentOffset + length > _text.length) {
resizeTextArray(_text.length * 2);
}
System.arraycopy(ch, start, _text, _currentOffset, length);
_currentOffset += length;
makeTextNode(true);
}
/**
* SAX2: Receive an object for locating the origin of SAX document
* events.
*/
public void setDocumentLocator(Locator locator) {
// Not handled
}
/**
* SAX2: Receive notification of a skipped entity.
*/
public void skippedEntity(String name) {
// Not handled
}
/**
* SAX2: Begin the scope of a prefix-URI Namespace mapping.
*/
public void startPrefixMapping(String prefix, String uri)
throws SAXException {
// Get the stack associated with this namespace prefix
Stack stack = (Stack)_nsPrefixes.get(prefix);
if (stack == null) {
stack = new Stack();
stack.push(new Integer(_prefixCount++));
_nsPrefixes.put(prefix, stack);
}
// Check if the URI already exists before pushing on stack
Integer idx;
if ((idx = (Integer)_nsIndex.get(uri)) == null) {
_nsIndex.put(uri, idx = new Integer(_uriCount++));
}
stack.push(uri);
if (!prefix.equals(EMPTYSTRING) || !uri.equals(EMPTYSTRING)) {
makeTextNode(false);
int attr = makeNamespaceNode(prefix, uri);
if (_nextNamespace == DOM.NULL)
_nextNamespace = attr;
else
_nextSibling2[attr-1] = attr;
_nextSibling2[attr] = DOM.NULL;
// _prefix2[attr] = idx.shortValue();
_prefix2[attr] = ((Integer) stack.elementAt(0)).shortValue();
}
}
/**
* SAX2: End the scope of a prefix-URI Namespace mapping.
*/
public void endPrefixMapping(String prefix) {
// Get the stack associated with this namespace prefix
final Stack stack = (Stack)_nsPrefixes.get(prefix);
if ((stack != null) && (!stack.empty())) stack.pop();
}
/**
* SAX2: Report an XML comment anywhere in the document.
*/
public void comment(char[] ch, int start, int length) {
makeTextNode(false);
if (_currentOffset + length > _text.length) {
resizeTextArray(_text.length * 2);
}
System.arraycopy(ch, start, _text, _currentOffset, length);
_currentOffset += length;
final int node = makeTextNode(false);
_type[node] = COMMENT;
}
/**
* SAX2: Ignored events
*/
public void startCDATA() {}
public void endCDATA() {}
public void startDTD(String name, String publicId, String systemId) {}
public void endDTD() {}
public void startEntity(String name) {}
public void endEntity(String name) {}
/**
* Similar to the SAX2 method character(char[], int, int), but this
* method takes a string as its only parameter. The effect is the same.
*/
private void characters(final String string) {
final int length = string.length();
if (_currentOffset + length > _text.length) {
// GTM: resizeTextArray(_text.length * 2);
// bug fix 6189, contributed by Mirko Seifert
resizeTextArray(
Math.max(_text.length * 2, _currentOffset + length));
}
string.getChars(0, length, _text, _currentOffset);
_currentOffset += length;
}
private void resizeArrays(final int newSize, int length) {
if ((length < newSize) && (newSize == _currentNode))
length = _currentNode;
// Resize the '_type' array
final short[] newType = new short[newSize];
System.arraycopy(_type, 0, newType, 0, length);
_type = newType;
// Resize the '_parent' array
final int[] newParent = new int[newSize];
System.arraycopy(_parent, 0, newParent, 0, length);
_parent = newParent;
// Resize the '_nextSibling' array
final int[] newNextSibling = new int[newSize];
System.arraycopy(_nextSibling, 0, newNextSibling, 0, length);
_nextSibling = newNextSibling;
// Resize the '_offsetOrChild' array
final int[] newOffsetOrChild = new int[newSize];
System.arraycopy(_offsetOrChild, 0, newOffsetOrChild, 0,length);
_offsetOrChild = newOffsetOrChild;
// Resize the '_lengthOrAttr' array
final int[] newLengthOrAttr = new int[newSize];
System.arraycopy(_lengthOrAttr, 0, newLengthOrAttr, 0, length);
_lengthOrAttr = newLengthOrAttr;
// Resize the '_whitespace' array (a BitArray instance)
_whitespace.resize(newSize);
// Resize the '_dontEscape' array (a BitArray instance)
if (_dontEscape != null) {
_dontEscape.resize(newSize);
}
// Resize the '_prefix' array
final short[] newPrefix = new short[newSize];
System.arraycopy(_prefix, 0, newPrefix, 0, length);
_prefix = newPrefix;
}
private void resizeArrays2(final int newSize, final int length) {
if (newSize > length) {
// Resize the '_type2' array (attribute types)
final short[] newType = new short[newSize];
System.arraycopy(_type2, 0, newType, 0, length);
_type2 = newType;
// Resize the '_parent2' array (attribute parent elements)
final int[] newParent = new int[newSize];
System.arraycopy(_parent2, 0, newParent, 0, length);
_parent2 = newParent;
// Resize the '_nextSibling2' array (you get the idea...)
final int[] newNextSibling = new int[newSize];
System.arraycopy(_nextSibling2, 0, newNextSibling, 0, length);
_nextSibling2 = newNextSibling;
// Resize the '_offset' array (attribute value start)
final int[] newOffset = new int[newSize];
System.arraycopy(_offset, 0, newOffset, 0, length);
_offset = newOffset;
// Resize the 'length' array (attribute value length)
final int[] newLength = new int[newSize];
System.arraycopy(_length, 0, newLength, 0, length);
_length = newLength;
// Resize the '_prefix2' array
final short[] newPrefix = new short[newSize];
System.arraycopy(_prefix2, 0, newPrefix, 0, length);
_prefix2 = newPrefix;
}
}
private void shiftAttributes(final int shift) {
int i = 0;
int next = 0;
final int limit = _currentAttributeNode;
int lastParent = -1;
for (i = 0; i < limit; i++) {
if (_parent2[i] != lastParent) {
lastParent = _parent2[i];
_lengthOrAttr[lastParent] = i + shift;
}
next = _nextSibling2[i];
_nextSibling2[i] = next != 0 ? next + shift : 0;
}
}
private void appendAttributes() {
final int len = _currentAttributeNode;
if (len > 0) {
final int dst = _currentNode;
System.arraycopy(_type2, 0, _type, dst, len);
System.arraycopy(_prefix2, 0, _prefix, dst, len);
System.arraycopy(_parent2, 0, _parent, dst, len);
System.arraycopy(_nextSibling2, 0, _nextSibling, dst, len);
System.arraycopy(_offset, 0, _offsetOrChild, dst, len);
System.arraycopy(_length, 0, _lengthOrAttr, dst, len);
}
}
public boolean setEscaping(boolean value) {
final boolean temp = _escaping;
_escaping = value;
return temp;
}
} // end of DOMBuilder
}