Package org.apache.xindice.xml.dom

Source Code of org.apache.xindice.xml.dom.ContainerNodeImpl

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*
* $Id: ContainerNodeImpl.java 518460 2007-03-15 03:47:19Z vgritsenko $
*/

package org.apache.xindice.xml.dom;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.util.ByteArrayInput;
import org.apache.xindice.xml.SymbolTable;
import org.apache.xindice.xml.XMLCompressedInput;

import org.w3c.dom.DOMException;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.w3c.dom.traversal.DocumentTraversal;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.NodeIterator;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
* ContainerNodeImpl performs most of the child-rearing behavior of the
* Element and Document implementations.
*
* @version $Revision: 518460 $, $Date: 2007-03-14 23:47:19 -0400 (Wed, 14 Mar 2007) $
*/
public abstract class ContainerNodeImpl extends NodeImpl {

    private static final Log log = LogFactory.getLog(ContainerNodeImpl.class);

    protected NodeListImpl childNodes = new NodeListImpl(this);


    public ContainerNodeImpl() {
    }

    public ContainerNodeImpl(NodeImpl parentNode, byte[] data, int pos, int len) {
        super(parentNode, data, pos, len);
    }

    public ContainerNodeImpl(NodeImpl parentNode, boolean dirty) {
        super(parentNode, dirty);
    }

    protected boolean isNodeTypeValid(short type) {
        return true;
    }

    protected final Node getPreviousSibling(Node node) {
        checkLoaded();
        int pos = childNodes.indexOf(node) - 1;
        return pos >= 0 ? childNodes.item(pos) : null;
    }

    protected final Node getNextSibling(Node node) {
        checkLoaded();
        int pos = childNodes.indexOf(node) + 1;
        return pos < childNodes.getLength() ? childNodes.item(pos) : null;
    }

    protected void checkLoaded() {
        if (loaded) {
            return;
        }

        loaded = true;
        try {
            if (data != null) {
                DocumentImpl doc = (DocumentImpl) getOwnerDocument();
                loadChildren(doc.getSymbols());
            }
        } catch (Exception e) {
            if (log.isWarnEnabled()) {
                log.warn("ignored exception", e);
            }
        }
    }

    protected final void loadChildren(SymbolTable st) throws IOException {
        ByteArrayInput bis = new ByteArrayInput(data, pos, len);
        XMLCompressedInput in = new XMLCompressedInput(bis, st);

        if (getNodeType() == Node.ELEMENT_NODE) {
            // Have to skip the attributes
            in.readSignature();
            in.readContentSize();
            in.readShort();                          // Element Symbol
            int attrCount = in.readAttributeCount();
            for (int i = 0; i < attrCount; i++) {
                in.readShort();                      // Attribute Symbol
                in.skip(in.readShort());             // Attribute Length
            }
        } else {
            in.readInt();
        }

        while (in.available() > 0) {
            int pos = bis.getPos();
            in.readSignature();             // Skip signature
            int len = in.readContentSize();
            if (len == 0) {
                len = 1;
            }

            switch (in.getNodeType()) {

                case Node.ELEMENT_NODE:
                    childNodes.add(new ElementImpl(this, data, pos, len));
                    break;

                case Node.TEXT_NODE:
                    childNodes.add(new TextImpl(this, data, pos, len));
                    break;

                case Node.CDATA_SECTION_NODE:
                    childNodes.add(new CDATASectionImpl(this, data, pos, len));
                    break;

                case Node.ENTITY_REFERENCE_NODE:
                    childNodes.add(new EntityReferenceImpl(this, data, pos, len));
                    break;

                case Node.ENTITY_NODE:
                    childNodes.add(new EntityImpl(this, data, pos, len));
                    break;

                case Node.PROCESSING_INSTRUCTION_NODE:
                    childNodes.add(new ProcessingInstructionImpl(this, data, pos, len));
                    break;

                case Node.COMMENT_NODE:
                    childNodes.add(new CommentImpl(this, data, pos, len));
                    break;

                case Node.NOTATION_NODE:
                    childNodes.add(new NotationImpl(this, data, pos, len));
                    break;

                default:
                    if (log.isWarnEnabled()) {
                        log.warn("invalid node type : " + in.getNodeType());
                    }
            }

            bis.setPos(pos);
            bis.skip(len);
        }
    }

    /**
     *  This is a convenience method to allow easy determination of whether a
     * node has any children.
     * @return  <code>true</code> if the node has any children,
     *   <code>false</code> if the node has no children.
     */
    public final boolean hasChildNodes() {
        checkLoaded();
        return childNodes.getLength() > 0;
    }

    public final NodeList getChildNodes() {
        checkLoaded();
        return childNodes;
    }

    /**
     * The first child of this node. If there is no such node, this returns
     * <code>null</code>.
     */
    public final Node getFirstChild() {
        checkLoaded();
        if (childNodes.size() > 0) {
            return childNodes.item(0);
        } else {
            return null;
        }
    }

    /**
     * The last child of this node. If there is no such node, this returns
     * <code>null</code>.
     */
    public final Node getLastChild() {
        checkLoaded();
        if (childNodes.size() > 0) {
            return childNodes.item(childNodes.getLength() - 1);
        } else {
            return null;
        }
    }

    /**
     * Replaces the child node <code>oldChild</code> with <code>newChild</code>
     * in the list of children, and returns the <code>oldChild</code> node. If
     * the <code>newChild</code> is already in the tree, it is first removed.
     * @param newChild The new node to put in the child list.
     * @param oldChild The node being replaced in the list.
     * @return The node replaced.
     * @exception DOMException
     *   HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
     *   allow children of the type of the <code>newChild</code> node, or it
     *   the node to put in is one of this node's ancestors.
     *   <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
     *   from a different document than the one that created this node.
     *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     *   <br>NOT_FOUND_ERR: Raised if <code>oldChild</code> is not a child of
     *   this node.
     */
    public final synchronized Node replaceChild(Node newChild, Node oldChild) throws DOMException {
        checkLoaded();
        checkReadOnly();
        if (!isNodeTypeValid(newChild.getNodeType())) {
            throw EX_HIERARCHY_REQUEST;
        }
        int idx = childNodes.indexOf(oldChild);
        if (idx >= 0) {
            if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) {
                childNodes.remove(idx);
                NodeList nl = newChild.getChildNodes();
                for (int i = 0; i < nl.getLength(); i++) {
                    NodeImpl impl = (NodeImpl) nl.item(i);
                    impl.setParentNode(this);
                    childNodes.add(idx + i, impl);
                }
            } else {
                NodeImpl impl = (NodeImpl) newChild;
                impl.setParentNode(this);
                childNodes.set(idx, impl);
            }
        }
        setDirty();
        return oldChild;
    }

    /**
     * Inserts the node <code>newChild</code> before the existing child node
     * <code>refChild</code>. If <code>refChild</code> is <code>null</code>,
     * insert <code>newChild</code> at the end of the list of children.
     * <br>If <code>newChild</code> is a <code>DocumentFragment</code> object,
     * all of its children are inserted, in the same order, before
     * <code>refChild</code>. If the <code>newChild</code> is already in the
     * tree, it is first removed.
     * @param newChild The node to insert.
     * @param refChild The reference node, i.e., the node before which the new
     *   node must be inserted.
     * @return The node being inserted.
     * @exception DOMException
     *   HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
     *   allow children of the type of the <code>newChild</code> node, or if
     *   the node to insert is one of this node's ancestors.
     *   <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
     *   from a different document than the one that created this node.
     *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     *   <br>NOT_FOUND_ERR: Raised if <code>refChild</code> is not a child of
     *   this node.
     */
    public final synchronized Node insertBefore(Node newChild, Node refChild) throws DOMException {
        checkLoaded();
        checkReadOnly();
        int idx = childNodes.indexOf(refChild);
        if (idx >= 0) {
            if (!isNodeTypeValid(newChild.getNodeType())) {
                throw EX_HIERARCHY_REQUEST;
            }
            if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) {
                NodeList nl = newChild.getChildNodes();
                for (int i = 0; i < nl.getLength(); i++) {
                    NodeImpl impl = (NodeImpl) nl.item(i);
                    impl.setParentNode(this);
                    childNodes.add(idx + i, impl);
                }
            } else {
                NodeImpl impl = (NodeImpl) newChild;
                impl.setParentNode(this);
                childNodes.add(idx, impl);
            }
        }
        setDirty();
        return newChild;
    }

    /**
     * Removes the child node indicated by <code>oldChild</code> from the list
     * of children, and returns it.
     * @param oldChild The node being removed.
     * @return The node removed.
     * @exception DOMException
     *   NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     *   <br>NOT_FOUND_ERR: Raised if <code>oldChild</code> is not a child of
     *   this node.
     */
    public final synchronized Node removeChild(Node oldChild) throws DOMException {
        checkLoaded();
        checkReadOnly();
        if (!childNodes.remove(oldChild)) {
            throw EX_NOT_FOUND;
        }
        setDirty();
        return oldChild;
    }

    /**
     * Adds the node <code>newChild</code> to the end of the list of children of
     * this node. If the <code>newChild</code> is already in the tree, it is
     * first removed.
     * @param newChild The node to add.If it is a  <code>DocumentFragment</code>
     *   object, the entire contents of the document fragment are moved into
     *   the child list of this node
     * @return The node added.
     * @exception DOMException
     *   HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not
     *   allow children of the type of the <code>newChild</code> node, or if
     *   the node to append is one of this node's ancestors.
     *   <br>WRONG_DOCUMENT_ERR: Raised if <code>newChild</code> was created
     *   from a different document than the one that created this node.
     *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     */
    public final synchronized Node appendChild(Node newChild) throws DOMException {
        checkLoaded();
        checkReadOnly();
        if (!isNodeTypeValid(newChild.getNodeType())) {
            throw EX_HIERARCHY_REQUEST;
        }
        if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) {
            NodeList nl = newChild.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                NodeImpl impl = (NodeImpl) nl.item(i);
                impl.setParentNode(this);
                childNodes.add(impl);
            }
        } else {
            NodeImpl impl = (NodeImpl) newChild;
            impl.setParentNode(this);
            childNodes.add(impl);
        }
        setDirty();
        return newChild;
    }

    /**
     *  Puts all <code>Text</code> nodes in the full depth of the sub-tree
     * underneath this <code>Node</code> , including attribute nodes, into a
     * "normal" form where only markup (e.g., tags, comments, processing
     * instructions, CDATA sections, and entity references) separates
     * <code>Text</code> nodes, i.e., there are neither adjacent
     * <code>Text</code> nodes nor empty <code>Text</code> nodes. This can be
     * used to ensure that the DOM view  of a document is the same as if it
     * were saved and re-loaded, and is useful when operations (such as
     * XPointer lookups) that depend on a particular document tree structure
     * are to be used. In cases where the document contains
     * <code>CDATASections</code> , the normalize operation alone may not be
     * sufficient, since XPointers do not differentiate between
     * <code>Text</code> nodes and <code>CDATASection</code> nodes.
     * @since DOM Level 2
     */
    public final synchronized void normalize() {
        checkLoaded();
        checkReadOnly();
        List set = new ArrayList();

        NodeListImpl newList = new NodeListImpl(this);
        boolean modified = false;

        int size = childNodes.size();
        for (int i = 0; i < size; i++) {
            Node add = null;
            Node n = (Node) childNodes.get(i);
            short type = n.getNodeType();

            switch (type) {
                case Node.TEXT_NODE:
                    set.add(n);
                    break;
                case Node.ELEMENT_NODE:
                    n.normalize();
                default :
                    add = n;
            }

            if (!set.isEmpty() && (type != Node.TEXT_NODE || i == size - 1)) {
                Text s = (Text) set.get(0);
                int len = set.size();
                for (int j = 1; j < len; j++) {
                    modified = true;
                    Text a = (Text) set.get(j);
                    s.appendData(a.getData());
                }
                newList.add(s);
                set.clear();
            }
            if (add != null) {
                newList.add(add);
            }
        }

        if (modified) {
            childNodes = newList;
            setDirty();
        }
    }

    /**
     * Returns a <code>NodeList</code> of all descendant elements with a given
     * tag name, in the order in which they would be encountered in a preorder
     * traversal of the <code>Element</code> tree.
     *
     * @param name The name of the tag to match on. The special value "*"
     *             matches all tags.
     * @return A list of matching <code>Element</code> nodes.
     */
    public final NodeList getElementsByTagName(final String name) {
        checkLoaded();
        NodeListImpl list = new NodeListImpl(this);

        NodeFilter filter = new NodeFilter() {
            public short acceptNode(Node node) {
                if (node.getNodeName().equals(name)) {
                    return NodeFilter.FILTER_ACCEPT;
                } else {
                    return NodeFilter.FILTER_SKIP;
                }
            }
        };

        NodeIterator iter = ((DocumentTraversal) getOwnerDocument()).createNodeIterator(this, NodeFilter.SHOW_ELEMENT, filter, false);
        Node node;
        do {
            node = iter.nextNode();
            if (node != null) {
                list.add(node);
            }
        } while (node != null);

        return list;
    }

    public final NodeList getElementsByTagNameNS(final String namespaceURI, final String localName) {
        checkLoaded();
        NodeListImpl list = new NodeListImpl(this);

        NodeFilter filter = new NodeFilter() {
            public short acceptNode(Node node) {
                if (node.getLocalName().equals(localName) && node.getNamespaceURI().equals(namespaceURI)) {
                    return NodeFilter.FILTER_ACCEPT;
                } else {
                    return NodeFilter.FILTER_SKIP;
                }
            }
        };

        NodeIterator iter = ((DocumentTraversal) getOwnerDocument()).createNodeIterator(this, NodeFilter.SHOW_ELEMENT, filter, false);
        Node node;
        do {
            node = iter.nextNode();
            if (node != null) {
                list.add(node);
            }
        } while (node != null);

        return list;
    }

    public final Element getElementById(String elementId) {
        return null;
    }

    //
    // DOM Level 3 Implementation
    //

    /**
     * @since DOM Level 3
     */
    public String getTextContent() {
        StringBuffer val = new StringBuffer();

        NodeList children = getChildNodes();
        if (children == null || children.getLength() == 0) {
            return "";
        }

        for (int i = 0; i < children.getLength(); i++ ) {
            val.append(((NodeImpl) children.item(i)).getTextContent());
        }
        return val.toString();
    }

    /**
     * @since DOM Level 3
     */
    public boolean isEqualNode(Node other) {
        if (!super.isEqualNode(other)) {
            return false;
        }

        // The childNodes NodeLists have to be equal
        other.normalize();
        normalize();

        if (!hasChildNodes() && !other.hasChildNodes()) {
            return true;
        }

        if (!hasChildNodes() || !other.hasChildNodes()) {
            return false;
        }

        NodeList thisChildren = getChildNodes();
        NodeList otherChildren = other.getChildNodes();
        if (thisChildren.getLength() != otherChildren.getLength()) {
            return false;
        }

        for (int i = 0; i < thisChildren.getLength(); i++) {
            if (!((NodeImpl) thisChildren.item(i)).isEqualNode(otherChildren.item(i))) {
                return false;
            }
        }

        return true;
    }
}
TOP

Related Classes of org.apache.xindice.xml.dom.ContainerNodeImpl

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.