Package org.exist.dom

Source Code of org.exist.dom.VirtualNodeSet

/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-2007 The eXist Project
* http://exist-db.org
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*  $Id$
*/
package org.exist.dom;

import java.io.IOException;
import java.util.Iterator;

import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;

import org.exist.collections.Collection;
import org.exist.indexing.StructuralIndex;
import org.exist.numbering.NodeId;
import org.exist.stax.EmbeddedXMLStreamReader;
import org.exist.stax.ExtendedXMLStreamReader;
import org.exist.storage.DBBroker;
import org.exist.storage.ElementValue;
import org.exist.xquery.Constants;
import org.exist.xquery.Expression;
import org.exist.xquery.NodeTest;
import org.exist.xquery.XPathException;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.SequenceIterator;
import org.exist.xquery.value.Type;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* This node set is called virtual because it is just a placeholder for
* the set of relevant nodes. For XPath expressions like //* or //node(),
* it would be totally unefficient to actually retrieve all descendant nodes.
* In many cases, the expression can be resolved at a later point in time
* without retrieving the whole node set.
*
* VirtualNodeSet basically provides method getFirstParent to retrieve the first
* matching descendant of its context according to the primary type axis.
*
* Class LocationStep will always return an instance of VirtualNodeSet
* if it finds something like descendant::* etc..
*
* @author Wolfgang Meier
* @author Timo Boehme
*/
public class VirtualNodeSet extends AbstractNodeSet {

    private static final int MAX_CHILD_COUNT_FOR_OPTIMIZE = 5;

    protected int axis = Constants.UNKNOWN_AXIS;
    protected NodeTest test;
    protected NodeSet context;
    protected NodeSet realSet = null;
    protected boolean realSetIsComplete = false;
    protected boolean inPredicate = false;
    protected boolean useSelfAsContext = false;
    protected int contextId = Expression.NO_CONTEXT_ID;

    private DocumentSet realDocumentSet = null;
   
    private boolean knownIsEmptyCardinality = false;
    private boolean knownHasOneCardinality = false;
    private boolean knownHasManyCardinality = false;
   
    protected boolean hasMany = false;

    private DBBroker broker;

    /**
     * Creates a new <code>VirtualNodeSet</code> instance.
     *
     * @param axis an <code>int</code> value
     * @param test a <code>NodeTest</code> value
     * @param contextId an <code>int</code> value
     * @param context a <code>NodeSet</code> value
     */
    public VirtualNodeSet(DBBroker broker, int axis, NodeTest test, int contextId, NodeSet context) {
        isEmpty = true;
        hasOne = false;
        this.axis = axis;
        this.test = test;
        this.context = context;
        this.contextId = contextId;
        this.broker = broker;
    }

    /**
     * The method <code>contains</code>
     *
     * @param p a <code>NodeProxy</code> value
     * @return a <code>boolean</code> value
     */
    @Override
    public boolean contains(NodeProxy p) {
        final NodeProxy firstParent = getFirstParent(p, null, (axis == Constants.SELF_AXIS), 0);
        // Timo Boehme: getFirstParent returns now only real parents
        // therefore test if node is child of context
        if (firstParent != null) {
            return true;
        }
        return false;
    }

    /**
     * The method <code>setInPredicate</code>
     *
     * @param predicate a <code>boolean</code> value
     */
    public void setInPredicate(boolean predicate) {
        inPredicate = predicate;
    }

    /* (non-Javadoc)
     * @see org.exist.dom.AbstractNodeSet#getDocumentSet()
     */
    @Override
    public DocumentSet getDocumentSet() {
        //If we know what are our documents, return them...
        if (realDocumentSet != null)
            {return realDocumentSet;}
        //... otherwise, we default to every *ptotentially* concerned document
        return context.getDocumentSet();
    }

    /**
     * The method <code>getCollectionIterator</code>
     *
     * @return an <code>Iterator</code> value
     */
    @Override
    public Iterator<Collection> getCollectionIterator() {
        return context.getCollectionIterator();
    }

    /**
     * The method <code>getFirstParent</code>
     *
     * @param node a <code>NodeProxy</code> value
     * @param first a <code>NodeProxy</code> value
     * @param includeSelf a <code>boolean</code> value
     * @param recursions an <code>int</code> value
     * @return a <code>NodeProxy</code> value
     */
    private NodeProxy getFirstParent(NodeProxy self, NodeProxy firstParent,
             boolean includeSelf, int recursions) {
        return getFirstParent(self, firstParent, includeSelf, true, recursions);
    }

