OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / main / java / org / apache / harmony / xml / dom / InnerNodeImpl.java
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
17 package org.apache.harmony.xml.dom;
18
19 import java.util.ArrayList;
20 import java.util.List;
21 import org.w3c.dom.DOMException;
22 import org.w3c.dom.Node;
23 import org.w3c.dom.NodeList;
24
25 /**
26  * Provides a straightforward implementation of the corresponding W3C DOM
27  * interface. The class is used internally only, thus only notable members that
28  * are not in the original interface are documented (the W3C docs are quite
29  * extensive).
30  *
31  * <p>Some of the fields may have package visibility, so other classes belonging
32  * to the DOM implementation can easily access them while maintaining the DOM
33  * tree structure.
34  *
35  * <p>This class represents a Node that has a parent Node as well as
36  * (potentially) a number of children.
37  *
38  * <p>Some code was adapted from Apache Xerces.
39  */
40 public abstract class InnerNodeImpl extends LeafNodeImpl {
41
42     // Maintained by LeafNodeImpl and ElementImpl.
43     List<LeafNodeImpl> children = new ArrayList<LeafNodeImpl>();
44
45     protected InnerNodeImpl(DocumentImpl document) {
46         super(document);
47     }
48
49     public Node appendChild(Node newChild) throws DOMException {
50         return insertChildAt(newChild, children.size());
51     }
52
53     public NodeList getChildNodes() {
54         NodeListImpl list = new NodeListImpl();
55
56         for (NodeImpl node : children) {
57             list.add(node);
58         }
59
60         return list;
61     }
62
63     public Node getFirstChild() {
64         return (!children.isEmpty() ? children.get(0) : null);
65     }
66
67     public Node getLastChild() {
68         return (!children.isEmpty() ? children.get(children.size() - 1) : null);
69     }
70
71     public Node getNextSibling() {
72         if (parent == null || index + 1 >= parent.children.size()) {
73             return null;
74         }
75
76         return parent.children.get(index + 1);
77     }
78
79     public boolean hasChildNodes() {
80         return children.size() != 0;
81     }
82
83     public Node insertBefore(Node newChild, Node refChild) throws DOMException {
84         LeafNodeImpl refChildImpl = (LeafNodeImpl) refChild;
85
86         if (refChildImpl.document != document) {
87             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
88         }
89
90         if (refChildImpl.parent != this) {
91             throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
92         }
93
94         return insertChildAt(newChild, refChildImpl.index);
95     }
96
97     /**
98      * Inserts a new child node into this node at a given position. If the new
99      * node is already child of another node, it is first removed from there.
100      * This method is the generalization of the appendChild() and insertBefore()
101      * methods.
102      *
103      * @param newChild The new child node to add.
104      * @param index The index at which to insert the new child node.
105      *
106      * @return The node added.
107      *
108      * @throws DOMException If the attempted operation violates the XML/DOM
109      *         well-formedness rules.
110      */
111     public Node insertChildAt(Node newChild, int index) throws DOMException {
112         LeafNodeImpl newChildImpl = (LeafNodeImpl) newChild;
113
114         if (document != null && newChildImpl.document != null && newChildImpl.document != document) {
115             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
116         }
117
118         if (newChildImpl.isParentOf(this)) {
119             throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
120         }
121
122         if (newChildImpl.parent != null) {
123             int oldIndex = newChildImpl.index;
124             newChildImpl.parent.children.remove(oldIndex);
125             newChildImpl.parent.refreshIndices(oldIndex);
126         }
127
128         children.add(index, newChildImpl);
129         newChildImpl.parent = this;
130         refreshIndices(index);
131
132         return newChild;
133     }
134
135     public boolean isParentOf(Node node) {
136         LeafNodeImpl nodeImpl = (LeafNodeImpl) node;
137
138         while (nodeImpl != null) {
139             if (nodeImpl == this) {
140                 return true;
141             }
142
143             nodeImpl = nodeImpl.parent;
144         }
145
146         return false;
147     }
148
149     /**
150      * Normalize the text nodes within this subtree. Although named similarly,
151      * this method is unrelated to Document.normalize.
152      */
153     @Override
154     public final void normalize() {
155         Node next;
156         for (Node node = getFirstChild(); node != null; node = next) {
157             next = node.getNextSibling();
158             node.normalize();
159
160             if (node.getNodeType() == Node.TEXT_NODE) {
161                 ((TextImpl) node).minimize();
162             }
163         }
164     }
165
166     private void refreshIndices(int fromIndex) {
167         for (int i = fromIndex; i < children.size(); i++) {
168             children.get(i).index = i;
169         }
170     }
171
172     public Node removeChild(Node oldChild) throws DOMException {
173         LeafNodeImpl oldChildImpl = (LeafNodeImpl) oldChild;
174
175         if (oldChildImpl.document != document) {
176             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
177         }
178
179         if (oldChildImpl.parent != this) {
180             throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
181         }
182
183         int index = oldChildImpl.index;
184         children.remove(index);
185         oldChildImpl.parent = null;
186         refreshIndices(index);
187
188         return oldChild;
189     }
190
191     public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
192         LeafNodeImpl oldChildImpl = (LeafNodeImpl) oldChild;
193         LeafNodeImpl newChildImpl = (LeafNodeImpl) newChild;
194
195         if (oldChildImpl.document != document
196                 || newChildImpl.document != document) {
197             throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
198         }
199
200         if (oldChildImpl.parent != this || newChildImpl.isParentOf(this)) {
201             throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
202         }
203
204         int index = oldChildImpl.index;
205         children.set(index, newChildImpl);
206         oldChildImpl.parent = null;
207         newChildImpl.parent = this;
208         refreshIndices(index);
209
210         return oldChildImpl;
211     }
212
213     public String getTextContent() throws DOMException {
214         Node child = getFirstChild();
215         if (child == null) {
216             return "";
217         }
218
219         Node next = child.getNextSibling();
220         if (next == null) {
221             return hasTextContent(child) ? child.getTextContent() : "";
222         }
223
224         StringBuilder buf = new StringBuilder();
225         getTextContent(buf);
226         return buf.toString();
227     }
228
229     void getTextContent(StringBuilder buf) throws DOMException {
230         Node child = getFirstChild();
231         while (child != null) {
232             if (hasTextContent(child)) {
233                 ((NodeImpl) child).getTextContent(buf);
234             }
235             child = child.getNextSibling();
236         }
237     }
238
239     final boolean hasTextContent(Node child) {
240         // TODO: skip text nodes with ignorable whitespace?
241         return child.getNodeType() != Node.COMMENT_NODE
242                 && child.getNodeType() != Node.PROCESSING_INSTRUCTION_NODE;
243     }
244 }