Package org.apache.xindice.xml.dom

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

/*
* 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: ElementImpl.java 571948 2007-09-02 10:51:37Z vgritsenko $
*/

package org.apache.xindice.xml.dom;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.core.data.Key;
import org.apache.xindice.util.ByteArrayInput;
import org.apache.xindice.xml.NodeSource;
import org.apache.xindice.xml.SymbolTable;
import org.apache.xindice.xml.XMLCompressedInput;

import org.w3c.dom.Attr;
import org.w3c.dom.DOMException;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.TypeInfo;
import org.w3c.dom.UserDataHandler;

import java.io.IOException;
import java.util.HashSet;

/**
* ElementImpl
*
* @version $Revision: 571948 $, $Date: 2007-09-02 06:51:37 -0400 (Sun, 02 Sep 2007) $
*/
public final class ElementImpl extends ContainerNodeImpl
                               implements Element {

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

    // private static final String SRC_NS = XMLNS_PREFIX + ":src";
    // private static final String SRC_COL = "src:" + NodeSource.SOURCE_COL;
    // private static final String SRC_KEY = "src:" + NodeSource.SOURCE_KEY;

    private NamedNodeMapImpl attributes = new NamedNodeMapImpl(this);
    private short symbolID = -1;


    public ElementImpl() {
    }

    public ElementImpl(NodeImpl parent, byte[] data, int pos, int len) {
        super(parent, data, pos, len);
        DocumentImpl doc = (DocumentImpl) getOwnerDocument();
        try {
            loadAttributes(doc.getSymbols());
        } catch (Exception e) {
            if (log.isWarnEnabled()) {
                log.warn("ignored exception", e);
            }
        }
    }

    public ElementImpl(NodeImpl parent, boolean dirty) {
        super(parent, dirty);
    }

    public ElementImpl(NodeImpl parent, String nodeName) {
        super(parent, true);
        this.nodeName = nodeName;
    }

    protected boolean isNodeTypeValid(short type) {
        return type == Node.ELEMENT_NODE
            || type == Node.COMMENT_NODE
            || type == Node.TEXT_NODE
            || type == Node.CDATA_SECTION_NODE
            || type == Node.ENTITY_REFERENCE_NODE;
    }

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

        loaded = true;
        try {
            if (data != null) {
                DocumentImpl doc = (DocumentImpl) getOwnerDocument();
                SymbolTable st = doc.getSymbols();

                ByteArrayInput bis = new ByteArrayInput(data, pos, len);
                XMLCompressedInput in = new XMLCompressedInput(bis, st);

                in.readSignature(); // Skip The Signature
                in.readContentSize(); // Skip The Content Size

                symbolID = in.readShort();
                SymbolTable.SymbolInfo si = st.getSymbolInfo(symbolID);
                nodeName = si.getQName();
                nsURI = si.getNamespaceURI();

                loadChildren(st);
            }
        } catch (IOException e) {
            if (log.isWarnEnabled()) {
                log.warn("ignored exception", e);
            }
        }
    }

    public short getSymbolID() {
        return symbolID;
    }

    /**
     * Add "src" and "col" attributes in {@link NodeSource#SOURCE_NS} namespace.
     */
    public void expandSource() {
        NodeSource src = getSource();
        if (src != null) {
            final String prefix = sourcePrefix("src", NodeSource.SOURCE_NS);

            setAttribute(XMLNS_PREFIX + ":" + prefix, NodeSource.SOURCE_NS);
            setAttribute(prefix + ":" + NodeSource.SOURCE_COL, src.getCollection().getCanonicalName());
            Key k = src.getKey();
            if (k != null) {
                setAttribute(prefix + ":" + NodeSource.SOURCE_KEY, k.toString());
            }
        }
    }

    /**
     * Choose unique prefix for a namespace.
     * Reuse existing prefix if namespace is already defined.
     */
    private String sourcePrefix(final String candidatePrefix, final String nsuri) {
        Element element = this;
        HashSet prefixes = new HashSet();
        while (element != null) {
            NamedNodeMap nm = element.getAttributes();
            for (int i = 0; i < nm.getLength(); i++) {
                final Attr a = (Attr) nm.item(i);
                final String name = a.getNodeName();
                if (name.startsWith("xmlns:")) {
                    final String prefix = name.substring(6);
                    if (nsuri.equals(a.getValue())) {
                        return prefix;
                    }
                    prefixes.add(prefix);
                }
            }
            element = element.getParentNode().getNodeType() == ELEMENT_NODE ? (Element) element.getParentNode() : null;
        }

        String result = candidatePrefix;
        while (prefixes.contains(result)) {
            result = candidatePrefix + System.currentTimeMillis();
        }

        return result;
    }

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

        in.readSignature();
        in.readContentSize();
        in.readShort(); // Some "elemSymbol" - symbol ID?
        int attrCount = in.readAttributeCount();

        for (int i = 0; i < attrCount; i++) {
            short symbol = in.readShort();
            short strLen = in.readShort();
            byte[] b = new byte[strLen];
            in.read(b);
            SymbolTable.SymbolInfo si = st.getSymbolInfo(symbol);
            String name = si.getQName();
            String nsURI = si.getNamespaceURI();
            AttrImpl attr = new AttrImpl(this, name, nsURI, symbol, new String(b, "UTF-8"));
            attributes.setNamedItem(attr);
        }
    }

    public short getNodeType() {
        return Node.ELEMENT_NODE;
    }

    /**
     * A <code>NamedNodeMap</code> containing the attributes of this node (if it
     * is an <code>Element</code>) or <code>null</code> otherwise.
     */
    public NamedNodeMap getAttributes() {
        return attributes;
    }

    public boolean hasAttributes() {
        return attributes.size() > 0;
    }

    public boolean hasAttribute(String name) {
        return attributes.getNamedItem(name) != null;
    }

    /**
     * Removes an attribute by name. If the removed attribute has a
     * default value it is immediately replaced.  If the named
     * attribute does not exist, this method has no effect.
     *
     * @param name The name of the attribute to remove.
     * @exception DOMException
     *   NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     **/
    public void removeAttribute(String name) throws DOMException {
        checkReadOnly();
        AttrImpl attr = (AttrImpl) attributes.removeNamedItem(name);
        if (attr != null) {
            attr.setParentNode((NodeImpl) this.getOwnerDocument());
        }
        setDirty();
    }

    /**
     * The name of the element. For example, in: &lt;elementExample
     * id="demo"&gt;  ... &lt;/elementExample&gt; , <code>tagName</code> has
     * the value <code>"elementExample"</code>. Note that this is
     * case-preserving in XML, as are all of the operations of the DOM. The
     * HTML DOM returns the <code>tagName</code> of an HTML element in the
     * canonical uppercase form, regardless of the case in the  source HTML
     * document.
     */
    public String getTagName() {
        return getNodeName();
    }

    /**
     * Retrieves an attribute value by name.
     * @param name The name of the attribute to retrieve.
     * @return The <code>Attr</code> value as a string, or the empty  string if
     *   that attribute does not have a specified or default value.
     */
    public String getAttribute(String name) {
        Attr attr = (Attr) attributes.getNamedItem(name);
        return attr != null ? attr.getValue() : "";
    }

    /**
     * Adds a new attribute. If an attribute with that name is already present
     * in the element, it is replaced by the new one.
     * @param newAttr The <code>Attr</code> node to add to the attribute list.
     * @return If the <code>newAttr</code> attribute replaces an existing
     *   attribute with the same name, the  previously existing
     *   <code>Attr</code> node is returned, otherwise <code>null</code> is
     *   returned.
     * @exception DOMException
     *   WRONG_DOCUMENT_ERR: Raised if <code>newAttr</code> was created from a
     *   different document than the one that created the element.
     *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     *   <br>INUSE_ATTRIBUTE_ERR: Raised if <code>newAttr</code> is already an
     *   attribute of another <code>Element</code> object. The DOM user must
     *   explicitly clone <code>Attr</code> nodes to re-use them in other
     *   elements.
     */
    public Attr setAttributeNode(Attr newAttr) throws DOMException {
        checkReadOnly();
        Attr oldAttr = (Attr) attributes.getNamedItem(newAttr.getName());
        DocumentImpl doc = (DocumentImpl) getOwnerDocument();
        if (newAttr.getParentNode().getNodeType() == Node.ELEMENT_NODE) {
            throw EX_INUSE_ATTRIBUTE;
        }
        if (doc != newAttr.getOwnerDocument()) {
            throw EX_WRONG_DOCUMENT;
        }
        ((AttrImpl) newAttr).setParentNode(this);
        attributes.setNamedItem(newAttr);
        setDirty();
        return oldAttr;
    }

    /**
     * Adds a new attribute. If an attribute with that name is already present
     * in the element, its value is changed to be that of the value parameter.
     * This value is a simple string, it is not parsed as it is being set. So
     * any markup (such as syntax to be recognized as an entity reference) is
     * treated as literal text, and needs to be appropriately escaped by the
     * implementation when it is written out. In order to assign an attribute
     * value that contains entity references, the user must create an
     * <code>Attr</code> node plus any <code>Text</code> and
     * <code>EntityReference</code> nodes, build the appropriate subtree, and
     * use <code>setAttributeNode</code> to assign it as the value of an
     * attribute.
     * @param name The name of the attribute to create or alter.
     * @param value Value to set in string form.
     * @exception DOMException
     *   INVALID_CHARACTER_ERR: Raised if the specified name contains an
     *   invalid character.
     *   <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     */
    public void setAttribute(String name, String value) throws DOMException {
        Attr attr = getOwnerDocument().createAttribute(name);
        attr.setValue(value);
        setAttributeNode(attr);
    }

    /**
     * Retrieves an <code>Attr</code> node by name.
     * @param name The name of the attribute to retrieve.
     * @return The <code>Attr</code> node with the specified attribute name or
     *   <code>null</code> if there is no such attribute.
     */
    public Attr getAttributeNode(String name) {
        return (Attr) attributes.getNamedItem(name);
    }

    /**
     * Removes the specified attribute.
     * @param oldAttr The <code>Attr</code> node to remove from the attribute
     *   list. If the removed <code>Attr</code> has a default value it is
     *   immediately replaced.
     * @return The <code>Attr</code> node that was removed.
     * @exception DOMException
     *   NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     *   <br>NOT_FOUND_ERR: Raised if <code>oldAttr</code> is not an attribute
     *   of the element.
     **/
    public Attr removeAttributeNode(Attr oldAttr) throws DOMException {
        checkReadOnly();
        if (oldAttr.getParentNode() != this) {
            throw EX_NOT_FOUND;
        }
        attributes.remove(oldAttr.getName());
        ((AttrImpl) oldAttr).setParentNode((NodeImpl) this.getOwnerDocument());
        setDirty();
        return oldAttr;
    }

    /**
     *  Retrieves an attribute value by local name and namespace URI.
     * HTML-only DOM implementations do not need to implement this method.
     * @param namespaceURI  The  namespace URI of the attribute to retrieve.
     * @param localName  The  local name of the attribute to retrieve.
     * @return  The <code>Attr</code> value as a string, or the empty
     *   string if that attribute does not have a specified or default
     *   value.
     * @since DOM Level 2
     */
    public String getAttributeNS(String namespaceURI, String localName) {
        Attr attr = (Attr) attributes.getNamedItemNS(namespaceURI, localName);
        return attr != null ? attr.getValue() : "";
    }

    /**
     *  Adds a new attribute. If an attribute with the same local name and
     * namespace URI is already present on the element, its prefix is changed
     * to be the prefix part of the <code>qualifiedName</code> , and its
     * value is changed to be the <code>value</code> parameter. This value is
     * a simple string; it is not parsed as it is being set. So any markup
     * (such as syntax to be recognized as an entity reference) is treated as
     * literal text, and needs to be appropriately escaped by the
     * implementation when it is written out. In order to assign an attribute
     * value that contains entity references, the user must create an
     * <code>Attr</code> node plus any <code>Text</code> and
     * <code>EntityReference</code> nodes, build the appropriate subtree, and
     * use <code>setAttributeNodeNS</code> or <code>setAttributeNode</code> to
     *  assign it as the value of an attribute.
     * <br> HTML-only DOM implementations do not need to implement this method.
     * @param namespaceURI  The  namespace URI of the attribute to create or
     *   alter.
     * @param qualifiedName  The  qualified name of the attribute to create or
     *   alter.
     * @param value  The value to set in string form.
     * @exception DOMException
     *    INVALID_CHARACTER_ERR: Raised if the specified qualified name
     *   contains an illegal character.
     *   <br> NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     *   <br> NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is
     *   malformed, if the <code>qualifiedName</code> has a prefix and the
     *   <code>namespaceURI</code> is <code>null</code> or an empty string,
     *   if the <code>qualifiedName</code> has a prefix that is "xml" and the
     *   <code>namespaceURI</code> is different from
     *   "http://www.w3.org/XML/1998/namespace", if the
     *   <code>qualifiedName</code> has a prefix that is "xmlns" and the
     *   <code>namespaceURI</code> is different from
     *   "http://www.w3.org/2000/xmlns/", or if the <code>qualifiedName</code>
     *    is "xmlns" and the <code>namespaceURI</code> is different from
     *   "http://www.w3.org/2000/xmlns/".
     * @since DOM Level 2
     */
    public void setAttributeNS(String namespaceURI, String qualifiedName, String value) {
        Attr attr = getOwnerDocument().createAttributeNS(namespaceURI, qualifiedName);
        attr.setValue(value);
        setAttributeNodeNS(attr);
    }

    /**
     * Removes an attribute by local name and namespace URI. If the
     * removed attribute has a default value it is immediately
     * replaced. The replacing attribute has the same namespace URI
     * and local name, as well as the original prefix.
     *
     * <br> HTML-only DOM implementations do not need to implement this
     * method.
     *
     * @param namespaceURI  The  namespace URI of the attribute to remove.
     * @param localName  The  local name of the attribute to remove.
     * @exception DOMException
     *    NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     * @since DOM Level 2
     **/
    public void removeAttributeNS(String namespaceURI, String localName) {
        checkReadOnly();
        AttrImpl attr = (AttrImpl) attributes.removeNamedItemNS(namespaceURI, localName);
        if (attr != null) {
            attr.setParentNode((NodeImpl) this.getOwnerDocument());
        }
        setDirty();
    }

    /**
     *  Retrieves an <code>Attr</code> node by local name and namespace URI.
     * HTML-only DOM implementations do not need to implement this method.
     * @param namespaceURI  The  namespace URI of the attribute to retrieve.
     * @param localName  The  local name of the attribute to retrieve.
     * @return  The <code>Attr</code> node with the specified attribute local
     *   name and namespace URI or <code>null</code> if there is no such
     *   attribute.
     * @since DOM Level 2
     */
    public Attr getAttributeNodeNS(String namespaceURI, String localName) {
        return (Attr) attributes.getNamedItemNS(namespaceURI, localName);
    }

    /**
     *  Adds a new attribute. If an attribute with that local name and that
     * namespace URI is already present in the element, it is replaced by the
     * new one.
     * <br> HTML-only DOM implementations do not need to implement this method.
     * @param newAttr  The <code>Attr</code> node to add to the attribute list.
     * @return  If the <code>newAttr</code> attribute replaces an existing
     *   attribute with the same  local name and  namespace URI , the
     *   replaced <code>Attr</code> node is returned, otherwise
     *   <code>null</code> is returned.
     * @exception DOMException
     *    WRONG_DOCUMENT_ERR: Raised if <code>newAttr</code> was created from
     *   a different document than the one that created the element.
     *   <br> NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
     *   <br> INUSE_ATTRIBUTE_ERR: Raised if <code>newAttr</code> is already
     *   an attribute of another <code>Element</code> object. The DOM user
     *   must explicitly clone <code>Attr</code> nodes to re-use them in
     *   other elements.
     * @since DOM Level 2
     */
    public Attr setAttributeNodeNS(Attr newAttr) {
        return setAttributeNode(newAttr);
    }

    /**
     * @since DOM Level 2
     */
    public boolean hasAttributeNS(String namespaceURI, String localName) {
        return attributes.getNamedItemNS(namespaceURI, localName) != null;
    }

    //
    // DOM Level 3 Implementation
    //

    /**
     * @since DOM Level 3
     */
    public TypeInfo getSchemaTypeInfo() {
        return null;
    }

    /**
     * @since DOM Level 3
     */
    public void setIdAttribute(String name, boolean isId) throws DOMException {
    }

    /**
     * @since DOM Level 3
     */
    public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException {
    }

    /**
     * @since DOM Level 3
     */
    public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException {
    }

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

        // Attributes of the nodes have to be the same, but can have different order
        if (!hasAttributes() && !other.hasAttributes()) {
            return true;
        }
        if (!hasAttributes() || !other.hasAttributes()) {
            return false;
        }

        NamedNodeMap attrMap = getAttributes();
        NamedNodeMap otherAttrMap = other.getAttributes();
        if (attrMap.getLength() != otherAttrMap.getLength()) {
            return false;
        }

        for (int i = 0; i < attrMap.getLength(); i++) {
            String name = attrMap.item(i).getNodeName();
            Node attr = otherAttrMap.getNamedItem(name);
            if (attr == null || !attrMap.item(i).getNodeValue().equals(attr.getNodeValue())) {
                return false;
            }
        }

        return true;
    }

    Node renameNode(String namespaceURI, String qualifiedName, String prefix) throws DOMException {
        checkLoaded();
        checkReadOnly();

        nodeName = qualifiedName;
        if (prefix != null) {
            setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + prefix, namespaceURI);
            nsURI = namespaceURI;
        } else {
            removeAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + nsURI);
            nsURI = null;
        }

        invokeHandlers(UserDataHandler.NODE_RENAMED, this, null);
        return this;
    }
}
TOP

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

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.