Package org.exist.dom

Source Code of org.exist.dom.DocumentImpl

/*
* eXist Open Source Native XML Database
* Copyright (C) 2001-2007 The eXist team
* 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 org.exist.EXistException;
import org.exist.collections.Collection;
import org.exist.collections.CollectionConfiguration;
import org.exist.numbering.NodeId;
import org.exist.security.*;
import org.exist.security.SecurityManager;
import org.exist.storage.BrokerPool;
import org.exist.storage.DBBroker;
import org.exist.storage.ElementValue;
import org.exist.storage.NodePath;
import org.exist.storage.StorageAddress;
import org.exist.storage.io.VariableByteInput;
import org.exist.storage.io.VariableByteOutputStream;
import org.exist.storage.lock.Lock;
import org.exist.storage.lock.MultiReadReentrantLock;
import org.exist.storage.txn.Txn;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.Constants;
import org.exist.xquery.DescendantSelector;
import org.exist.xquery.Expression;
import org.exist.xquery.NodeSelector;
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.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.w3c.dom.UserDataHandler;

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

/**
*  Represents a persistent document object in the database;
*  it can be an XML_FILE , a BINARY_FILE, or Xquery source code.
*@author     Wolfgang Meier <wolfgang@exist-db.org>
*/
public class DocumentImpl extends NodeImpl implements Document, DocumentAtExist, Iterable<NodeImpl> { //Comparable<DocumentImpl>,

    public final static int UNKNOWN_DOCUMENT_ID = -1;
   
    public final static byte XML_FILE = 0;
    public final static byte BINARY_FILE = 1;

    public static int LENGTH_DOCUMENT_ID = 4; //sizeof int
    public static int LENGTH_DOCUMENT_TYPE = 1; //sizeof byte

    //public final static byte DOCUMENT_NODE_SIGNATURE = 0x0F;

    protected BrokerPool pool = null;

    /** number of child nodes */
    private int children = 0;

    private long[] childAddress = null;

    /** the collection this document belongs to */
    private transient Collection collection = null;

    /** the document's id */
    private int docId = UNKNOWN_DOCUMENT_ID;

    /** the document's file name */
    private XmldbURI fileURI = null;

    private Permission permissions = null;

    private transient Lock updateLock = null;

    private DocumentMetadata metadata = null;   

    /**
     * Creates a new <code>DocumentImpl</code> instance.
     *
     * @param pool a <code>BrokerPool</code> instance representing the db
     */
    public DocumentImpl(BrokerPool pool) {
        this(pool, null, null);
    }

    /**
     * Creates a new <code>DocumentImpl</code> instance.
     *
     * @param pool a <code>BrokerPool</code> instance representing the db
     * @param collection a <code>Collection</code> value
     * @param fileURI a <code>XmldbURI</code> value
     */
    public DocumentImpl(BrokerPool pool, Collection collection, XmldbURI fileURI) {
        this.pool = pool;
        this.collection = collection;
        this.fileURI = fileURI;

        // the permissions assigned to this document
        this.permissions = PermissionFactory.getDefaultResourcePermission();

        //inherit the group to the resource if current collection is setGid
        if(collection != null && collection.getPermissions().isSetGid()) {
            try {
                this.permissions.setGroupFrom(collection.getPermissions());
            } catch(final PermissionDeniedException pde) {
                throw new IllegalArgumentException(pde); //TODO improve
            }
        }
    }

    public BrokerPool getBrokerPool() {
        return pool;
    }

    public BrokerPool getDatabase() {
        return pool;
    }

    /**
     * The method <code>getLocalName</code>
     *
     * @return a <code>String</code> value
     */
    @Override
    public String getLocalName() {
        return "";
    }

    /**
     * The method <code>getNamespaceURI</code>
     *
     * @return a <code>String</code> value
     */
    @Override
    public String getNamespaceURI() {
        return "";
    }

    /************************************************
     *
     * Document metadata
     *
     ************************************************/

    /**
     * The method <code>getCollection</code>
     *
     * @return a <code>Collection</code> value
     */
    public Collection getCollection() {
        return collection;
    }

    /**
     * The method <code>setCollection</code>
     *
     * @param parent a <code>Collection</code> value
     */
    public void setCollection(Collection parent) {
        this.collection = parent;
    }

    /**
     * The method <code>getDocId</code>
     *
     * @return an <code>int</code> value
     */
    public int getDocId() {
        return docId;
    }

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

    /**
     * Returns the type of this resource, either  {@link #XML_FILE} or
     * {@link #BINARY_FILE}.
     *
     */
    public byte getResourceType() {
        return XML_FILE;
    }

    /**
     * The method <code>getFileURI</code>
     *
     * @return a <code>XmldbURI</code> value
     */
    public XmldbURI getFileURI() {
        //checkAvail();
        return fileURI;
    }
   
    /**
     * The method <code>setFileURI</code>
     *
     * @param fileURI a <code>XmldbURI</code> value
     */
    public void setFileURI(XmldbURI fileURI) {
        this.fileURI = fileURI;
    }
   
    /**
     * The method <code>getURI</code>
     *
     * @return a <code>XmldbURI</code> value
     */
    public XmldbURI getURI() {
        if(collection == null) {
            return fileURI;
        } else {
            return collection.getURI().append(fileURI);
        }
    }
   
    public boolean isCollectionConfig() {
      return fileURI.endsWith(CollectionConfiguration.COLLECTION_CONFIG_SUFFIX_URI);
    }
   
    /**
     * The method <code>getMode</code>
     *
     * @return a <code>Permission</code> value
     */
    public Permission getPermissions() {
        return permissions;
    }

    /**
     * The method <code>setMode</code>
     *
     * @param perm a <code>Permission</code> value
     *
     * @deprecated This function is considered a security problem
     * and should be removed, move code to copyOf or Constructor
     */
    @Deprecated
    public void setPermissions(Permission perm) {
        permissions = perm;
    }

    /**
     * The method <code>setMetadata</code>
     *
     * @param meta a <code>DocumentMetadata</code> value
     *
     * @deprecated This function is considered a security problem
     * and should be removed, move code to copyOf or Constructor
     */
    @Deprecated
    public void setMetadata(DocumentMetadata meta) {
        this.metadata = meta;
    }

    /**
     * The method <code>getMetadata</code>
     *
     * @return a <code>DocumentMetadata</code> value
     */
    public DocumentMetadata getMetadata() {
        if (metadata == null) {
            DBBroker broker = null;
            try {
                broker = pool.get(null);
                broker.getResourceMetadata(this);
            } catch (final EXistException e) {
                LOG.warn("Error while loading document metadata: " + e.getMessage(), e);
            } finally {
                pool.release(broker);
            }
        }
        return metadata;
    }

    /************************************************
     *
     * Persistent node methods
     *
     ************************************************/

    /**
     * Copy the relevant internal fields from the specified document object.
     * This is called by {@link Collection} when replacing a document.
     *
     * @param other a <code>DocumentImpl</code> value
     * @param preserve Cause copyOf to preserve the following attributes of
     *                 each source file in the copy: modification time,
     *                 access time, file mode, user ID, and group ID,
     *                 as allowed by permissions and  Access Control
     *                 Lists (ACLs)
     */
    public void copyOf(final DocumentImpl other, final boolean preserve) {
        childAddress = null;
        children = 0;

        //XXX: why reusing? better to create new instance? -shabanovd
        metadata = getMetadata();
        if (metadata == null) {
            metadata = new DocumentMetadata();
        }
       
        //copy metadata
        metadata.copyOf(other.getMetadata());

        if(preserve) {
            //copy permission
            permissions = ((UnixStylePermission)other.permissions).copy();
            //created and last modified are done by metadata.copyOf
            //metadata.setCreated(other.getMetadata().getCreated());
            //metadata.setLastModified(other.getMetadata().getLastModified());
        } else {
            //update timestamp
            final long timestamp = System.currentTimeMillis();
            metadata.setCreated(timestamp);
            metadata.setLastModified(timestamp);
        }

        // reset pageCount: will be updated during storage
        metadata.setPageCount(0);
    }

    /**
     * The method <code>copyChildren</code>
     *
     * @param other a <code>DocumentImpl</code> value
     */
    public void copyChildren(DocumentImpl other) {
        childAddress = other.childAddress;
        children = other.children;
    }

    /**
     * Returns true if the document is currently locked for
     * write.
     *
     */
    public synchronized boolean isLockedForWrite() {
        return getUpdateLock().isLockedForWrite();
    }

    /**
     * Returns the update lock associated with this
     * resource.
     *
     */
    public final synchronized Lock getUpdateLock() {
        if(updateLock == null)
            {updateLock = new MultiReadReentrantLock(fileURI);}
        return updateLock;
    }

    /**
     * The method <code>setUserLock</code>
     *
     * @param user an <code>User</code> value
     */
    public void setUserLock(Account user) {
        getMetadata().setUserLock(user == null ? 0 : user.getId());
    }

    /**
     * The method <code>getUserLock</code>
     *
     * @return an <code>User</code> value
     */
    public Account getUserLock() {
        final int lockOwnerId = getMetadata().getUserLock();
        if(lockOwnerId == 0)
            {return null;}
        final SecurityManager secman = pool.getSecurityManager();
        return secman.getAccount(lockOwnerId);
    }

    /**
     * Returns the estimated size of the data in this document.
     *
     * As an estimation, the number of pages occupied by the document
     * is multiplied with the current page size.
     *
     */
    public long getContentLength() {
        final long length = getMetadata().getPageCount() * pool.getPageSize();
        return (length < 0) ? 0 : length;
    }

    /**
     * The method <code>triggerDefrag</code>
     *
     */
    public void triggerDefrag() {   
        int fragmentationLimit = -1;
        final Object property = pool.getConfiguration().getProperty(DBBroker.PROPERTY_XUPDATE_FRAGMENTATION_FACTOR);
        if (property != null)
            {fragmentationLimit = ((Integer)property).intValue();}
        if (fragmentationLimit != -1)
            {getMetadata().setSplitCount(fragmentationLimit);}
    }

    /**
     * The method <code>getNode</code>
     *
     * @param nodeId a <code>NodeId</code> value
     * @return a <code>Node</code> value
     */
    public Node getNode(NodeId nodeId) {
        if (nodeId.getTreeLevel() == 1)
            {return getDocumentElement();}
        DBBroker broker = null;
        try {
            broker = pool.get(null);
            return broker.objectWith(this, nodeId);
        } catch (final EXistException e) {
            LOG.warn("Error occured while retrieving node: " + e.getMessage(), e);
        } finally {
            pool.release(broker);
        }
        return null;
    }

    /**
     * The method <code>getNode</code>
     *
     * @param p a <code>NodeProxy</code> value
     * @return a <code>Node</code> value
     */
    public Node getNode(NodeProxy p) {
        if(p.getNodeId().getTreeLevel() == 1)
            {return getDocumentElement();}
        DBBroker broker = null;
        try {
            broker = pool.get(null);
            return broker.objectWith(p);
        } catch (final Exception e) {
            LOG.warn("Error occured while retrieving node: " + e.getMessage(), e);
        } finally {
            pool.release(broker);
        }
        return null;
    }

    /**
     * The method <code>resizeChildList</code>
     *
     */
    private void resizeChildList() {
        long[] newChildList = new long[children];
        if(childAddress != null)
            {System.arraycopy(childAddress, 0, newChildList, 0, childAddress.length);}
        childAddress = newChildList;
    }

    /**
     * The method <code>appendChild</code>
     *
     * @param child a <code>StoredNode</code> value
     * @exception DOMException if an error occurs
     */
    public void appendChild(StoredNode child) throws DOMException {
        ++children;
        resizeChildList();
        childAddress[children - 1] = child.getInternalAddress();
    }

    /**
     * The method <code>write</code>
     *
     * @param ostream a <code>VariableByteOutputStream</code> value
     * @exception IOException if an error occurs
     */
    public void write(VariableByteOutputStream ostream) throws IOException {
        try {
            if (!getCollection().isTempCollection() && !getUpdateLock().isLockedForWrite()) {
                LOG.warn("document not locked for write !");
            }
            ostream.writeInt(docId);
            ostream.writeUTF(fileURI.toString());
            getPermissions().write(ostream);
            ostream.writeInt(children);
            if (children > 0) {
                for(int i = 0; i < children; i++) {
                    ostream.writeInt(StorageAddress.pageFromPointer(childAddress[i]));
                    ostream.writeShort(StorageAddress.tidFromPointer(childAddress[i]));
                }
            }
            getMetadata().write(pool, ostream);
        } catch (final IOException e) {
            LOG.warn("io error while writing document data", e);
            //TODO : raise exception ?
        }
    }

    /**
     * The method <code>read</code>
     *
     * @param istream a <code>VariableByteInput</code> value
     * @exception IOException if an error occurs
     * @exception EOFException if an error occurs
     */
    public void read(VariableByteInput istream) throws IOException, EOFException {
        try {
            docId = istream.readInt();
            fileURI = XmldbURI.createInternal(istream.readUTF());
            getPermissions().read(istream);
            //Should be > 0 ;-)
            children = istream.readInt();
            childAddress = new long[children];
            for (int i = 0; i < children; i++) {
                childAddress[i] = StorageAddress.createPointer(istream.readInt(), istream.readShort());
            }
        } catch (final IOException e) {
            LOG.error("IO error while reading document data for document " + fileURI, e);
            //TODO : raise exception ?
        }
    }

