All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mozilla.javascript.xmlimpl.XmlNode Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.javascript.xmlimpl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.mozilla.javascript.Undefined;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.UserDataHandler;

class XmlNode implements Serializable {
    private static final String XML_NAMESPACES_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";

    private static final String USER_DATA_XMLNODE_KEY = XmlNode.class.getName();

    private static final boolean DOM_LEVEL_3 = true;

    private static XmlNode getUserData(Node node) {
        if (DOM_LEVEL_3) {
            return (XmlNode)node.getUserData(USER_DATA_XMLNODE_KEY);
        }
        return null;
    }

    private static void setUserData(Node node, XmlNode wrap) {
        if (DOM_LEVEL_3) {
            node.setUserData(USER_DATA_XMLNODE_KEY, wrap, wrap.events);
        }
    }

    private static XmlNode createImpl(Node node) {
        if (node instanceof Document) throw new IllegalArgumentException();
        XmlNode rv = null;
        if (getUserData(node) == null) {
            rv = new XmlNode();
            rv.dom = node;
            setUserData(node, rv);
        } else {
            rv = getUserData(node);
        }
        return rv;
    }

    static XmlNode newElementWithText(XmlProcessor processor, XmlNode reference, XmlNode.QName qname, String value) {
        if (reference instanceof org.w3c.dom.Document) throw new IllegalArgumentException("Cannot use Document node as reference");
        Document document = null;
        if (reference != null) {
            document = reference.dom.getOwnerDocument();
        } else {
            document = processor.newDocument();
        }
        Node referenceDom = (reference != null) ? reference.dom : null;
        Namespace ns = qname.getNamespace();
        Element e = (ns == null || ns.getUri().length() == 0)
            ? document.createElementNS(null, qname.getLocalName())
            : document.createElementNS(ns.getUri(),
                                       qname.qualify(referenceDom));
        if (value != null) {
            e.appendChild(document.createTextNode(value));
        }
        return XmlNode.createImpl(e);
    }

    static XmlNode createText(XmlProcessor processor, String value) {
        return createImpl( processor.newDocument().createTextNode(value) );
    }

    static XmlNode createElementFromNode(Node node) {
        if (node instanceof Document)
            node = ((Document) node).getDocumentElement();
        return createImpl(node);
    }

    static XmlNode createElement(XmlProcessor processor, String namespaceUri, String xml) throws org.xml.sax.SAXException {
        return createImpl( processor.toXml(namespaceUri, xml) );
    }

    static XmlNode createEmpty(XmlProcessor processor) {
        return createText(processor, "");
    }

    private static XmlNode copy(XmlNode other) {
        return createImpl( other.dom.cloneNode(true) );
    }

    private static final long serialVersionUID = 1L;

    private UserDataHandler events = new XmlNodeUserDataHandler();

    private Node dom;

    private XML xml;

    private XmlNode() {
    }

    String debug() {
        XmlProcessor raw = new XmlProcessor();
        raw.setIgnoreComments(false);
        raw.setIgnoreProcessingInstructions(false);
        raw.setIgnoreWhitespace(false);
        raw.setPrettyPrinting(false);
        return raw.ecmaToXmlString(this.dom);
    }

    @Override
    public String toString() {
        return "XmlNode: type=" + dom.getNodeType() + " dom=" + dom.toString();
    }

    XML getXml() {
        return xml;
    }

    void setXml(XML xml) {
        this.xml = xml;
    }

    int getChildCount() {
        return this.dom.getChildNodes().getLength();
    }

    XmlNode parent() {
        Node domParent = dom.getParentNode();
        if (domParent instanceof Document) return null;
        if (domParent == null) return null;
        return createImpl(domParent);
    }

