OSDN Git Service

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