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 org.w3c.dom.DOMException;
20 import org.w3c.dom.Node;
21 import org.w3c.dom.NodeList;
23 import java.util.ArrayList;
24 import java.util.List;
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
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
36 * <p>This class represents a Node that has a parent Node as well as
37 * (potentially) a number of children.
39 * <p>Some code was adapted from Apache Xerces.
41 public abstract class InnerNodeImpl extends LeafNodeImpl {
43 // Maintained by LeafNodeImpl and ElementImpl.
44 List<LeafNodeImpl> children = new ArrayList<LeafNodeImpl>();
46 public InnerNodeImpl(DocumentImpl document) {
50 public Node appendChild(Node newChild) throws DOMException {
51 return insertChildAt(newChild, children.size());
54 public NodeList getChildNodes() {
55 NodeListImpl list = new NodeListImpl();
57 for (NodeImpl node : children) {
64 public Node getFirstChild() {
65 return (!children.isEmpty() ? children.get(0) : null);
68 public Node getLastChild() {
69 return (!children.isEmpty() ? children.get(children.size() - 1) : null);
72 public Node getNextSibling() {
73 if (parent == null || index + 1 >= parent.children.size()) {
77 return parent.children.get(index + 1);
80 public boolean hasChildNodes() {
81 return children.size() != 0;
84 public Node insertBefore(Node newChild, Node refChild) throws DOMException {
85 LeafNodeImpl refChildImpl = (LeafNodeImpl) refChild;
87 if (refChildImpl.document != document) {
88 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
91 if (refChildImpl.parent != this) {
92 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
95 return insertChildAt(newChild, refChildImpl.index);
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()
104 * @param newChild The new child node to add.
105 * @param index The index at which to insert the new child node.
107 * @return The node added.
109 * @throws DOMException If the attempted operation violates the XML/DOM
110 * well-formedness rules.
112 public Node insertChildAt(Node newChild, int index) throws DOMException {
113 LeafNodeImpl newChildImpl = (LeafNodeImpl) newChild;
115 if (document != null && newChildImpl.document != null && newChildImpl.document != document) {
116 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
119 if (newChildImpl.isParentOf(this)) {
120 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
123 if (newChildImpl.parent != null) {
124 int oldIndex = newChildImpl.index;
125 newChildImpl.parent.children.remove(oldIndex);
126 newChildImpl.parent.refreshIndices(oldIndex);
129 children.add(index, newChildImpl);
130 newChildImpl.parent = this;
131 refreshIndices(index);
136 public boolean isParentOf(Node node) {
137 LeafNodeImpl nodeImpl = (LeafNodeImpl) node;
139 while (nodeImpl != null) {
140 if (nodeImpl == this) {
144 nodeImpl = nodeImpl.parent;
151 * Normalize the text nodes within this subtree. Although named similarly,
152 * this method is unrelated to Document.normalize.
155 public final void normalize() {
157 for (Node node = getFirstChild(); node != null; node = next) {
158 next = node.getNextSibling();
161 if (node.getNodeType() == Node.TEXT_NODE) {
162 ((TextImpl) node).minimize();
167 private void refreshIndices(int fromIndex) {
168 for (int i = fromIndex; i < children.size(); i++) {
169 children.get(i).index = i;
173 public Node removeChild(Node oldChild) throws DOMException {
174 LeafNodeImpl oldChildImpl = (LeafNodeImpl) oldChild;
176 if (oldChildImpl.document != document) {
177 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
180 if (oldChildImpl.parent != this) {
181 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
184 int index = oldChildImpl.index;
185 children.remove(index);
186 oldChildImpl.parent = null;
187 refreshIndices(index);
192 public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
193 LeafNodeImpl oldChildImpl = (LeafNodeImpl) oldChild;
194 LeafNodeImpl newChildImpl = (LeafNodeImpl) newChild;
196 if (oldChildImpl.document != document
197 || newChildImpl.document != document) {
198 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null);
201 if (oldChildImpl.parent != this || newChildImpl.isParentOf(this)) {
202 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, null);
205 int index = oldChildImpl.index;
206 children.set(index, newChildImpl);
207 oldChildImpl.parent = null;
208 newChildImpl.parent = this;
209 refreshIndices(index);
214 public String getTextContent() throws DOMException {
215 Node child = getFirstChild();
220 Node next = child.getNextSibling();
222 return hasTextContent(child) ? child.getTextContent() : "";
225 StringBuilder buf = new StringBuilder();
227 return buf.toString();
230 void getTextContent(StringBuilder buf) throws DOMException {
231 Node child = getFirstChild();
232 while (child != null) {
233 if (hasTextContent(child)) {
234 ((NodeImpl) child).getTextContent(buf);
236 child = child.getNextSibling();
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;