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

Fri, 14 Feb 2014 11:13:45 +0100

author
mkos
date
Fri, 14 Feb 2014 11:13:45 +0100
changeset 515
6cd506508147
parent 368
0989ad8c0860
child 637
9c07ef4934dd
permissions
-rw-r--r--

8026188: Enhance envelope factory
Summary: Avoiding caching data initialized via TCCL in static context; fix also reviewed by Alexander Fomin
Reviewed-by: ahgross, mgrebac, skoivu

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

mercurial