2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org.apache.harmony.xml.dom;
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;
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
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
35 * <p>This class represents a Node that has a parent Node as well as
36 * (potentially) a number of children.
38 * <p>Some code was adapted from Apache Xerces.
40 public abstract class InnerNodeImpl extends LeafNodeImpl {
42 // Maintained by LeafNodeImpl and ElementImpl.
43 List<LeafNodeImpl> children = new ArrayList<LeafNodeImpl>();
45 protected InnerNodeImpl(DocumentImpl document) {
49 public Node appendChild(Node newChild) throws DOMException {
50 return insertChildAt(newChild, children.size());
53 public NodeList getChildNodes() {
54 NodeListImpl list = new NodeListImpl();
56 for (NodeImpl node : children) {
63 public Node getFirstChild() {
64 return (!children.isEmpty() ? children.get(0) : null);
67 public Node getLastChild() {
68 return (!children.isEmpty() ? children.get(children.size() - 1) : null);
71 public Node getNextSibling() {
72 if (parent == null || index + 1 >= parent.children.size()) {
76 return parent.children.get(index + 1);
79 public boolean hasChildNodes() {
80 return children.size() != 0;
83 public Node insertBefore(Node newChild, Node refChild) throws DOMException {
84 LeafNodeImpl refChildImpl = (LeafNodeImpl) refChild;
86 if (refChildImpl.document != document) {
87 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
90 if (refChildImpl.parent != this) {
91 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
94 return insertChildAt(newChild, refChildImpl.index);
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()
103 * @param newChild The new child node to add.
104 * @param index The index at which to insert the new child node.
106 * @return The node added.
108 * @throws DOMException If the attempted operation violates the XML/DOM
109 * well-formedness rules.
111 public Node insertChildAt(Node newChild, int index) throws DOMException {
112 LeafNodeImpl newChildImpl = (LeafNodeImpl) newChild;
114 if (document != null && newChildImpl.document != null && newChildImpl.document != document) {
115 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
118 if (newChildImpl.isParentOf(this)) {
119 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
122 if (newChildImpl.parent != null) {
123 int oldIndex = newChildImpl.index;
124 newChildImpl.parent.children.remove(oldIndex);
125 newChildImpl.parent.refreshIndices(oldIndex);
128 children.add(index, newChildImpl);
129 newChildImpl.parent = this;
130 refreshIndices(index);
135 public boolean isParentOf(Node node) {
136 LeafNodeImpl nodeImpl = (LeafNodeImpl) node;
138 while (nodeImpl != null) {
139 if (nodeImpl == this) {
143 nodeImpl = nodeImpl.parent;
150 * Normalize the text nodes within this subtree. Although named similarly,
151 * this method is unrelated to Document.normalize.
154 public final void normalize() {
156 for (Node node = getFirstChild(); node != null; node = next) {
157 next = node.getNextSibling();
160 if (node.getNodeType() == Node.TEXT_NODE) {
161 ((TextImpl) node).minimize();
166 private void refreshIndices(int fromIndex) {
167 for (int i = fromIndex; i < children.size(); i++) {
168 children.get(i).index = i;
172 public Node removeChild(Node oldChild) throws DOMException {
173 LeafNodeImpl oldChildImpl = (LeafNodeImpl) oldChild;
175 if (oldChildImpl.document != document) {
176 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
179 if (oldChildImpl.parent != this) {
180 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
183 int index = oldChildImpl.index;
184 children.remove(index);
185 oldChildImpl.parent = null;
186 refreshIndices(index);
191 public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
192 LeafNodeImpl oldChildImpl = (LeafNodeImpl) oldChild;
193 LeafNodeImpl newChildImpl = (LeafNodeImpl) newChild;
195 if (oldChildImpl.document != document
196 || newChildImpl.document != document) {
197 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
200 if (oldChildImpl.parent != this || newChildImpl.isParentOf(this)) {
201 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
204 int index = oldChildImpl.index;
205 children.set(index, newChildImpl);
206 oldChildImpl.parent = null;
207 newChildImpl.parent = this;
208 refreshIndices(index);
213 public String getTextContent() throws DOMException {
214 Node child = getFirstChild();
219 Node next = child.getNextSibling();
221 return hasTextContent(child) ? child.getTextContent() : "";
224 StringBuilder buf = new StringBuilder();
226 return buf.toString();
229 void getTextContent(StringBuilder buf) throws DOMException {
230 Node child = getFirstChild();
231 while (child != null) {
232 if (hasTextContent(child)) {
233 ((NodeImpl) child).getTextContent(buf);
235 child = child.getNextSibling();
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;