/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-06 Wolfgang M. Meier
* wolfgang@exist-db.org
* http://exist.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
package org.exist.memtree;
import org.exist.dom.DocumentSet;
import org.exist.dom.EmptyNodeSet;
import org.exist.dom.NodeSet;
import org.exist.dom.QName;
import org.exist.dom.QNameable;
import org.exist.dom.StoredNode;
import org.exist.numbering.NodeId;
import org.exist.storage.DBBroker;
import org.exist.storage.serializers.Serializer;
import org.exist.util.serializer.DOMStreamer;
import org.exist.util.serializer.Receiver;
import org.exist.util.serializer.SerializerPool;
import org.exist.xquery.Cardinality;
import org.exist.xquery.Constants;
import org.exist.xquery.NodeTest;
import org.exist.xquery.XPathException;
import org.exist.xquery.value.AtomicValue;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.MemoryNodeSet;
import org.exist.xquery.value.NodeValue;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceIterator;
import org.exist.xquery.value.StringValue;
import org.exist.xquery.value.Type;
import org.exist.xquery.value.UntypedAtomicValue;
import org.exist.xquery.value.ValueSequence;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.UserDataHandler;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import java.util.Iterator;
import java.util.Properties;
public class NodeImpl implements Node, NodeValue, QNameable, Comparable {
public final static short REFERENCE_NODE = 100;
public final static short NAMESPACE_NODE = 101;
protected int nodeNumber;
protected DocumentImpl document;
public NodeImpl(DocumentImpl doc, int nodeNumber) {
this.document = doc;
this.nodeNumber = nodeNumber;
}
public int getNodeNumber() {
return nodeNumber;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.NodeValue#getImplementation()
*/
public int getImplementationType() {
return NodeValue.IN_MEMORY_NODE;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#getDocumentSet()
*/
public DocumentSet getDocumentSet() {
return DocumentSet.EMPTY_DOCUMENT_SET;
}
public Iterator getCollectionIterator() {
return EmptyNodeSet.EMPTY_ITERATOR;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.NodeValue#getNode()
*/
public Node getNode() {
return this;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getNodeName()
*/
public String getNodeName() {
switch (getType()) {
case Type.DOCUMENT :
return "#document";
case Type.ELEMENT :
case Type.PROCESSING_INSTRUCTION :
QName qn = document.nodeName[nodeNumber];
//TODO : check !
return qn.getStringValue();
case Type.ATTRIBUTE:
return (document.attrName[nodeNumber]).getStringValue();
case Type.NAMESPACE:
return (document.namespaceCode[nodeNumber]).getStringValue();
case Type.TEXT :
return "#text";
case Type.COMMENT :
return "#comment";
case Type.CDATA_SECTION :
return "#cdata-section";
default :
return "#unknown";
}
}
//TODO : what are the semantics ? IMHO, QName.EMPTY_QNAME shouldn't be QNameable ! -pb
public QName getQName() {
switch (getNodeType()) {
case Node.ATTRIBUTE_NODE :
case Node.ELEMENT_NODE :
case Node.PROCESSING_INSTRUCTION_NODE :
QName qn = document.nodeName[nodeNumber];
return qn;
case Node.DOCUMENT_NODE :
return QName.EMPTY_QNAME;
case Node.COMMENT_NODE:
return QName.EMPTY_QNAME;
case Node.TEXT_NODE :
return QName.EMPTY_QNAME;
case Node.CDATA_SECTION_NODE :
return QName.EMPTY_QNAME;
default :
return null;
}
}
public NodeId getNodeId() {
expand();
return document.nodeId[nodeNumber];
}
public void expand() throws DOMException {
document.expand();
}
public void deepCopy() throws DOMException {
DocumentImpl newDoc = document.expandRefs(this);
if (newDoc != document) {
// we received a new document
this.nodeNumber = 1;
this.document = newDoc;
}
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getNodeValue()
*/
public String getNodeValue() throws DOMException {
throw new RuntimeException(getClass().getName() + ": can not call getNodeValue() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#setNodeValue(java.lang.String)
*/
public void setNodeValue(String arg0) throws DOMException {
throw new RuntimeException("Can not call setNodeValue() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getNodeType()
*/
public short getNodeType() {
//Workaround for fn:string-length(fn:node-name(document {""}))
if (this.document == null)
return Node.DOCUMENT_NODE;
return document.nodeKind[nodeNumber];
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getParentNode()
*/
public Node getParentNode() {
int next = document.next[nodeNumber];
while (next > nodeNumber) {
next = document.next[next];
}
if (next < 0)
return null;
return document.getNode(next);
}
public Node selectParentNode() {
// as getParentNode() but doesn't return the document itself
if (nodeNumber == 0)
return null;
int next = document.next[nodeNumber];
while (next > nodeNumber) {
next = document.next[next];
}
if (next < 0) //Is this even possible ?
return null;
if (next == 0)
return this.document.explicitCreation ? this.document : null;
return document.getNode(next);
}
public void addContextNode(int contextId, NodeValue node) {
throw new RuntimeException("Can not call addContextNode() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if(!(obj instanceof NodeImpl))
return false;
NodeImpl o = (NodeImpl) obj;
return document == o.document && nodeNumber == o.nodeNumber &&
getNodeType() == o.getNodeType();
}
/* (non-Javadoc)
* @see org.exist.xquery.value.NodeValue#equals(org.exist.xquery.value.NodeValue)
*/
public boolean equals(NodeValue other) throws XPathException {
if (other.getImplementationType() != NodeValue.IN_MEMORY_NODE)
return false;
NodeImpl o = (NodeImpl) other;
return document == o.document && nodeNumber == o.nodeNumber &&
getNodeType() == o.getNodeType();
}
/* (non-Javadoc)
* @see org.exist.xquery.value.NodeValue#after(org.exist.xquery.value.NodeValue)
*/
public boolean after(NodeValue other, boolean isFollowing) throws XPathException {
if (other.getImplementationType() != NodeValue.IN_MEMORY_NODE)
throw new XPathException("cannot compare persistent node with in-memory node");
return nodeNumber > ((NodeImpl) other).nodeNumber;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.NodeValue#before(org.exist.xquery.value.NodeValue)
*/
public boolean before(NodeValue other, boolean isPreceding) throws XPathException {
if (other.getImplementationType() != NodeValue.IN_MEMORY_NODE)
throw new XPathException("cannot compare persistent node with in-memory node");
return nodeNumber < ((NodeImpl) other).nodeNumber;
}
public int compareTo(Object other) {
if(!(other instanceof NodeImpl))
return Constants.INFERIOR;
NodeImpl n = (NodeImpl)other;
if(n.document == document) {
if (nodeNumber == n.nodeNumber && getNodeType() == n.getNodeType())
return Constants.EQUAL;
else if (nodeNumber < n.nodeNumber)
return Constants.INFERIOR;
else
return Constants.SUPERIOR;
} else if (document.docId < n.document.docId)
return Constants.INFERIOR;
else
return Constants.SUPERIOR;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getChildNodes()
*/
public NodeList getChildNodes() {
throw new RuntimeException("Can not call getChildNodes() on node type " + this.getNodeType());
//return new NodeListImpl();
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getFirstChild()
*/
public Node getFirstChild() {
throw new RuntimeException("Can not call getFirstChild() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getLastChild()
*/
public Node getLastChild() {
throw new RuntimeException("Can not call getLastChild() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getPreviousSibling()
*/
public Node getPreviousSibling() {
if (nodeNumber == 0)
return null;
int parent = document.getParentNodeFor(nodeNumber);
int nextNode = document.getFirstChildFor(parent);
while (nextNode >= parent && nextNode < nodeNumber) {
int following = document.next[nextNode];
if (following == nodeNumber)
return document.getNode(nextNode);
nextNode = following;
}
return null;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getNextSibling()
*/
public Node getNextSibling() {
int nextNr = document.next[nodeNumber];
return nextNr < nodeNumber ? null : document.getNode(nextNr);
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getAttributes()
*/
public NamedNodeMap getAttributes() {
throw new RuntimeException("Can not call getAttributes() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getOwnerDocument()
*/
public Document getOwnerDocument() {
return document;
}
public DocumentImpl getDocument() {
return document;
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#insertBefore(org.w3c.dom.Node, org.w3c.dom.Node)
*/
public Node insertBefore(Node arg0, Node arg1) throws DOMException {
throw new RuntimeException("Can not call insertBefore() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#replaceChild(org.w3c.dom.Node, org.w3c.dom.Node)
*/
public Node replaceChild(Node arg0, Node arg1) throws DOMException {
throw new RuntimeException("Can not call replaceChild() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#removeChild(org.w3c.dom.Node)
*/
public Node removeChild(Node arg0) throws DOMException {
throw new RuntimeException("Can not call removeChild() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#newChild(org.w3c.dom.Node)
*/
public Node appendChild(Node arg0) throws DOMException {
throw new RuntimeException("Can not call appendChild() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#hasChildNodes()
*/
public boolean hasChildNodes() {
throw new RuntimeException("Can not call hasChildNodes() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#cloneNode(boolean)
*/
public Node cloneNode(boolean arg0) {
throw new RuntimeException("Can not call cloneNode() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#normalize()
*/
public void normalize() {
throw new RuntimeException("Can not call normalize() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#isSupported(java.lang.String, java.lang.String)
*/
public boolean isSupported(String arg0, String arg1) {
throw new RuntimeException("Can not call isSupported() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getNamespaceURI()
*/
public String getNamespaceURI() {
throw new RuntimeException("Can not call getNamespaceURI() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getPrefix()
*/
public String getPrefix() {
throw new RuntimeException("Can not call getPrefix() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#setPrefix(java.lang.String)
*/
public void setPrefix(String arg0) throws DOMException {
throw new RuntimeException("Can not call setPrefix() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#getLocalName()
*/
public String getLocalName() {
throw new RuntimeException("Can not call getLocalName() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.w3c.dom.Node#hasAttributes()
*/
public boolean hasAttributes() {
throw new RuntimeException("Can not call hasAttributes() on node type " + this.getNodeType());
}
/*
* Methods of interface Item
*/
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#getType()
*/
public int getType() {
//Workaround for fn:string-length(fn:node-name(document {""}))
if (this.document == null)
return Type.DOCUMENT;
int type = getNodeType();
switch (type) {
case Node.DOCUMENT_NODE :
return Type.DOCUMENT;
case Node.COMMENT_NODE :
return Type.COMMENT;
case Node.PROCESSING_INSTRUCTION_NODE :
return Type.PROCESSING_INSTRUCTION;
case Node.ELEMENT_NODE :
return Type.ELEMENT;
case Node.ATTRIBUTE_NODE :
return Type.ATTRIBUTE;
case Node.TEXT_NODE :
return Type.TEXT;
case Node.CDATA_SECTION_NODE :
return Type.CDATA_SECTION;
default :
return Type.NODE;
}
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#getStringValue()
*/
public String getStringValue() {
int level = document.treeLevel[nodeNumber];
int next = nodeNumber + 1;
int startOffset = 0;
int len = -1;
while (next < document.size && document.treeLevel[next] > level) {
if (document.nodeKind[next] == Node.TEXT_NODE ||
document.nodeKind[next] == Node.CDATA_SECTION_NODE) {
if (len < 0) {
startOffset = document.alpha[next];
len = document.alphaLen[next];
} else
len += document.alphaLen[next];
} else
return getStringValueSlow();
++next;
}
return len < 0 ? "" : new String(document.characters, startOffset, len);
}
private String getStringValueSlow() {
int level = document.treeLevel[nodeNumber];
StringBuilder buf = null;
int next = nodeNumber + 1;
while (next < document.size && document.treeLevel[next] > level) {
switch (document.nodeKind[next]) {
case Node.TEXT_NODE :
case Node.CDATA_SECTION_NODE :
if (buf == null)
buf = new StringBuilder();
buf.append(
document.characters,
document.alpha[next],
document.alphaLen[next]);
break;
case REFERENCE_NODE :
if (buf == null)
buf = new StringBuilder();
buf.append(document.references[document.alpha[next]].getStringValue());
break;
}
++next;
}
return buf == null ? "" : buf.toString();
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#toSequence()
*/
public Sequence toSequence() {
return this;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#convertTo(int)
*/
public AtomicValue convertTo(int requiredType) throws XPathException {
return new StringValue(getStringValue()).convertTo(requiredType);
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#atomize()
*/
public AtomicValue atomize() throws XPathException {
return new UntypedAtomicValue(getStringValue());
}
/*
* Methods of interface Sequence
*/
public boolean isEmpty() {
return false;
}
public boolean hasOne() {
return true;
}
public boolean hasMany() {
return false;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#add(org.exist.xquery.value.Item)
*/
public void add(Item item) throws XPathException {
throw new RuntimeException("Can not call add() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#addAll(org.exist.xquery.value.Sequence)
*/
public void addAll(Sequence other) throws XPathException {
throw new RuntimeException("Can not call addAll() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#getItemType()
*/
public int getItemType() {
return Type.NODE;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#iterate()
*/
public SequenceIterator iterate() throws XPathException {
return new SingleNodeIterator(this);
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#unorderedIterator()
*/
public SequenceIterator unorderedIterator() {
return new SingleNodeIterator(this);
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#getItemCount()
*/
public int getItemCount() {
return 1;
}
public int getLength() {
//Let the derived classes do it...
throw new RuntimeException("Can not call getLength() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#getCardinality()
*/
public int getCardinality() {
return Cardinality.EXACTLY_ONE;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#itemAt(int)
*/
public Item itemAt(int pos) {
return pos == 0 ? this : null;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#effectiveBooleanValue()
*/
public boolean effectiveBooleanValue() throws XPathException {
//A node evaluates to true()
return true;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#toNodeSet()
*/
public NodeSet toNodeSet() throws XPathException {
// throw new XPathException("Querying constructed nodes is not yet implemented");
ValueSequence seq = new ValueSequence();
seq.add(this);
return seq.toNodeSet();
}
public MemoryNodeSet toMemNodeSet() throws XPathException {
return new ValueSequence(this).toMemNodeSet();
}
private final static class SingleNodeIterator implements SequenceIterator {
NodeImpl node;
public SingleNodeIterator(NodeImpl node) {
this.node = node;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.SequenceIterator#hasNext()
*/
public boolean hasNext() {
return node != null;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.SequenceIterator#nextItem()
*/
public Item nextItem() {
NodeImpl next = node;
node = null;
return next;
}
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#toSAX(org.exist.storage.DBBroker, org.xml.sax.ContentHandler)
*/
public void toSAX(DBBroker broker, ContentHandler handler, Properties properties) throws SAXException {
Serializer serializer = broker.getSerializer();
serializer.reset();
serializer.setProperty(Serializer.GENERATE_DOC_EVENTS, "false");
if (properties != null)
serializer.setProperties(properties);
if(handler instanceof LexicalHandler) {
serializer.setSAXHandlers(handler, (LexicalHandler) handler);
} else {
serializer.setSAXHandlers(handler, null);
}
serializer.toSAX(this);
}
public void copyTo(DBBroker broker, DocumentBuilderReceiver receiver) throws SAXException {
//Null test for document nodes
if (document != null)
document.copyTo(this, receiver);
}
public void streamTo(Serializer serializer, Receiver receiver) throws SAXException {
//Null test for document nodes
if (document != null)
document.streamTo(serializer, this, receiver);
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#conversionPreference(java.lang.Class)
*/
public int conversionPreference(Class javaClass) {
if (javaClass.isAssignableFrom(NodeImpl.class))
return 0;
if (javaClass.isAssignableFrom(Node.class))
return 1;
if (javaClass == String.class || javaClass == CharSequence.class)
return 2;
if (javaClass == Character.class || javaClass == char.class)
return 2;
if (javaClass == Double.class || javaClass == double.class)
return 10;
if (javaClass == Float.class || javaClass == float.class)
return 11;
if (javaClass == Long.class || javaClass == long.class)
return 12;
if (javaClass == Integer.class || javaClass == int.class)
return 13;
if (javaClass == Short.class || javaClass == short.class)
return 14;
if (javaClass == Byte.class || javaClass == byte.class)
return 15;
if (javaClass == Boolean.class || javaClass == boolean.class)
return 16;
if (javaClass == Object.class)
return 20;
return Integer.MAX_VALUE;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Item#toJavaObject(java.lang.Class)
*/
public Object toJavaObject(Class target) throws XPathException {
if (target.isAssignableFrom(NodeImpl.class))
return this;
else if (target.isAssignableFrom(Node.class))
return this;
else if (target == Object.class)
return this;
else {
StringValue v = new StringValue(getStringValue());
return v.toJavaObject(target);
}
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#setSelfAsContext(int)
*/
public void setSelfAsContext(int contextId) {
throw new RuntimeException("Can not call setSelfAsContext() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#isCached()
*/
public boolean isCached() {
// always return false
return false;
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#setIsCached(boolean)
*/
public void setIsCached(boolean cached) {
// ignore
throw new RuntimeException("Can not call setIsCached() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#removeDuplicates()
*/
public void removeDuplicates() {
// do nothing: this is a single node
}
/** ? @see org.w3c.dom.Node#getBaseURI()
*/
public String getBaseURI() {
return null;
}
public void selectAttributes(NodeTest test, Sequence result) throws XPathException {
// do nothing
}
public void selectDescendantAttributes(NodeTest test, Sequence result) throws XPathException {
// do nothing
}
public void selectChildren(NodeTest test, Sequence result) throws XPathException {
// do nothing
}
public void selectDescendants(boolean includeSelf, NodeTest test, Sequence result) throws XPathException {
if (includeSelf && test.matches(this))
result.add(this);
}
public void selectAncestors(boolean includeSelf, NodeTest test, Sequence result) throws XPathException {
if (nodeNumber < 2)
return;
if (includeSelf) {
NodeImpl n = document.getNode(nodeNumber);
if (test.matches(n))
result.add(n);
}
int nextNode = document.getParentNodeFor(nodeNumber);
while (nextNode > 0) {
NodeImpl n = document.getNode(nextNode);
if (test.matches(n))
result.add(n);
nextNode = document.getParentNodeFor(nextNode);
}
}
public void selectPrecedingSiblings(NodeTest test, Sequence result) throws XPathException {
int parent = document.getParentNodeFor(nodeNumber);
int nextNode = document.getFirstChildFor(parent);
while (nextNode >= parent && nextNode < nodeNumber) {
NodeImpl n = document.getNode(nextNode);
if (test.matches(n))
result.add(n);
nextNode = document.next[nextNode];
}
}
public void selectPreceding(NodeTest test, Sequence result, int position) throws XPathException {
NodeId myNodeId = getNodeId();
int count = 0;
for (int i = nodeNumber - 1; i > 0; i--) {
NodeImpl n = document.getNode(i);
if (!myNodeId.isDescendantOf(n.getNodeId()) && test.matches(n)) {
if (position < 0 || ++count == position)
result.add(n);
if (count == position)
break;
}
}
}
public void selectFollowingSiblings(NodeTest test, Sequence result) throws XPathException {
int parent = document.getParentNodeFor(nodeNumber);
if (parent == 0) {
// parent is the document node
if (getNodeType() == Node.ELEMENT_NODE)
return;
NodeImpl next = (NodeImpl) getNextSibling();
while (next != null) {
if (test.matches(next))
result.add(next);
if (next.getNodeType() == Node.ELEMENT_NODE)
break;
next = (NodeImpl) next.getNextSibling();
}
} else {
int nextNode = document.getFirstChildFor(parent);
while (nextNode > parent) {
NodeImpl n = document.getNode(nextNode);
if (nextNode > nodeNumber && test.matches(n))
result.add(n);
nextNode = document.next[nextNode];
}
}
}
public void selectFollowing(NodeTest test, Sequence result, int position) throws XPathException {
int parent = document.getParentNodeFor(nodeNumber);
if (parent == 0) {
// parent is the document node
if (getNodeType() == Node.ELEMENT_NODE)
return;
NodeImpl next = (NodeImpl) getNextSibling();
while (next != null) {
if (test.matches(next))
next.selectDescendants(true, test, result);
if (next.getNodeType() == Node.ELEMENT_NODE)
break;
next = (NodeImpl) next.getNextSibling();
}
} else {
NodeId myNodeId = getNodeId();
int count = 0;
int nextNode = nodeNumber + 1;
while (nextNode < document.size) {
NodeImpl n = document.getNode(nextNode);
if (!n.getNodeId().isDescendantOf(myNodeId) && test.matches(n)) {
if (position < 0 || ++count == position)
result.add(n);
if (count == position)
break;
}
nextNode++;
}
}
}
/** ? @see org.w3c.dom.Node#compareDocumentPosition(org.w3c.dom.Node)
*/
public short compareDocumentPosition(Node other) throws DOMException {
throw new RuntimeException("Can not call compareDocumentPosition() on node type " + this.getNodeType());
//return 0;
}
/** ? @see org.w3c.dom.Node#getTextContent()
*/
public String getTextContent() throws DOMException {
throw new RuntimeException("Can not call getTextContent() on node type " + this.getNodeType());
}
/** ? @see org.w3c.dom.Node#setTextContent(java.lang.String)
*/
public void setTextContent(String textContent) throws DOMException {
throw new RuntimeException("Can not call setTextContent() on node type " + this.getNodeType());
}
/** ? @see org.w3c.dom.Node#isSameNode(org.w3c.dom.Node)
*/
public boolean isSameNode(Node other) {
throw new RuntimeException("Can not call isSameNode() on node type " + this.getNodeType());
}
/** ? @see org.w3c.dom.Node#lookupPrefix(java.lang.String)
*/
public String lookupPrefix(String namespaceURI) {
throw new RuntimeException("Can not call lookupPrefix() on node type " + this.getNodeType());
}
/** ? @see org.w3c.dom.Node#isDefaultNamespace(java.lang.String)
*/
public boolean isDefaultNamespace(String namespaceURI) {
throw new RuntimeException("Can not call isDefaultNamespace() on node type " + this.getNodeType());
}
/** ? @see org.w3c.dom.Node#lookupNamespaceURI(java.lang.String)
*/
public String lookupNamespaceURI(String prefix) {
throw new RuntimeException("Can not call lookupNamespaceURI() on node type " + this.getNodeType());
}
/** ? @see org.w3c.dom.Node#isEqualNode(org.w3c.dom.Node)
*/
public boolean isEqualNode(Node arg) {
throw new RuntimeException("Can not call isEqualNode() on node type " + this.getNodeType());
}
/** ? @see org.w3c.dom.Node#getFeature(java.lang.String, java.lang.String)
*/
public Object getFeature(String feature, String version) {
throw new RuntimeException("Can not call getFeature() on node type " + this.getNodeType());
}
/** ? @see org.w3c.dom.Node#setUserData(java.lang.String, java.lang.Object, org.w3c.dom.UserDataHandler)
*/
public Object setUserData(String key, Object data, UserDataHandler handler) {
throw new RuntimeException("Can not call setUserData() on node type " + this.getNodeType());
}
/** ? @see org.w3c.dom.Node#getUserData(java.lang.String)
*/
public Object getUserData(String key) {
throw new RuntimeException("Can not call getUserData() on node type " + this.getNodeType());
}
/* (non-Javadoc)
* @see org.exist.xquery.value.Sequence#isPersistentSet()
*/
public boolean isPersistentSet() {
//See package's name ;-)
return false;
}
public void nodeMoved(NodeId oldNodeId, StoredNode newNode) {
// can not be applied to in-memory nodes
}
public void clearContext(int contextId) {
}
public int getState() {
return 0;
}
public boolean isCacheable() {
return true;
}
public boolean hasChanged(int previousState) {
return false; // will never change
}
}