com.marklogic.dom.NodeImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.marklogic.dom.NodeImpl.java

Source

/*
 * Copyright 2003-2016 MarkLogic Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.marklogic.dom;

import java.util.ArrayList;
import java.util.Stack;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.UserDataHandler;

import com.marklogic.tree.ExpandedTree;
import com.marklogic.tree.NodeKind;

/**
 * A read-only W3C DOM Node implementation of MarkLogic's internal
 * representation of a node as stored in the expanded tree cache of a forest on
 * disk.
 * 
 * <p>
 * This interface is effectively read-only: Setters and update methods inherited
 * from <code>org.w3c.Node</code> are not supported and will raise an exception
 * if called.
 * </p>
 * 
 * @author jchen
 */
public abstract class NodeImpl implements Node {
    public static final Log LOG = LogFactory.getLog(NodeImpl.class);

    private static final NodeList emptyNodeList = new NodeList() {

        public int getLength() {
            return 0;
        }

        public Node item(int index) {
            return null;
        }
    };

    protected final ExpandedTree tree;

    protected final int node;

    /**
     * No public constructor; only subclasses of Node should be instantiated
     */
    NodeImpl(ExpandedTree tree, int node) {
        this.tree = tree;
        this.node = node;
    }

    public ExpandedTree getExpandedTree() {
        return tree;
    }

    /** Unsupported. */
    public Node appendChild(Node newChild) throws DOMException {
        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
    }

    /**
     * CloneNode is only supported for document node.
     * 
     * {@inheritDoc}
     * 
     */
    public Node cloneNode(boolean deep) {
        throw new UnsupportedOperationException();
    }

    /**
     * Given a owner document, clone the node
     * 
     * @param doc
     *            owner document
     * @param deep
     *            the flag to indicate deep clone or not
     * @return null
     */
    protected Node cloneNode(Document doc, boolean deep) {
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public short compareDocumentPosition(Node other) throws DOMException {
        if (other instanceof NodeImpl) {
            NodeImpl otherNode = (NodeImpl) other;
            if (this.tree == otherNode.tree) {
                if (tree.nodeOrdinal[node] > tree.nodeOrdinal[otherNode.node]) {
                    int ancestor = tree.nodeParentNodeRepID[node];
                    while (ancestor != Integer.MAX_VALUE
                            && tree.nodeOrdinal[ancestor] >= tree.nodeOrdinal[otherNode.node]) {
                        if (ancestor == otherNode.node)
                            return DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING;
                        ancestor = tree.nodeParentNodeRepID[ancestor];
                    }
                    return DOCUMENT_POSITION_PRECEDING;
                } else {
                    int ancestor = tree.nodeParentNodeRepID[otherNode.node];
                    while (ancestor != Integer.MAX_VALUE
                            && tree.nodeOrdinal[ancestor] <= tree.nodeOrdinal[otherNode.node]) {
                        if (ancestor == node)
                            return DOCUMENT_POSITION_CONTAINED_BY | DOCUMENT_POSITION_FOLLOWING;
                        ancestor = tree.nodeParentNodeRepID[ancestor];
                    }
                    return DOCUMENT_POSITION_FOLLOWING;
                }
            } else {
                return DOCUMENT_POSITION_DISCONNECTED;
            }
        } else {
            throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null);
        }
    }

    /** {@inheritDoc} */
    public NamedNodeMap getAttributes() {
        return null;
    }

    /** {@inheritDoc} */
    public String getBaseURI() {
        return tree.getDocumentURI();
    }

    /** {@inheritDoc} */
    public NodeList getChildNodes() {
        return emptyNodeList;
    }

    /** Unsupported. */
    public Object getFeature(String feature, String version) {
        assert (false);
        return this;
    }

    /** {@inheritDoc} */
    public Node getFirstChild() {
        return null;
    }

    /** {@inheritDoc} */
    public Node getLastChild() {
        return null;
    }

    /** {@inheritDoc} */
    public String getLocalName() {
        return null;
    }

    /** {@inheritDoc} */
    public String getNamespaceURI() {
        return null;
    }

    protected Node getNextChild(int node) {
        return null;
    }

    /** {@inheritDoc} */
    public Node getNextSibling() {
        NodeImpl p = (NodeImpl) getParentNode();
        return (p == null ? null : p.getNextChild(node));
    }

    /** {@inheritDoc} */
    public abstract String getNodeName();

    /** {@inheritDoc} */
    public short getNodeType() {
        return NodeKind.domType(tree.nodeKind[node]);
    }

    /** {@inheritDoc} */
    public String getNodeValue() throws DOMException {
        return null; // overridden in some subclasses
    }

    /** 
     * {@inheritDoc} 
     * 
     * OwnerDocument of namespace attribute is created artificially, 
     * which only contains the attribute only.
     */
    public Document getOwnerDocument() {
        return (DocumentImpl) (this.tree.node(0));
    }

