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 }