001: /*
002: * Portions Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.internal.ws.util;
027:
028: import java.util.AbstractCollection;
029: import java.util.AbstractSet;
030: import java.util.Collection;
031: import java.util.Iterator;
032: import java.util.Map;
033: import java.util.Set;
034:
035: /*
036: * This class was lifted from JDK 1.4 (where it's called java.util.AbstractMap)
037: * so that we can use it on 1.3.1.
038: *
039: * @author WS Development Team
040: */
041:
042: /**
043: * This class provides a skeletal implementation of the <tt>Map</tt>
044: * interface, to minimize the effort required to implement this interface. <p>
045: *
046: * To implement an unmodifiable map, the programmer needs only to extend this
047: * class and provide an implementation for the <tt>entrySet</tt> method, which
048: * returns a set-view of the map's mappings. Typically, the returned set
049: * will, in turn, be implemented atop <tt>AbstractSet</tt>. This set should
050: * not support the <tt>add</tt> or <tt>remove</tt> methods, and its iterator
051: * should not support the <tt>remove</tt> method.<p>
052: *
053: * To implement a modifiable map, the programmer must additionally override
054: * this class's <tt>put</tt> method (which otherwise throws an
055: * <tt>UnsupportedOperationException</tt>), and the iterator returned by
056: * <tt>entrySet().iterator()</tt> must additionally implement its
057: * <tt>remove</tt> method.<p>
058: *
059: * The programmer should generally provide a void (no argument) and map
060: * constructor, as per the recommendation in the <tt>Map</tt> interface
061: * specification.<p>
062: *
063: * The documentation for each non-abstract methods in this class describes its
064: * implementation in detail. Each of these methods may be overridden if the
065: * map being implemented admits a more efficient implementation.
066: *
067: * @author Josh Bloch
068: * @version 1.32, 12/03/01
069: * @see Map
070: * @see Collection
071: * @since 1.2
072: */
073:
074: public abstract class MapBase implements Map {
075: /**
076: * Sole constructor. (For invocation by subclass constructors, typically
077: * implicit.)
078: */
079: protected MapBase() {
080: }
081:
082: // Query Operations
083:
084: /**
085: * Returns the number of key-value mappings in this map. If the map
086: * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
087: * <tt>Integer.MAX_VALUE</tt>.<p>
088: *
089: * This implementation returns <tt>entrySet().size()</tt>.
090: *
091: * @return the number of key-value mappings in this map.
092: */
093: public int size() {
094: return entrySet().size();
095: }
096:
097: /**
098: * Returns <tt>true</tt> if this map contains no key-value mappings. <p>
099: *
100: * This implementation returns <tt>size() == 0</tt>.
101: *
102: * @return <tt>true</tt> if this map contains no key-value mappings.
103: */
104: public boolean isEmpty() {
105: return size() == 0;
106: }
107:
108: /**
109: * Returns <tt>true</tt> if this map maps one or more keys to this value.
110: * More formally, returns <tt>true</tt> if and only if this map contains
111: * at least one mapping to a value <tt>v</tt> such that <tt>(value==null ?
112: * v==null : value.equals(v))</tt>. This operation will probably require
113: * time linear in the map size for most implementations of map.<p>
114: *
115: * This implementation iterates over entrySet() searching for an entry
116: * with the specified value. If such an entry is found, <tt>true</tt> is
117: * returned. If the iteration terminates without finding such an entry,
118: * <tt>false</tt> is returned. Note that this implementation requires
119: * linear time in the size of the map.
120: *
121: * @param value value whose presence in this map is to be tested.
122: *
123: * @return <tt>true</tt> if this map maps one or more keys to this value.
124: */
125: public boolean containsValue(Object value) {
126: Iterator i = entrySet().iterator();
127: if (value == null) {
128: while (i.hasNext()) {
129: Entry e = (Entry) i.next();
130: if (e.getValue() == null)
131: return true;
132: }
133: } else {
134: while (i.hasNext()) {
135: Entry e = (Entry) i.next();
136: if (value.equals(e.getValue()))
137: return true;
138: }
139: }
140: return false;
141: }
142:
143: /**
144: * Returns <tt>true</tt> if this map contains a mapping for the specified
145: * key. <p>
146: *
147: * This implementation iterates over <tt>entrySet()</tt> searching for an
148: * entry with the specified key. If such an entry is found, <tt>true</tt>
149: * is returned. If the iteration terminates without finding such an
150: * entry, <tt>false</tt> is returned. Note that this implementation
151: * requires linear time in the size of the map; many implementations will
152: * override this method.
153: *
154: * @param key key whose presence in this map is to be tested.
155: * @return <tt>true</tt> if this map contains a mapping for the specified
156: * key.
157: *
158: * @throws NullPointerException key is <tt>null</tt> and this map does not
159: * not permit <tt>null</tt> keys.
160: */
161: public boolean containsKey(Object key) {
162: Iterator i = entrySet().iterator();
163: if (key == null) {
164: while (i.hasNext()) {
165: Entry e = (Entry) i.next();
166: if (e.getKey() == null)
167: return true;
168: }
169: } else {
170: while (i.hasNext()) {
171: Entry e = (Entry) i.next();
172: if (key.equals(e.getKey()))
173: return true;
174: }
175: }
176: return false;
177: }
178:
179: /**
180: * Returns the value to which this map maps the specified key. Returns
181: * <tt>null</tt> if the map contains no mapping for this key. A return
182: * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
183: * map contains no mapping for the key; it's also possible that the map
184: * explicitly maps the key to <tt>null</tt>. The containsKey operation
185: * may be used to distinguish these two cases. <p>
186: *
187: * This implementation iterates over <tt>entrySet()</tt> searching for an
188: * entry with the specified key. If such an entry is found, the entry's
189: * value is returned. If the iteration terminates without finding such an
190: * entry, <tt>null</tt> is returned. Note that this implementation
191: * requires linear time in the size of the map; many implementations will
192: * override this method.
193: *
194: * @param key key whose associated value is to be returned.
195: * @return the value to which this map maps the specified key.
196: *
197: * @throws NullPointerException if the key is <tt>null</tt> and this map
198: * does not not permit <tt>null</tt> keys.
199: *
200: * @see #containsKey(Object)
201: */
202: public Object get(Object key) {
203: Iterator i = entrySet().iterator();
204: if (key == null) {
205: while (i.hasNext()) {
206: Entry e = (Entry) i.next();
207: if (e.getKey() == null)
208: return e.getValue();
209: }
210: } else {
211: while (i.hasNext()) {
212: Entry e = (Entry) i.next();
213: if (key.equals(e.getKey()))
214: return e.getValue();
215: }
216: }
217: return null;
218: }
219:
220: // Modification Operations
221:
222: /**
223: * Associates the specified value with the specified key in this map
224: * (optional operation). If the map previously contained a mapping for
225: * this key, the old value is replaced.<p>
226: *
227: * This implementation always throws an
228: * <tt>UnsupportedOperationException</tt>.
229: *
230: * @param key key with which the specified value is to be associated.
231: * @param value value to be associated with the specified key.
232: *
233: * @return previous value associated with specified key, or <tt>null</tt>
234: * if there was no mapping for key. (A <tt>null</tt> return can
235: * also indicate that the map previously associated <tt>null</tt>
236: * with the specified key, if the implementation supports
237: * <tt>null</tt> values.)
238: *
239: * @throws UnsupportedOperationException if the <tt>put</tt> operation is
240: * not supported by this map.
241: *
242: * @throws ClassCastException if the class of the specified key or value
243: * prevents it from being stored in this map.
244: *
245: * @throws IllegalArgumentException if some aspect of this key or value *
246: * prevents it from being stored in this map.
247: *
248: * @throws NullPointerException this map does not permit <tt>null</tt>
249: * keys or values, and the specified key or value is
250: * <tt>null</tt>.
251: */
252: public Object put(Object key, Object value) {
253: throw new UnsupportedOperationException();
254: }
255:
256: /**
257: * Removes the mapping for this key from this map if present (optional
258: * operation). <p>
259: *
260: * This implementation iterates over <tt>entrySet()</tt> searching for an
261: * entry with the specified key. If such an entry is found, its value is
262: * obtained with its <tt>getValue</tt> operation, the entry is is removed
263: * from the Collection (and the backing map) with the iterator's
264: * <tt>remove</tt> operation, and the saved value is returned. If the
265: * iteration terminates without finding such an entry, <tt>null</tt> is
266: * returned. Note that this implementation requires linear time in the
267: * size of the map; many implementations will override this method.<p>
268: *
269: * Note that this implementation throws an
270: * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt> iterator
271: * does not support the <tt>remove</tt> method and this map contains a
272: * mapping for the specified key.
273: *
274: * @param key key whose mapping is to be removed from the map.
275: * @return previous value associated with specified key, or <tt>null</tt>
276: * if there was no entry for key. (A <tt>null</tt> return can
277: * also indicate that the map previously associated <tt>null</tt>
278: * with the specified key, if the implementation supports
279: * <tt>null</tt> values.)
280: * @throws UnsupportedOperationException if the <tt>remove</tt> operation
281: * is not supported by this map.
282: */
283: public Object remove(Object key) {
284: Iterator i = entrySet().iterator();
285: Entry correctEntry = null;
286: if (key == null) {
287: while (correctEntry == null && i.hasNext()) {
288: Entry e = (Entry) i.next();
289: if (e.getKey() == null)
290: correctEntry = e;
291: }
292: } else {
293: while (correctEntry == null && i.hasNext()) {
294: Entry e = (Entry) i.next();
295: if (key.equals(e.getKey()))
296: correctEntry = e;
297: }
298: }
299:
300: Object oldValue = null;
301: if (correctEntry != null) {
302: oldValue = correctEntry.getValue();
303: i.remove();
304: }
305: return oldValue;
306: }
307:
308: // Bulk Operations
309:
310: /**
311: * Copies all of the mappings from the specified map to this map
312: * (optional operation). These mappings will replace any mappings that
313: * this map had for any of the keys currently in the specified map.<p>
314: *
315: * This implementation iterates over the specified map's
316: * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>
317: * operation once for each entry returned by the iteration.<p>
318: *
319: * Note that this implementation throws an
320: * <tt>UnsupportedOperationException</tt> if this map does not support
321: * the <tt>put</tt> operation and the specified map is nonempty.
322: *
323: * @param t mappings to be stored in this map.
324: *
325: * @throws UnsupportedOperationException if the <tt>putAll</tt> operation
326: * is not supported by this map.
327: *
328: * @throws ClassCastException if the class of a key or value in the
329: * specified map prevents it from being stored in this map.
330: *
331: * @throws IllegalArgumentException if some aspect of a key or value in
332: * the specified map prevents it from being stored in this map.
333: * @throws NullPointerException the specified map is <tt>null</tt>, or if
334: * this map does not permit <tt>null</tt> keys or values, and the
335: * specified map contains <tt>null</tt> keys or values.
336: */
337: public void putAll(Map t) {
338: Iterator i = t.entrySet().iterator();
339: while (i.hasNext()) {
340: Entry e = (Entry) i.next();
341: put(e.getKey(), e.getValue());
342: }
343: }
344:
345: /**
346: * Removes all mappings from this map (optional operation). <p>
347: *
348: * This implementation calls <tt>entrySet().clear()</tt>.
349: *
350: * Note that this implementation throws an
351: * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
352: * does not support the <tt>clear</tt> operation.
353: *
354: * @throws UnsupportedOperationException clear is not supported
355: * by this map.
356: */
357: public void clear() {
358: entrySet().clear();
359: }
360:
361: // Views
362:
363: /**
364: * Each of these fields are initialized to contain an instance of the
365: * appropriate view the first time this view is requested. The views are
366: * stateless, so there's no reason to create more than one of each.
367: */
368: transient volatile Set keySet = null;
369: transient volatile Collection values = null;
370:
371: /**
372: * Returns a Set view of the keys contained in this map. The Set is
373: * backed by the map, so changes to the map are reflected in the Set,
374: * and vice-versa. (If the map is modified while an iteration over
375: * the Set is in progress, the results of the iteration are undefined.)
376: * The Set supports element removal, which removes the corresponding entry
377: * from the map, via the Iterator.remove, Set.remove, removeAll
378: * retainAll, and clear operations. It does not support the add or
379: * addAll operations.<p>
380: *
381: * This implementation returns a Set that subclasses
382: * AbstractSet. The subclass's iterator method returns a "wrapper
383: * object" over this map's entrySet() iterator. The size method delegates
384: * to this map's size method and the contains method delegates to this
385: * map's containsKey method.<p>
386: *
387: * The Set is created the first time this method is called,
388: * and returned in response to all subsequent calls. No synchronization
389: * is performed, so there is a slight chance that multiple calls to this
390: * method will not all return the same Set.
391: *
392: * @return a Set view of the keys contained in this map.
393: */
394: public Set keySet() {
395: if (keySet == null) {
396: keySet = new AbstractSet() {
397: public Iterator iterator() {
398: return new Iterator() {
399: private Iterator i = entrySet().iterator();
400:
401: public boolean hasNext() {
402: return i.hasNext();
403: }
404:
405: public Object next() {
406: return ((Entry) i.next()).getKey();
407: }
408:
409: public void remove() {
410: i.remove();
411: }
412: };
413: }
414:
415: public int size() {
416: return MapBase.this .size();
417: }
418:
419: public boolean contains(Object k) {
420: return MapBase.this .containsKey(k);
421: }
422: };
423: }
424: return keySet;
425: }
426:
427: /**
428: * Returns a collection view of the values contained in this map. The
429: * collection is backed by the map, so changes to the map are reflected in
430: * the collection, and vice-versa. (If the map is modified while an
431: * iteration over the collection is in progress, the results of the
432: * iteration are undefined.) The collection supports element removal,
433: * which removes the corresponding entry from the map, via the
434: * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
435: * <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt> operations.
436: * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.<p>
437: *
438: * This implementation returns a collection that subclasses abstract
439: * collection. The subclass's iterator method returns a "wrapper object"
440: * over this map's <tt>entrySet()</tt> iterator. The size method
441: * delegates to this map's size method and the contains method delegates
442: * to this map's containsValue method.<p>
443: *
444: * The collection is created the first time this method is called, and
445: * returned in response to all subsequent calls. No synchronization is
446: * performed, so there is a slight chance that multiple calls to this
447: * method will not all return the same Collection.
448: *
449: * @return a collection view of the values contained in this map.
450: */
451: public Collection values() {
452: if (values == null) {
453: values = new AbstractCollection() {
454: public Iterator iterator() {
455: return new Iterator() {
456: private Iterator i = entrySet().iterator();
457:
458: public boolean hasNext() {
459: return i.hasNext();
460: }
461:
462: public Object next() {
463: return ((Entry) i.next()).getValue();
464: }
465:
466: public void remove() {
467: i.remove();
468: }
469: };
470: }
471:
472: public int size() {
473: return MapBase.this .size();
474: }
475:
476: public boolean contains(Object v) {
477: return MapBase.this .containsValue(v);
478: }
479: };
480: }
481: return values;
482: }
483:
484: /**
485: * Returns a set view of the mappings contained in this map. Each element
486: * in this set is a Map.Entry. The set is backed by the map, so changes
487: * to the map are reflected in the set, and vice-versa. (If the map is
488: * modified while an iteration over the set is in progress, the results of
489: * the iteration are undefined.) The set supports element removal, which
490: * removes the corresponding entry from the map, via the
491: * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt>,
492: * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not support
493: * the <tt>add</tt> or <tt>addAll</tt> operations.
494: *
495: * @return a set view of the mappings contained in this map.
496: */
497: public abstract Set entrySet();
498:
499: // Comparison and hashing
500:
501: /**
502: * Compares the specified object with this map for equality. Returns
503: * <tt>true</tt> if the given object is also a map and the two maps
504: * represent the same mappings. More formally, two maps <tt>t1</tt> and
505: * <tt>t2</tt> represent the same mappings if
506: * <tt>t1.keySet().equals(t2.keySet())</tt> and for every key <tt>k</tt>
507: * in <tt>t1.keySet()</tt>, <tt> (t1.get(k)==null ? t2.get(k)==null :
508: * t1.get(k).equals(t2.get(k))) </tt>. This ensures that the
509: * <tt>equals</tt> method works properly across different implementations
510: * of the map interface.<p>
511: *
512: * This implementation first checks if the specified object is this map;
513: * if so it returns <tt>true</tt>. Then, it checks if the specified
514: * object is a map whose size is identical to the size of this set; if
515: * not, it it returns <tt>false</tt>. If so, it iterates over this map's
516: * <tt>entrySet</tt> collection, and checks that the specified map
517: * contains each mapping that this map contains. If the specified map
518: * fails to contain such a mapping, <tt>false</tt> is returned. If the
519: * iteration completes, <tt>true</tt> is returned.
520: *
521: * @param o object to be compared for equality with this map.
522: * @return <tt>true</tt> if the specified object is equal to this map.
523: */
524: public boolean equals(Object o) {
525: if (o == this )
526: return true;
527:
528: if (!(o instanceof Map))
529: return false;
530: Map t = (Map) o;
531: if (t.size() != size())
532: return false;
533:
534: try {
535: Iterator i = entrySet().iterator();
536: while (i.hasNext()) {
537: Entry e = (Entry) i.next();
538: Object key = e.getKey();
539: Object value = e.getValue();
540: if (value == null) {
541: if (!(t.get(key) == null && t.containsKey(key)))
542: return false;
543: } else {
544: if (!value.equals(t.get(key)))
545: return false;
546: }
547: }
548: } catch (ClassCastException unused) {
549: return false;
550: } catch (NullPointerException unused) {
551: return false;
552: }
553:
554: return true;
555: }
556:
557: /**
558: * Returns the hash code value for this map. The hash code of a map is
559: * defined to be the sum of the hash codes of each entry in the map's
560: * <tt>entrySet()</tt> view. This ensures that <tt>t1.equals(t2)</tt>
561: * implies that <tt>t1.hashCode()==t2.hashCode()</tt> for any two maps
562: * <tt>t1</tt> and <tt>t2</tt>, as required by the general contract of
563: * Object.hashCode.<p>
564: *
565: * This implementation iterates over <tt>entrySet()</tt>, calling
566: * <tt>hashCode</tt> on each element (entry) in the Collection, and adding
567: * up the results.
568: *
569: * @return the hash code value for this map.
570: * @see java.util.Map.Entry#hashCode()
571: * @see Object#hashCode()
572: * @see Object#equals(Object)
573: * @see Set#equals(Object)
574: */
575: public int hashCode() {
576: int h = 0;
577: Iterator i = entrySet().iterator();
578: while (i.hasNext())
579: h += i.next().hashCode();
580: return h;
581: }
582:
583: /**
584: * Returns a string representation of this map. The string representation
585: * consists of a list of key-value mappings in the order returned by the
586: * map's <tt>entrySet</tt> view's iterator, enclosed in braces
587: * (<tt>"{}"</tt>). Adjacent mappings are separated by the characters
588: * <tt>", "</tt> (comma and space). Each key-value mapping is rendered as
589: * the key followed by an equals sign (<tt>"="</tt>) followed by the
590: * associated value. Keys and values are converted to strings as by
591: * <tt>String.valueOf(Object)</tt>.<p>
592: *
593: * This implementation creates an empty string buffer, appends a left
594: * brace, and iterates over the map's <tt>entrySet</tt> view, appending
595: * the string representation of each <tt>map.entry</tt> in turn. After
596: * appending each entry except the last, the string <tt>", "</tt> is
597: * appended. Finally a right brace is appended. A string is obtained
598: * from the stringbuffer, and returned.
599: *
600: * @return a String representation of this map.
601: */
602: public String toString() {
603: StringBuffer buf = new StringBuffer();
604: buf.append("{");
605:
606: Iterator i = entrySet().iterator();
607: boolean hasNext = i.hasNext();
608: while (hasNext) {
609: Entry e = (Entry) (i.next());
610: Object key = e.getKey();
611: Object value = e.getValue();
612: buf.append((key == this ? "(this Map)" : key) + "="
613: + (value == this ? "(this Map)" : value));
614:
615: hasNext = i.hasNext();
616: if (hasNext)
617: buf.append(", ");
618: }
619:
620: buf.append("}");
621: return buf.toString();
622: }
623:
624: /**
625: * Returns a shallow copy of this <tt>MapBase</tt> instance: the keys
626: * and values themselves are not cloned.
627: *
628: * @return a shallow copy of this map.
629: */
630: protected Object clone() throws CloneNotSupportedException {
631: MapBase result = (MapBase) super .clone();
632: result.keySet = null;
633: result.values = null;
634: return result;
635: }
636:
637: /**
638: * This should be made public as soon as possible. It greately simplifies
639: * the task of implementing Map.
640: */
641: static class SimpleEntry implements Entry {
642: Object key;
643: Object value;
644:
645: public SimpleEntry(Object key, Object value) {
646: this .key = key;
647: this .value = value;
648: }
649:
650: public SimpleEntry(Map.Entry e) {
651: this .key = e.getKey();
652: this .value = e.getValue();
653: }
654:
655: public Object getKey() {
656: return key;
657: }
658:
659: public Object getValue() {
660: return value;
661: }
662:
663: public Object setValue(Object value) {
664: Object oldValue = this .value;
665: this .value = value;
666: return oldValue;
667: }
668:
669: public boolean equals(Object o) {
670: if (!(o instanceof Map.Entry))
671: return false;
672: Map.Entry e = (Map.Entry) o;
673: return eq(key, e.getKey()) && eq(value, e.getValue());
674: }
675:
676: public int hashCode() {
677: Object v;
678: return ((key == null) ? 0 : key.hashCode())
679: ^ ((value == null) ? 0 : value.hashCode());
680: }
681:
682: public String toString() {
683: return key + "=" + value;
684: }
685:
686: private static boolean eq(Object o1, Object o2) {
687: return (o1 == null ? o2 == null : o1.equals(o2));
688: }
689: }
690: }
|