    int getChildIndex() {
        if (this.isAttributeType()) return -1;
        if (parent() == null) return -1;
        org.w3c.dom.NodeList siblings = this.dom.getParentNode().getChildNodes();
        for (int i=0; i map = new HashMap();
        private Map uriToPrefix = new HashMap();

        Namespaces() {
        }

        void declare(Namespace n) {
            if (map.get(n.prefix) == null) {
                map.put(n.prefix, n.uri);
            }
            //    TODO    I think this is analogous to the other way, but have not really thought it through ... should local scope
            //            matter more than outer scope?
            if (uriToPrefix.get(n.uri) == null) {
                uriToPrefix.put(n.uri, n.prefix);
            }
        }

        Namespace getNamespaceByUri(String uri) {
            if (uriToPrefix.get(uri) == null) return null;
            return Namespace.create(uri, uriToPrefix.get(uri));
        }

        Namespace getNamespace(String prefix) {
            if (map.get(prefix) == null) return null;
            return Namespace.create(prefix, map.get(prefix));
        }

        Namespace[] getNamespaces() {
            ArrayList rv = new ArrayList();
            for (String prefix: map.keySet()) {
                String uri = map.get(prefix);
                Namespace n = Namespace.create(prefix, uri);
                if (!n.isEmpty()) {
                    rv.add(n);
                }
            }
            return rv.toArray(new Namespace[rv.size()]);
        }
    }

    final XmlNode copy() {
        return copy( this );
    }

    //    Returns whether this node is capable of being a parent
    final boolean isParentType() {
        return isElementType();
    }

    final boolean isTextType() {
        return dom.getNodeType() == Node.TEXT_NODE || dom.getNodeType() == Node.CDATA_SECTION_NODE;
    }

    final boolean isAttributeType() {
        return dom.getNodeType() == Node.ATTRIBUTE_NODE;
    }

    final boolean isProcessingInstructionType() {
        return dom.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE;
    }

    final boolean isCommentType() {
        return dom.getNodeType() == Node.COMMENT_NODE;
    }

    final boolean isElementType() {
        return dom.getNodeType() == Node.ELEMENT_NODE;
    }

    final void renameNode(QName qname) {
        this.dom = dom.getOwnerDocument().renameNode(dom, qname.getNamespace().getUri(), qname.qualify(dom));
    }

    void invalidateNamespacePrefix() {
        if (!(dom instanceof Element)) throw new IllegalStateException();
        String prefix = this.dom.getPrefix();
        QName after = QName.create(this.dom.getNamespaceURI(), this.dom.getLocalName(), null);
        renameNode(after);
        NamedNodeMap attrs = this.dom.getAttributes();
        for (int i=0; i 0) {
            e.setAttributeNS(XML_NAMESPACES_NAMESPACE_URI, "xmlns:" + prefix, uri);
        } else {
            e.setAttribute("xmlns", uri);
        }
    }

    void declareNamespace(String prefix, String uri) {
        if (!(dom instanceof Element)) throw new IllegalStateException();
        if (dom.lookupNamespaceURI(uri) != null && dom.lookupNamespaceURI(uri).equals(prefix)) {
            //    do nothing
        } else {
            Element e = (Element)dom;
            declareNamespace(e, prefix, uri);
        }
    }

    private Namespace getDefaultNamespace() {
        String prefix = "";
        String uri = (dom.lookupNamespaceURI(null) == null) ? "" : dom.lookupNamespaceURI(null);
        return Namespace.create(prefix, uri);
    }

    private String getExistingPrefixFor(Namespace namespace) {
        if (getDefaultNamespace().getUri().equals(namespace.getUri())) {
            return "";
        }
        return dom.lookupPrefix(namespace.getUri());
    }

    private Namespace getNodeNamespace() {
        String uri = dom.getNamespaceURI();
        String prefix = dom.getPrefix();
        if (uri == null) uri = "";
        if (prefix == null) prefix = "";
        return Namespace.create(prefix, uri);
    }

    Namespace getNamespace() {
        return getNodeNamespace();
    }

    void removeNamespace(Namespace namespace) {
        Namespace current = getNodeNamespace();

        //    Do not remove in-use namespace
        if (namespace.is(current)) return;
        NamedNodeMap attrs = this.dom.getAttributes();
        for (int i=0; i rv = new ArrayList();
        NodeList nodes = this.dom.getChildNodes();
        for (int i=0; i 0) return prefix + ":" + localName;
            return localName;
        }

        private Namespace namespace;
        private String localName;

        private QName() {
        }

        @Override
        public String toString() {
            return "XmlNode.QName [" + localName + "," + namespace + "]";
        }

        private boolean equals(String one, String two) {
            if (one == null && two == null) return true;
            if (one == null || two == null) return false;
            return one.equals(two);
        }

        private boolean namespacesEqual(Namespace one, Namespace two) {
            if (one == null && two == null) return true;
            if (one == null || two == null) return false;
            return equals(one.getUri(), two.getUri());
        }

        final boolean equals(QName other) {
            if (!namespacesEqual(this.namespace, other.namespace)) return false;
            if (!equals(this.localName, other.localName)) return false;
            return true;
        }

        @Override
        public boolean equals(Object obj) {
            if(!(obj instanceof QName)) {
                return false;
            }
            return equals((QName)obj);
        }

        @Override
        public int hashCode() {
            return localName == null ? 0 : localName.hashCode();
        }

        void lookupPrefix(org.w3c.dom.Node node) {
            if (node == null) throw new IllegalArgumentException("node must not be null");
            String prefix = node.lookupPrefix(namespace.getUri());
            if (prefix == null) {
                //    check to see if we match the default namespace
                String defaultNamespace = node.lookupNamespaceURI(null);
                if (defaultNamespace == null) defaultNamespace = "";
                String nodeNamespace = namespace.getUri();
                if (nodeNamespace.equals(defaultNamespace)) {
                    prefix = "";
                }
            }
            int i = 0;
            while(prefix == null) {
                String generatedPrefix = "e4x_" + i++;
                String generatedUri = node.lookupNamespaceURI(generatedPrefix);
                if (generatedUri == null) {
                    prefix = generatedPrefix;
                    org.w3c.dom.Node top = node;
                    while(top.getParentNode() != null && top.getParentNode() instanceof org.w3c.dom.Element) {
                        top = top.getParentNode();
                    }
                    ((org.w3c.dom.Element)top).setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + prefix, namespace.getUri());
                }
            }
            namespace.setPrefix(prefix);
        }

        String qualify(org.w3c.dom.Node node) {
            if (namespace.getPrefix() == null) {
                if (node != null) {
                    lookupPrefix(node);
                } else {
                    if (namespace.getUri().equals("")) {
                        namespace.setPrefix("");
                    } else {
                        //    TODO    I am not sure this is right, but if we are creating a standalone node, I think we can set the
                        //            default namespace on the node itself and not worry about setting a prefix for that namespace.
                        namespace.setPrefix("");
                    }
                }
            }
            return qualify(namespace.getPrefix(), localName);
        }

        void setAttribute(org.w3c.dom.Element element, String value) {
            if (namespace.getPrefix() == null) lookupPrefix(element);
            element.setAttributeNS(namespace.getUri(), qualify(namespace.getPrefix(), localName), value);
        }

        Namespace getNamespace() {
            return namespace;
        }

        String getLocalName() {
            return localName;
        }
    }

    static class InternalList implements Serializable {
        private static final long serialVersionUID = -3633151157292048978L;
        private List list;

        InternalList() {
            list = new ArrayList();
        }

        private void _add(XmlNode n) {
            list.add(n);
        }

        XmlNode item(int index) {
            return list.get(index);
        }

        void remove(int index) {
            list.remove(index);
        }

        void add(InternalList other) {
            for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy