blob: ce1b08109e63f3a86c336352bcd7579d317e3cbe [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";
// Types for generic elements and attributes
private final static Integer elementInt = new Integer(ELEMENT);
private final static Integer attributeInt = new Integer(ATTRIBUTE);
/**
* 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() {
synchronized (getClass()) { // synchornize access to static
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);
}
}
}
BasisLibrary.runTimeError(BasisLibrary.NAMESPACE_PREFIX_ERR, prefix);
return null;
}
/**
* 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];
return (node2 < _treeNodeLimit && node1 < node2);
}
/**
* 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 {
_currentChild = hasChildren(node) ? _offsetOrChild[node]
: END;
}
return resetPosition();
}
return this;
}
public int next() {
int node = _currentChild;
if (_includeSelf) {
if (node == -1) {
node = _startNode;
_currentChild = hasChildren(node) ? _offsetOrChild[node]
: 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 = 0;
int node;
if ((node = _offsetOrChild[_startNode]) != END) {
do {
_last++;
} while ((node = _nextSibling[node]) != END);
}
}
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() {
_currentChild = hasChildren(_startNode) ?
_offsetOrChild[_startNode] : END;
return resetPosition();
}
public int next() {
final short[] type = _type;
final int nodeType = _nodeType;
final int[] nextSibling = _nextSibling;
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;
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
// If not an element node, then set iterator at END
if (!isElement(node)) {
_attribute = END;
return resetPosition();
}
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 _sibling;
public boolean isReverse() {
return true;
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
_node = node;
_sibling = _startNode = (node >= _firstAttributeNode) ?
NULL : _offsetOrChild[_parent[node]];
_last = -1;
return resetPosition();
}
return this;
}
public int next() {
// Return NULL if end already reached
if (_sibling == NULL) {
return END;
}
if (_sibling == _node) {
return (_node = END);
}
final int current = _sibling;
_sibling = _nextSibling[current];
return returnNode(current);
}
public NodeIterator reset() {
_sibling = _startNode;
return resetPosition();
}
public void setMark() {
_markedNode = _sibling;
}
public void gotoMark() {
_sibling = _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;
private int _ancestor;
private int _index, _markedIndex;
private IntegerArray _ancestorOrSelf = new IntegerArray();
public boolean isReverse() {
return true;
}
public NodeIterator cloneIterator() {
try {
final PrecedingIterator clone =
(PrecedingIterator) super.clone();
clone.setRestartable(false);
clone._ancestorOrSelf = (IntegerArray) _ancestorOrSelf.clone();
return clone.reset();
}
catch (CloneNotSupportedException e) {
BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
e.toString());
return null;
}
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
_ancestorOrSelf.clear();
if (node >= _firstAttributeNode) {
node = _parent[node];
}
do {
_ancestorOrSelf.add(node);
} while ((node = _parent[node]) > ROOTNODE);
_index = _ancestorOrSelf.cardinality() - 1;
_node = _ancestorOrSelf.at(_index) + 1;
_ancestor = (_index > 0) ? _ancestorOrSelf.at(--_index)
: ROOTNODE;
_last = -1;
return resetPosition();
}
return this;
}
public int next() {
while (true) {
if (_node < _ancestor) {
return returnNode(_node++);
}
if (--_index < 0) break;
_ancestor = _ancestorOrSelf.at(_index);
_node++; // skip ancestor
}
return END;
}
public NodeIterator reset() {
_index = _ancestorOrSelf.cardinality() - 1;
_node = _ancestorOrSelf.at(_index) + 1;
_ancestor = (_index > 0) ? _ancestorOrSelf.at(--_index) : ROOTNODE;
return resetPosition();
}
public void setMark() {
_markedNode = _node;
_markedIndex = _index;
}
public void gotoMark() {
_node = _markedNode;
_index = _markedIndex;
_ancestor = _ancestorOrSelf.at(_markedIndex);
}
} // 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 IntegerArray _cache = new IntegerArray();
public NodeIterator cloneIterator() {
try {
final AncestorIterator clone = (AncestorIterator)super.clone();
clone.setRestartable(false); // must set to false for any clone
clone._cache = (IntegerArray) _cache.clone();
return clone.reset();
}
catch (CloneNotSupportedException e) {
BasisLibrary.runTimeError(BasisLibrary.ITERATOR_CLONE_ERR,
e.toString());
return null;
}
}
public NodeIterator setStartNode(int node) {
if (_isRestartable) {
_cache.clear();
if (_includeSelf) {
_cache.add(node);
}
while ((node = _parent[node]) != NULL) {
_cache.add(node);
}
_last = _cache.cardinality();
_startNode = _index = _last - 1;
return resetPosition();
}
return this;
}
public boolean isReverse() {
return true;
}
public int getLast() {
return _last;
}
public NodeIterator reset() {
_index = _startNode;
return resetPosition();
}
public int next() {
return (_index >= 0) ?
returnNode(_cache.at(_index--)) : END;
}
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 NodeIterator setStartNode(int node) {
if (_isRestartable) {
_cache.clear();
if (_includeSelf && _type[node] == _nodeType) {
_cache.add(node);
}
while ((node = _parent[node]) != NULL) {
if (_nodeType == _type[node]) {
_cache.add(node);
}
}
_last = _cache.cardinality();
_startNode = _index = _last - 1;
return resetPosition();
}
return this;
}
} // 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;
}
else if ((_limit = _nextSibling[node]) == 0) {
final int parent = _parent[node];
if (parent == 0 || (_limit = _nextSibling[parent]) == 0) {
_limit = _treeNodeLimit;
}
else {
// _limit already set to parent's next sibling.
}
}
else {
// _limit already set to next sibling.
}
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 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];
}
/**
* 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) {
return (node >= _type.length) ? 0 : _type[node];
}
/**
* Returns the namespace type of a specific node
*/
public int getNamespaceType(final int node) {
final int type = _type[node];
return (type >= NTYPES) ? _namespace[type-NTYPES]
: 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(' ');
return (col > 0) ? pistr.substring(col+1) : pistr;
default:
return (node < _firstAttributeNode) ? getElementValue(node) :
makeStringValue(node);
}
}
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) {
Integer type = (Integer)_types.get(name);
if (type == null) {
// memorize default type
_types.put(name,
type = (name.charAt(0) == '@') ? attributeInt : elementInt);
}
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++;
}
result[i] = (short) ((name.length() > 0 && name.charAt(len) == '@') ?
ATTRIBUTE : 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]);
result[i] = (type == null) ? -1 : 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);
}
/*
* These init sizes have been tuned for the average case. Do not
* change these values unless you know exactly what you're doing.
*/
static private final int SMALL_TEXT_SIZE = 1024;
static private final int DEFAULT_INIT_SIZE = 1024;
static private final int DEFAULT_TEXT_FACTOR = 10;
public DOMImpl() {
this(DEFAULT_INIT_SIZE);
}
public DOMImpl(int size) {
initialize(size, size < 128 ? SMALL_TEXT_SIZE :
size * DEFAULT_TEXT_FACTOR);
}
private void initialize(int size, int textsize) {
_type = new short[size];
_parent = new int[size];
_nextSibling = new int[size];
_offsetOrChild = new int[size];
_lengthOrAttr = new int[size];
_text = new char[textsize];
_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];
return (index < _prefixArray.length) ? _prefixArray[index]
: EMPTYSTRING;
case DOM.PROCESSING_INSTRUCTION:
final String pistr = makeStringValue(node);
final int col = pistr.indexOf(' ');
return (col > -1) ? pistr.substring(0,col) : 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];
return (name == null) ? EMPTYSTRING : 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);
return (attr != NULL) ? makeStringValue(attr) : EMPTYSTRING;
}
/**
* Returns true if a given element has an attribute of a given type
*/
public boolean hasAttribute(final int type, final int node) {
return (getAttributeNode(type, node) != NULL);
}
/**
* 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) {
return hasChildren(node) ? new ChildrenIterator()
: 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) {
/* 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).
*/
// Most common case handled first
if (axis == Axis.CHILD && type != ELEMENT) {
return new TypedChildrenIterator(type);
}
if (type == NO_TYPE) {
return EMPTYITERATOR;
}
if (type == ELEMENT && axis != Axis.NAMESPACE) {
return new FilterIterator(getAxisIterator(axis),
getElementFilter());
}
else {
switch (axis) {
case Axis.SELF:
return new TypedSingletonIterator(type);
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:
return new TypedDescendantIterator(type);
case Axis.DESCENDANTORSELF:
return (new TypedDescendantIterator(type)).includeSelf();
case Axis.FOLLOWING:
return new TypedFollowingIterator(type);
case Axis.PRECEDING:
return new TypedPrecedingIterator(type);
case Axis.FOLLOWINGSIBLING:
return new TypedFollowingSiblingIterator(type);
case Axis.PRECEDINGSIBLING:
return new TypedPrecedingSiblingIterator(type);
case Axis.NAMESPACE:
return (type == ELEMENT) ?
(NodeIterator) new NamespaceIterator() :
(NodeIterator) new TypedNamespaceIterator(type);
default:
BasisLibrary.runTimeError(BasisLibrary.TYPED_AXIS_SUPPORT_ERR,
Axis.names[axis]);
}
}
return null;
}
/**
* 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) {
if (ns == NO_TYPE) {
return EMPTYITERATOR;
}
else {
switch (axis) {
case Axis.CHILD:
return new NamespaceChildrenIterator(ns);
case Axis.ATTRIBUTE:
return new NamespaceAttributeIterator(ns);
default:
BasisLibrary.runTimeError(BasisLibrary.TYPED_AXIS_SUPPORT_ERR,
Axis.names[axis]);
}
}
return null;
}
/**
* Returns an iterator with all descendants of a node that are of
* a given type.
*/
public NodeIterator getTypedDescendantIterator(int type) {
return (type == ELEMENT) ? (NodeIterator)
new FilterIterator(new DescendantIterator(), getElementFilter())
: (NodeIterator) new TypedDescendantIterator(type);
}
/**
* Returns the nth descendant of a node
*/
public NodeIterator getNthDescendant(int type, int n, boolean includeself) {
NodeIterator source = (type == ELEMENT) ? (NodeIterator)
new FilterIterator(new DescendantIterator(), getElementFilter())
: (NodeIterator) 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);
name = prefix.equals(EMPTYSTRING) ? local : (prefix + ':' + local);
handler.startElement(name);
handler.namespace(prefix, uri);
}
else {
if (ui > 0) {
handler.startElement(name = getLocalName(node));
handler.namespace(EMPTYSTRING, _uriArray[ui]);
}
else {
handler.startElement(name);
}
}
// Copy element namespaces
for (int a = _lengthOrAttr[node]; a != NULL; a = _nextSibling[a]) {
if (_type[a] == NAMESPACE) {
handler.namespace(_prefixArray[_prefix[a]],
makeStringValue(a));
}
}
return name;
}
/**
* Returns the string value of the entire tree
*/
private String _cachedStringValue = null;
public String getStringValue() {
if (_cachedStringValue == null) {
_cachedStringValue = getElementValue(ROOTNODE);
}
return _cachedStringValue;
}
/**
* 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);
int attribute = _lengthOrAttr[element];
while (attribute != NULL) {
// Skip namespace nodes
if (_type[attribute] != NAMESPACE) {
buffer.append(' ').append(getNodeName(attribute))
.append("=\"").append(getNodeValue(attribute))
.append('"');
}
attribute = _nextSibling[attribute];
}
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 = 0; // turned off
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 _currentNode = 0;
private int _currentOffset = 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 _lastNamespace = DOM.NULL;
private int _nextNamespace = 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);
return (stack != null && !stack.empty()) ? (String) stack.peek()
: 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) {
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;
definePrefixAndUri(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
{
// System.out.println("DOMImpl.startElement() qname = " + qname);
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?
_nextSibling2[(!attrsAdded && last != -1) ? last : attr] = DOM.NULL;
}
final int col = qname.lastIndexOf(':');
// Assign an internal type to this element (may exist)
_type[node] = (uri != null && localName.length() > 0) ?
makeElementNode(uri, localName) : 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(
Math.max(_text.length * 2, _currentOffset + length));
}
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
{
final Stack stack = definePrefixAndUri(prefix, uri);
makeTextNode(false);
int attr = makeNamespaceNode(prefix, uri);
if (_nextNamespace == DOM.NULL) {
_nextNamespace = attr;
}
else {
_nextSibling2[attr-1] = attr;
}
_nextSibling2[attr] = DOM.NULL;
_prefix2[attr] = ((Integer) stack.elementAt(0)).shortValue();
}
private Stack definePrefixAndUri(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);
return stack;
}
/**
* 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(
Math.max(_text.length * 2, _currentOffset + length));
}
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) {
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
}