Package org.exist.memtree

Source Code of org.exist.memtree.DocumentImpl

/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-2010 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.memtree;

import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;

import org.xml.sax.SAXException;

import org.exist.Database;
import org.exist.EXistException;
import org.exist.Namespaces;
import org.exist.dom.DocumentAtExist;
import org.exist.dom.NodeListImpl;
import org.exist.dom.NodeProxy;
import org.exist.dom.QName;
import org.exist.numbering.NodeId;
import org.exist.numbering.NodeIdFactory;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.ElementValue;
import org.exist.storage.serializers.Serializer;
import org.exist.util.hashtable.NamePool;
import org.exist.util.serializer.AttrList;
import org.exist.util.serializer.Receiver;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.NodeTest;
import org.exist.xquery.XPathException;
import org.exist.xquery.XQueryContext;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.Type;

import java.util.Arrays;


/**
* An in-memory implementation of Document.
*
* <p>This implementation stores all node data in the document object. Nodes from another document, i.e. a persistent document in the database, can be
* stored as reference nodes, i.e. the nodes are not copied into this document object. Instead a reference is inserted which will only be expanded
* during serialization.</p>
*
* @author  wolf
*/
public class DocumentImpl extends NodeImpl implements DocumentAtExist {

    private static long        nextDocId        = 0;

    private final static int   NODE_SIZE        = 16;
    private final static int   ATTR_SIZE        = 8;
    private final static int   CHAR_BUF_SIZE    = 256;
    private final static int   REF_SIZE         = 8;

    protected XQueryContext    context;

    protected NamePool         namePool;

    // holds the node type of a node
    protected short[]          nodeKind = null;

    // the tree level of a node
    protected short[]          treeLevel;

    // the node number of the next sibling
    protected int[]            next;

    // pointer into the namePool
    protected QName[]          nodeName;

    protected NodeId[]         nodeId;

    protected int[]            alpha;

    protected int[]            alphaLen;

    protected char[]           characters = null;

    protected int              nextChar = 0;

    // attributes
    protected QName[]          attrName;

    protected int[]            attrType;

    protected NodeId[]         attrNodeId;

    protected int[]            attrParent;

    protected String[]         attrValue;

    protected int              nextAttr         = 0;

    // namespaces
    protected int[]            namespaceParent  = null;

    protected QName[]          namespaceCode    = null;

    protected int              nextNamespace    = 0;

    // the current number of nodes in the doc
    protected int              size             = 1;

    protected int              documentRootNode = -1;

    protected String           documentURI      = null;

    // reference nodes (link to an external, persistent document fragment)
    protected NodeProxy[]      references       = null;
    protected int              nextRef          = 0;
    protected long             docId;
    boolean                    explicitCreation = false;
   
    boolean replaceAttribute = false;
   
    private Database db = null;

    public DocumentImpl(XQueryContext context, boolean explicitCreation) {
        super(null, 0);
        this.context = context;
        this.explicitCreation = explicitCreation;
        this.docId = createDocId();
        if(context == null) {
            namePool = new NamePool();
        } else {
            db = context.getDatabase();
            namePool = context.getSharedNamePool();
        }
    }

    public Database getDatabase() {
        if (db == null)
            try {
                db = BrokerPool.getInstance();
            } catch (final EXistException e) {
                throw new NullPointerException();
            }
        return db;
    }

    private static long createDocId() {
        return( nextDocId++ );
    }

