DocumentImpl.java revision cacbb893cd1361e7c7a63aa49ae85c0232813b9c
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.apache.harmony.xml.dom;
18
19import org.w3c.dom.Attr;
20import org.w3c.dom.CDATASection;
21import org.w3c.dom.CharacterData;
22import org.w3c.dom.Comment;
23import org.w3c.dom.DOMConfiguration;
24import org.w3c.dom.DOMException;
25import org.w3c.dom.DOMImplementation;
26import org.w3c.dom.Document;
27import org.w3c.dom.DocumentFragment;
28import org.w3c.dom.DocumentType;
29import org.w3c.dom.Element;
30import org.w3c.dom.EntityReference;
31import org.w3c.dom.NamedNodeMap;
32import org.w3c.dom.Node;
33import org.w3c.dom.NodeList;
34import org.w3c.dom.ProcessingInstruction;
35import org.w3c.dom.Text;
36
37/**
38 * Provides a straightforward implementation of the corresponding W3C DOM
39 * interface. The class is used internally only, thus only notable members that
40 * are not in the original interface are documented (the W3C docs are quite
41 * extensive). Hope that's ok.
42 * <p>
43 * Some of the fields may have package visibility, so other classes belonging to
44 * the DOM implementation can easily access them while maintaining the DOM tree
45 * structure.
46 */
47public class DocumentImpl extends InnerNodeImpl implements Document {
48
49    private DOMImplementation domImplementation;
50
51    DocumentImpl(DOMImplementationImpl impl, String namespaceURI,
52            String qualifiedName, DocumentType doctype) {
53        super(null);
54
55        this.domImplementation = impl;
56        // this.document = this;
57
58        if (doctype != null) {
59            appendChild(doctype);
60        }
61
62        if (qualifiedName != null) {
63            appendChild(createElementNS(namespaceURI, qualifiedName));
64        }
65    }
66
67    private static boolean isXMLIdentifierStart(char c) {
68        return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_');
69    }
70
71    private static boolean isXMLIdentifierPart(char c) {
72        return isXMLIdentifierStart(c) || (c >= '0' && c <= '9') || (c == '-') || (c == '.');
73    }
74
75    static boolean isXMLIdentifier(String s) {
76        if (s.length() == 0) {
77            return false;
78        }
79
80        if (!isXMLIdentifierStart(s.charAt(0))) {
81            return false;
82        }
83
84        for (int i = 1; i < s.length(); i++) {
85            if (!isXMLIdentifierPart(s.charAt(i))) {
86                return false;
87            }
88        }
89
90        return true;
91    }
92
93    /**
94     * Clones a node and (if requested) its children. The source node(s) may
95     * have been created by a different DocumentImpl or even DOM implementation.
96     *
97     * @param node The node to clone.
98     * @param deep If true, a deep copy is created (including all child nodes).
99     *
100     * @return The new node.
101     */
102    Node cloneNode(Node node, boolean deep) throws DOMException {
103        // TODO: callback the UserDataHandler with a NODE_CLONED event
104
105        Node target;
106
107        switch (node.getNodeType()) {
108            case Node.ATTRIBUTE_NODE: {
109                Attr source = (Attr)node;
110                target = createAttributeNS(source.getNamespaceURI(), source.getLocalName());
111                target.setPrefix(source.getPrefix());
112                target.setNodeValue(source.getNodeValue());
113                break;
114            }
115            case Node.CDATA_SECTION_NODE: {
116                CharacterData source = (CharacterData)node;
117                target = createCDATASection(source.getData());
118                break;
119            }
120            case Node.COMMENT_NODE: {
121                Comment source = (Comment)node;
122                target = createComment(source.getData());
123                break;
124            }
125            case Node.DOCUMENT_FRAGMENT_NODE: {
126                // Source is irrelevant in this case.
127                target = createDocumentFragment();
128                break;
129            }
130            case Node.DOCUMENT_NODE: {
131                throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone a Document node");
132            }
133            case Node.DOCUMENT_TYPE_NODE: {
134                throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone a DocumentType node");
135            }
136            case Node.ELEMENT_NODE: {
137                Element source = (Element)node;
138                target = createElementNS(source.getNamespaceURI(), source.getLocalName());
139                target.setPrefix(source.getPrefix());
140
141                NamedNodeMap map = source.getAttributes();
142                for (int i = 0; i < map.getLength(); i++) {
143                    Attr attr = (Attr)map.item(i);
144                    ((Element)target).setAttributeNodeNS((Attr)cloneNode(attr, deep));
145                }
146                break;
147            }
148            case Node.ENTITY_NODE: {
149                throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone an Entity node");
150            }
151            case Node.ENTITY_REFERENCE_NODE: {
152                EntityReference source = (EntityReference)node;
153                target = createEntityReference(source.getNodeName());
154                break;
155            }
156            case Node.NOTATION_NODE: {
157                throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone a Notation node");
158            }
159            case Node.PROCESSING_INSTRUCTION_NODE: {
160                ProcessingInstruction source = (ProcessingInstruction)node;
161                target = createProcessingInstruction(source.getTarget(), source.getData());
162                break;
163            }
164            case Node.TEXT_NODE: {
165                Text source = (Text)node;
166                target = createTextNode(source.getData());
167                break;
168            }
169            default: {
170                throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "Cannot clone unknown node type " + node.getNodeType() + " (" + node.getClass().getSimpleName() + ")");
171            }
172        }
173
174        if (deep) {
175            NodeList list = node.getChildNodes();
176            for (int i = 0; i < list.getLength(); i++) {
177                Node child = cloneNode(list.item(i), deep);
178                target.appendChild(child);
179            }
180        }
181
182        return target;
183    }
184
185    public AttrImpl createAttribute(String name) throws DOMException {
186        return new AttrImpl(this, name);
187    }
188
189    public Attr createAttributeNS(String namespaceURI, String qualifiedName)
190            throws DOMException {
191        return new AttrImpl(this, namespaceURI, qualifiedName);
192    }
193
194    public CDATASection createCDATASection(String data) throws DOMException {
195        return new CDATASectionImpl(this, data);
196    }
197
198    public Comment createComment(String data) {
199        return new CommentImpl(this, data);
200    }
201
202    public DocumentFragment createDocumentFragment() {
203        return new DocumentFragmentImpl(this);
204    }
205
206    public Element createElement(String tagName) throws DOMException {
207        return new ElementImpl(this, tagName);
208    }
209
210    public Element createElementNS(String namespaceURI, String qualifiedName)
211            throws DOMException {
212        return new ElementImpl(this, namespaceURI, qualifiedName);
213    }
214
215    public EntityReference createEntityReference(String name)
216            throws DOMException {
217        return new EntityReferenceImpl(this, name);
218    }
219
220    public ProcessingInstruction createProcessingInstruction(String target,
221            String data) throws DOMException {
222        return new ProcessingInstructionImpl(this, target, data);
223    }
224
225    public Text createTextNode(String data) {
226        return new TextImpl(this, data);
227    }
228
229    public DocumentType getDoctype() {
230        for (int i = 0; i < children.size(); i++) {
231            if (children.get(i) instanceof DocumentType) {
232                return (DocumentType) children.get(i);
233            }
234        }
235
236        return null;
237    }
238
239    public Element getDocumentElement() {
240        for (int i = 0; i < children.size(); i++) {
241            if (children.get(i) instanceof Element) {
242                return (Element) children.get(i);
243            }
244        }
245
246        return null;
247    }
248
249    public Element getElementById(String elementId) {
250        ElementImpl root = (ElementImpl) getDocumentElement();
251
252        return (root == null ? null : root.getElementById(elementId));
253    }
254
255    public NodeList getElementsByTagName(String tagname) {
256        ElementImpl root = (ElementImpl) getDocumentElement();
257
258        return (root == null ? new NodeListImpl()
259                : root.getElementsByTagName(tagname));
260    }
261
262    public NodeList getElementsByTagNameNS(String namespaceURI, String localName) {
263        ElementImpl root = (ElementImpl) getDocumentElement();
264
265        return (root == null ? new NodeListImpl() : root.getElementsByTagNameNS(
266                namespaceURI, localName));
267    }
268
269    public DOMImplementation getImplementation() {
270        return domImplementation;
271    }
272
273    @Override
274    public String getNodeName() {
275        return "#document";
276    }
277
278    @Override
279    public short getNodeType() {
280        return Node.DOCUMENT_NODE;
281    }
282
283    public Node importNode(Node importedNode, boolean deep) throws DOMException {
284        // TODO: callback the UserDataHandler with a NODE_IMPORTED event
285        return cloneNode(importedNode, deep);
286    }
287
288    @Override
289    public Node insertChildAt(Node newChild, int index) throws DOMException {
290        // Make sure we have at most one root element and one DTD element.
291        if (newChild instanceof Element && getDocumentElement() != null) {
292            throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
293                    "Only one root element allowed");
294        } else if (newChild instanceof DocumentType && getDoctype() != null) {
295            throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR,
296                    "Only one DOCTYPE element allowed");
297        }
298
299        return super.insertChildAt(newChild, index);
300    }
301
302    @Override public String getTextContent() throws DOMException {
303        return null;
304    }
305
306    public String getInputEncoding() {
307        throw new UnsupportedOperationException(); // TODO
308    }
309
310    public String getXmlEncoding() {
311        throw new UnsupportedOperationException(); // TODO
312    }
313
314    public boolean getXmlStandalone() {
315        throw new UnsupportedOperationException(); // TODO
316    }
317
318    public void setXmlStandalone(boolean xmlStandalone) throws DOMException {
319        throw new UnsupportedOperationException(); // TODO
320    }
321
322    public String getXmlVersion() {
323        throw new UnsupportedOperationException(); // TODO
324    }
325
326    public void setXmlVersion(String xmlVersion) throws DOMException {
327        throw new UnsupportedOperationException(); // TODO
328    }
329
330    public boolean getStrictErrorChecking() {
331        throw new UnsupportedOperationException(); // TODO
332    }
333
334    public void setStrictErrorChecking(boolean strictErrorChecking) {
335        throw new UnsupportedOperationException(); // TODO
336    }
337
338    public String getDocumentURI() {
339        throw new UnsupportedOperationException(); // TODO
340    }
341
342    public void setDocumentURI(String documentURI) {
343        throw new UnsupportedOperationException(); // TODO
344    }
345
346    public Node adoptNode(Node source) throws DOMException {
347        // TODO: callback the UserDataHandler with a NODE_ADOPTED event
348        throw new UnsupportedOperationException(); // TODO
349    }
350
351    public DOMConfiguration getDomConfig() {
352        throw new UnsupportedOperationException(); // TODO
353    }
354
355    public void normalizeDocument() {
356        throw new UnsupportedOperationException(); // TODO
357    }
358
359    public Node renameNode(Node n, String namespaceURI, String qualifiedName)
360            throws DOMException {
361        // TODO: callback the UserDataHandler with a NODE_RENAMED event
362        throw new UnsupportedOperationException(); // TODO
363    }
364}
365