View Javadoc
1 /* 2 * Copyright (C) 2002 Carsten Krebs (Team-Konzept GmbH & Co.KG) 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 */ 18 package com.teamkonzept.dom4jb.dom; 19 20 import com.teamkonzept.dom4jb.schema.ContentIterator; 21 22 public class NamedNodeMap implements org.w3c.dom.NamedNodeMap { 23 24 private static final int UNSIGNED_MASK = 0x7FFFFFFF; 25 protected static final int initialCapacity = 11; 26 27 /*** 28 * The load factor for the hashtable. 29 * 30 * @serial 31 */ 32 private static final float loadFactor = 0.75f; 33 34 /*** 35 * The parent node of this map 36 */ 37 private Node parent; 38 39 /*** 40 * The hash table data. 41 */ 42 private Entry[] nodes; 43 44 /*** 45 * The total number of mappings in the hash table. 46 */ 47 private transient int count; 48 49 /*** 50 * The table is rehashed when its size exceeds this threshold. (The 51 * value of this field is (int)(capacity * loadFactor).) 52 * 53 * @serial 54 */ 55 private int threshold; 56 57 /*** Creates new NamedNodeMap */ 58 protected NamedNodeMap() { 59 nodes = new Entry[initialCapacity]; 60 threshold = (int) (initialCapacity * loadFactor); 61 } 62 63 /*** Creates new NamedNodeMap */ 64 public NamedNodeMap(final Node parent, final ContentIterator iterator) { 65 this(); 66 67 this.parent = parent; 68 while (iterator.hasNext()) { 69 final NamedNode node = (NamedNode) iterator.next(); 70 node.setParentNode(parent); 71 put(node); 72 } 73 } 74 75 /*** 76 * Associates the specified value with the specified key in this map. 77 * If the map previously contained a mapping for this key, the old 78 * value is replaced. 79 * 80 * @param key key with which the specified value is to be associated. 81 * @param value value to be associated with the specified key. 82 * @return previous value associated with specified key, or <tt>null</tt> 83 * if there was no mapping for key. A <tt>null</tt> return can 84 * also indicate that the HashMap previously associated 85 * <tt>null</tt> with the specified key. 86 */ 87 protected NamedNode put(final NamedNode node) { 88 // Makes sure the key is not already in the NamedNodeMap. 89 Entry tab[] = nodes; 90 91 final NodeName key = node.getNamingItem(); 92 final int hash = key.hashCode(); 93 int index = (hash & UNSIGNED_MASK) % tab.length; 94 for (Entry e = tab[index]; e != null; e = e.next) { 95 if ((e.hash == hash) && key.equals(e.node.getNamingItem())) { 96 final NamedNode old = e.node; 97 e.node = node; 98 return old; 99 } 100 } 101 102 if (count >= threshold) { 103 // Rehash the table if the threshold is exceeded 104 rehash(); 105 106 tab = nodes; 107 index = (hash & UNSIGNED_MASK) % tab.length; 108 } 109 110 // Creates the new entry. 111 final Entry e = new Entry(hash, node, tab[index]); 112 tab[index] = e; 113 count++; 114 115 return null; 116 } 117 118 /*** 119 * Removes the mapping for this key from this map if present. 120 * 121 * @param key key whose mapping is to be removed from the map. 122 * @return previous value associated with specified key, or <tt>null</tt> 123 * if there was no mapping for key. A <tt>null</tt> return can 124 * also indicate that the map previously associated <tt>null</tt> 125 * with the specified key. 126 */ 127 protected NamedNode remove(final NodeName node) { 128 final Entry tab[] = nodes; 129 130 final int hash = node.hashCode(); 131 final int index = (hash & UNSIGNED_MASK) % tab.length; 132 133 for (Entry e = tab[index], prev = null; 134 e != null; 135 prev = e, e = e.next) { 136 if ((e.hash == hash) && node.equals(e.node.getNamingItem())) { 137 if (prev != null) { 138 prev.next = e.next; 139 } else { 140 tab[index] = e.next; 141 } 142 143 count--; 144 final NamedNode oldNode = e.node; 145 e.node = null; 146 return oldNode; 147 } 148 } 149 150 return null; 151 } 152 153 /*** 154 * Returns the value to which this map maps the specified key. Returns 155 * <tt>null</tt> if the map contains no mapping for this key. 156 * 157 * @return the value to which this map maps the specified key. 158 * @param key key whose associated value is to be returned. 159 */ 160 public NamedNode get(final NodeName key) { 161 final Entry tab[] = nodes; 162 163 final int hash = key.hashCode(); 164 final int index = (hash & UNSIGNED_MASK) % tab.length; 165 for (Entry e = tab[index]; e != null; e = e.next) { 166 if ((e.hash == hash) && key.equals(e.node.getNamingItem())) 167 return e.node; 168 } 169 170 return null; 171 } 172 173 /*** 174 * Rehashes the contents of this map into a new <tt>NamedNodeMap</tt> 175 * instance with a larger capacity. This method is called automatically 176 * when the number of keys in this map exceeds its capacity and load factor. 177 */ 178 private void rehash() { 179 final Entry oldMap[] = nodes; 180 final int oldCapacity = oldMap.length; 181 182 final int newCapacity = oldCapacity * 2 + 1; 183 final Entry newMap[] = new Entry[newCapacity]; 184 185 threshold = (int) (newCapacity * loadFactor); 186 nodes = newMap; 187 188 for (int i = oldCapacity; i-- > 0;) { 189 for (Entry old = oldMap[i]; old != null;) { 190 final Entry e = old; 191 old = old.next; 192 193 final int index = (e.hash & UNSIGNED_MASK) % newCapacity; 194 e.next = newMap[index]; 195 newMap[index] = e; 196 } 197 } 198 } 199 200 /*** 201 * The number of nodes in this map. The range of valid child node indices 202 * is <code>0</code> to <code>length-1</code> inclusive. 203 */ 204 public int getLength() { 205 return count; 206 } 207 208 /*** 209 * Removes a node specified by name. When this map contains the attributes 210 * attached to an element, if the removed attribute is known to have a 211 * default value, an attribute immediately appears containing the 212 * default value as well as the corresponding namespace URI, local name, 213 * and prefix when applicable. 214 * @param name The <code>nodeName</code> of the node to remove. 215 * @return The node removed from this map if a node with such a name 216 * exists. 217 */ 218 public org.w3c.dom.Node removeNamedItem(final String name) { 219 // 220 // @task knoten durch default-Knoten ersetzen, falls dieser 221 // sowas besitzt 222 // 223 final NamedNode node = remove(NodeName.getInstance(name)); 224 if (node == null) { 225 throw new DOMException( 226 DOMException.NOT_FOUND_ERR, 227 "no such node " + name); 228 } 229 return node; 230 } 231 232 /*** 233 * Adds a node using its <code>namespaceURI</code> and 234 * <code>localName</code>. If a node with that namespace URI and that 235 * local name is already present in this map, it is replaced by the new 236 * one. 237 * @param arg A node to store in this map. The node will later be 238 * accessible using the value of its <code>namespaceURI</code> and 239 * <code>localName</code> attributes. 240 * @return If the new <code>Node</code> replaces an existing node the 241 * replaced <code>Node</code> is returned, otherwise <code>null</code> 242 * is returned. 243 * @since DOM Level 2 244 */ 245 public org.w3c.dom.Node setNamedItemNS(final org.w3c.dom.Node arg) { 246 return put((NamedNode) arg); 247 } 248 249 /*** 250 * Adds a node using its <code>nodeName</code> attribute. If a node with 251 * that name is already present in this map, it is replaced by the new 252 * one. 253 * <br>As the <code>nodeName</code> attribute is used to derive the name 254 * which the node must be stored under, multiple nodes of certain types 255 * (those that have a "special" string value) cannot be stored as the 256 * names would clash. This is seen as preferable to allowing nodes to be 257 * aliased. 258 * @param arg A node to store in this map. The node will later be 259 * accessible using the value of its <code>nodeName</code> attribute. 260 * @return If the new <code>Node</code> replaces an existing node the 261 * replaced <code>Node</code> is returned, otherwise <code>null</code> 262 * is returned. 263 */ 264 public org.w3c.dom.Node setNamedItem(final org.w3c.dom.Node arg) { 265 return put((NamedNode) arg); 266 } 267 268 /*** 269 * Retrieves a node specified by local name and namespace URI. 270 * <br>Documents which do not support the "XML" feature will permit only 271 * the DOM Level 1 calls for creating/setting elements and attributes. 272 * Hence, if you specify a non-null namespace URI, these DOMs will never 273 * find a matching node. 274 * @param namespaceURI The namespace URI of the node to retrieve. 275 * @param localName The local name of the node to retrieve. 276 * @return A <code>Node</code> (of any type) with the specified local 277 * name and namespace URI, or <code>null</code> if they do not 278 * identify any node in this map. 279 * @since DOM Level 2 280 */ 281 public org.w3c.dom.Node getNamedItemNS( 282 final String namespaceURI, 283 final String localName) { 284 return get(NodeNameNS.getInstance(namespaceURI, localName)); 285 } 286 287 /*** 288 * Returns the <code>index</code>th item in the map. If <code>index</code> 289 * is greater than or equal to the number of nodes in this map, this 290 * returns <code>null</code>. 291 * @param index Index into this map. 292 * @return The node at the <code>index</code>th position in the map, or 293 * <code>null</code> if that is not a valid index. 294 */ 295 public org.w3c.dom.Node item(int index) { 296 if (index < 0 || index >= count) { 297 return null; 298 } 299 300 final Entry tab[] = nodes; 301 Entry e = null; 302 303 for (int i = 0; index >= 0;) { 304 if (e == null) { 305 e = tab[i]; 306 i++; 307 } else { 308 e = e.next; 309 } 310 if (e != null && e.node != null) { 311 index--; 312 } 313 } 314 return e.node; 315 } 316 317 /*** 318 * Retrieves a node specified by name. 319 * @param name The <code>nodeName</code> of a node to retrieve. 320 * @return A <code>Node</code> (of any type) with the specified 321 * <code>nodeName</code>, or <code>null</code> if it does not identify 322 * any node in this map. 323 */ 324 public org.w3c.dom.Node getNamedItem(final String name) { 325 return get(NodeName.getInstance(name)); 326 } 327 328 /*** 329 * Removes a node specified by local name and namespace URI. A removed 330 * attribute may be known to have a default value when this map contains 331 * the attributes attached to an element, as returned by the attributes 332 * attribute of the <code>Node</code> interface. If so, an attribute 333 * immediately appears containing the default value as well as the 334 * corresponding namespace URI, local name, and prefix when applicable. 335 * <br>Documents which do not support the "XML" feature will permit only 336 * the DOM Level 1 calls for creating/setting elements and attributes. 337 * Hence, if you specify a non-null namespace URI, these DOMs will never 338 * find a matching node. 339 * @param namespaceURI The namespace URI of the node to remove. 340 * @param localName The local name of the node to remove. 341 * @return The node removed from this map if a node with such a local 342 * name and namespace URI exists. 343 * @since DOM Level 2 344 */ 345 public org.w3c.dom.Node removeNamedItemNS(final String namespaceURI, 346 final String localName) { 347 // 348 // @task knoten durch default-Knoten ersetzen, falls 349 // dieser sowas besitzt 350 // 351 final NamedNode node = 352 remove(NodeNameNS.getInstance(namespaceURI, localName)); 353 if (node == null) { 354 throw new DOMException( 355 DOMException.NOT_FOUND_ERR, 356 "no such node " + namespaceURI + ":" + localName); 357 } 358 return node; 359 } 360 } 361 362 /*** 363 * NamedNodeMap collision list entry. 364 */ 365 final class Entry { 366 final int hash; 367 NamedNode node; 368 Entry next; 369 370 Entry(final int hash, final NamedNode node, final Entry next) { 371 this.hash = hash; 372 this.node = node; 373 this.next = next; 374 } 375 376 public int hashCode() { 377 return hash ^ node.getNamingItem().hashCode(); 378 } 379 380 public String toString() { 381 return node.toString(); 382 } 383 }

This page was automatically generated by Maven