src/share/jaxws_classes/com/sun/xml/internal/ws/util/QNameMap.java

changeset 0
373ffda63c9a
child 637
9c07ef4934dd
equal deleted inserted replaced
-1:000000000000 0:373ffda63c9a
1 /*
2 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.xml.internal.ws.util;
27
28 import com.sun.istack.internal.NotNull;
29
30 import javax.xml.namespace.QName;
31 import java.util.AbstractSet;
32 import java.util.Collection;
33 import java.util.HashSet;
34 import java.util.Iterator;
35 import java.util.Map;
36 import java.util.NoSuchElementException;
37 import java.util.Set;
38
39 /**
40 * Map keyed by {@link QName}.
41 *
42 * This specialized map allows a look up operation without constructing
43 * a new QName instance, for a performance reason. This {@link Map} assumes
44 * that both namespace URI and local name are {@link String#intern() intern}ed.
45 *
46 * @since JAXB 2.0
47 */
48 public final class QNameMap<V> {
49 /**
50 * The default initial capacity - MUST be a power of two.
51 */
52 private static final int DEFAULT_INITIAL_CAPACITY = 16;
53
54 /**
55 * The maximum capacity, used if a higher value is implicitly specified
56 * by either of the constructors with arguments.
57 * MUST be a power of two <= 1<<30.
58 */
59 private static final int MAXIMUM_CAPACITY = 1 << 30;
60
61 /**
62 * The table, resized as necessary. Length MUST Always be a power of two.
63 */
64 transient Entry<V>[] table = new Entry[DEFAULT_INITIAL_CAPACITY];
65
66 /**
67 * The number of key-value mappings contained in this identity hash map.
68 */
69 transient int size;
70
71 /**
72 * The next size value at which to resize . Taking it as
73 * MAXIMUM_CAPACITY
74 * @serial
75 */
76 private int threshold;
77
78 /**
79 * The load factor used when none specified in constructor.
80 **/
81 private static final float DEFAULT_LOAD_FACTOR = 0.75f;
82
83
84
85 /**
86 * Gives an entrySet view of this map
87 */
88 private Set<Entry<V>> entrySet = null;
89
90 public QNameMap() {
91 threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
92 table = new Entry[DEFAULT_INITIAL_CAPACITY];
93
94 }
95
96 /**
97 * Associates the specified value with the specified keys in this map.
98 * If the map previously contained a mapping for this key, the old
99 * value is replaced.
100 *
101 * @param namespaceUri First key with which the specified value is to be associated.
102 * @param localname Second key with which the specified value is to be associated.
103 * @param value value to be associated with the specified key.
104 *
105 */
106 public void put(String namespaceUri,String localname, V value ) {
107 //keys cannot be null
108 assert localname !=null;
109 assert namespaceUri !=null;
110
111 int hash = hash(localname);
112 int i = indexFor(hash, table.length);
113
114 for (Entry<V> e = table[i]; e != null; e = e.next) {
115 if (e.hash == hash && localname.equals(e.localName) && namespaceUri.equals(e.nsUri)) {
116 e.value = value;
117 return;
118 }
119 }
120
121 addEntry(hash, namespaceUri,localname, value, i);
122
123 }
124
125 public void put(QName name, V value ) {
126 put(name.getNamespaceURI(),name.getLocalPart(),value);
127 }
128
129 /**
130 * Returns the value to which the specified keys are mapped in this QNameMap,
131 * or <tt>null</tt> if the map contains no mapping for this key.
132 *
133 * @param nsUri the namespaceUri key whose associated value is to be returned.
134 * @param localPart the localPart key whose associated value is to be returned.
135 * @return the value to which this map maps the specified set of keya, or
136 * <tt>null</tt> if the map contains no mapping for this set of keys.
137 * @see #put(String,String, Object)
138 */
139 public V get( @NotNull String nsUri, String localPart ) {
140 Entry<V> e = getEntry(nsUri,localPart);
141 if(e==null) return null;
142 else return e.value;
143 }
144
145 public V get( QName name ) {
146 return get(name.getNamespaceURI(),name.getLocalPart());
147 }
148
149 /**
150 * Returns the number of keys-value mappings in this map.
151 *
152 * @return the number of keys-value mappings in this map.
153 */
154 public int size() {
155 return size;
156 }
157
158 /**
159 * Copies all of the mappings from the specified map to this map
160 * These mappings will replace any mappings that
161 * this map had for any of the keys currently in the specified map.
162 *
163 * @param map mappings to be stored in this map.
164 *
165 */
166 public QNameMap<V> putAll(QNameMap<? extends V> map) {
167 int numKeysToBeAdded = map.size();
168 if (numKeysToBeAdded == 0)
169 return this;
170
171
172 if (numKeysToBeAdded > threshold) {
173 int targetCapacity = numKeysToBeAdded;
174 if (targetCapacity > MAXIMUM_CAPACITY)
175 targetCapacity = MAXIMUM_CAPACITY;
176 int newCapacity = table.length;
177 while (newCapacity < targetCapacity)
178 newCapacity <<= 1;
179 if (newCapacity > table.length)
180 resize(newCapacity);
181 }
182
183 for( Entry<? extends V> e : map.entrySet() )
184 put(e.nsUri,e.localName,e.getValue());
185 return this;
186 }
187
188 public QNameMap<V> putAll(Map<QName,? extends V> map) {
189 for (Map.Entry<QName, ? extends V> e : map.entrySet()) {
190 QName qn = e.getKey();
191 put(qn.getNamespaceURI(),qn.getLocalPart(),e.getValue());
192 }
193 return this;
194 }
195
196
197 /**
198 * Returns a hash value for the specified object.The hash value is computed
199 * for the localName.
200 */
201 private static int hash(String x) {
202 int h = x.hashCode();
203
204 h += ~(h << 9);
205 h ^= (h >>> 14);
206 h += (h << 4);
207 h ^= (h >>> 10);
208 return h;
209 }
210
211 /**
212 * Returns index for hash code h.
213 */
214 private static int indexFor(int h, int length) {
215 return h & (length-1);
216 }
217
218 /**
219 * Add a new entry with the specified keys, value and hash code to
220 * the specified bucket. It is the responsibility of this
221 * method to resize the table if appropriate.
222 *
223 */
224 private void addEntry(int hash, String nsUri, String localName, V value, int bucketIndex) {
225 Entry<V> e = table[bucketIndex];
226 table[bucketIndex] = new Entry<V>(hash, nsUri, localName, value, e);
227 if (size++ >= threshold)
228 resize(2 * table.length);
229 }
230
231
232 /**
233 * Rehashes the contents of this map into a new array with a
234 * larger capacity. This method is called automatically when the
235 * number of keys in this map reaches its threshold.
236 */
237 private void resize(int newCapacity) {
238 Entry[] oldTable = table;
239 int oldCapacity = oldTable.length;
240 if (oldCapacity == MAXIMUM_CAPACITY) {
241 threshold = Integer.MAX_VALUE;
242 return;
243 }
244
245 Entry[] newTable = new Entry[newCapacity];
246 transfer(newTable);
247 table = newTable;
248 threshold = newCapacity;
249 }
250
251 /**
252 * Transfer all entries from current table to newTable.
253 */
254 private void transfer(Entry<V>[] newTable) {
255 Entry<V>[] src = table;
256 int newCapacity = newTable.length;
257 for (int j = 0; j < src.length; j++) {
258 Entry<V> e = src[j];
259 if (e != null) {
260 src[j] = null;
261 do {
262 Entry<V> next = e.next;
263 int i = indexFor(e.hash, newCapacity);
264 e.next = newTable[i];
265 newTable[i] = e;
266 e = next;
267 } while (e != null);
268 }
269 }
270 }
271
272 /**
273 * Returns one random item in the map.
274 * If this map is empty, return null.
275 *
276 * <p>
277 * This method is useful to obtain the value from a map that only contains one element.
278 */
279 public Entry<V> getOne() {
280 for( Entry<V> e : table ) {
281 if(e!=null)
282 return e;
283 }
284 return null;
285 }
286
287 public Collection<QName> keySet() {
288 Set<QName> r = new HashSet<QName>();
289 for (Entry<V> e : entrySet()) {
290 r.add(e.createQName());
291 }
292 return r;
293 }
294
295 public Iterable<V> values() {
296 return views;
297 }
298
299 private transient Iterable<V> views = new Iterable<V>() {
300 public Iterator<V> iterator() {
301 return new ValueIterator();
302 }
303 };
304
305 private abstract class HashIterator<E> implements Iterator<E> {
306 Entry<V> next; // next entry to return
307 int index; // current slot
308
309 HashIterator() {
310 Entry<V>[] t = table;
311 int i = t.length;
312 Entry<V> n = null;
313 if (size != 0) { // advance to first entry
314 while (i > 0 && (n = t[--i]) == null)
315 ;
316 }
317 next = n;
318 index = i;
319 }
320
321 public boolean hasNext() {
322 return next != null;
323 }
324
325 Entry<V> nextEntry() {
326 Entry<V> e = next;
327 if (e == null)
328 throw new NoSuchElementException();
329
330 Entry<V> n = e.next;
331 Entry<V>[] t = table;
332 int i = index;
333 while (n == null && i > 0)
334 n = t[--i];
335 index = i;
336 next = n;
337 return e;
338 }
339
340 public void remove() {
341 throw new UnsupportedOperationException();
342 }
343 }
344
345 private class ValueIterator extends HashIterator<V> {
346 public V next() {
347 return nextEntry().value;
348 }
349 }
350
351 public boolean containsKey(@NotNull String nsUri,String localName) {
352 return getEntry(nsUri,localName)!=null;
353 }
354
355
356 /**
357 * Returns true if this map is empty.
358 */
359 public boolean isEmpty() {
360 return size == 0;
361 }
362
363
364 public static final class Entry<V> {
365 /** The namespace URI. */
366 public final String nsUri;
367
368 /** The localPart. */
369 public final String localName;
370
371 V value;
372 final int hash;
373 Entry<V> next;
374
375 /**
376 * Create new entry.
377 */
378 Entry(int h, String nsUri, String localName, V v, Entry<V> n) {
379 value = v;
380 next = n;
381 this.nsUri = nsUri;
382 this.localName = localName;
383 hash = h;
384 }
385
386 /**
387 * Creates a new QName object from {@link #nsUri} and {@link #localName}.
388 */
389 public QName createQName() {
390 return new QName(nsUri,localName);
391 }
392
393 public V getValue() {
394 return value;
395 }
396
397 public V setValue(V newValue) {
398 V oldValue = value;
399 value = newValue;
400 return oldValue;
401 }
402
403 public boolean equals(Object o) {
404 if (!(o instanceof Entry))
405 return false;
406 Entry e = (Entry)o;
407 String k1 = nsUri;
408 String k2 = e.nsUri;
409 String k3 = localName;
410 String k4 = e.localName;
411 if (k1.equals(k2) && k3.equals(k4)) {
412 Object v1 = getValue();
413 Object v2 = e.getValue();
414 if (v1 == v2 || (v1 != null && v1.equals(v2)))
415 return true;
416 }
417 return false;
418 }
419
420 public int hashCode() {
421 return ( localName.hashCode()) ^
422 (value==null ? 0 : value.hashCode());
423 }
424
425 public String toString() {
426 return '"'+nsUri +"\",\"" +localName + "\"=" + getValue();
427 }
428 }
429
430 public Set<Entry<V>> entrySet() {
431 Set<Entry<V>> es = entrySet;
432 return es != null ? es : (entrySet = new EntrySet());
433 }
434
435 private Iterator<Entry<V>> newEntryIterator() {
436 return new EntryIterator();
437 }
438
439 private class EntryIterator extends HashIterator<Entry<V>> {
440 public Entry<V> next() {
441 return nextEntry();
442 }
443 }
444 private class EntrySet extends AbstractSet<Entry<V>> {
445 public Iterator<Entry<V>> iterator() {
446 return newEntryIterator();
447 }
448 public boolean contains(Object o) {
449 if (!(o instanceof Entry))
450 return false;
451 Entry<V> e = (Entry<V>) o;
452 Entry<V> candidate = getEntry(e.nsUri,e.localName);
453 return candidate != null && candidate.equals(e);
454 }
455 public boolean remove(Object o) {
456 throw new UnsupportedOperationException();
457 }
458 public int size() {
459 return size;
460 }
461 }
462
463 private Entry<V> getEntry(@NotNull String nsUri,String localName) {
464 int hash = hash(localName);
465 int i = indexFor(hash, table.length);
466 Entry<V> e = table[i];
467 while (e != null && !(localName.equals(e.localName) && nsUri.equals(e.nsUri)))
468 e = e.next;
469 return e;
470 }
471
472 public String toString() {
473 StringBuilder buf = new StringBuilder();
474 buf.append('{');
475
476 for( Entry<V> e : entrySet() ) {
477 if(buf.length()>1)
478 buf.append(',');
479 buf.append('[');
480 buf.append(e);
481 buf.append(']');
482 }
483
484 buf.append('}');
485 return buf.toString();
486 }
487 }

mercurial