001 /*
002 * Copyright (c) 2007 Henri Sivonen
003 * Copyright (c) 2008 Mozilla Foundation
004 *
005 * Permission is hereby granted, free of charge, to any person obtaining a
006 * copy of this software and associated documentation files (the "Software"),
007 * to deal in the Software without restriction, including without limitation
008 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
009 * and/or sell copies of the Software, and to permit persons to whom the
010 * Software is furnished to do so, subject to the following conditions:
011 *
012 * The above copyright notice and this permission notice shall be included in
013 * all copies or substantial portions of the Software.
014 *
015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
018 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
020 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
021 * DEALINGS IN THE SOFTWARE.
022 */
023
024 package nu.validator.saxtree;
025
026 import org.xml.sax.Locator;
027
028 /**
029 * Common superclass for parent nodes.
030 * @version $Id$
031 * @author hsivonen
032 */
033 public abstract class ParentNode extends Node {
034
035 /**
036 * The end locator.
037 */
038 protected Locator endLocator;
039
040 /**
041 * The first child.
042 */
043 private Node firstChild = null;
044
045 /**
046 * The last child (for efficiency).
047 */
048 private Node lastChild = null;
049
050 /**
051 * The constuctor.
052 * @param locator the locator
053 */
054 ParentNode(Locator locator) {
055 super(locator);
056 }
057
058 /**
059 * Sets the endLocator.
060 *
061 * @param endLocator the endLocator to set
062 */
063 public void setEndLocator(Locator endLocator) {
064 this.endLocator = new LocatorImpl(endLocator);
065 }
066
067 /**
068 * Copies the endLocator from another node.
069 *
070 * @param another the another node
071 */
072 public void copyEndLocator(ParentNode another) {
073 this.endLocator = another.endLocator;
074 }
075
076 /**
077 * Returns the firstChild.
078 *
079 * @return the firstChild
080 */
081 public final Node getFirstChild() {
082 return firstChild;
083 }
084
085 /**
086 * Returns the lastChild.
087 *
088 * @return the lastChild
089 */
090 public final Node getLastChild() {
091 return lastChild;
092 }
093
094 /**
095 * Insert a new child before a pre-existing child and return the newly inserted child.
096 * @param child the new child
097 * @param sibling the existing child before which to insert (must be a child of this node) or <code>null</code> to append
098 * @return <code>child</code>
099 */
100 public Node insertBefore(Node child, Node sibling) {
101 assert sibling == null || this == sibling.getParentNode();
102 if (sibling == null) {
103 return appendChild(child);
104 }
105 child.detach();
106 child.setParentNode(this);
107 if (firstChild == sibling) {
108 child.setNextSibling(sibling);
109 firstChild = child;
110 } else {
111 Node prev = firstChild;
112 Node next = firstChild.getNextSibling();
113 while (next != sibling) {
114 prev = next;
115 next = next.getNextSibling();
116 }
117 prev.setNextSibling(child);
118 child.setNextSibling(next);
119 }
120 return child;
121 }
122
123 public Node insertBetween(Node child, Node prev, Node next) {
124 assert prev == null || this == prev.getParentNode();
125 assert next == null || this == next.getParentNode();
126 assert prev != null || next == firstChild;
127 assert next != null || prev == lastChild;
128 assert prev == null || next == null || prev.getNextSibling() == next;
129 if (next == null) {
130 return appendChild(child);
131 }
132 child.detach();
133 child.setParentNode(this);
134 child.setNextSibling(next);
135 if (prev == null) {
136 firstChild = child;
137 } else {
138 prev.setNextSibling(child);
139 }
140 return child;
141 }
142
143 /**
144 * Append a child to this node and return the child.
145 *
146 * @param child the child to append.
147 * @return <code>child</code>
148 */
149 public Node appendChild(Node child) {
150 child.detach();
151 child.setParentNode(this);
152 if (firstChild == null) {
153 firstChild = child;
154 } else {
155 lastChild.setNextSibling(child);
156 }
157 lastChild = child;
158 return child;
159 }
160
161 /**
162 * Append the children of another node to this node removing them from the other node .
163 * @param parent the other node whose children to append to this one
164 */
165 public void appendChildren(Node parent) {
166 Node child = parent.getFirstChild();
167 if (child == null) {
168 return;
169 }
170 ParentNode another = (ParentNode) parent;
171 if (firstChild == null) {
172 firstChild = child;
173 } else {
174 lastChild.setNextSibling(child);
175 }
176 lastChild = another.lastChild;
177 do {
178 child.setParentNode(this);
179 } while ((child = child.getNextSibling()) != null);
180 another.firstChild = null;
181 another.lastChild = null;
182 }
183
184 /**
185 * Remove a child from this node.
186 * @param node the child to remove
187 */
188 void removeChild(Node node) {
189 assert this == node.getParentNode();
190 if (firstChild == node) {
191 firstChild = node.getNextSibling();
192 if (lastChild == node) {
193 lastChild = null;
194 }
195 } else {
196 Node prev = firstChild;
197 Node next = firstChild.getNextSibling();
198 while (next != node) {
199 prev = next;
200 next = next.getNextSibling();
201 }
202 prev.setNextSibling(node.getNextSibling());
203 if (lastChild == node) {
204 lastChild = prev;
205 }
206 }
207 }
208 }