InnerNodeImpl.java revision e14d736a739d6523fd13d98f13e9d51167197a34
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.DOMException; 20import org.w3c.dom.Node; 21import org.w3c.dom.NodeList; 22 23import java.util.ArrayList; 24import 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 */ 41public abstract class InnerNodeImpl extends LeafNodeImpl { 42 43 // Maintained by LeafNodeImpl and ElementImpl. 44 List<LeafNodeImpl> children = new ArrayList<LeafNodeImpl>(); 45 46 protected 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} 246