Package org.exist.memtree

Source Code of org.exist.memtree.NodeImpl

/*
*  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.xquery.*;
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 org.exist.EXistException;
import org.exist.collections.Collection;
import org.exist.dom.DocumentAtExist;
import org.exist.dom.DocumentSet;
import org.exist.dom.EmptyNodeSet;
import org.exist.dom.NodeAtExist;
import org.exist.dom.NodeSet;
import org.exist.dom.QName;
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.Receiver;
import org.exist.xmldb.XmldbURI;
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 java.util.Iterator;
import java.util.Properties;


public abstract class NodeImpl implements NodeAtExist, NodeValue {

    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<Collection> getCollectionIterator() {
        return( EmptyNodeSet.EMPTY_COLLECTION_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:
                final 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" );
        }
    }

    public QName getQName() {
        switch( getNodeType() ) {
            case Node.ATTRIBUTE_NODE:
            case Node.ELEMENT_NODE:
            case Node.PROCESSING_INSTRUCTION_NODE:
                final 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)
     */
    @Override
    public boolean equals( Object obj ) {
        if( !( obj instanceof NodeImpl ) ) {
            return( false );
        }
        final 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 );
        }
        final 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 );
        }
        final 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 );
        }
    }

    public Sequence tail() throws XPathException {
      return Sequence.EMPTY_SEQUENCE;
    }
   
    /* (non-Javadoc)
     * @see org.w3c.dom.Node#getChildNodes()
     */
    public NodeList getChildNodes() {
        throw( new RuntimeException( "Can not call getChildNodes() on node type " + this.getNodeType() ) );
    }

    /* (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 );
        }
        final 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() {
        final 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 );
    }

    public DocumentAtExist getDocumentAtExist() {
        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());
        //the default value is
        return( false );
    }

    /* (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() {
    }

    /* (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 );
        }
        switch(getNodeType()) {
            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() {
        final 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 )
                || ( document.nodeKind[next] == Node.PROCESSING_INSTRUCTION_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() {
        final 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:
              case Node.PROCESSING_INSTRUCTION_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( UntypedAtomicValue.convertTo( null, getStringValue(), 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 {
        final ValueSequence seq = new ValueSequence();
        seq.add( this );
        return( seq.toNodeSet() );
    }

    public MemoryNodeSet toMemNodeSet() throws XPathException {
        return( new ValueSequence( this ).toMemNodeSet() );
    }

    /* (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 {
        final 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)
     */
    @Override
    public <T> T toJavaObject(final Class<T> target) throws XPathException {
        if(target.isAssignableFrom(NodeImpl.class)) {
            return (T)this;
        } else if(target.isAssignableFrom(Node.class)) {
            return (T)this;
        } else if(target == Object.class) {
            return (T)this;
        } else {
            final 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
    }

    public String getBaseURI() {
        return( null );
    }

    public void destroy(XQueryContext context, Sequence contextSequence) {
        // nothing to do
    }

    protected XmldbURI calculateBaseURI() {
      return null;
    }

    public abstract void selectAttributes( NodeTest test, Sequence result ) throws XPathException;
   
    public abstract void selectDescendantAttributes( NodeTest test, Sequence result ) throws XPathException;

    public abstract void selectChildren( NodeTest test, Sequence result ) throws XPathException;

    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 < 1 ) {
            return;
        }
        if( includeSelf ) {
            final NodeImpl n = document.getNode( nodeNumber );
            if( test.matches( n ) ) {
                result.add( n );
            }
        }
        int nextNode = document.getParentNodeFor( nodeNumber );
        while( nextNode > 0 ) {
            final 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 {
        final int parent   = document.getParentNodeFor( nodeNumber );
        int nextNode = document.getFirstChildFor( parent );
        while( ( nextNode >= parent ) && ( nextNode < nodeNumber ) ) {
            final 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 {
        final NodeId myNodeId = getNodeId();
        int    count    = 0;

        for( int i = nodeNumber - 1; i > 0; i-- ) {
            final 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 {
        final 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 ) {
                final 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 {
        final 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 {
            final NodeId myNodeId = getNodeId();
            int    count    = 0;
            int    nextNode = nodeNumber + 1;
            while( nextNode < document.size ) {
                final 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++;
            }
        }
    }

    public boolean matchAttributes( NodeTest test ) {
        // do nothing
        return( false );
        //TODO : make abstract
    }

    public boolean matchDescendantAttributes( NodeTest test ) throws XPathException {
        // do nothing
        return( false );
        //TODO : make abstract
    }

    public boolean matchChildren( NodeTest test ) throws XPathException {
        // do nothing
        return( false );
        //TODO : make abstract
    }

    public boolean matchDescendants( boolean includeSelf, NodeTest test ) throws XPathException {
        return( includeSelf && test.matches( this ) );
    }

    public boolean matchAncestors( boolean includeSelf, NodeTest test ) {
        if( nodeNumber < 2 ) {
            return( false );
        }
        if( includeSelf ) {
            final NodeImpl n = document.getNode( nodeNumber );
            if( test.matches( n ) ) {
                return( true );
            }
        }
        int nextNode = document.getParentNodeFor( nodeNumber );
        while( nextNode > 0 ) {
            final NodeImpl n = document.getNode( nextNode );
            if( test.matches( n ) ) {
                return( true );
            }
            nextNode = document.getParentNodeFor( nextNode );
        }
        return( false );
    }

    public boolean matchPrecedingSiblings( NodeTest test ) {
        final int parent   = document.getParentNodeFor( nodeNumber );
        int nextNode = document.getFirstChildFor( parent );
        while( ( nextNode >= parent ) && ( nextNode < nodeNumber ) ) {
            final NodeImpl n = document.getNode( nextNode );
            if( test.matches( n ) ) {
                return( true );
            }
            nextNode = document.next[nextNode];
        }
        return( false );
    }

    public boolean matchPreceding( NodeTest test, int position )
            throws EXistException {
        final NodeId myNodeId = getNodeId();
        int    count    = 0;
        for( int i = nodeNumber - 1; i > 0; i-- ) {
            final NodeImpl n = document.getNode( i );
            if( !myNodeId.isDescendantOf( n.getNodeId() ) && test.matches( n ) ) {
                if( ( position < 0 ) || ( ++count == position ) ) {
                    return( true );
                }
                if( count == position ) {
                    break;
                }
            }
        }
        return( false );
    }

    public boolean matchFollowingSiblings( NodeTest test ) {
        final int parent = document.getParentNodeFor( nodeNumber );
        if( parent == 0 ) {
            // parent is the document node
            if( getNodeType() == Node.ELEMENT_NODE ) {
                return( false );
            }
            NodeImpl next = (NodeImpl)getNextSibling();
            while( next != null ) {
                if( test.matches( next ) ) {
                    return( true );
                }
                if( next.getNodeType() == Node.ELEMENT_NODE ) {
                    break;
                }
                next = (NodeImpl)next.getNextSibling();
            }
        } else {
            int nextNode = document.getFirstChildFor( parent );
            while( nextNode > parent ) {
                final NodeImpl n = document.getNode( nextNode );
                if( ( nextNode > nodeNumber ) && test.matches( n ) ) {
                    return( true );
                }
                nextNode = document.next[nextNode];
            }
        }
        return( false );
    }

    public boolean matchFollowing( NodeTest test, int position )
            throws XPathException, EXistException {
        final int parent = document.getParentNodeFor( nodeNumber );
        if( parent == 0 ) {
            // parent is the document node
            if( getNodeType() == Node.ELEMENT_NODE ) {
                return( false );
            }
            NodeImpl next = (NodeImpl)getNextSibling();
            while( next != null ) {
                if( test.matches( next ) ) {
                    if( next.matchDescendants( true, test ) ) {
                        return( true );
                    }
                }
                if( next.getNodeType() == Node.ELEMENT_NODE ) {
                    break;
                }
                next = (NodeImpl)next.getNextSibling();
            }
        } else {
            final NodeId myNodeId = getNodeId();
            int    count    = 0;
            int    nextNode = nodeNumber + 1;
            while( nextNode < document.size ) {
                final NodeImpl n = document.getNode( nextNode );
                if( !n.getNodeId().isDescendantOf( myNodeId ) && test.matches( n ) ) {
                    if( ( position < 0 ) || ( ++count == position ) ) {
                        return( true );
                    }
                    if( count == position ) {
                        break;
                    }
                }
                nextNode++;
            }
        }
        return( false );
    }

    /**
     * ? @see org.w3c.dom.Node#compareDocumentPosition(org.w3c.dom.Node)
     *
     * @param   other  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     *
     * @throws  DOMException  DOCUMENT ME!
     */
    public short compareDocumentPosition( Node other ) throws DOMException {
        throw( new RuntimeException( "Can not call compareDocumentPosition() on node type " + this.getNodeType() ) );
    }

    /**
     * ? @see org.w3c.dom.Node#getTextContent()
     *
     * @return  DOCUMENT ME!
     *
     * @throws  DOMException  DOCUMENT ME!
     */
    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)
     *
     * @param   textContent  DOCUMENT ME!
     *
     * @throws  DOMException  DOCUMENT ME!
     */
    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)
     *
     * @param   other  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     */
    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)
     *
     * @param   namespaceURI  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     */
    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)
     *
     * @param   namespaceURI  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     */
    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)
     *
     * @param   prefix  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     */
    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)
     *
     * @param   arg  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     */
    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)
     *
     * @param   feature  DOCUMENT ME!
     * @param   version  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     */
    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)
     *
     * @param   key      DOCUMENT ME!
     * @param   data     DOCUMENT ME!
     * @param   handler  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     */
    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)
     *
     * @param   key  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     */
    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 ) {
        //Nothing to do
    }

    public int getState() {
        return( 0 );
    }

    public boolean isCacheable() {
        return( true );
    }

    public boolean hasChanged( int previousState ) {
        return( false ); // will never change
    }

    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() {
            final NodeImpl next = node;
            node = null;
            return( next );
        }

    }
}
TOP

Related Classes of org.exist.memtree.NodeImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.