1f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/*
2f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
3f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
4f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * you may not use this file except in compliance with the License.
6f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * You may obtain a copy of the License at
7f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
8f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
10f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * See the License for the specific language governing permissions and
14f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * limitations under the License.
15f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */
16f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
17f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectpackage org.apache.harmony.xml.dom;
18f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
193c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilsonimport org.apache.xml.serializer.utils.SystemIDResolver;
203c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilsonimport org.apache.xml.utils.URI;
21a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilsonimport org.w3c.dom.Attr;
2212547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilsonimport org.w3c.dom.CharacterData;
23f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport org.w3c.dom.DOMException;
24f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport org.w3c.dom.Document;
25cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilsonimport org.w3c.dom.Element;
26f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport org.w3c.dom.NamedNodeMap;
27f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport org.w3c.dom.Node;
28f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport org.w3c.dom.NodeList;
2912547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilsonimport org.w3c.dom.ProcessingInstruction;
3092e01317d2428856cee52965745d17699a33be5aJesse Wilsonimport org.w3c.dom.TypeInfo;
314c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilsonimport org.w3c.dom.UserDataHandler;
32f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
333c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilsonimport javax.xml.transform.TransformerException;
34cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilsonimport java.util.ArrayList;
35cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilsonimport java.util.List;
36e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilsonimport java.util.Map;
37cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
38f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/**
39a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson * A straightforward implementation of the corresponding W3C DOM node.
40a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson *
41a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson * <p>Some fields have package visibility so other classes can access them while
42a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson * maintaining the DOM structure.
43a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson *
44a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson * <p>This class represents a Node that has neither a parent nor children.
45a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson * Subclasses may have either.
46a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson *
47a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson * <p>Some code was adapted from Apache Xerces.
48f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */
49f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectpublic abstract class NodeImpl implements Node {
50f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
51f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private static final NodeList EMPTY_LIST = new NodeListImpl();
5292e01317d2428856cee52965745d17699a33be5aJesse Wilson
5392e01317d2428856cee52965745d17699a33be5aJesse Wilson    static final TypeInfo NULL_TYPE_INFO = new TypeInfo() {
5492e01317d2428856cee52965745d17699a33be5aJesse Wilson        public String getTypeName() {
5592e01317d2428856cee52965745d17699a33be5aJesse Wilson            return null;
5692e01317d2428856cee52965745d17699a33be5aJesse Wilson        }
5792e01317d2428856cee52965745d17699a33be5aJesse Wilson        public String getTypeNamespace() {
5892e01317d2428856cee52965745d17699a33be5aJesse Wilson            return null;
5992e01317d2428856cee52965745d17699a33be5aJesse Wilson        }
6092e01317d2428856cee52965745d17699a33be5aJesse Wilson        public boolean isDerivedFrom(
6192e01317d2428856cee52965745d17699a33be5aJesse Wilson                String typeNamespaceArg, String typeNameArg, int derivationMethod) {
6292e01317d2428856cee52965745d17699a33be5aJesse Wilson            return false;
6392e01317d2428856cee52965745d17699a33be5aJesse Wilson        }
6492e01317d2428856cee52965745d17699a33be5aJesse Wilson    };
6592e01317d2428856cee52965745d17699a33be5aJesse Wilson
66f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    DocumentImpl document;
67f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
68f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    NodeImpl(DocumentImpl document) {
69f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        this.document = document;
70f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
71f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
72f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Node appendChild(Node newChild) throws DOMException {
73f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
74f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
75f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
764daa600befc82e6f29ee943645dbbcc51f12097eJesse Wilson    public final Node cloneNode(boolean deep) {
774daa600befc82e6f29ee943645dbbcc51f12097eJesse Wilson        return document.cloneOrImportNode(UserDataHandler.NODE_CLONED, this, deep);
78f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
79f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
80f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public NamedNodeMap getAttributes() {
81f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
82f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
83f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
84f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public NodeList getChildNodes() {
85f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return EMPTY_LIST;
86f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
87f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
88f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Node getFirstChild() {
89f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
90f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
91f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
92f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Node getLastChild() {
93f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
94f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
95f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
96f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public String getLocalName() {
97f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
98f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
99f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
100f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public String getNamespaceURI() {
101f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
102f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
103f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
104f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Node getNextSibling() {
105f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
106f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
107f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
108f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public String getNodeName() {
109f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
110f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
111f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
112f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public abstract short getNodeType();
113f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
114f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public String getNodeValue() throws DOMException {
115f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
116f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
117f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
118e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson    public final Document getOwnerDocument() {
119e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        return document == this ? null : document;
120f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
121f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
122f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Node getParentNode() {
123f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
124f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
125f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
126f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public String getPrefix() {
127f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
128f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
129f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
130f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Node getPreviousSibling() {
131f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return null;
132f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
133f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
134f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public boolean hasAttributes() {
135f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return false;
136f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
137f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
138f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public boolean hasChildNodes() {
139f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return false;
140f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
141f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
142f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Node insertBefore(Node newChild, Node refChild) throws DOMException {
143f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
144f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
145f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
146f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public boolean isSupported(String feature, String version) {
147f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return DOMImplementationImpl.getInstance().hasFeature(feature, version);
148f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
149f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
150f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public void normalize() {
151f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
152f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
153f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Node removeChild(Node oldChild) throws DOMException {
154f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
155f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
156f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
157f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
158f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
159f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
160f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
16112547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson    public final void setNodeValue(String nodeValue) throws DOMException {
16212547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson        switch (getNodeType()) {
16312547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case CDATA_SECTION_NODE:
16412547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case COMMENT_NODE:
16512547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case TEXT_NODE:
16612547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson                ((CharacterData) this).setData(nodeValue);
16712547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson                return;
16812547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson
16912547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case PROCESSING_INSTRUCTION_NODE:
17012547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson                ((ProcessingInstruction) this).setData(nodeValue);
17112547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson                return;
17212547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson
17312547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case ATTRIBUTE_NODE:
17412547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson                ((Attr) this).setValue(nodeValue);
17512547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson                return;
17612547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson
17712547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case ELEMENT_NODE:
17812547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case ENTITY_REFERENCE_NODE:
17912547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case ENTITY_NODE:
18012547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case DOCUMENT_NODE:
18112547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case DOCUMENT_TYPE_NODE:
18212547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case DOCUMENT_FRAGMENT_NODE:
18312547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            case NOTATION_NODE:
18412547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson                return; // do nothing!
18512547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson
18612547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson            default:
18712547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson                throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
18812547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson                        "Unsupported node type " + getNodeType());
18912547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson        }
190f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
191f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
192f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public void setPrefix(String prefix) throws DOMException {
193f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
194f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
195f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
196a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * Validates the element or attribute namespace prefix on this node.
197a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     *
198a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * @param namespaceAware whether this node is namespace aware
199a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * @param namespaceURI this node's namespace URI
200a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     */
201271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson    static String validatePrefix(String prefix, boolean namespaceAware, String namespaceURI) {
202a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        if (!namespaceAware) {
203a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            throw new DOMException(DOMException.NAMESPACE_ERR, prefix);
204a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        }
205a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
206a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        if (prefix != null) {
207a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            if (namespaceURI == null
208a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    || !DocumentImpl.isXMLIdentifier(prefix)
209a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    || "xml".equals(prefix) && !"http://www.w3.org/XML/1998/namespace".equals(namespaceURI)
210a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    || "xmlns".equals(prefix) && !"http://www.w3.org/2000/xmlns/".equals(namespaceURI)) {
211a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                throw new DOMException(DOMException.NAMESPACE_ERR, prefix);
212a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            }
213a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        }
214a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
215a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        return prefix;
216a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    }
217a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
218a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    /**
219271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson     * Sets the element or attribute node to be namespace-aware and assign it
220271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson     * the specified name and namespace URI.
221271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson     *
222271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson     * @param node an AttrImpl or ElementImpl node.
223271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson     * @param namespaceURI this node's namespace URI. May be null.
224271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson     * @param qualifiedName a possibly-prefixed name like "img" or "html:img".
225271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson     */
226271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson    static void setNameNS(NodeImpl node, String namespaceURI, String qualifiedName) {
227271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson        if (qualifiedName == null) {
228271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson            throw new DOMException(DOMException.NAMESPACE_ERR, qualifiedName);
229271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson        }
230271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson
231271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson        String prefix = null;
232271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson        int p = qualifiedName.lastIndexOf(":");
233271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson        if (p != -1) {
234271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson            prefix = validatePrefix(qualifiedName.substring(0, p), true, namespaceURI);
235271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson            qualifiedName = qualifiedName.substring(p + 1);
236271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson        }
237271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson
238271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson        if (!DocumentImpl.isXMLIdentifier(qualifiedName)) {
239271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson            throw new DOMException(DOMException.INVALID_CHARACTER_ERR, qualifiedName);
240271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson        }
241271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson
242271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson        switch (node.getNodeType()) {
243271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson            case ATTRIBUTE_NODE:
244271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                if ("xmlns".equals(qualifiedName)
245271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                        && !"http://www.w3.org/2000/xmlns/".equals(namespaceURI)) {
246271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                    throw new DOMException(DOMException.NAMESPACE_ERR, qualifiedName);
247271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                }
248271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson
249271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                AttrImpl attr = (AttrImpl) node;
250271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                attr.namespaceAware = true;
251271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                attr.namespaceURI = namespaceURI;
252271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                attr.prefix = prefix;
253271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                attr.localName = qualifiedName;
254271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                break;
255271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson
256271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson            case ELEMENT_NODE:
257271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                ElementImpl element = (ElementImpl) node;
258271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                element.namespaceAware = true;
259271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                element.namespaceURI = namespaceURI;
260271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                element.prefix = prefix;
261271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                element.localName = qualifiedName;
262271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                break;
263271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson
264271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson            default:
265271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
266271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson                        "Cannot rename nodes of type " + node.getNodeType());
267271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson        }
268271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson    }
269271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson
270271825415aa961bdd9f28a551575bcee6f27b4abJesse Wilson    /**
271f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * Checks whether a required string matches an actual string. This utility
272f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * method is used for comparing namespaces and such. It takes into account
273f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * null arguments and the "*" special case.
274f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *
275f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @param required The required string.
276f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @param actual The actual string.
277f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @return True if and only if the actual string matches the required one.
278f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
279f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private static boolean matchesName(String required, String actual, boolean wildcard) {
280f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (wildcard && "*".equals(required)) {
281f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            return true;
282f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
283f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
284f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (required == null) {
285f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            return (actual == null);
286f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
287f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
288f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return required.equals(actual);
289f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
290f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
291f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
292f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * Checks whether this node's name matches a required name. It takes into
293f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * account null arguments and the "*" special case.
294f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *
295f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @param name The required name.
296f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @return True if and only if the actual name matches the required one.
297f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
298f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public boolean matchesName(String name, boolean wildcard) {
299f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return matchesName(name, getNodeName(), wildcard);
300f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
301f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
302f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
303f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * Checks whether this node's namespace and local name match a required
304f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * pair of namespace and local name. It takes into account null arguments
305f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * and the "*" special case.
306f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *
307f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @param namespaceURI The required namespace.
308f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @param localName The required local name.
309f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * @return True if and only if the actual namespace and local name match
310f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     *         the required pair of namespace and local name.
311f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
312f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public boolean matchesNameNS(String namespaceURI, String localName, boolean wildcard) {
313f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        return matchesName(namespaceURI, getNamespaceURI(), wildcard) && matchesName(localName, getLocalName(), wildcard);
314f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
3154c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
3163c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson    public final String getBaseURI() {
3173c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson        switch (getNodeType()) {
3183c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case DOCUMENT_NODE:
3193c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                return sanitizeUri(((Document) this).getDocumentURI());
3203c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3213c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case ELEMENT_NODE:
3223c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                Element element = (Element) this;
3233c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                String uri = element.getAttributeNS(
3243c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                        "http://www.w3.org/XML/1998/namespace", "base"); // or "xml:base"
3253c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3263c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                // if this node has no base URI, return the parent's.
3273c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                if (uri == null || uri.length() == 0) {
3283c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                    return getParentBaseUri();
3293c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                }
3303c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3313c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                // if this node's URI is absolute, return that
3323c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                if (SystemIDResolver.isAbsoluteURI(uri)) {
3333c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                    return uri;
3343c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                }
3353c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3363c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                // this node has a relative URI. Try to resolve it against the
3373c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                // parent, but if that doesn't work just give up and return null.
3383c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                String parentUri = getParentBaseUri();
3393c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                if (parentUri == null) {
3403c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                    return null;
3413c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                }
3423c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                try {
3433c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                    return SystemIDResolver.getAbsoluteURI(uri, parentUri);
3443c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                } catch (TransformerException e) {
3453c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                    return null; // the spec requires that we swallow exceptions
3463c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                }
3473c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3483c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case PROCESSING_INSTRUCTION_NODE:
3493c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                return getParentBaseUri();
3503c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3513c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case NOTATION_NODE:
3523c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case ENTITY_NODE:
3533c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                // When we support these node types, the parser should
3543c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                // initialize a base URI field on these nodes.
3553c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                return null;
3563c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3573c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case ENTITY_REFERENCE_NODE:
3583c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                // TODO: get this value from the parser, falling back to the
3593c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                // referenced entity's baseURI if that doesn't exist
3603c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                return null;
3613c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3623c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case DOCUMENT_TYPE_NODE:
3633c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case DOCUMENT_FRAGMENT_NODE:
3643c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case ATTRIBUTE_NODE:
3653c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case TEXT_NODE:
3663c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case CDATA_SECTION_NODE:
3673c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            case COMMENT_NODE:
3683c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                return null;
3693c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3703c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            default:
3713c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
3723c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson                        "Unsupported node type " + getNodeType());
3733c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson        }
3743c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson    }
3753c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3763c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson    private String getParentBaseUri() {
3773c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson        Node parentNode = getParentNode();
3783c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson        return parentNode != null ? parentNode.getBaseURI() : null;
3793c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson    }
3803c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson
3813c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson    /**
3823c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson     * Returns the sanitized input if it is a URI, or {@code null} otherwise.
3833c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson     */
3843c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson    private String sanitizeUri(String uri) {
3853c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson        if (uri == null || uri.length() == 0) {
3863c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            return null;
3873c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson        }
3883c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson        try {
3893c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            return new URI(uri).toString();
3903c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson        } catch (URI.MalformedURIException e) {
3913c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson            return null;
3923c5ed1cf96d6b0c24076d60202c9db43315e451dJesse Wilson        }
3934c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
3944c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
3954c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    public short compareDocumentPosition(Node other)
3964c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson            throws DOMException {
3974c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson        throw new UnsupportedOperationException(); // TODO
3984c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
3994c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
4004c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    public String getTextContent() throws DOMException {
401fb1a80758587eecaa912130640bd9f3ef0d0d7bfJesse Wilson        return getNodeValue();
402fb1a80758587eecaa912130640bd9f3ef0d0d7bfJesse Wilson    }
403fb1a80758587eecaa912130640bd9f3ef0d0d7bfJesse Wilson
404fb1a80758587eecaa912130640bd9f3ef0d0d7bfJesse Wilson    void getTextContent(StringBuilder buf) throws DOMException {
405fb1a80758587eecaa912130640bd9f3ef0d0d7bfJesse Wilson        String content = getNodeValue();
406fb1a80758587eecaa912130640bd9f3ef0d0d7bfJesse Wilson        if (content != null) {
407fb1a80758587eecaa912130640bd9f3ef0d0d7bfJesse Wilson            buf.append(content);
408fb1a80758587eecaa912130640bd9f3ef0d0d7bfJesse Wilson        }
4094c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
4104c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
41112547dd7a3879e22e618f0464eb71a69cc15e2ccJesse Wilson    public final void setTextContent(String textContent) throws DOMException {
412a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        switch (getNodeType()) {
413a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case DOCUMENT_TYPE_NODE:
414a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case DOCUMENT_NODE:
415a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                return; // do nothing!
416a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
417a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case ELEMENT_NODE:
418a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case ENTITY_NODE:
419a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case ENTITY_REFERENCE_NODE:
420a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case DOCUMENT_FRAGMENT_NODE:
421a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                // remove all existing children
422a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                Node child;
423a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                while ((child = getFirstChild()) != null) {
424a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    removeChild(child);
425a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                }
426a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                // create a text node to hold the given content
427606d8d2d12b615f58bc7614c490f9c48cf923e95Jesse Wilson                if (textContent != null && textContent.length() != 0) {
428e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson                    appendChild(document.createTextNode(textContent));
429a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                }
430a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                return;
431a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
432a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case ATTRIBUTE_NODE:
433a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case TEXT_NODE:
434a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case CDATA_SECTION_NODE:
435a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case PROCESSING_INSTRUCTION_NODE:
436a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case COMMENT_NODE:
437a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case NOTATION_NODE:
438a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                setNodeValue(textContent);
439a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                return;
440a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
441a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            default:
442a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
443a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                        "Unsupported node type " + getNodeType());
444a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        }
4454c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
4464c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
4474c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    public boolean isSameNode(Node other) {
448a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        return this == other;
4494c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
4504c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
451a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    /**
452a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * Returns the element whose namespace definitions apply to this node. Use
453a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * this element when mapping prefixes to URIs and vice versa.
454a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     */
455a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    private NodeImpl getNamespacingElement() {
456a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        switch (this.getNodeType()) {
457a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case ELEMENT_NODE:
458a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                return this;
459a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
460a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case DOCUMENT_NODE:
461a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                return (NodeImpl) ((Document) this).getDocumentElement();
462a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
463a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case ENTITY_NODE:
464a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case NOTATION_NODE:
465a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case DOCUMENT_FRAGMENT_NODE:
466a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case DOCUMENT_TYPE_NODE:
467a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                return null;
468a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
469a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case ATTRIBUTE_NODE:
470a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                return (NodeImpl) ((Attr) this).getOwnerElement();
471a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
472a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case TEXT_NODE:
473a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case CDATA_SECTION_NODE:
474a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case ENTITY_REFERENCE_NODE:
475a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case PROCESSING_INSTRUCTION_NODE:
476a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            case COMMENT_NODE:
477a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                return getContainingElement();
478a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
479a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            default:
480a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
481a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                        "Unsupported node type " + getNodeType());
482a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        }
483a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    }
484a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
485a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    /**
486a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * Returns the nearest ancestor element that contains this node.
487a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     */
488a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    private NodeImpl getContainingElement() {
489a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        for (Node p = getParentNode(); p != null; p = p.getParentNode()) {
490a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            if (p.getNodeType() == ELEMENT_NODE) {
491a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                return (NodeImpl) p;
492a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            }
493a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        }
494a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        return null;
495a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    }
496a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
497a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    public final String lookupPrefix(String namespaceURI) {
498a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        if (namespaceURI == null) {
499a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            return null;
500a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        }
501a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
502a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        // the XML specs define some prefixes (like "xml" and "xmlns") but this
503a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        // API is explicitly defined to ignore those.
504a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
505a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        NodeImpl target = getNamespacingElement();
506a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        for (NodeImpl node = target; node != null; node = node.getContainingElement()) {
507a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            // check this element's namespace first
508a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            if (namespaceURI.equals(node.getNamespaceURI())
509a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    && target.isPrefixMappedToUri(node.getPrefix(), namespaceURI)) {
510a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                return node.getPrefix();
511a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            }
512a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
513a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            // search this element for an attribute of this form:
514a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            //   xmlns:foo="http://namespaceURI"
515a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            if (!node.hasAttributes()) {
516a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                continue;
517a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            }
518a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            NamedNodeMap attributes = node.getAttributes();
519a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            for (int i = 0, length = attributes.getLength(); i < length; i++) {
520a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                Node attr = attributes.item(i);
521a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                if (!"http://www.w3.org/2000/xmlns/".equals(attr.getNamespaceURI())
522a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                        || !"xmlns".equals(attr.getPrefix())
523a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                        || !namespaceURI.equals(attr.getNodeValue())) {
524a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    continue;
525a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                }
526a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                if (target.isPrefixMappedToUri(attr.getLocalName(), namespaceURI)) {
527a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    return attr.getLocalName();
528a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                }
529a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            }
530a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        }
531a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
532a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        return null;
533a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    }
534a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
535a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    /**
536a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * Returns true if the given prefix is mapped to the given URI on this
537a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * element. Since child elements can redefine prefixes, this check is
538a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * necessary: {@code
539a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * <foo xmlns:a="http://good">
540a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     *   <bar xmlns:a="http://evil">
541a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     *     <a:baz />
542a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     *   </bar>
543a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * </foo>}
544a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     *
545a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * @param prefix the prefix to find. Nullable.
546a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     * @param uri the URI to match. Non-null.
547a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson     */
548a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    boolean isPrefixMappedToUri(String prefix, String uri) {
549a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        if (prefix == null) {
550a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            return false;
551a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        }
552a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
553a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        String actual = lookupNamespaceURI(prefix);
554a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        return uri.equals(actual);
5554c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
5564c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
557cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson    public final boolean isDefaultNamespace(String namespaceURI) {
558cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        String actual = lookupNamespaceURI(null); // null yields the default namespace
559cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        return namespaceURI == null
560cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                ? actual == null
561cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                : namespaceURI.equals(actual);
5624c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
5634c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
564a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson    public final String lookupNamespaceURI(String prefix) {
565a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        NodeImpl target = getNamespacingElement();
566a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        for (NodeImpl node = target; node != null; node = node.getContainingElement()) {
567a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            // check this element's namespace first
568a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            String nodePrefix = node.getPrefix();
569a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            if (node.getNamespaceURI() != null) {
570a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                if (prefix == null // null => default prefix
571a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                        ? nodePrefix == null
572a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                        : prefix.equals(nodePrefix)) {
573a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    return node.getNamespaceURI();
574a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                }
575a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            }
576a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
577a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            // search this element for an attribute of the appropriate form.
578a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            //    default namespace: xmlns="http://resultUri"
579a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            //          non default: xmlns:specifiedPrefix="http://resultUri"
580a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            if (!node.hasAttributes()) {
581a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                continue;
582a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            }
583a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            NamedNodeMap attributes = node.getAttributes();
584a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            for (int i = 0, length = attributes.getLength(); i < length; i++) {
585a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                Node attr = attributes.item(i);
586a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                if (!"http://www.w3.org/2000/xmlns/".equals(attr.getNamespaceURI())) {
587a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    continue;
588a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                }
589a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                if (prefix == null // null => default prefix
590a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                        ? "xmlns".equals(attr.getNodeName())
591a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                        : "xmlns".equals(attr.getPrefix()) && prefix.equals(attr.getLocalName())) {
592a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    String value = attr.getNodeValue();
593a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                    return value.length() > 0 ? value : null;
594a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson                }
595a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson            }
596a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        }
597a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson
598a76b64ab7f0ece60b794af13154d4150ca0170faJesse Wilson        return null;
5994c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
6004c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
601cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson    /**
602cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson     * Returns a list of objects such that two nodes are equal if their lists
603cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson     * are equal. Be careful: the lists may contain NamedNodeMaps and Nodes,
604cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson     * neither of which override Object.equals(). Such values must be compared
605cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson     * manually.
606cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson     */
607cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson    private static List<Object> createEqualityKey(Node node) {
608cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        List<Object> values = new ArrayList<Object>();
609cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        values.add(node.getNodeType());
610cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        values.add(node.getNodeName());
611cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        values.add(node.getLocalName());
612cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        values.add(node.getNamespaceURI());
613cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        values.add(node.getPrefix());
614cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        values.add(node.getNodeValue());
615cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
616cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            values.add(child);
617cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        }
618cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
619cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        switch (node.getNodeType()) {
620cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            case DOCUMENT_TYPE_NODE:
621cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                DocumentTypeImpl doctype = (DocumentTypeImpl) node;
622cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                values.add(doctype.getPublicId());
623cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                values.add(doctype.getSystemId());
624cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                values.add(doctype.getInternalSubset());
625cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                values.add(doctype.getEntities());
626cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                values.add(doctype.getNotations());
627cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                break;
628cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
629cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            case ELEMENT_NODE:
630cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                Element element = (Element) node;
631cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                values.add(element.getAttributes());
632cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                break;
633cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        }
634cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
635cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        return values;
6364c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
6374c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
638cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson    public final boolean isEqualNode(Node arg) {
639cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        if (arg == this) {
640cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            return true;
641cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        }
642cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
643cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        List<Object> listA = createEqualityKey(this);
644cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        List<Object> listB = createEqualityKey(arg);
645cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
646cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        if (listA.size() != listB.size()) {
647cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            return false;
648cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        }
649cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
650cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        for (int i = 0; i < listA.size(); i++) {
651cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            Object a = listA.get(i);
652cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            Object b = listB.get(i);
653cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
654cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            if (a == b) {
655cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                continue;
656cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
657cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            } else if (a == null || b == null) {
658cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                return false;
659cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
660cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            } else if (a instanceof String || a instanceof Short) {
661cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                if (!a.equals(b)) {
662cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                    return false;
663cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                }
664cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
665cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            } else if (a instanceof NamedNodeMap) {
666cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                if (!(b instanceof NamedNodeMap)
667cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                        || !namedNodeMapsEqual((NamedNodeMap) a, (NamedNodeMap) b)) {
668cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                    return false;
669cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                }
670cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
671cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            } else if (a instanceof Node) {
672cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                if (!(b instanceof Node)
673cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                        || !((Node) a).isEqualNode((Node) b)) {
674cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                    return false;
675cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                }
676cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
677cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            } else {
678cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                throw new AssertionError(); // unexpected type
679cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            }
680cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        }
681cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
682cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        return true;
683cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson    }
684cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
685cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson    private boolean namedNodeMapsEqual(NamedNodeMap a, NamedNodeMap b) {
686cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        if (a.getLength() != b.getLength()) {
687cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            return false;
688cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        }
689cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        for (int i = 0; i < a.getLength(); i++) {
690cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            Node aNode = a.item(i);
691cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            Node bNode = aNode.getLocalName() == null
692cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                    ? b.getNamedItem(aNode.getNodeName())
693cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                    : b.getNamedItemNS(aNode.getNamespaceURI(), aNode.getLocalName());
694cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            if (bNode == null || !aNode.isEqualNode(bNode)) {
695cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson                return false;
696cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson            }
697cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        }
698cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        return true;
699cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson    }
700cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson
701cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson    public final Object getFeature(String feature, String version) {
702cacbb893cd1361e7c7a63aa49ae85c0232813b9cJesse Wilson        return isSupported(feature, version) ? this : null;
7034c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
7044c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
705e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson    public final Object setUserData(String key, Object data, UserDataHandler handler) {
706e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        if (key == null) {
707e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson            throw new NullPointerException();
708e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        }
709e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        Map<String, UserData> map = document.getUserDataMap(this);
710e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        UserData previous = data == null
711e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson                ? map.remove(key)
712e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson                : map.put(key, new UserData(data, handler));
713e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        return previous != null ? previous.value : null;
7144c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
7154c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson
716e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson    public final Object getUserData(String key) {
717e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        if (key == null) {
718e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson            throw new NullPointerException();
719e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        }
720e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        Map<String, UserData> map = document.getUserDataMapForRead(this);
721e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        UserData userData = map.get(key);
722e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        return userData != null ? userData.value : null;
723e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson    }
724e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson
725e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson    static class UserData {
726e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        final Object value;
727e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        final UserDataHandler handler;
728e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        UserData(Object value, UserDataHandler handler) {
729e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson            this.value = value;
730e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson            this.handler = handler;
731e14d736a739d6523fd13d98f13e9d51167197a34Jesse Wilson        }
7324c7a0d97cf2b27790e6236965a1d798d710d7ec7Jesse Wilson    }
733f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project}
734