    public void readWithMetadata(VariableByteInput istream) throws IOException, EOFException {
        try {
            docId = istream.readInt();
            fileURI = XmldbURI.createInternal(istream.readUTF());
            getPermissions().read(istream);
            //Should be > 0 ;-)
            children = istream.readInt();
            childAddress = new long[children];
            for (int i = 0; i < children; i++) {
                childAddress[i] = StorageAddress.createPointer(istream.readInt(), istream.readShort());
            }
            metadata = new DocumentMetadata();
            metadata.read(pool, istream);
        } catch (final IOException e) {
            LOG.error("IO error while reading document data for document " + fileURI, e);
            //TODO : raise exception ?
        }
    }

    /**
     * The method <code>readDocumentMeta</code>
     *
     * @param istream a <code>VariableByteInput</code> value
     */
    public void readDocumentMeta(VariableByteInput istream) {
        // skip over already known document data
        try {
            istream.skip(1); //docId
            istream.readUTF(); //fileURI.toString()

            //istream.skip(2 + 2); //uid, gid, mode, children count
            istream.skip(1); //unix style permission uses a single long
            if(permissions instanceof ACLPermission) {
                final int aceCount = istream.read();
                istream.skip(aceCount);
            }
           
            istream.skip(1); //children size
            istream.skip(children * 2); //actual children

            metadata = new DocumentMetadata();
            metadata.read(pool, istream);
           
        } catch (final IOException e) {
            LOG.error("IO error while reading document metadata for " + fileURI, e);
            //TODO : raise exception ?
        }
    }

    /**
     * The method <code>compareTo</code>
     *
     * @param other an <code>DocumentImpl</code> value
     * @return an <code>int</code> value
     */
    public final int compareTo(Object other) {
        if (!(other instanceof DocumentImpl)) {
            return Constants.INFERIOR;
        }
        final long otherId = ((DocumentImpl)other).docId;
        if (otherId == docId)
            {return Constants.EQUAL;}
        else if (docId < otherId)
            {return Constants.INFERIOR;}
        else
            {return Constants.SUPERIOR;}
    }