    private void init() {
        nodeKind  = new short[NODE_SIZE];
        treeLevel = new short[NODE_SIZE];
        next      = new int[NODE_SIZE];
        Arrays.fill(next, -1);
        nodeName = new QName[NODE_SIZE];
        nodeId   = new NodeId[NODE_SIZE];
        alpha    = new int[NODE_SIZE];
        alphaLen = new int[NODE_SIZE];
        Arrays.fill(alphaLen, -1);
        attrName     = new QName[ATTR_SIZE];
        attrParent   = new int[ATTR_SIZE];
        attrValue    = new String[ATTR_SIZE];
        attrType     = new int[ATTR_SIZE];
        attrNodeId   = new NodeId[NODE_SIZE];
        treeLevel[0] = 0;
        nodeKind[0= Node.DOCUMENT_NODE;
        document     = this;
    }

    public void reset() {
        size       = 0;
        nextChar   = 0;
        nextAttr   = 0;
        nextRef    = 0;
        references = null;
    }

    public int getSize() {
        return( size );
    }

    public int addNode(short kind, short level, QName qname) {
        if (nodeKind == null) {
            init();
        }
        if (size == nodeKind.length) {
            grow();
        }
        nodeKind[size= kind;
        treeLevel[size] = level;
        nodeName[size= qname != null ? namePool.getSharedName(qname) : null;
        alpha[size]     = -1; // undefined
        next[size]      = -1;
        return(size++);
    }

    public void addChars(int nodeNum, char[] ch, int start, int len) {
        if (nodeKind == null) {
            init();
        }
        if(characters == null) {
            characters = new char[len > CHAR_BUF_SIZE ? len : CHAR_BUF_SIZE];
        } else if ((nextChar + len) >= characters.length) {
            int newLen = (characters.length * 3) / 2;
            if (newLen < (nextChar + len)) {
                newLen = nextChar + len;
            }
            char[] nc = new char[newLen];
            System.arraycopy(characters, 0, nc, 0, characters.length);
            characters = nc;
        }
        alpha[nodeNum] = nextChar;
        alphaLen[nodeNum] = len;
        System.arraycopy(ch, start, characters, nextChar, len);
        nextChar += len;
    }

    public void addChars(int nodeNum, CharSequence s) {
        if (nodeKind == null) {
            init();
        }
        int len = (s == null) ? 0 : s.length();
        if (characters == null) {
            characters = new char[(len > CHAR_BUF_SIZE) ? len : CHAR_BUF_SIZE];
        } else if ((nextChar + len) >= characters.length) {
            int newLen = (characters.length * 3) / 2;
            if (newLen < (nextChar + len)) {
                newLen = nextChar + len;
            }
            char[] nc = new char[newLen];
            System.arraycopy(characters, 0, nc, 0, characters.length);
            characters = nc;
        }
        alpha[nodeNum] = nextChar;
        alphaLen[nodeNum] = len;
        for (int i = 0; i < len; i++) {
            characters[nextChar++] = s.charAt( i );
        }
    }

    public void appendChars(int nodeNum, char[] ch, int start, int len) {
        if (characters == null) {
            characters = new char[(len > CHAR_BUF_SIZE) ? len : CHAR_BUF_SIZE];
        } else if((nextChar + len) >= characters.length) {
            int newLen = (characters.length * 3) / 2;
            if (newLen < (nextChar + len)) {
                newLen = nextChar + len;
            }
            char[] nc = new char[newLen];
            System.arraycopy(characters, 0, nc, 0, characters.length);
            characters = nc;
        }
        alphaLen[nodeNum] = alphaLen[nodeNum] + len;
        System.arraycopy(ch, start, characters, nextChar, len);
        nextChar += len;
    }

    public void appendChars(int nodeNum, CharSequence s) {
        final int len = s.length();
        if (characters == null) {
            characters = new char[(len > CHAR_BUF_SIZE) ? len : CHAR_BUF_SIZE];
        } else if ((nextChar + len) >= characters.length) {
            int newLen = (characters.length * 3) / 2;
            if (newLen < (nextChar + len)) {
                newLen = nextChar + len;
            }
            char[] nc = new char[newLen];
            System.arraycopy(characters, 0, nc, 0, characters.length);
            characters = nc;
        }
        alphaLen[nodeNum] = alphaLen[nodeNum] + len;
        for (int i = 0; i < len; i++) {
            characters[nextChar++] = s.charAt(i);
        }
    }

    public void addReferenceNode(int nodeNum, NodeProxy proxy) {
        if (nodeKind == null) {
            init();
        }
        if (( references == null ) || ( nextRef == references.length)) {
            growReferences();
        }
        references[nextRef] = proxy;
        alpha[nodeNum] = nextRef++;
    }

    public void replaceReferenceNode(int nodeNum, CharSequence ch) {
        nodeKind[nodeNum] = Node.TEXT_NODE;
        references[alpha[nodeNum]] = null;
        addChars(nodeNum, ch);
    }

    public boolean hasReferenceNodes() {
        return (references != null) && (references[0] != null);
    }

    public int addAttribute(int nodeNum, QName qname, String value, int type) throws DOMException {
        if (nodeKind == null) {
            init();
        }
        if ((nodeNum > 0 ) && !(nodeKind[nodeNum] == Node.ELEMENT_NODE || nodeKind[nodeNum] == NodeImpl.NAMESPACE_NODE)) {
            throw( new DOMException( DOMException.INUSE_ATTRIBUTE_ERR,
                "err:XQTY0024: An attribute node cannot follow a node that is not an attribute node."));
        }
        int prevAttr = nextAttr - 1;
        int attrN;
        //Check if an attribute with the same qname exists in the parent element
        while ((nodeNum > 0) && (prevAttr > -1) && (attrParent[prevAttr] == nodeNum)) {
            attrN = prevAttr--;
            final QName prevQn = attrName[attrN];
            if (prevQn.equalsSimple(qname)) {
                if (replaceAttribute) {
                    attrValue[attrN] = value;
                    attrType[attrN] = type;
                    return attrN;
                } else
                    {throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR,
                        "err:XQDY0025: element has more than one attribute '" + qname + "'");}
            }
        }
        if (nextAttr == attrName.length) {
            growAttributes();
        }
        qname.setNameType(ElementValue.ATTRIBUTE);
        attrParent[nextAttr] = nodeNum;
        attrName[nextAttr]   = namePool.getSharedName(qname);
        attrValue[nextAttr= value;
        attrType[nextAttr]   = type;
        if (alpha[nodeNum] < 0) {
            alpha[nodeNum] = nextAttr;
        }
        return( nextAttr++ );
    }

    public int addNamespace(int nodeNum, QName qname) {
        if (nodeKind == null) {
            init();
        }
        if ((namespaceCode == null) || (nextNamespace == namespaceCode.length)) {
            growNamespaces();
        }
        namespaceCode[nextNamespace] = namePool.getSharedName(qname);
        namespaceParent[nextNamespace] = nodeNum;
        if (alphaLen[nodeNum] < 0) {
            alphaLen[nodeNum] = nextNamespace;
        }
        return nextNamespace++;
    }

    public short getTreeLevel(int nodeNum) {
        return treeLevel[nodeNum];
    }

    public int getLastNode() {
        return size - 1;
    }

    @Override
    public DocumentImpl getDocument() {
        return this;
    }

    public short getNodeType(int nodeNum) {
        if ((nodeKind == null) || (nodeNum < 0)) {
            return -1;
        }
        return nodeKind[nodeNum];
    }

    public String getStringValue() {
      if (document == null)
        {return "";}
     
      return super.getStringValue();
    }
   
    private void grow() {
        final int newSize = (size * 3) / 2;
        short[] newNodeKind = new short[newSize];
        System.arraycopy(nodeKind, 0, newNodeKind, 0, size);
        nodeKind = newNodeKind;
        short[] newTreeLevel = new short[newSize];
        System.arraycopy(treeLevel, 0, newTreeLevel, 0, size);
        treeLevel = newTreeLevel;
        int[] newNext = new int[newSize];
        Arrays.fill(newNext, -1);
        System.arraycopy(next, 0, newNext, 0, size);
        next = newNext;
        QName[] newNodeName = new QName[newSize];
        System.arraycopy(nodeName, 0, newNodeName, 0, size);
        nodeName = newNodeName;
        NodeId[] newNodeId = new NodeId[newSize];
        System.arraycopy(nodeId, 0, newNodeId, 0, size);
        nodeId = newNodeId;
        int[] newAlpha = new int[newSize];
        System.arraycopy(alpha, 0, newAlpha, 0, size);
        alpha = newAlpha;
        int[] newAlphaLen = new int[newSize];
        Arrays.fill(newAlphaLen, -1);
        System.arraycopy(alphaLen, 0, newAlphaLen, 0, size);
        alphaLen = newAlphaLen;
    }

    private void growAttributes() {
        final int size = attrName.length;
        final int newSize = (size * 3) / 2;
        QName[] newAttrName = new QName[newSize];
        System.arraycopy(attrName, 0, newAttrName, 0, size);
        attrName = newAttrName;
        int[] newAttrParent = new int[newSize];
        System.arraycopy(attrParent, 0, newAttrParent, 0, size);
        attrParent = newAttrParent;
        String[] newAttrValue = new String[newSize];
        System.arraycopy(attrValue, 0, newAttrValue, 0, size);
        attrValue = newAttrValue;
        int[] newAttrType = new int[newSize];
        System.arraycopy(attrType, 0, newAttrType, 0, size);
        attrType = newAttrType;
        NodeId[] newNodeId = new NodeId[newSize];
        System.arraycopy(attrNodeId, 0, newNodeId, 0, size);
        attrNodeId = newNodeId;
    }

    private void growReferences() {
        if (references == null) {
            references = new NodeProxy[REF_SIZE];
        } else {
            final int size = references.length;
            final int newSize = (size * 3) / 2;
            NodeProxy[] newReferences = new NodeProxy[newSize];
            System.arraycopy(references, 0, newReferences, 0, size);
            references = newReferences;
        }
    }

    private void growNamespaces() {
        if (namespaceCode == null) {
            namespaceCode = new QName[5];
            namespaceParent = new int[5];
        } else {
            final int size = namespaceCode.length;
            final int newSize = (size * 3) / 2;
            QName[] newCodes = new QName[newSize];
            System.arraycopy(namespaceCode, 0, newCodes, 0, size);
            namespaceCode = newCodes;
            int[] newParents = new int[newSize];
            System.arraycopy(namespaceParent, 0, newParents, 0, size);
            namespaceParent = newParents;
        }
    }

    public NodeImpl getAttribute( int nodeNum ) throws DOMException {
        return new AttributeImpl(this, nodeNum);
    }

    public NodeImpl getNamespaceNode( int nodeNum ) throws DOMException {
        return new NamespaceNode(this, nodeNum);
    }

    public NodeImpl getNode( int nodeNum ) throws DOMException {
        if (nodeNum == 0) {
            return this;
        }
        if (nodeNum >= size) {
            throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "node not found");
        }
        NodeImpl node;
        switch (nodeKind[nodeNum]) {
        case Node.ELEMENT_NODE:
            node = new ElementImpl(this, nodeNum);
            break;
        case Node.TEXT_NODE:
            node = new TextImpl(this, nodeNum);
            break;
        case Node.COMMENT_NODE:
            node = new CommentImpl(this, nodeNum);
            break;
        case Node.PROCESSING_INSTRUCTION_NODE:
            node = new ProcessingInstructionImpl(this, nodeNum);
            break;
        case Node.CDATA_SECTION_NODE:
            node = new CDATASectionImpl(this, nodeNum);
            break;
        case NodeImpl.REFERENCE_NODE:
            node = new ReferenceNode(this, nodeNum);
            break;
        default:
            throw new DOMException(DOMException.NOT_FOUND_ERR, "node not found");
        }
        return node;
    }

    public NodeImpl getLastAttr() {
        if (nextAttr == 0) {
            return null;
        }
        return new AttributeImpl(this, nextAttr - 1);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Node#getParentNode()
     */
    @Override
    public Node getParentNode() {
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#getDoctype()
     */
    public DocumentType getDoctype() {
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#getImplementation()
     */
    public DOMImplementation getImplementation() {
        return (
            new DOMImplementation() {

                public Document createDocument(String namespaceURI,
                    String qualifiedName, DocumentType doctype) throws DOMException {
                    return null;
                }

                public DocumentType createDocumentType( String qualifiedName,
                        String publicId, String systemId ) throws DOMException {
                    return null;
                }

                public Object getFeature( String feature, String version ) {
                    return null;
                }

                public boolean hasFeature( String feature, String version ) {
                    return ("XML".equals(feature) && ("1.0".equals(version) ||
                        "2.0".equals(version)));
                }
            }
        );
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#getDocumentElement()
     */
    public Element getDocumentElement() {
        if( size == 1 ) {
            return null;
        }
        int nodeNum = 1;
        while (nodeKind[nodeNum] != Node.ELEMENT_NODE) {
            if (next[nodeNum] < nodeNum)
                {return null;}
            nodeNum = next[nodeNum];
        }
        return (Element)getNode(nodeNum);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Node#getFirstChild()
     */
    @Override
    public Node getFirstChild() {
        if (size > 1)
            {return getNode(1);}
        return null;
    }

    public Node getLastChild() {
        return getFirstChild();
    }

    public int getAttributesCountFor(int nodeNumber) {
        int count = 0;
        int attr = alpha[nodeNumber];
        if(-1 < attr) {
            while ((attr < nextAttr) && (attrParent[attr++] == nodeNumber)) {
                ++count;
            }
        }
        return count;
    }

    public int getNamespacesCountFor(int nodeNumber) {
        int count = 0;
        int ns = alphaLen[nodeNumber];
        if(-1 < ns) {
            while((ns < nextNamespace) && (namespaceParent[ns++] == nodeNumber)) {
                ++count;
            }
        }
        return count;
    }

    public int getChildCountFor(int nr) {
        int count = 0;
        int nextNode = getFirstChildFor(nr);
        while (nextNode > nr) {
            ++count;
            nextNode = next[nextNode];
        }
        return count;
    }

    public int getFirstChildFor(int nodeNumber) {
        final short level = treeLevel[nodeNumber];
        final int nextNode = nodeNumber + 1;
        if ((nextNode < size) && (treeLevel[nextNode] > level))
            {return nextNode;}
        return -1;
    }

    public int getNextSiblingFor(int nodeNumber) {
        final int nextNr = next[nodeNumber];
        return (nextNr < nodeNumber ) ? -1 : nextNr;
    }

    /**
     * The method <code>getParentNodeFor.</code>
     *
     * @param   nodeNumber  an <code>int</code> value
     *
     * @return  an <code>int</code> value
     */
    public int getParentNodeFor(int nodeNumber) {
        int nextNode = next[nodeNumber];
        while (nextNode > nodeNumber) {
            nextNode = next[nextNode];
        }
        return nextNode;
    }

    @Override
    public void selectChildren(NodeTest test, Sequence result) throws XPathException {
        if (size == 1) {
            return;
        }
        NodeImpl next = (NodeImpl)getFirstChild();
        while (next != null) {
            if (test.matches(next)) {
                result.add(next);
            }
            next = (NodeImpl)next.getNextSibling();
        }
    }

    @Override
    public void selectDescendants(boolean includeSelf, NodeTest test, Sequence result)
            throws XPathException {
        if (includeSelf && test.matches(this)) {
            result.add(this);
        }
        if (size == 1) {
            return;
        }
        NodeImpl next = (NodeImpl)getFirstChild();
        while (next != null) {
            if (test.matches(next)) {
                result.add(next);
            }
            next.selectDescendants(includeSelf, test, result);
            next = (NodeImpl)next.getNextSibling();
        }
    }

    @Override
    public void selectDescendantAttributes(NodeTest test, Sequence result)
            throws XPathException {
        if (size == 1) {
            return;
        }
        NodeImpl next = (NodeImpl)getFirstChild();
        while (next != null) {
            if (test.matches(next)) {
                result.add(next);
            }
            next.selectDescendantAttributes(test, result);
            next = (NodeImpl)next.getNextSibling();
        }
    }

    public NodeImpl selectById(String id) {
        if (size == 1) {
            return null;
        }
        final ElementImpl root = (ElementImpl)getDocumentElement();
        if (hasIdAttribute(root.getNodeNumber(), id)) {
            return root;
        }
        final int treeLevel = this.treeLevel[root.getNodeNumber()];
        int nextNode  = root.getNodeNumber();
        while ((++nextNode < document.size) && (document.treeLevel[nextNode] > treeLevel)) {
            if ((document.nodeKind[nextNode] == Node.ELEMENT_NODE) &&
                    hasIdAttribute(nextNode, id)) {
                return getNode(nextNode);
            }
        }
        return null;
    }

    public NodeImpl selectByIdref(String id) {
        if (size == 1) {
            return null;
        }
        final ElementImpl root = (ElementImpl)getDocumentElement();
        AttributeImpl attr = getIdrefAttribute(root.getNodeNumber(), id);
        if (attr != null) {
            return attr;
        }
        final int treeLevel = this.treeLevel[root.getNodeNumber()];
        int nextNode  = root.getNodeNumber();
        while ((++nextNode < document.size ) && (document.treeLevel[nextNode] > treeLevel)) {
            if (document.nodeKind[nextNode] == Node.ELEMENT_NODE) {
                attr = getIdrefAttribute(nextNode, id);
                if (attr != null) {
                    return attr;
                }
            }
        }
        return null;
    }

    private boolean hasIdAttribute(int nodeNumber, String id) {
        int attr = document.alpha[nodeNumber];
        if (-1 < attr) {
            while ((attr < document.nextAttr) && (document.attrParent[attr] == nodeNumber)) {
                if ((document.attrType[attr] == AttributeImpl.ATTR_ID_TYPE) &&
                        id.equals(document.attrValue[attr])) {
                    return true;
                }
                ++attr;
            }
        }
        return false;
    }

    private AttributeImpl getIdrefAttribute(int nodeNumber, String id) {
        int attr = document.alpha[nodeNumber];
        if (-1 < attr) {
            while ((attr < document.nextAttr) && (document.attrParent[attr] == nodeNumber)) {
                if ((document.attrType[attr] == AttributeImpl.ATTR_IDREF_TYPE) &&
                        id.equals(document.attrValue[attr])) {
                    return new AttributeImpl(this, attr);
                }
                ++attr;
            }
        }
        return null;
    }

    @Override
    public boolean matchChildren(NodeTest test) throws XPathException {
        if (size == 1) {
            return false;
        }
        NodeImpl next = (NodeImpl)getFirstChild();
        while (next != null) {
            if (test.matches(next)) {
                return true;
            }
            next = (NodeImpl)next.getNextSibling();
        }
        return false;
    }

    @Override
    public boolean matchDescendants(boolean includeSelf, NodeTest test) throws XPathException {
        if (includeSelf && test.matches(this)) {
            return true;
        }
        if (size == 1) {
            return true;
        }
        NodeImpl next = (NodeImpl)getFirstChild();
        while (next != null) {
            if (test.matches(next)) {
                return true;
            }
            if (next.matchDescendants(includeSelf, test)) {
                return true;
            }
            next = (NodeImpl)next.getNextSibling();
        }
        return false;
    }

    @Override
    public boolean matchDescendantAttributes(NodeTest test) throws XPathException {
        if (size == 1) {
            return false;
        }
        NodeImpl next = (NodeImpl)getFirstChild();
        while (next != null) {
            if (test.matches(next)) {
                return true;
            }
            if (next.matchDescendantAttributes(test)) {
                return true;
            }
            next = (NodeImpl)next.getNextSibling();
        }
        return false;
    }

    /*
    * (non-Javadoc)
    *
    * @see org.w3c.dom.Document#createElement(java.lang.String)
    */
    public Element createElement(String tagName) throws DOMException {
        QName qn;
        try {
            qn = QName.parse(getContext(), tagName);
        }
        catch (final XPathException e) {
            throw new DOMException(DOMException.NAMESPACE_ERR, e.getMessage());
        }
        final int nodeNum = addNode(Node.ELEMENT_NODE, (short)1, qn);
        return new ElementImpl(this, nodeNum);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#createDocumentFragment()
     */
    public DocumentFragment createDocumentFragment() {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#createTextNode(java.lang.String)
     */
    public Text createTextNode(String arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#createComment(java.lang.String)
     */
    public Comment createComment(String arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#createCDATASection(java.lang.String)
     */
    public CDATASection createCDATASection(String arg0) throws DOMException {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#createProcessingInstruction(java.lang.String,
     *           java.lang.String)
     */
    public ProcessingInstruction createProcessingInstruction(String arg0, String arg1)
            throws DOMException {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#createAttribute(java.lang.String)
     */
    public Attr createAttribute(String arg0) throws DOMException {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#createEntityReference(java.lang.String)
     */
    public EntityReference createEntityReference(String arg0) throws DOMException {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#getElementsByTagName(java.lang.String)
     */
    public NodeList getElementsByTagName(String name) {
        final NodeListImpl nl = new NodeListImpl();
        for (int i = 1; i < size; i++) {
            if (nodeKind[i] == Node.ELEMENT_NODE) {
                final QName qn = nodeName[i];
                if (qn.getStringValue().equals(name)) {
                    nl.add(getNode(i));
                }
            }
        }
        return nl;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#importNode(org.w3c.dom.Node, boolean)
     */
    public Node importNode(Node arg0, boolean arg1) throws DOMException {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#createElementNS(java.lang.String,
     *           java.lang.String)
     */
    public Element createElementNS(String arg0, String arg1) throws DOMException {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#createAttributeNS(java.lang.String,
     *           java.lang.String)
     */
    public Attr createAttributeNS(String arg0, String arg1) throws DOMException {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#getElementsByTagNameNS(java.lang.String,
     *           java.lang.String)
     */
    public NodeList getElementsByTagNameNS(String namespaceURI, String localName) {
        final NodeListImpl nl = new NodeListImpl();
        for (int i = 1; i < size; i++) {
            if (nodeKind[i] == Node.ELEMENT_NODE) {
                final QName qn = nodeName[i];
                if (qn.getNamespaceURI().equals(namespaceURI) && qn.getLocalName().equals(localName)) {
                    nl.add(getNode(i));
                }
            }
        }
        return nl;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Document#getElementById(java.lang.String)
     */
    public Element getElementById(String arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.w3c.dom.Node#getOwnerDocument()
     */
    @Override
    public org.w3c.dom.Document getOwnerDocument() {
        return this;
    }

    /**
     * Copy the document fragment starting at the specified node to the given document builder.
     *
     * @param   node
     * @param   receiver
     *
     * @throws  SAXException  DOCUMENT ME!
     */
    public void copyTo(NodeImpl node, DocumentBuilderReceiver receiver) throws SAXException {
        copyTo(node, receiver, false);
    }

    protected void copyTo(NodeImpl node, DocumentBuilderReceiver receiver, boolean expandRefs)
            throws SAXException {
        final NodeImpl top = node;
        while (node != null) {
            copyStartNode(node, receiver, expandRefs);
            NodeImpl nextNode;
            if (node instanceof ReferenceNode) {
                //Nothing more to stream ?
                nextNode = null;
            } else {
                nextNode = (NodeImpl)node.getFirstChild();
            }
            while (nextNode == null) {
                copyEndNode(node, receiver);
                if ((top != null) && (top.nodeNumber == node.nodeNumber)) {
                    break;
                }
                //No nextNode if the top node is a Document node
                nextNode = (NodeImpl)node.getNextSibling();
                if (nextNode == null) {
                    node = (NodeImpl)node.getParentNode();
                    if ((node == null) || ((top != null) && (top.nodeNumber == node.nodeNumber))) {
                        copyEndNode(node, receiver);
                        break;
                    }
                }
            }
            node = nextNode;
        }
    }

    private void copyStartNode(NodeImpl node, DocumentBuilderReceiver receiver, boolean expandRefs)
            throws SAXException {
        final int nr = node.nodeNumber;
        switch(node.getNodeType()) {
        case Node.ELEMENT_NODE: {
            final QName nodeName = document.nodeName[nr];
            receiver.startElement(nodeName, null);
            int attr = document.alpha[nr];
            if(-1 < attr) {
                while ((attr < document.nextAttr) && (document.attrParent[attr] == nr)) {
                    final QName attrQName = document.attrName[attr];
                    receiver.attribute( attrQName, attrValue[attr] );
                    ++attr;
                }
            }
            int ns = document.alphaLen[nr];
            if (-1 < ns) {
                while ((ns < document.nextNamespace) && (document.namespaceParent[ns] == nr)) {
                    final QName nsQName = document.namespaceCode[ns];
                    receiver.addNamespaceNode(nsQName);
                    ++ns;
                }
            }
            break;
        }
        case Node.TEXT_NODE:
            receiver.characters(document.characters, document.alpha[nr], document.alphaLen[nr]);
            break;
        case Node.CDATA_SECTION_NODE:
            receiver.cdataSection(document.characters, document.alpha[nr], document.alphaLen[nr]);
            break;
        case Node.ATTRIBUTE_NODE:
            final QName attrQName = document.attrName[nr];
            receiver.attribute(attrQName, attrValue[nr]);
            break;
        case Node.COMMENT_NODE:
            receiver.comment(document.characters, document.alpha[nr], document.alphaLen[nr]);
            break;
        case Node.PROCESSING_INSTRUCTION_NODE:
            final QName qn   = document.nodeName[nr];
            final String data = new String(document.characters, document.alpha[nr], document.alphaLen[nr]);
            receiver.processingInstruction(qn.getLocalName(), data);
            break;
        case NodeImpl.NAMESPACE_NODE:
            receiver.addNamespaceNode(document.namespaceCode[nr]);
            break;
        case NodeImpl.REFERENCE_NODE:
            if (expandRefs) {
                DBBroker broker = null;
                try {
                    broker = getDatabase().get(null);
                    final Serializer serializer = broker.getSerializer();
                    serializer.reset();
                    serializer.setProperty(Serializer.GENERATE_DOC_EVENTS, "false");
                    serializer.setReceiver(receiver);
                    serializer.toReceiver(document.references[document.alpha[nr]], false, false);
                } catch (final EXistException e) {
                    throw new SAXException(e);
                } finally {
                    getDatabase().release(broker);
                }
            } else {
                receiver.addReferenceNode(document.references[document.alpha[nr]]);
            }
            break;
        }
    }

    private void copyEndNode(NodeImpl node, DocumentBuilderReceiver receiver)
            throws SAXException {
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            receiver.endElement(node.getQName());
        }
    }

    /**
     * Expand all reference nodes in the current document, i.e. replace them by real nodes. Reference nodes are just pointers to nodes from other
     * documents stored in the database. The XQuery engine uses reference nodes to speed up the creation of temporary doc fragments.
     *
     * <p>This method creates a new copy of the document contents and expands all reference nodes.</p>
     *
     * @throws  DOMException  DOCUMENT ME!
     */
    @Override
    public void expand() throws DOMException {
        if (size == 0) {
            return;
        }
        final DocumentImpl newDoc = expandRefs(null);
        copyDocContents(newDoc);
    }

    public DocumentImpl expandRefs(NodeImpl rootNode) throws DOMException {
        try {
            if (nextRef == 0) {
                computeNodeIds();
                return( this );
            }
            final MemTreeBuilder builder = new MemTreeBuilder(context);
            final DocumentBuilderReceiver receiver = new DocumentBuilderReceiver(builder);
            try {
                builder.startDocument();
                NodeImpl node = (rootNode == null) ? (NodeImpl)getFirstChild() : rootNode;
                while (node != null) {
                    copyTo(node, receiver, true);
                    node = (NodeImpl)node.getNextSibling();
                }
                receiver.endDocument();
            } catch (final SAXException e) {
                throw new DOMException(DOMException.INVALID_STATE_ERR, e.getMessage());
            }
            final DocumentImpl newDoc = builder.getDocument();
            newDoc.computeNodeIds();
            return newDoc;
        } catch (final EXistException e) {
            throw new DOMException(DOMException.INVALID_STATE_ERR, e.getMessage());
        }
    }

    public NodeImpl getNodeById(NodeId id) {
        expand();
        for (int i = 0; i < size; i++) {
            if (id.equals( nodeId[i])) {
                return getNode(i);
            }
        }
        return null;
    }

    private void computeNodeIds() throws EXistException {
        if (nodeId[0] != null) {
            return;
        }
        final NodeIdFactory nodeFactory = getDatabase().getNodeFactory();
        nodeId[0] = nodeFactory.documentNodeId();
        if (size == 1) {
            return;
        }
        NodeId nextId = nodeFactory.createInstance();
        NodeImpl next = (NodeImpl)getFirstChild();
        while (next != null) {
            computeNodeIds(nextId, next.nodeNumber);
            next = (NodeImpl)next.getNextSibling();
            nextId = nextId.nextSibling();
        }
    }

    private void computeNodeIds(NodeId id, int nodeNum) {
        nodeId[nodeNum] = id;
        if (nodeKind[nodeNum] == Node.ELEMENT_NODE) {
            NodeId nextId = id.newChild();
            int attr = document.alpha[nodeNum];
            if (-1 < attr) {
                while ((attr < document.nextAttr) && (document.attrParent[attr] == nodeNum)) {
                    attrNodeId[attr] = nextId;
                    nextId = nextId.nextSibling();
                    ++attr;
                }
            }
            int nextNode = getFirstChildFor(nodeNum);
            while (nextNode > nodeNum) {
                computeNodeIds(nextId, nextNode);
                nextNode = document.next[nextNode];
                if (nextNode > nodeNum) {
                    nextId = nextId.nextSibling();
                }
            }
        }
    }

    /**
     * DOCUMENT ME!
     *
     * @param  newDoc
     */
    private void copyDocContents( DocumentImpl newDoc ) {
        namePool         = newDoc.namePool;
        nodeKind         = newDoc.nodeKind;
        treeLevel        = newDoc.treeLevel;
        next             = newDoc.next;
        nodeName         = newDoc.nodeName;
        nodeId           = newDoc.nodeId;
        alpha            = newDoc.alpha;
        alphaLen         = newDoc.alphaLen;
        characters       = newDoc.characters;
        nextChar         = newDoc.nextChar;
        attrName         = newDoc.attrName;
        attrNodeId       = newDoc.attrNodeId;
        attrParent       = newDoc.attrParent;
        attrValue        = newDoc.attrValue;
        nextAttr         = newDoc.nextAttr;
        namespaceParent  = newDoc.namespaceParent;
        namespaceCode    = newDoc.namespaceCode;
        nextNamespace    = newDoc.nextNamespace;
        size             = newDoc.size;
        documentRootNode = newDoc.documentRootNode;
        references       = newDoc.references;
        nextRef          = newDoc.nextRef;
    }

    /**
     * Stream the specified document fragment to a receiver. This method
     * is called by the serializer to output in-memory nodes.
     *
     * @param   serializer
     * @param   node
     * @param   receiver
     *
     * @throws  SAXException
     */
    public void streamTo( Serializer serializer, NodeImpl node, Receiver receiver )
            throws SAXException {
        final NodeImpl top = node;
        while (node != null) {
            startNode(serializer, node, receiver);
            NodeImpl nextNode;
            if (node instanceof ReferenceNode) {
                //Nothing more to stream ?
                nextNode = null;
            } else {
                nextNode = (NodeImpl)node.getFirstChild();
            }
            while (nextNode == null) {
                endNode(node, receiver);
                if ((top != null) && (top.nodeNumber == node.nodeNumber)) {
                    break;
                }
                nextNode = (NodeImpl)node.getNextSibling();
                if (nextNode == null) {
                    node = (NodeImpl)node.getParentNode();
                    if ((node == null) || ((top != null) && (top.nodeNumber == node.nodeNumber))) {
                        endNode(node, receiver);
                        break;
                    }
                }
            }
            node = nextNode;
        }
    }

    private void startNode(Serializer serializer, NodeImpl node, Receiver receiver)
            throws SAXException {
        final int nr = node.nodeNumber;
        switch (node.getNodeType()) {
        case Node.ELEMENT_NODE:
            final QName nodeName = document.nodeName[nr];
            //Output required namespace declarations
            int ns = document.alphaLen[nr];
            if (-1 < ns) {
                while ((ns < document.nextNamespace) && (document.namespaceParent[ns] == nr)) {
                    final QName nsQName = document.namespaceCode[ns];
                    if ("xmlns".equals(nsQName.getLocalName())) {
                        receiver.startPrefixMapping("", nsQName.getNamespaceURI());
                    } else {
                        receiver.startPrefixMapping(nsQName.getLocalName(), nsQName.getNamespaceURI());
                    }
                    ++ns;
                }
            }
            //Create the attribute list
            AttrList attribs = null;
            int attr = document.alpha[nr];
            if (-1 < attr) {
                attribs = new AttrList();
                while ((attr < document.nextAttr) && (document.attrParent[attr] == nr)) {
                    final QName attrQName = document.attrName[attr];
                    attribs.addAttribute(attrQName, attrValue[attr]);
                    ++attr;
                }
            }
            receiver.startElement(nodeName, attribs);
            break;
        case Node.TEXT_NODE:
            receiver.characters(new String(document.characters, document.alpha[nr],
                document.alphaLen[nr]));
            break;
        case Node.ATTRIBUTE_NODE:
            final QName attrQName = document.attrName[nr];
            receiver.attribute(attrQName, attrValue[nr]);
            break;
        case Node.COMMENT_NODE:
            receiver.comment(document.characters, document.alpha[nr], document.alphaLen[nr]);
            break;
        case Node.PROCESSING_INSTRUCTION_NODE:
            final QName qn = document.nodeName[nr];
            final String data = new String(document.characters, document.alpha[nr], document.alphaLen[nr]);
            receiver.processingInstruction(qn.getLocalName(), data);
            break;
        case Node.CDATA_SECTION_NODE:
            receiver.cdataSection(document.characters, document.alpha[nr], document.alphaLen[nr]);
            break;
        case NodeImpl.REFERENCE_NODE:
            serializer.toReceiver(document.references[document.alpha[nr]], true, false);
            break;
        }
    }

    private void endNode(NodeImpl node, Receiver receiver) throws SAXException {
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            receiver.endElement(node.getQName());
            //End all prefix mappings used for the element
            final int nr = node.nodeNumber;
            int ns = document.alphaLen[nr];
            if (-1 < ns) {
                while ((ns < document.nextNamespace) && (document.namespaceParent[ns] == nr)) {
                    final QName nsQName = document.namespaceCode[ns];
                    if ("xmlns".equals( nsQName.getLocalName())) {
                        receiver.endPrefixMapping("");
                    } else {
                        receiver.endPrefixMapping(nsQName.getLocalName());
                    }
                    ++ns;
                }
            }
        }
    }

    public org.exist.dom.DocumentImpl makePersistent() throws XPathException {
        if (size <= 1) {
            return null;
        }
        return context.storeTemporaryDoc(this);
    }

    public int getChildCount() {
        int count = 0;
        int top = (size > 1) ? 1 : -1;
        while (top > 0) {
            ++count;
            top = getNextSiblingFor(top);
        }
        return count;
    }

    @Override
    public boolean hasChildNodes() {
        return (getChildCount() > 0);
    }

    public NodeList getChildNodes() {
        final NodeListImpl nl = new NodeListImpl(1);
        final Element el = getDocumentElement();
        if (el != null)
            {nl.add(el);}
        return nl;
    }

    @Override
    public String getLocalName() {
        return "";
    }

    @Override
    public String getNamespaceURI() {
        return "";
    }

    /**
     * ? @see org.w3c.dom.Document#getInputEncoding()
     *
     * @return  DOCUMENT ME!
     */
    public String getInputEncoding() {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
        return null;
    }

    /**
     * ? @see org.w3c.dom.Document#getXmlEncoding()
     *
     * @return  DOCUMENT ME!
     */
    public String getXmlEncoding() {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
        return null;
    }

    /**
     * ? @see org.w3c.dom.Document#getXmlStandalone()
     *
     * @return  DOCUMENT ME!
     */
    public boolean getXmlStandalone() {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
        return false;
    }

    /**
     * ? @see org.w3c.dom.Document#setXmlStandalone(boolean)
     *
     * @param   xmlStandalone  DOCUMENT ME!
     *
     * @throws  DOMException  DOCUMENT ME!
     */
    public void setXmlStandalone( boolean xmlStandalone ) throws DOMException {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
    }

    /**
     * ? @see org.w3c.dom.Document#getXmlVersion()
     *
     * @return  DOCUMENT ME!
     */
    public String getXmlVersion() {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
        return null;
    }

    /**
     * ? @see org.w3c.dom.Document#setXmlVersion(java.lang.String)
     *
     * @param   xmlVersion  DOCUMENT ME!
     *
     * @throws  DOMException  DOCUMENT ME!
     */
    public void setXmlVersion(String xmlVersion) throws DOMException {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
    }

    /**
     * ? @see org.w3c.dom.Document#getStrictErrorChecking()
     *
     * @return  DOCUMENT ME!
     */
    public boolean getStrictErrorChecking() {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
        return false;
    }

    /**
     * ? @see org.w3c.dom.Document#setStrictErrorChecking(boolean)
     *
     * @param  strictErrorChecking  DOCUMENT ME!
     */
    public void setStrictErrorChecking( boolean strictErrorChecking ) {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
    }

    /**
     * ? @see org.w3c.dom.Document#getDocumentURI()
     *
     * @return  DOCUMENT ME!
     */
    public String getDocumentURI() {
        return documentURI;
    }

    /**
     * ? @see org.w3c.dom.Document#setDocumentURI(java.lang.String)
     *
     * @param  documentURI  DOCUMENT ME!
     */
    public void setDocumentURI(String documentURI) {
        this.documentURI = documentURI;
    }

    /**
     * ? @see org.w3c.dom.Document#adoptNode(org.w3c.dom.Node)
     *
     * @param   source  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     *
     * @throws  DOMException  DOCUMENT ME!
     */
    public Node adoptNode(Node source) throws DOMException {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
        return null;
    }

    /**
     * ? @see org.w3c.dom.Document#getDomConfig()
     *
     * @return  DOCUMENT ME!
     */
    public DOMConfiguration getDomConfig() {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
        return null;
    }

    /**
     * ? @see org.w3c.dom.Document#normalizeDocument()
     */
    public void normalizeDocument() {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
    }

    /**
     * ? @see org.w3c.dom.Document#renameNode(org.w3c.dom.Node, java.lang.String, java.lang.String)
     *
     * @param   n              DOCUMENT ME!
     * @param   namespaceURI   DOCUMENT ME!
     * @param   qualifiedName  DOCUMENT ME!
     *
     * @return  DOCUMENT ME!
     *
     * @throws  DOMException  DOCUMENT ME!
     */
    public Node renameNode(Node n, String namespaceURI, String qualifiedName)
            throws DOMException {
        // maybe _TODO_ - new DOM interfaces - Java 5.0
        return null;
    }

    public void setContext(XQueryContext context) {
        this.context = context;
    }

    public XQueryContext getContext() {
        return context;
    }

    /**
     * ? @see org.w3c.dom.Node#getBaseURI()
     *
     * @return  DOCUMENT ME!
     */
    @Override
    public String getBaseURI() {
        final Element el = getDocumentElement();
        if (el != null) {
            final String baseURI = getDocumentElement().getAttributeNS(Namespaces.XML_NS, "base");
            if (baseURI != null)
                {return baseURI;}
        }
        final String docURI = getDocumentURI();
        if (docURI != null)
            {return docURI;}
        else {
            if (context.isBaseURIDeclared()) {
                try {
                    return context.getBaseURI().getStringValue();
                } catch (final XPathException e) {
                    //TODO : make something !
                }
            }
            return XmldbURI.EMPTY_URI.toString();
        }
    }

    @Override
    public int getItemType() {
        return Type.DOCUMENT;
    }

    @Override
    public String toString() {
        final StringBuilder result = new StringBuilder();
        result.append("in-memory#");
        result.append("document {");
        if (size != 1) {
            int nodeNum = 1;
            while (true) {
                result.append(getNode(nodeNum).toString());
                if (next[nodeNum] < nodeNum) {
                    break;
                }
                nodeNum = next[nodeNum];
            }
        }
        result.append("} ");
        return(result.toString());
    }

    public int getNextNodeNumber(int nextNode) throws DOMException {
        return document.next[nextNode];
    }

    @Override
    public XmldbURI getURI() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void selectAttributes(NodeTest test, Sequence result)
            throws XPathException {
        // TODO Auto-generated method stub
    }

    @Override
    public int getDocId() {
        return 0;
    }

  @Override
  public Object getUUID() {
    return null;
  }
}
TOP

Related Classes of org.exist.memtree.DocumentImpl

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.