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