    /** {@inheritDoc} */
    public Node getParentNode() {
        // assume no linkNodeKind
        return tree.node(tree.nodeParentNodeRepID[node]);
    }

    protected int getPrefixID(int uriAtom) {
        return -1;
    }

    /** {@inheritDoc} */
    public String getPrefix() {
        return null;
    }

    protected Node getPreviousChild(int child) {
        return null;
    }

    public Node getPreviousSibling() {
        NodeImpl p = (NodeImpl) getParentNode();
        return (p == null ? null : p.getPreviousChild(node));
    }

    // visit every child node, excluding COMMENT_NODE and
    // PROCESSING_INSTRUCTION_NODE nodes.
    private boolean hasTextContent(Node child) {
        return child.getNodeType() != Node.COMMENT_NODE && child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE;
    }

    /** {@inheritDoc} 
     * 
     *  Overwritten by TextImpl, CommentImpl and ProcessingInstructionImpl
     */
    public String getTextContent() throws DOMException {
        StringBuilder sb = new StringBuilder();
        getTextContent(sb);
        return sb.toString();
    }

    // internal method taking a StringBuffer in parameter
    private void getTextContent(StringBuilder sb) throws DOMException {
        NodeList children = getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (hasTextContent(child)) {
                sb.append(child.getTextContent());
            }
        }
    }

    /** Unsupported. */
    public Object getUserData(String key) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null);
    }

    /** {@inheritDoc} */
    public boolean hasAttributes() {
        return false;
    }

    /** {@inheritDoc} */
    public boolean hasChildNodes() {
        return false;
    }

    /** Unsupported. */
    public Node insertBefore(Node newChild, Node refChild) throws DOMException {
        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
    }

    /**
     * {@inheritDoc}
     * <p>
     * Not supported for namespace declaration. Overrided by DocumentImpl and
     * ElementImpl
     * </p>
     */
    public boolean isDefaultNamespace(String namespaceURI) {
        int type = getNodeType();
        if (type == NodeKind.ATTR) {
            if (this instanceof AttrImpl == false) {
                // ns decl
                throw new UnsupportedOperationException();
            }
        }
        Node p = getParentNode();
        return p.isDefaultNamespace(namespaceURI);
    }

    private boolean notequals(String a, String b) {
        if (a == null)
            return (b != null);
        return !a.equals(b);
    }

    /** {@inheritDoc} */
    public boolean isEqualNode(Node other) {

        // Note that normalization can affect equality; to avoid this,
        // nodes should be normalized before being compared.
        // For the moment, normalization cannot be done.
        if (other == null)
            return false;
        if (getNodeType() != other.getNodeType())
            return false;
        if (!getLocalName().equals(other.getLocalName()))
            return false;
        if (notequals(getNamespaceURI(), other.getNamespaceURI()))
            return false;
        if (notequals(getPrefix(), other.getPrefix()))
            return false;
        if (notequals(getNodeValue(), other.getNodeValue()))
            return false;
        if (hasChildNodes() != other.hasChildNodes())
            return false;
        if (hasAttributes() != other.hasAttributes())
            return false;
        if (hasChildNodes()) {
            NamedNodeMap thisAttr = getAttributes();
            NamedNodeMap otherAttr = other.getAttributes();
            if (thisAttr.getLength() != otherAttr.getLength())
                return false;
            for (int i = 0; i < thisAttr.getLength(); i++)
                if (thisAttr.item(i).isEqualNode(otherAttr.item(i)))
                    return false;
        }
        if (hasAttributes()) {
            NodeList thisChild = getChildNodes();
            NodeList otherChild = other.getChildNodes();
            if (thisChild.getLength() != otherChild.getLength())
                return false;
            for (int i = 0; i < thisChild.getLength(); i++)
                if (thisChild.item(i).isEqualNode(otherChild.item(i)))
                    return false;
        }
        return true;
    }

    /** {@inheritDoc} */
    public boolean isSameNode(Node other) {
        return (other instanceof NodeImpl) && (((NodeImpl) other).tree == tree)
                && (((NodeImpl) other).node == node);
    }

    // TODO: Consider implementing Traversal
    // TODO - override in subclasses?

    public boolean isSupported(String feature, String version) {
        if (feature.equalsIgnoreCase("Core"))
            return true;
        if (feature.equalsIgnoreCase("XML"))
            return true;
        return false;
    }

    protected int getNSNodeID(long ordinal, long minOrdinal) {
        if (ordinal < minOrdinal)
            return -1;
        int idx = getNSNodeID(ordinal);
        if (idx >= 0 && tree.nsNodeOrdinal[idx] >= minOrdinal)
            return idx;
        return -1;
    }

    protected int getNSNodeID(long ordinal) {
        int R = tree.numNSNodeReps;
        if (R == 0)
            return -1;
        int L = 0;
        while (L + 1 < R) {
            int M = (L + R) >>> 1;
            if (ordinal < tree.nsNodeOrdinal[M])
                R = M;
            else
                L = M;
        }
        if (ordinal < tree.nsNodeOrdinal[L])
            --L;
        for (;;) {
            if (L == -1)
                break;
            if (tree.nsNodePrefixAtom[L] != -1)
                break;
            L = tree.nsNodePrevNSNodeRepID[L];
        }
        return L;
    }

    protected int nextNSNodeID(int ns, long minOrdinal) {
        for (;;) {
            ns = tree.nsNodePrevNSNodeRepID[ns];
            if (ns == -1)
                return -1;
            if (tree.nsNodePrefixAtom[ns] != -1)
                break;
        }
        if (tree.nsNodeOrdinal[ns] < minOrdinal)
            ns = -1;
        return ns;
    }

    /** {@inheritDoc} */
    // http://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#lookupNamespacePrefixAlgo
    public String lookupNamespaceURI(String prefix) {
        return null;
    }

    /** {@inheritDoc} */
    public String lookupPrefix(String namespaceURI) {
        return null;
    }

    /** Unsupported. */
    public void normalize() {
        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
    }

    /** Unsupported. */
    public Node removeChild(Node oldChild) throws DOMException {
        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
    }

    /** Unsupported. */
    public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
    }

    /** Unsupported. */
    public void setNodeValue(String nodeValue) throws DOMException {
        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
    }

    /** Unsupported. */
    public void setPrefix(String prefix) throws DOMException {
        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
    }

    /** Unsupported. */
    public void setTextContent(String textContent) throws DOMException {
        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
    }

    /** Unsupported. */
    public Object setUserData(String key, Object data, UserDataHandler handler) {
        throw new DOMException(DOMException.NOT_SUPPORTED_ERR, null);
    }

    protected NodeList getElementsByTagNameNSOrNodeName(String namespaceURI, String name, final boolean nodeName) {

        final String tagname = name;
        final String ns = namespaceURI;
        final Node thisNode = this;

        return new NodeList() {
            protected ArrayList<Node> elementList = new ArrayList<Node>();
            protected boolean done = false;

            protected void init() {
                if (done)
                    return;
                Stack<Node> childrenStack = new Stack<Node>();
                childrenStack.push(thisNode);
                boolean root = true;
                while (!childrenStack.isEmpty()) {
                    Node curr = childrenStack.pop();
                    NodeList children = curr.getChildNodes();
                    for (int childi = children.getLength() - 1; childi >= 0; childi--)
                        if (children.item(childi).getNodeType() == Node.ELEMENT_NODE)
                            childrenStack.push(children.item(childi));
                    if (root) {
                        root = false;
                        continue;
                    }
                    if (nodeName) {
                        if (curr.getNodeName().equals(tagname) || tagname.equals("*"))
                            elementList.add(curr);
                    } else {
                        // do nothing if only one of the two is null
                        if ("*".equals(ns) && "*".equals(tagname)) {
                            elementList.add(curr);
                            continue;
                        }
                        if (ns != null) {
                            if ((ns.equals("*") || ns.equals(curr.getNamespaceURI()))
                                    && (tagname.equals("*") || tagname.equals(curr.getLocalName())))
                                elementList.add(curr);
                        } else if (tagname.equals("*") || tagname.equals(curr.getLocalName()))
                            elementList.add(curr);
                    }
                }
                done = true;
            }

            public int getLength() {
                init();
                return elementList.size();
            }

            public Node item(int index) {
                init();
                return (index < getLength()) ? elementList.get(index) : null;
            }

        };
    }

    protected String builtinNSPrefix(String URI) {
        if (URI == null)
            return null;
        if (URI.equals("http://www.w3.org/XML/1998/namespace"))
            return "xml";
        if (URI.equals("http://www.w3.org/2000/xmlns/"))
            return "xmlns";
        if (URI.equals("http://www.w3.org/2001/XMLSchema"))
            return "xs";
        if (URI.equals("http://www.w3.org/2001/XMLSchema-instance"))
            return "xsi";
        if (URI.equals("http://www.w3.org/2003/05/xpath-datatypes"))
            return "xdt";
        if (URI.equals("http://marklogic.com/xdmp"))
            return "xdmp";
        if (URI.equals("http://marklogic.com/xqe"))
            return "xqe";
        if (URI.equals("http://marklogic.com/xdmp/security"))
            return "sec";
        if (URI.equals("http://www.w3.org/2005/xqt-errors"))
            return "err";
        if (URI.equals("http://marklogic.com/xdmp/error"))
            return "error";
        if (URI.equals("http://marklogic.com/xdmp/directory"))
            return "dir";
        if (URI.equals("DAV:"))
            return "dav";
        if (URI.equals("http://marklogic.com/xdmp/lock"))
            return "lock";
        if (URI.equals("http://marklogic.com/xdmp/property"))
            return "prop";
        return null;
    }
}