    /**
     * The method <code>getFirstParent</code>
     *
     * @param node a <code>NodeProxy</code> value
     * @param first a <code>NodeProxy</code> value
     * @param includeSelf a <code>boolean</code> value
     * @param directParent a <code>boolean</code> value
     * @param recursions an <code>int</code> value
     * @return a <code>NodeProxy</code> value
     */
    private NodeProxy getFirstParent(NodeProxy self, NodeProxy candidateFirstParent,
             boolean includeSelf, boolean restrictToDirectParent, int recursions) {
        /* if the node is a doument node we still need to
         * complete this method to check if we have found a potential parent
         * in one of the iterations before.
         */
        final NodeId parentOfSelfId = self.getNodeId().getParentId();
        // check if the start-node should be included, e.g. to process an
        // expression like *[. = 'xxx']
        //TODO : investigate on expression like *[.//* = 'xxx']
        if (recursions == 0 && includeSelf && test.matches(self)) {
            // if we're on the child axis, test if
            // the node is a direct child of the context node
            if (axis == Constants.CHILD_AXIS) {
                //WARNING : get() realizes virtual node sets
                //TODO : investigate more efficient solutions
                final NodeProxy parent = context.get(self.getDocument(), parentOfSelfId);
                if (parent != null) {
                    self.copyContext(parent);
                    if (useSelfAsContext && inPredicate) {
                        self.addContextNode(contextId, self);
                    } else if (inPredicate)
                        {self.addContextNode(contextId, parent);}
                    return self;
                }
            } else {
                // descendant axis: remember the node and continue
                candidateFirstParent = self;
            }
        }
        // if this is the first call to this method, remember the first
        // parent node and continue to evaluate the method. We can't just return
        // the first parent as we need a parent that is *actually* contained
        // in the context set. We will thus call the method again to complete.
        if (candidateFirstParent == null) {
            //given node was already document element -> no parent
            if (parentOfSelfId == NodeId.DOCUMENT_NODE) {
                return null;
            }
            candidateFirstParent = new NodeProxy(self.getDocument(), parentOfSelfId, Node.ELEMENT_NODE);
            // if we are on the self axis, check if the first parent can be selected
            if (axis == Constants.DESCENDANT_SELF_AXIS) {
                //WARNING : get() realizes virtual node sets
                //TODO : investigate more efficient solutions
                final NodeProxy parent = context.get(candidateFirstParent.getDocument(), parentOfSelfId);
                if (parent != null && test.matches(parent)) {
                    candidateFirstParent.copyContext(parent);
                    if (useSelfAsContext && inPredicate) {
                        candidateFirstParent.addContextNode(contextId, candidateFirstParent);
                    } else if (inPredicate)
                        {candidateFirstParent.addContextNode(contextId, parent);}
                    return candidateFirstParent;
                }
            }
            // We need a real parent : keep the candidate and continue to iterate from this one
            return getFirstParent(candidateFirstParent, candidateFirstParent, false,
                restrictToDirectParent, recursions + 1);
        }
        // is the node's parent in the context set?
        //WARNING : get() realizes virtual node sets
        //TODO : investigate more efficient solutions
        NodeProxy parentOfSelf = context.get(self.getDocument(), parentOfSelfId);
        if (parentOfSelf != null && test.matches(self)) {
            if (axis != Constants.CHILD_AXIS) {
                // if we are on the descendant axis, we return the first node
                // we found while walking bottom-up.
                // Otherwise, we return the last one (which is the node itself)
                self = candidateFirstParent;
            }
            self.copyContext(parentOfSelf);
            if (useSelfAsContext && inPredicate) {
                self.addContextNode(contextId, self);
            } else if (inPredicate) {
                self.addContextNode(contextId, parentOfSelf);
            }
            // Timo Boehme: we return the ancestor which is child of context
            return self;
        } else if (parentOfSelfId == NodeId.DOCUMENT_NODE) {
            // no matching node has been found in the context
            return null;
        } else if (restrictToDirectParent && axis == Constants.CHILD_AXIS && recursions == 1) {
            // break here if the expression is like /*/n
            return null;
        } else {
            // continue for expressions like //*/n or /*//n
            parentOfSelf = new NodeProxy(self.getDocument(), parentOfSelfId, Node.ELEMENT_NODE);
            return getFirstParent(parentOfSelf, candidateFirstParent, false, false, recursions + 1);
        }
    }

