001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.ws.util;
038:
039: import javax.xml.namespace.QName;
040: import java.util.AbstractSet;
041: import java.util.Collection;
042: import java.util.HashSet;
043: import java.util.Iterator;
044: import java.util.Map;
045: import java.util.NoSuchElementException;
046: import java.util.Set;
047:
048: /**
049: * Map keyed by {@link QName}.
050: *
051: * This specialized map allows a look up operation without constructing
052: * a new QName instance, for a performance reason. This {@link Map} assumes
053: * that both namespace URI and local name are {@link String#intern() intern}ed.
054: *
055: * @since JAXB 2.0
056: */
057: public final class QNameMap<V> {
058: /**
059: * The default initial capacity - MUST be a power of two.
060: */
061: private static final int DEFAULT_INITIAL_CAPACITY = 16;
062:
063: /**
064: * The maximum capacity, used if a higher value is implicitly specified
065: * by either of the constructors with arguments.
066: * MUST be a power of two <= 1<<30.
067: */
068: private static final int MAXIMUM_CAPACITY = 1 << 30;
069:
070: /**
071: * The table, resized as necessary. Length MUST Always be a power of two.
072: */
073: transient Entry<V>[] table = new Entry[DEFAULT_INITIAL_CAPACITY];
074:
075: /**
076: * The number of key-value mappings contained in this identity hash map.
077: */
078: transient int size;
079:
080: /**
081: * The next size value at which to resize . Taking it as
082: * MAXIMUM_CAPACITY
083: * @serial
084: */
085: private int threshold;
086:
087: /**
088: * The load factor used when none specified in constructor.
089: **/
090: private static final float DEFAULT_LOAD_FACTOR = 0.75f;
091:
092: /**
093: * Gives an entrySet view of this map
094: */
095: private Set<Entry<V>> entrySet = null;
096:
097: public QNameMap() {
098: threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
099: table = new Entry[DEFAULT_INITIAL_CAPACITY];
100:
101: }
102:
103: /**
104: * Associates the specified value with the specified keys in this map.
105: * If the map previously contained a mapping for this key, the old
106: * value is replaced.
107: *
108: * @param namespaceUri First key with which the specified value is to be associated.
109: * @param localname Second key with which the specified value is to be associated.
110: * @param value value to be associated with the specified key.
111: *
112: */
113: public void put(String namespaceUri, String localname, V value) {
114: //keys cannot be null
115: assert localname != null;
116: assert namespaceUri != null;
117:
118: int hash = hash(localname);
119: int i = indexFor(hash, table.length);
120:
121: for (Entry<V> e = table[i]; e != null; e = e.next) {
122: if (e.hash == hash && localname.equals(e.localName)
123: && namespaceUri.equals(e.nsUri)) {
124: e.value = value;
125: return;
126: }
127: }
128:
129: addEntry(hash, namespaceUri, localname, value, i);
130:
131: }
132:
133: public void put(QName name, V value) {
134: put(name.getNamespaceURI(), name.getLocalPart(), value);
135: }
136:
137: /**
138: * Returns the value to which the specified keys are mapped in this QNameMap,
139: * or <tt>null</tt> if the map contains no mapping for this key.
140: *
141: * @param nsUri the namespaceUri key whose associated value is to be returned.
142: * @param localPart the localPart key whose associated value is to be returned.
143: * @return the value to which this map maps the specified set of keya, or
144: * <tt>null</tt> if the map contains no mapping for this set of keys.
145: * @see #put(String,String, Object)
146: */
147: public V get(String nsUri, String localPart) {
148: Entry<V> e = getEntry(nsUri, localPart);
149: if (e == null)
150: return null;
151: else
152: return e.value;
153: }
154:
155: public V get(QName name) {
156: return get(name.getNamespaceURI(), name.getLocalPart());
157: }
158:
159: /**
160: * Returns the number of keys-value mappings in this map.
161: *
162: * @return the number of keys-value mappings in this map.
163: */
164: public int size() {
165: return size;
166: }
167:
168: /**
169: * Copies all of the mappings from the specified map to this map
170: * These mappings will replace any mappings that
171: * this map had for any of the keys currently in the specified map.
172: *
173: * @param map mappings to be stored in this map.
174: *
175: */
176: public QNameMap<V> putAll(QNameMap<? extends V> map) {
177: int numKeysToBeAdded = map.size();
178: if (numKeysToBeAdded == 0)
179: return this ;
180:
181: if (numKeysToBeAdded > threshold) {
182: int targetCapacity = numKeysToBeAdded;
183: if (targetCapacity > MAXIMUM_CAPACITY)
184: targetCapacity = MAXIMUM_CAPACITY;
185: int newCapacity = table.length;
186: while (newCapacity < targetCapacity)
187: newCapacity <<= 1;
188: if (newCapacity > table.length)
189: resize(newCapacity);
190: }
191:
192: for (Entry<? extends V> e : map.entrySet())
193: put(e.nsUri, e.localName, e.getValue());
194: return this ;
195: }
196:
197: public QNameMap<V> putAll(Map<QName, ? extends V> map) {
198: for (Map.Entry<QName, ? extends V> e : map.entrySet()) {
199: QName qn = e.getKey();
200: put(qn.getNamespaceURI(), qn.getLocalPart(), e.getValue());
201: }
202: return this ;
203: }
204:
205: /**
206: * Returns a hash value for the specified object.The hash value is computed
207: * for the localName.
208: */
209: private static int hash(String x) {
210: int h = x.hashCode();
211:
212: h += ~(h << 9);
213: h ^= (h >>> 14);
214: h += (h << 4);
215: h ^= (h >>> 10);
216: return h;
217: }
218:
219: /**
220: * Returns index for hash code h.
221: */
222: private static int indexFor(int h, int length) {
223: return h & (length - 1);
224: }
225:
226: /**
227: * Add a new entry with the specified keys, value and hash code to
228: * the specified bucket. It is the responsibility of this
229: * method to resize the table if appropriate.
230: *
231: */
232: private void addEntry(int hash, String nsUri, String localName,
233: V value, int bucketIndex) {
234: Entry<V> e = table[bucketIndex];
235: table[bucketIndex] = new Entry<V>(hash, nsUri, localName,
236: value, e);
237: if (size++ >= threshold)
238: resize(2 * table.length);
239: }
240:
241: /**
242: * Rehashes the contents of this map into a new array with a
243: * larger capacity. This method is called automatically when the
244: * number of keys in this map reaches its threshold.
245: */
246: private void resize(int newCapacity) {
247: Entry[] oldTable = table;
248: int oldCapacity = oldTable.length;
249: if (oldCapacity == MAXIMUM_CAPACITY) {
250: threshold = Integer.MAX_VALUE;
251: return;
252: }
253:
254: Entry[] newTable = new Entry[newCapacity];
255: transfer(newTable);
256: table = newTable;
257: threshold = newCapacity;
258: }
259:
260: /**
261: * Transfer all entries from current table to newTable.
262: */
263: private void transfer(Entry<V>[] newTable) {
264: Entry<V>[] src = table;
265: int newCapacity = newTable.length;
266: for (int j = 0; j < src.length; j++) {
267: Entry<V> e = src[j];
268: if (e != null) {
269: src[j] = null;
270: do {
271: Entry<V> next = e.next;
272: int i = indexFor(e.hash, newCapacity);
273: e.next = newTable[i];
274: newTable[i] = e;
275: e = next;
276: } while (e != null);
277: }
278: }
279: }
280:
281: /**
282: * Returns one random item in the map.
283: * If this map is empty, return null.
284: *
285: * <p>
286: * This method is useful to obtain the value from a map that only contains one element.
287: */
288: public Entry<V> getOne() {
289: for (Entry<V> e : table) {
290: if (e != null)
291: return e;
292: }
293: return null;
294: }
295:
296: public Collection<QName> keySet() {
297: Set<QName> r = new HashSet<QName>();
298: for (Entry<V> e : entrySet()) {
299: r.add(e.createQName());
300: }
301: return r;
302: }
303:
304: public Iterable<V> values() {
305: return views;
306: }
307:
308: private transient Iterable<V> views = new Iterable<V>() {
309: public Iterator<V> iterator() {
310: return new ValueIterator();
311: }
312: };
313:
314: private abstract class HashIterator<E> implements Iterator<E> {
315: Entry<V> next; // next entry to return
316: int index; // current slot
317:
318: HashIterator() {
319: Entry<V>[] t = table;
320: int i = t.length;
321: Entry<V> n = null;
322: if (size != 0) { // advance to first entry
323: while (i > 0 && (n = t[--i]) == null)
324: ;
325: }
326: next = n;
327: index = i;
328: }
329:
330: public boolean hasNext() {
331: return next != null;
332: }
333:
334: Entry<V> nextEntry() {
335: Entry<V> e = next;
336: if (e == null)
337: throw new NoSuchElementException();
338:
339: Entry<V> n = e.next;
340: Entry<V>[] t = table;
341: int i = index;
342: while (n == null && i > 0)
343: n = t[--i];
344: index = i;
345: next = n;
346: return e;
347: }
348:
349: public void remove() {
350: throw new UnsupportedOperationException();
351: }
352: }
353:
354: private class ValueIterator extends HashIterator<V> {
355: public V next() {
356: return nextEntry().value;
357: }
358: }
359:
360: public boolean containsKey(String nsUri, String localName) {
361: return getEntry(nsUri, localName) != null;
362: }
363:
364: /**
365: * Returns true if this map is empty.
366: */
367: public boolean isEmpty() {
368: return size == 0;
369: }
370:
371: public static final class Entry<V> {
372: /** The namespace URI. */
373: public final String nsUri;
374:
375: /** The localPart. */
376: public final String localName;
377:
378: V value;
379: final int hash;
380: Entry<V> next;
381:
382: /**
383: * Create new entry.
384: */
385: Entry(int h, String nsUri, String localName, V v, Entry<V> n) {
386: value = v;
387: next = n;
388: this .nsUri = nsUri;
389: this .localName = localName;
390: hash = h;
391: }
392:
393: /**
394: * Creates a new QName object from {@link #nsUri} and {@link #localName}.
395: */
396: public QName createQName() {
397: return new QName(nsUri, localName);
398: }
399:
400: public V getValue() {
401: return value;
402: }
403:
404: public V setValue(V newValue) {
405: V oldValue = value;
406: value = newValue;
407: return oldValue;
408: }
409:
410: public boolean equals(Object o) {
411: if (!(o instanceof Entry))
412: return false;
413: Entry e = (Entry) o;
414: String k1 = nsUri;
415: String k2 = e.nsUri;
416: String k3 = localName;
417: String k4 = e.localName;
418: if (k1.equals(k2) && k3.equals(k4)) {
419: Object v1 = getValue();
420: Object v2 = e.getValue();
421: if (v1 == v2 || (v1 != null && v1.equals(v2)))
422: return true;
423: }
424: return false;
425: }
426:
427: public int hashCode() {
428: return (localName.hashCode())
429: ^ (value == null ? 0 : value.hashCode());
430: }
431:
432: public String toString() {
433: return '"' + nsUri + "\",\"" + localName + "\"="
434: + getValue();
435: }
436: }
437:
438: public Set<Entry<V>> entrySet() {
439: Set<Entry<V>> es = entrySet;
440: return es != null ? es : (entrySet = new EntrySet());
441: }
442:
443: private Iterator<Entry<V>> newEntryIterator() {
444: return new EntryIterator();
445: }
446:
447: private class EntryIterator extends HashIterator<Entry<V>> {
448: public Entry<V> next() {
449: return nextEntry();
450: }
451: }
452:
453: private class EntrySet extends AbstractSet<Entry<V>> {
454: public Iterator<Entry<V>> iterator() {
455: return newEntryIterator();
456: }
457:
458: public boolean contains(Object o) {
459: if (!(o instanceof Entry))
460: return false;
461: Entry<V> e = (Entry<V>) o;
462: Entry<V> candidate = getEntry(e.nsUri, e.localName);
463: return candidate != null && candidate.equals(e);
464: }
465:
466: public boolean remove(Object o) {
467: throw new UnsupportedOperationException();
468: }
469:
470: public int size() {
471: return size;
472: }
473: }
474:
475: private Entry<V> getEntry(String nsUri, String localName) {
476: int hash = hash(localName);
477: int i = indexFor(hash, table.length);
478: Entry<V> e = table[i];
479: while (e != null
480: && !(localName.equals(e.localName) && nsUri
481: .equals(e.nsUri)))
482: e = e.next;
483: return e;
484: }
485:
486: public String toString() {
487: StringBuilder buf = new StringBuilder();
488: buf.append('{');
489:
490: for (Entry<V> e : entrySet()) {
491: if (buf.length() > 1)
492: buf.append(',');
493: buf.append('[');
494: buf.append(e);
495: buf.append(']');
496: }
497:
498: buf.append('}');
499: return buf.toString();
500: }
501: }
|