    /* (non-Javadoc)
     * @see org.exist.dom.NodeImpl#updateChild(org.w3c.dom.Node, org.w3c.dom.Node)
     */
    @Override
    public StoredNode updateChild(Txn transaction, Node oldChild, Node newChild) throws DOMException {
        if (!(oldChild instanceof StoredNode))
            {throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, "Node does not belong to this document");}
        final StoredNode oldNode = (StoredNode) oldChild;
        final StoredNode newNode = (StoredNode) newChild;
        final StoredNode previousNode = (StoredNode) oldNode.getPreviousSibling();
        if (previousNode == null)
            {throw new DOMException(DOMException.NOT_FOUND_ERR, "No previous sibling for the old child");}
        DBBroker broker = null;
        try {
            broker = pool.get(null);
            if (oldChild.getNodeType() == Node.ELEMENT_NODE) {
                // replace the document-element
                //TODO : be more precise in the type test -pb
                if (newChild.getNodeType() != Node.ELEMENT_NODE)
                    {throw new DOMException(
                       DOMException.INVALID_MODIFICATION_ERR,
                       "A node replacing the document root needs to be an element");}
                broker.removeNode(transaction, oldNode, oldNode.getPath(), null);
                broker.endRemove(transaction);
                newNode.setNodeId(oldNode.getNodeId());
                broker.insertNodeAfter(null, previousNode, newNode);
                final NodePath path = newNode.getPath();
                broker.indexNode(transaction, newNode, path);
                broker.endElement(newNode, path, null);
                broker.flush();
            } else {
                broker.removeNode(transaction, oldNode, oldNode.getPath(), null);
                broker.endRemove(transaction);
                newNode.setNodeId(oldNode.getNodeId());
                broker.insertNodeAfter(transaction, previousNode, newNode);
            }
        } catch (final EXistException e) {
            LOG.warn("Exception while updating child node: " + e.getMessage(), e);
            //TODO : thow exception ?
        } finally {
            pool.release(broker);
        }
        return newNode;
    }

    /*
     * @see org.exist.dom.NodeImpl#insertBefore(org.w3c.dom.NodeList, org.w3c.dom.Node)
     */
    public void insertBefore(NodeList nodes, Node refChild) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "not implemented");
    }

    /*
     * @see org.exist.dom.NodeImpl#insertAfter(org.w3c.dom.NodeList, org.w3c.dom.Node)
     */
    public void insertAfter(NodeList nodes, Node refChild) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "not implemented");
    }

    /* (non-Javadoc)
     * @see org.w3c.dom.Node#getFirstChild()
     */
    @Override
    public Node getFirstChild() {
        if (children == 0)
            {return null;}
        DBBroker broker = null;
        try {
            broker = pool.get(null);
            return broker.objectWith(new NodeProxy(this, NodeId.DOCUMENT_NODE, childAddress[0]));
        } catch (final EXistException e) {
            LOG.warn("Exception while inserting node: " + e.getMessage(), e);
            //TODO : throw exception ?
        } finally {
            pool.release(broker);
        }
        return null;
    }

    public NodeProxy getFirstChildProxy() {
        return new NodeProxy(this, NodeId.ROOT_NODE, Node.ELEMENT_NODE, childAddress[0]);
    }

    /**
     * The method <code>getFirstChildAddress</code>
     *
     * @return a <code>long</code> value
     */
    public long getFirstChildAddress() {
        if (children == 0)
            {return StoredNode.UNKNOWN_NODE_IMPL_ADDRESS;}
        return childAddress[0];
    }

    /**
     * The method <code>getChildNodes</code>
     *
     * @return a <code>NodeList</code> value
     */
    @Override
    public NodeList getChildNodes() {
        final NodeListImpl list = new NodeListImpl();
        DBBroker broker = null;
        try {
            broker = pool.get(null);
            for (int i = 0; i < children; i++) {
                final Node child = broker.objectWith(new NodeProxy(this, NodeId.DOCUMENT_NODE, childAddress[i]));
                list.add(child);
            }
        } catch (final EXistException e) {
            LOG.warn("Exception while retrieving child nodes: " + e.getMessage(), e);
        } finally {
            pool.release(broker);
        }
        return list;
    }

    /**
     * The method <code>getPreviousSibling</code>
     *
     * @param node a <code>StoredNode</code> value
     * @return a <code>Node</code> value
     */
    protected Node getPreviousSibling(StoredNode node) {
        final NodeList cl = getChildNodes();
        for (int i = 0; i < cl.getLength(); i++) {
            final StoredNode next = (StoredNode) cl.item(i);
            if (StorageAddress.equals(node.getInternalAddress(), next.getInternalAddress()))
                {return i == 0 ? null : cl.item(i - 1);}
        }
        return null;
    }

    /**
     * The method <code>getFollowingSibling</code>
     *
     * @param node a <code>StoredNode</code> value
     * @return a <code>Node</code> value
     */
    protected Node getFollowingSibling(StoredNode node) {
        final NodeList cl = getChildNodes();
        for (int i = 0; i < cl.getLength(); i++) {
            final StoredNode next = (StoredNode) cl.item(i);
            if (StorageAddress.equals(node.getInternalAddress(), next.getInternalAddress()))
                {return i == children - 1 ? null : cl.item(i + 1);}
        }
        return null;
    }

    /**
     * The method <code>findElementsByTagName</code>
     *
     * @param root a <code>StoredNode</code> value
     * @param qname a <code>QName</code> value
     * @return a <code>NodeList</code> value
     */
    protected NodeList findElementsByTagName(StoredNode root, QName qname) {
        DBBroker broker = null;
        try {
            broker = pool.get(null);
            final MutableDocumentSet docs = new DefaultDocumentSet();
            docs.add(this);
            final NodeProxy p = new NodeProxy(this, root.getNodeId(), root.getInternalAddress());
            final NodeSelector selector = new DescendantSelector(p, Expression.NO_CONTEXT_ID);
            return broker.getStructuralIndex().findElementsByTagName(ElementValue.ELEMENT, docs, qname, selector, null);
        } catch (final Exception e) {
            LOG.warn("Exception while finding elements: " + e.getMessage(), e);
        } finally {
            pool.release(broker);
        }
        return NodeSet.EMPTY_SET;
    }

    /************************************************
     *
     * NodeImpl methods
     *
     ************************************************/
   

    /**
     * The method <code>getDoctype</code>
     *
     * @return a <code>DocumentType</code> value
     */
    public DocumentType getDoctype() {
        return getMetadata().getDocType();
    }

    /**
     * The method <code>setDocumentType</code>
     *
     * @param docType a <code>DocumentType</code> value
     */
    public void setDocumentType(DocumentType docType) {
        getMetadata().setDocType(docType);
    }

    /**
     * The method <code>getOwnerDocument</code>
     *
     * @return a <code>Document</code> value
     */
    public Document getOwnerDocument() {
        return this;
    }

    /**
     * The method <code>setOwnerDocument</code>
     *
     * @param doc a <code>Document</code> value
     */
    public void setOwnerDocument(Document doc) {
        if (doc != this)
            {throw new IllegalArgumentException("Can't set owner document");}
    }

    /**
     * The method <code>getQName</code>
     *
     * @return a <code>QName</code> value
     */
    public QName getQName() {
        return QName.DOCUMENT_QNAME;
    }

    /**
     * The method <code>getNodeType</code>
     *
     * @return a <code>short</code> value
     */
    public short getNodeType() {
        return Node.DOCUMENT_NODE;
    }

    /**
     * The method <code>getPreviousSibling</code>
     *
     * @return a <code>Node</code> value
     */
    public Node getPreviousSibling() {
        //Documents don't have siblings
        return null;
    }

    /**
     * The method <code>getNextSibling</code>
     *
     * @return a <code>Node</code> value
     */
    public Node getNextSibling() {
        //Documents don't have siblings
        return null;
    }

    /**
     * The method <code>createAttribute</code>
     *
     * @param name a <code>String</code> value
     * @return an <code>Attr</code> value
     * @exception DOMException if an error occurs
     */
    public Attr createAttribute(String name) throws DOMException {
        final AttrImpl attr = new AttrImpl(new QName(name, "", null), getBrokerPool().getSymbols());
        attr.setOwnerDocument(this);
        return attr;
    }

    /**
     * The method <code>createAttributeNS</code>
     *
     * @param namespaceURI a <code>String</code> value
     * @param qualifiedName a <code>String</code> value
     * @return an <code>Attr</code> value
     * @exception DOMException if an error occurs
     */
    public Attr createAttributeNS(String namespaceURI, String qualifiedName) throws DOMException {
        String name;
        String prefix;
        final int p = qualifiedName.indexOf(':');
        if (p == Constants.STRING_NOT_FOUND) {
            prefix =null;
            name =  qualifiedName;
        } else {
            prefix = qualifiedName.substring(0, p);
            name = qualifiedName.substring(p);
        }
        final AttrImpl attr = new AttrImpl(new QName(name, namespaceURI, prefix), getBrokerPool().getSymbols());
        attr.setOwnerDocument(this);
        return attr;
    }

    /**
     * The method <code>createElement</code>
     *
     * @param tagName a <code>String</code> value
     * @return an <code>Element</code> value
     * @exception DOMException if an error occurs
     */
    public Element createElement(String tagName) throws DOMException {
        final ElementImpl element = new ElementImpl(new QName(tagName, "", null), getBrokerPool().getSymbols());
        element.setOwnerDocument(this);
        return element;
    }

    /**
     * The method <code>createElementNS</code>
     *
     * @param namespaceURI a <code>String</code> value
     * @param qualifiedName a <code>String</code> value
     * @return an <code>Element</code> value
     * @exception DOMException if an error occurs
     */
    public Element createElementNS(String namespaceURI, String qualifiedName) throws DOMException {
        String name;
        String prefix;
        final int p = qualifiedName.indexOf(':');
        if (p == Constants.STRING_NOT_FOUND) {
            prefix =null;
            name = qualifiedName;
        } else {
            prefix = qualifiedName.substring(0, p);
            name = qualifiedName.substring(p);
        }
        final ElementImpl element = new ElementImpl(new QName(name, namespaceURI, prefix), getBrokerPool().getSymbols());
        element.setOwnerDocument(this);
        return element;
    }

    /**
     * The method <code>createTextNode</code>
     *
     * @param data a <code>String</code> value
     * @return a <code>Text</code> value
     */
    public Text createTextNode(String data) {
        final TextImpl text = new TextImpl(data);
        text.setOwnerDocument(this);
        return text;
    }

    /*
     *  W3C Document-Methods
     */

    /**
     * The method <code>getDocumentElement</code>
     *
     * @return an <code>Element</code> value
     */
    public Element getDocumentElement() {
        final NodeList cl = getChildNodes();
        for (int i = 0; i < cl.getLength(); i++) {
            if (cl.item(i).getNodeType() == Node.ELEMENT_NODE)
                {return (Element) cl.item(i);}
        }
        return null;
    }

    /**
     * The method <code>getElementsByTagName</code>
     *
     * @param tagname a <code>String</code> value
     * @return a <code>NodeList</code> value
     */
    public NodeList getElementsByTagName(String tagname) {
        return getElementsByTagNameNS("", tagname);
    }

    /**
     * The method <code>getElementsByTagNameNS</code>
     *
     * @param namespaceURI a <code>String</code> value
     * @param localName a <code>String</code> value
     * @return a <code>NodeList</code> value
     */
    public NodeList getElementsByTagNameNS(String namespaceURI, String localName) {
        DBBroker broker = null;
        try {
            broker = pool.get(null);
            final MutableDocumentSet docs = new DefaultDocumentSet();
            docs.add(this);
            final QName qname = new QName(localName, namespaceURI, null);
            return broker.getStructuralIndex().findElementsByTagName(ElementValue.ELEMENT, docs, qname, null, null);
        } catch (final Exception e) {
            LOG.warn("Exception while finding elements: " + e.getMessage(), e);
            //TODO : throw exception ?
        } finally {
            pool.release(broker);
        }
        return NodeSet.EMPTY_SET;
    }

    /* (non-Javadoc)
     * @see org.w3c.dom.Node#getParentNode()
     */
    public Node getParentNode() {
        //Documents don't have parents
        return null;
    }

    /**
     * The method <code>getChildCount</code>
     *
     * @return an <code>int</code> value
     */
    @Override
    public int getChildCount() {
        return children;
    }

    /**
     * The method <code>setChildCount</code>
     *
     * @param count an <code>int</code> value
     */
    @Override
    public void setChildCount(int count) {
        children = count;
        if (children == 0)
            {childAddress = null;}
    }

    /**
     * The method <code>getEncoding</code>
     *
     * @return a <code>String</code> value
     */
    public String getEncoding() {
        //TODO : on demand result (e.g. from serializer's settings) ? -pb
        return "UTF-8";
    }

    /**
     * The method <code>setEncoding</code>
     *
     * @param enc a <code>String</code> value
     */
    public void setEncoding(String enc) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "setEncoding not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>getVersion</code>
     *
     * @return a <code>String</code> value
     */
    public String getVersion() {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getVersion not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>setVersion</code>
     *
     * @param version a <code>String</code> value
     */
    public void setVersion(String version) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "setVersion not implemented on class " + getClass().getName());
    }    

    /**
     * The method <code>getStandalone</code>
     *
     * @return a <code>boolean</code> value
     */
    public boolean getStandalone() {
        //TODO : on demand result (e.g. from serializer's settings) ? -pb
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getStandalone not implemented on class " + getClass().getName());
    }   

    /**
     * The method <code>setStandalone</code>
     *
     * @param alone a <code>boolean</code> value
     */
    public void setStandalone(boolean alone) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "setStandalone not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>createCDATASection</code>
     *
     * @param data a <code>String</code> value
     * @return a <code>CDATASection</code> value
     * @exception DOMException if an error occurs
     */
    public CDATASection createCDATASection(String data) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "createCDATASection not implemented on class " + getClass().getName());       
    }

    /**
     * The method <code>createComment</code>
     *
     * @param data a <code>String</code> value
     * @return a <code>Comment</code> value
     */
    public Comment createComment(String data) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "createComment not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>createDocumentFragment</code>
     *
     * @return a <code>DocumentFragment</code> value
     * @exception DOMException if an error occurs
     */
    public DocumentFragment createDocumentFragment() throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "createDocumentFragment not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>createEntityReference</code>
     *
     * @param name a <code>String</code> value
     * @return an <code>EntityReference</code> value
     * @exception DOMException if an error occurs
     */
    public EntityReference createEntityReference(String name) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "createEntityReference not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>createProcessingInstruction</code>
     *
     * @param target a <code>String</code> value
     * @param data a <code>String</code> value
     * @return a <code>ProcessingInstruction</code> value
     * @exception DOMException if an error occurs
     */
    public ProcessingInstruction createProcessingInstruction(String target, String data)
            throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "createProcessingInstruction not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>getElementById</code>
     *
     * @param elementId a <code>String</code> value
     * @return an <code>Element</code> value
     */
    public Element getElementById(String elementId) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getElementById not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>getImplementation</code>
     *
     * @return an <code>org.w3c.dom.DOMImplementation</code> value
     */
    public org.w3c.dom.DOMImplementation getImplementation() {
        return new StoredDOMImplementation();
    }

    /**
     * The method <code>getStrictErrorChecking</code>
     *
     * @return a <code>boolean</code> value
     */
    public boolean getStrictErrorChecking() {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getStrictErrorChecking not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>adoptNode</code>
     *
     * @param node a <code>Node</code> value
     * @return a <code>Node</code> value
     * @exception DOMException if an error occurs
     */
    public Node adoptNode(Node node) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "adoptNode not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>importNode</code>
     *
     * @param importedNode a <code>Node</code> value
     * @param deep a <code>boolean</code> value
     * @return a <code>Node</code> value
     * @exception DOMException if an error occurs
     */
    public Node importNode(Node importedNode, boolean deep) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "importNode not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>isSupported</code>
     *
     * @param type a <code>String</code> value
     * @param value a <code>String</code> value
     * @return a <code>boolean</code> value
     */
    @Override
    public boolean isSupported(String type, String value) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "isSupported not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>setStrictErrorChecking</code>
     *
     * @param strict a <code>boolean</code> value
     */
    public void setStrictErrorChecking(boolean strict) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "setStrictErrorChecking not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Document#getInputEncoding()
     */
    public String getInputEncoding() {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "String getInputEncoding not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Document#getXmlEncoding()
     */
    public String getXmlEncoding() {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getXmlEncoding not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Document#getXmlStandalone()
     */
    public boolean getXmlStandalone() {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getXmlStandalone not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Document#setXmlStandalone(boolean)
     */
    public void setXmlStandalone(boolean xmlStandalone) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "setXmlStandalone not implemented on class " + getClass().getName());   
    }

    /** ? @see org.w3c.dom.Document#getXmlVersion()
     */
    public String getXmlVersion() {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getXmlVersion not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Document#setXmlVersion(java.lang.String)
     */
    public void setXmlVersion(String xmlVersion) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "setXmlVersion not implemented on class " + getClass().getName());   
    }

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

    /** ? @see org.w3c.dom.Document#setDocumentURI(java.lang.String)
     */
    public void setDocumentURI(String documentURI) {
        //TODO : non-writable -pb
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "setDocumentURI not implemented on class " + getClass().getName())
    }

    /** ? @see org.w3c.dom.Document#getDomConfig()
     */
    public DOMConfiguration getDomConfig() {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getDomConfig not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Document#normalizeDocument()
     */
    public void normalizeDocument() {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "normalizeDocument not implemented on class " + getClass().getName())
    }

    /** ? @see org.w3c.dom.Document#renameNode(org.w3c.dom.Node, java.lang.String, java.lang.String)
     */
    public Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "renameNode not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Node#getBaseURI()
     */
    @Override
    public String getBaseURI() {
        try {
            return getURI() + "";
        } catch (final Exception e) {
            System.out.println("dom/DocumentImpl::getBaseURI() 2 exception catched: ");
        }
        return XmldbURI.ROOT_COLLECTION_URI + "";
    }

    /** ? @see org.w3c.dom.Node#compareDocumentPosition(org.w3c.dom.Node)
     */
    @Override
    public short compareDocumentPosition(Node other) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "compareDocumentPosition not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Node#getTextContent()
     */
    @Override
    public String getTextContent() throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getTextContent not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Node#setTextContent(java.lang.String)
     */
    @Override
    public void setTextContent(String textContent) throws DOMException {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "setTextContent not implemented on class " + getClass().getName())
    }

    /** ? @see org.w3c.dom.Node#isSameNode(org.w3c.dom.Node)
     */
    @Override
    public boolean isSameNode(Node other) {
        //TODO : compare node identities ? -pb
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "isSameNode not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Node#lookupPrefix(java.lang.String)
     */
    @Override
    public String lookupPrefix(String namespaceURI) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "lookupPrefix(String namespaceURI) not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Node#isDefaultNamespace(java.lang.String)
     */
    @Override
    public boolean isDefaultNamespace(String namespaceURI) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "isDefaultNamespace not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Node#lookupNamespaceURI(java.lang.String)
     */
    @Override
    public String lookupNamespaceURI(String prefix) {
        //TODO : use broker's context ? -pb
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "lookupNamespaceURI not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Node#isEqualNode(org.w3c.dom.Node)
     */
    @Override
    public boolean isEqualNode(Node arg) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "isEqualNode not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Node#getFeature(java.lang.String, java.lang.String)
     */
    @Override
    public Object getFeature(String feature, String version) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getFeature not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Node#setUserData(java.lang.String, java.lang.Object, org.w3c.dom.UserDataHandler)
     */
    @Override
    public Object setUserData(String key, Object data, UserDataHandler handler) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "setUserData not implemented on class " + getClass().getName());
    }

    /** ? @see org.w3c.dom.Node#getUserData(java.lang.String)
     */
    @Override
    public Object getUserData(String key) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getUserData not implemented on class " + getClass().getName());
    }

    /**
     * The method <code>toString</code>
     *
     * @return a <code>String</code> value
     */
    @Override
    public String toString() {
        return getURI() + " - <" +
            (getDocumentElement() != null ? getDocumentElement().getNodeName() : null) + ">"
    }

    public Iterator<NodeImpl> iterator() {
        //XXX: implement
        return null;
    }

    public DocumentAtExist getDocumentAtExist() {
        return this;
    }

    @Override
    public int getNodeNumber() {
        // TODO Auto-generated method stub
        return 0;
    }

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

    @Override
    public int getFirstChildFor(int nodeNumber) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public NodeAtExist getNode(int nodeNr) throws DOMException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getNextNodeNumber(int nodeNr) throws DOMException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public boolean hasReferenceNodes() {
        // TODO Auto-generated method stub
        return false;
    }

  public Object getUUID() {
    // TODO Auto-generated method stub
    return null;
  }
}
TOP

Related Classes of org.exist.dom.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.