    private void addInternal(NodeProxy p) {
        if (realSet == null)
            {realSet = new NewArrayNodeSet(256);}
        realSet.add(p);
        knownIsEmptyCardinality = true;
        knownHasOneCardinality = true;
        knownHasManyCardinality = true;
        isEmpty = realSet.isEmpty();
        hasOne = realSet.hasOne();
        hasMany = !(isEmpty || hasOne);
        //Reset the real document set
        //TODO : use realDocumentSet.add(p.getDocument()) ?
        realDocumentSet = null;
        realSetIsComplete = false;
    }

    /**
     * The method <code>parentWithChild</code>
     *
     * @param proxy a <code>NodeProxy</code> value
     * @param restrictToDirectParent a <code>boolean</code> value
     * @param includeSelf a <code>boolean</code> value
     * @param level an <code>int</code> value
     * @return a <code>NodeProxy</code> value
     */
    @Override
    public NodeProxy parentWithChild(NodeProxy proxy, boolean restrictToDirectParent,
            boolean includeSelf, int level) {
        if (realSet != null && realSetIsComplete)
            {return realSet.parentWithChild(proxy, restrictToDirectParent, includeSelf, level);}
        final NodeProxy first = getFirstParent(proxy, null, includeSelf, restrictToDirectParent, 0);
        if (first != null)
            //TODO : should we set an empty cardinality here ?
            {addInternal(first);}
        return first;
    }

    /**
     * The method <code>parentWithChild</code>
     *
     * @param doc a <code>DocumentImpl</code> value
     * @param nodeId a <code>NodeId</code> value
     * @param restrictToDirectParent a <code>boolean</code> value
     * @param includeSelf a <code>boolean</code> value
     * @return a <code>NodeProxy</code> value
     */
    @Override
    public NodeProxy parentWithChild(DocumentImpl doc, NodeId nodeId,
            boolean restrictToDirectParent, boolean includeSelf) {
        if (realSet != null && realSetIsComplete)
            {return realSet.parentWithChild(doc, nodeId, restrictToDirectParent, includeSelf);}
        final NodeProxy first = getFirstParent(new NodeProxy(doc, nodeId), null,
            includeSelf, restrictToDirectParent, 0);
        if (first != null)
            //TODO : should we set an empty cardinality here ?
            {addInternal(first);}
        return first;
    }

    /**
     * Realize the node set by recursively scanning the
     * DOM.
     */
    private final NodeSet getNodes() {
        final NewArrayNodeSet result = new NewArrayNodeSet();
        for (final Iterator<NodeProxy> i = context.iterator(); i.hasNext();) {
            final NodeProxy proxy = i.next();
            if (proxy.getNodeId() == NodeId.DOCUMENT_NODE) {
                if (proxy.getDocument().getResourceType() == DocumentImpl.BINARY_FILE) {
                    // skip binary resources
                    continue;
                }
                // Add root node if axis is either self, ancestor-self or descendant-self /ljo
                if ((axis == Constants.SELF_AXIS ||
                    axis == Constants.ANCESTOR_SELF_AXIS ||
                    axis == Constants.DESCENDANT_SELF_AXIS) &&
                    test.matches(proxy)) {
                    result.add(proxy);
                }
                if ((axis == Constants.CHILD_AXIS || axis == Constants.ATTRIBUTE_AXIS) &&
                        proxy.getDocument().getChildCount() == 1) {
                    // Optimization: if the document has just 1 child node, we know that
                    // it has to be an element. Instead of calling Document.getChildNodes(),
                    // we just create a NodeProxy for the first child and return it if the
                    // test matches
                    final NodeProxy p = proxy.getDocument().getFirstChildProxy();
                    if (test.matches(p)) {
                        if (useSelfAsContext && inPredicate) {
                            p.addContextNode(contextId, p);
                        }
                        p.addMatches(proxy);
                        result.add(p);
                    }
                } else {
                    final NodeList cl = proxy.getDocument().getChildNodes();
                    for (int j = 0; j < cl.getLength(); j++) {
                        final StoredNode node = (StoredNode) cl.item(j);
                        final NodeProxy p = new NodeProxy(node);
                        if (test.matches(p)) {
                            // fixme! check for unwanted
                            // side effects. /ljo
                            //p.deepCopyContext(proxy);
                            if (useSelfAsContext && inPredicate) {
                                p.addContextNode(contextId, p);
                            }
                            result.add(p);
                        }
                        if (node.getNodeType() == Node.ELEMENT_NODE &&
                            (axis == Constants.DESCENDANT_AXIS ||
                             axis == Constants.DESCENDANT_SELF_AXIS ||
                             axis == Constants.DESCENDANT_ATTRIBUTE_AXIS)) {
                            // note: we create a copy of the docElemProxy here to
                            // be used as context when traversing the tree.
                            final NodeProxy contextNode = new NodeProxy(p);
                            contextNode.deepCopyContext(proxy);
                            //TODO : is this StoredNode construction necessary ?
                            final Iterator<StoredNode> domIter = broker.getNodeIterator(new StoredNode(contextNode));
                            domIter.next();
                            contextNode.setMatches(proxy.getMatches());
                            addChildren(contextNode, result, node, domIter, 0);
                        }
                        if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE
                            && (axis == Constants.CHILD_AXIS ||
                                axis == Constants.DESCENDANT_AXIS ||
                                axis == Constants.DESCENDANT_SELF_AXIS ||
                                // fixme! self axis probably not needed /ljo
                                axis == Constants.SELF_AXIS ||
                                axis == Constants.PRECEDING_AXIS ||
                                axis == Constants.FOLLOWING_AXIS)) {
                            if (test.matches(node)) {
                                result.add(p);
                            }
                        }
                    }
                }
                continue;
            }
            if ((axis == Constants.SELF_AXIS ||
                axis == Constants.ANCESTOR_SELF_AXIS ||
                axis == Constants.DESCENDANT_SELF_AXIS) &&
                test.matches(proxy)) {
                    if(useSelfAsContext && inPredicate) {
                        proxy.addContextNode(contextId, proxy);
                    }
                    result.add(proxy);
            }
            if (test.getType() == Type.PROCESSING_INSTRUCTION ||
                test.getType() == Type.COMMENT ||
                test.getType() == Type.CDATA_SECTION) {
                final DocumentImpl doc = proxy.getDocument();
                if (axis == Constants.PRECEDING_AXIS) {
                    StoredNode ps = (StoredNode) doc.getFirstChild();
                    final StoredNode pe = (StoredNode) doc.getDocumentElement();
                    while (ps != null && !ps.equals(pe)) {
                        if (test.matches(ps)) {
                            result.add(new NodeProxy(ps));
                        }
                        ps = (StoredNode) doc.getFollowingSibling(ps);
                    }
                }
                if (axis == Constants.FOLLOWING_AXIS) {
                    final StoredNode pe = (StoredNode) doc.getDocumentElement();
                    StoredNode pf = (StoredNode) doc.getFollowingSibling(pe);
                    while (pf != null) {
                        if (test.matches(pf)) {
                            result.add(new NodeProxy(pf));
                        }
                        pf = (StoredNode) doc.getFollowingSibling(pf);
                    }
                }
                if (axis == Constants.SELF_AXIS ||
                    axis == Constants.ANCESTOR_SELF_AXIS ||
                    axis == Constants.DESCENDANT_SELF_AXIS) {
                    result.add(proxy);
                }
            }
            if (axis != Constants.SELF_AXIS) {
                addChildren(proxy, result);
            }
        }
        realDocumentSet = result.getDocumentSet();
        return result;
    }

    /**
     * Realize the node set by scanning the structural index.
     * This is usually cheaper than calling {@link #getNodes()}.
     */
    private NodeSet getNodesFromIndex() {
        final StructuralIndex index = broker.getStructuralIndex();
        final byte type = test.getType() == Type.ELEMENT ? ElementValue.ELEMENT : ElementValue.ATTRIBUTE;
        final NodeSet result = index.scanByType(type, axis, test, useSelfAsContext && inPredicate,
            context.getDocumentSet(), context, contextId);
        realDocumentSet = result.getDocumentSet();
        return result;
    }

    /**
     * recursively adds child nodes
     * @param contextNode a <code>NodeProxy</code> value
     * @param result a <code>NodeSet</code> value
     * @param node a <code>StoredNode</code> value
     * @param iter an <code>Iterator</code> value
     * @param recursions an <code>int</code> value
     */
    private void addChildren(NodeProxy contextNode, NodeSet result,
           StoredNode node, Iterator<StoredNode> iter, int recursions) {
        if (node.hasChildNodes()) {
            for (int i = 0; i < node.getChildCount(); i++) {
                final StoredNode child = iter.next();
                if (child == null) {
                    LOG.debug("CHILD == NULL; doc = " +
                          ((DocumentImpl)node.getOwnerDocument()).getURI());
                    //TODO : throw exception ? -pb
                    return;
                }
                if (node.getOwnerDocument() == null) {
                    LOG.debug("DOC == NULL");
                    //TODO : throw exception ? -pb
                    return;
                }
                child.setOwnerDocument((DocumentImpl)node.getOwnerDocument());
                final NodeProxy p = new NodeProxy(child);
                p.setMatches(contextNode.getMatches());
                if (test.matches(child)) {
                    if (((axis == Constants.CHILD_AXIS
                          || axis == Constants.ATTRIBUTE_AXIS)
                         && recursions == 0) ||
                        (axis == Constants.DESCENDANT_AXIS
                         || axis == Constants.DESCENDANT_SELF_AXIS
                         || axis == Constants.DESCENDANT_ATTRIBUTE_AXIS)) {
                        p.deepCopyContext(contextNode);
                        if (useSelfAsContext && inPredicate) {
                            p.addContextNode(contextId, p);
                        } else if (inPredicate) {
                            p.addContextNode(contextId, contextNode);
                        }
                        result.add(p);
                    }
                }
                addChildren(contextNode, result, child, iter, recursions + 1);
            }
        }
    }

    private void addChildren(NodeProxy contextNode, NodeSet result) {
        try {
            final EmbeddedXMLStreamReader reader = broker.getXMLStreamReader(contextNode, true);
            int status = reader.next();
            int level = 0;
            if (status == XMLStreamConstants.START_ELEMENT) {
                while (reader.hasNext()) {
                    status = reader.next();
                    if (axis == Constants.ATTRIBUTE_AXIS && status != XMLStreamConstants.ATTRIBUTE)
                        {break;}
                    switch (status) {
                    case XMLStreamConstants.END_ELEMENT:
                        if (--level < 0)
                            {return;}
                        break;
                    case XMLStreamConstants.ATTRIBUTE:
                        if ((axis == Constants.ATTRIBUTE_AXIS && level == 0) ||
                            axis == Constants.DESCENDANT_ATTRIBUTE_AXIS) {
                            final AttrImpl attr = (AttrImpl) reader.getNode();
                            if (test.matches(attr)) {
                                final NodeProxy p = new NodeProxy(attr);
                                p.deepCopyContext(contextNode);
                                if (useSelfAsContext && inPredicate) {
                                    p.addContextNode(contextId, p);
                                } else if (inPredicate) {
                                    p.addContextNode(contextId, contextNode);
                                }
                                result.add(p);
                            }
                        }
                        break;
                    default:
                        if (((axis == Constants.CHILD_AXIS && level == 0) ||
                            axis == Constants.DESCENDANT_AXIS ||
                            axis == Constants.DESCENDANT_SELF_AXIS) &&
                            test.matches(reader)) {
                            final NodeId nodeId = (NodeId) reader.getProperty(ExtendedXMLStreamReader.PROPERTY_NODE_ID);
                            final NodeProxy p = new NodeProxy(contextNode.getDocument(), nodeId,
                                reader.getNodeType(), reader.getCurrentPosition());
                            p.deepCopyContext(contextNode);
                            if (useSelfAsContext && inPredicate) {
                                p.addContextNode(contextId, p);
                            } else if (inPredicate) {
                                p.addContextNode(contextId, contextNode);
                            }
                            result.add(p);
                        }
                        break;
                    }
                    if (status == XMLStreamConstants.START_ELEMENT)
                        {++level;}
                }
            }
        } catch (final IOException e) {
            e.printStackTrace();
            //TODO : throw exception ,
        } catch (final XMLStreamException e) {
            e.printStackTrace();
            //TODO : throw exception ? -pb
        }
    }

    /**
     * Realize the node set. This should only be done if the
     * wildcard step is the last step in a path expression.
     *
     */
    public final void realize() {
        if (realSet != null && realSetIsComplete)
            {return;}
        // check if we can use the structural index or need to do a scan over the nodes
        if (test.getType() == Type.ELEMENT &&
                (axis == Constants.CHILD_AXIS || axis == Constants.DESCENDANT_AXIS ||
                axis == Constants.DESCENDANT_SELF_AXIS)) {
            realSet = getNodesFromIndex();
        } else {
            realSet = getNodes();
        }
        knownIsEmptyCardinality = true;
        knownHasOneCardinality = true;
        knownHasManyCardinality = true;
        isEmpty = realSet.isEmpty();
        hasOne = realSet.hasOne();
        hasMany = realSet.hasMany();
        realSetIsComplete = true;
    }

    /**
     * The method <code>preferTreeTraversal</code>
     *
     * @return a <code>boolean</code> value
     */
    public boolean preferTreeTraversal() {
        if (realSet != null && realSetIsComplete)
            {return true;}
        if (axis != Constants.CHILD_AXIS)
            {return false;}
        final int contextLen = context.getLength();
        final int docs = context.getDocumentSet().getDocumentCount();
        if (contextLen > docs * MAX_CHILD_COUNT_FOR_OPTIMIZE)
            {return false;} // more than 5 nodes per document
        for (final Iterator<NodeProxy> i = context.iterator(); i.hasNext();) {
            final NodeProxy p = i.next();
            if (p.getNodeId() == NodeId.DOCUMENT_NODE)
                {return false;}
            final NodeImpl n = (NodeImpl) p.getNode();
            if (n.getNodeType() == Node.ELEMENT_NODE && n.getChildCount() > MAX_CHILD_COUNT_FOR_OPTIMIZE)
                {return false;}
        }
        return true;
    }

    /**
     * The method <code>setSelfIsContext</code>
     *
     */
    public void setSelfIsContext() {
        useSelfAsContext = true;
        if (realSet != null && realSetIsComplete) {
            for (final Iterator<NodeProxy> i = realSet.iterator(); i.hasNext();) {
                final NodeProxy p = i.next();
                p.addContextNode(contextId, p);
            }
        }
    }

    /**
     * The method <code>setContextId</code>
     *
     * @param contextId an <code>int</code> value
     */
    public void setContextId(int contextId) {
        this.contextId = contextId;
    }

    /* (non-Javadoc)
     * @see org.exist.dom.NodeSet#hasIndex()
     */
    public boolean hasIndex() {
        // Always return false: there's no index
        return false;
    }

    /* the following methods are normally never called in this context,
     * we just provide them because they are declared abstract
     * in the super class
     */ 

    /**
     * The method <code>isEmpty</code>
     *
     * @return a <code>boolean</code> value
     */
    @Override
    public boolean isEmpty() {
        if (knownIsEmptyCardinality)
            {return isEmpty;}
        return getLength() == 0;
    }

    /**
     * The method <code>hasOne</code>
     *
     * @return a <code>boolean</code> value
     */
    @Override
    public boolean hasOne() {
        if (knownHasOneCardinality)
            {return hasOne;}
        return getLength() == 1;
    }

    /**
     * The method <code>hasMany</code>
     *
     * @return a <code>boolean</code> value
     */
    @Override
    public boolean hasMany() {
        if (knownHasManyCardinality)
            {return hasMany;}
        return getLength() > 1;
    }

    /**
     * The method <code>add</code>
     *
     * @param doc a <code>DocumentImpl</code> value
     * @param nodeId a <code>long</code> value
     */
    public void add(DocumentImpl doc, long nodeId) {
        //Nothing to do
    }

    /**
     * The method <code>add</code>
     *
     * @param node a <code>Node</code> value
     */
    public void add(Node node) {
        //Nothing to do
    }

    /**
     * The method <code>add</code>
     *
     * @param proxy a <code>NodeProxy</code> value
     */
    @Override
    public void add(NodeProxy proxy) {
        //Nothing to do
    }

    /**
     * The method <code>addAll</code>
     *
     * @param other a <code>NodeList</code> value
     */
    public void addAll(NodeList other) {
        //Nothing to do
    }

    /**
     * The method <code>addAll</code>
     *
     * @param other a <code>NodeSet</code> value
     */
    @Override
    public void addAll(NodeSet other) {
        //Nothing to do
    }

    /**
     * The method <code>set</code>
     *
     * @param position an <code>int</code> value
     * @param doc a <code>DocumentImpl</code> value
     * @param nodeId a <code>long</code> value
     */
    public void set(int position, DocumentImpl doc, long nodeId) {
        //Nothing to do
    }

    /**
     * The method <code>remove</code>
     *
     * @param node a <code>NodeProxy</code> value
     */
    public void remove(NodeProxy node) {
        //Nothing to do
    }

    /**
     * The method <code>getLength</code>
     *
     * @return an <code>int</code> value
     */
    @Override
    public int getLength() {
        realize();
        return realSet.getLength();
    }

    @Override
    public int getItemType() {
        if (realSet != null && realSetIsComplete)
            {return realSet.getItemType();}
        return Type.NODE;
    }

    /**
     * The method <code>getItemCount</code>
     *
     * @return an <code>int</code> value
     */
    @Override
    public int getItemCount() {
        //TODO : evaluate both semantics
        realize();
        return realSet.getItemCount();
    }

    /**
     * The method <code>item</code>
     *
     * @param pos an <code>int</code> value
     * @return a <code>Node</code> value
     */
    @Override
    public Node item(int pos) {
        realize();
        return realSet.item(pos);
    }

    /**
     * The method <code>get</code>
     *
     * @param pos an <code>int</code> value
     * @return a <code>NodeProxy</code> value
     */
    @Override
    public NodeProxy get(int pos) {
        realize();
        return realSet.get(pos);
    }

    /**
     * The method <code>itemAt</code>
     *
     * @param pos an <code>int</code> value
     * @return an <code>Item</code> value
     */
    @Override
    public Item itemAt(int pos) {
        realize();
        return realSet.itemAt(pos);
    }

    /**
     * The method <code>get</code>
     *
     * @param doc a <code>DocumentImpl</code> value
     * @param nodeId a <code>NodeId</code> value
     * @return a <code>NodeProxy</code> value
     */
    public NodeProxy get(DocumentImpl doc, NodeId nodeId) {
        realize();
        return realSet.get(doc, nodeId);
    }

    /**
     * The method <code>get</code>
     *
     * @param proxy a <code>NodeProxy</code> value
     * @return a <code>NodeProxy</code> value
     */
    @Override
    public NodeProxy get(NodeProxy proxy) {
        realize();
        return realSet.get(proxy);
    }

    /**
     * The method <code>iterator</code>
     *
     * @return a <code>NodeSetIterator</code> value
     */
    @Override
    public NodeSetIterator iterator() {
        realize();
        return realSet.iterator();
    }

    /* (non-Javadoc)
     * @see org.exist.dom.NodeSet#iterate()
     */
    @Override
    public SequenceIterator iterate() throws XPathException {
        realize();
        return realSet.iterate();
    }

    /* (non-Javadoc)
     * @see org.exist.dom.AbstractNodeSet#unorderedIterator()
     */
    @Override
    public SequenceIterator unorderedIterator() throws XPathException {
        realize();
        return realSet.unorderedIterator();
    }

    /**
     * The method <code>intersection</code>
     *
     * @param other a <code>NodeSet</code> value
     * @return a <code>NodeSet</code> value
     */
    @Override
    public NodeSet intersection(NodeSet other) {
        realize();
        return realSet.intersection(other);
    }

    /**
     * The method <code>union</code>
     *
     * @param other a <code>NodeSet</code> value
     * @return a <code>NodeSet</code> value
     */
    @Override
    public NodeSet union(NodeSet other) {
        realize();
        return realSet.union(other);
    }

    /**
     * The method <code>filterDocuments</code>
     *
     * @param otherSet a <code>NodeSet</code> value
     * @return a <code>NodeSet</code> value
     */
    @Override
    public NodeSet filterDocuments(NodeSet otherSet) {
        return this;
    }

    /**
     * The method <code>clearContext</code>
     *
     */
    public void clearContext() {
        // ignored for a virtual set
    }

    /**
     * The method <code>toString</code>
     *
     * @return a <code>String</code> value
     */
    @Override
    public String toString() {
        if (realSet == null)
            {return "Virtual#unknown";}
        final StringBuilder result = new StringBuilder();
        return result.toString();
    }
}
TOP

Related Classes of org.exist.dom.VirtualNodeSet

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.