001: package org.apache.ojb.broker.util;
002:
003: /* Copyright 2004-2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import java.io.IOException;
019: import java.io.ObjectInputStream;
020: import java.io.ObjectOutputStream;
021: import java.lang.ref.Reference;
022: import java.lang.ref.ReferenceQueue;
023: import java.lang.ref.SoftReference;
024: import java.lang.ref.WeakReference;
025: import java.util.AbstractCollection;
026: import java.util.AbstractMap;
027: import java.util.AbstractSet;
028: import java.util.ArrayList;
029: import java.util.Arrays;
030: import java.util.Collection;
031: import java.util.ConcurrentModificationException;
032: import java.util.Iterator;
033: import java.util.Map;
034: import java.util.NoSuchElementException;
035: import java.util.Set;
036:
037: /**
038: * <p>
039: * Modified version of {@link org.apache.commons.collections.ReferenceMap}, using
040: * object identity for key comparison (). This class
041: * simply extended {@link org.apache.commons.collections.ReferenceMap} with an extra field
042: * "useSystemIdentity" which is initialized in constructor and is used every time we
043: * want to compare (or hash) keys or values.
044: * </p>
045: * <p>
046: * Javadoc of ReferenceMap starts here:
047: * <br/>
048: * Hashtable-based {@link java.util.Map} implementation that allows
049: * mappings to be removed by the garbage collector.
050: * </p>
051: * <P>
052: * When you construct a <Code>ReferenceMap</Code>, you can
053: * specify what kind of references are used to store the
054: * map's keys and values. If non-hard references are
055: * used, then the garbage collector can remove mappings
056: * if a key or value becomes unreachable, or if the
057: * JVM's memory is running low. For information on how
058: * the different reference types behave, see
059: * {@link java.lang.ref.Reference}.<P>
060: *
061: * Different types of references can be specified for keys
062: * and values. The keys can be configured to be weak but
063: * the values hard, in which case this class will behave
064: * like a <A HREF="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
065: * <Code>WeakHashMap</Code></A>. However, you
066: * can also specify hard keys and weak values, or any other
067: * combination. The default constructor uses hard keys
068: * and soft values, providing a memory-sensitive cache.<P>
069: *
070: * The algorithms used are basically the same as those
071: * in {@link java.util.HashMap}. In particular, you
072: * can specify a load factor and capacity to suit your
073: * needs. All optional {@link java.util.Map} operations are
074: * supported.<P>
075: *
076: * However, this {@link java.util.Map} implementation does <I>not</I>
077: * allow null elements. Attempting to add a null key or
078: * or a null value to the map will raise a
079: * <Code>NullPointerException</Code>.<P>
080: *
081: * As usual, this implementation is not synchronized. You
082: * can use {@link java.util.Collections#synchronizedMap} to
083: * provide synchronized access to a <Code>ReferenceMap</Code>.
084: *
085: * @deprecated use {@link org.apache.commons.collections.map.ReferenceIdentityMap} instead.
086: * @author Andy Malakov
087: * @version $Id: ReferenceMap.java,v 1.6.2.2 2005/12/21 22:27:47 tomdz Exp $
088: */
089: public class ReferenceMap extends AbstractMap {
090:
091: /**
092: * For serialization.
093: */
094: final private static long serialVersionUID = -3370601314380922368L;
095:
096: /**
097: * Constant indicating that hard references should be used.
098: */
099: final public static int HARD = 0;
100:
101: /**
102: * Constant indiciating that soft references should be used.
103: */
104: final public static int SOFT = 1;
105:
106: /**
107: * Constant indicating that weak references should be used.
108: */
109: final public static int WEAK = 2;
110:
111: // --- serialized instance variables:
112:
113: /**
114: * The reference type for keys. Must be HARD, SOFT, WEAK.
115: * Note: I originally marked this field as final, but then this class
116: * didn't compile under JDK1.2.2.
117: * @serial
118: */
119: private int keyType;
120:
121: /**
122: * The reference type for values. Must be HARD, SOFT, WEAK.
123: * Note: I originally marked this field as final, but then this class
124: * didn't compile under JDK1.2.2.
125: * @serial
126: */
127: private int valueType;
128:
129: /**
130: * The threshold variable is calculated by multiplying
131: * table.length and loadFactor.
132: * Note: I originally marked this field as final, but then this class
133: * didn't compile under JDK1.2.2.
134: * @serial
135: */
136: private float loadFactor;
137:
138: // -- Non-serialized instance variables
139:
140: /**
141: * ReferenceQueue used to eliminate stale mappings.
142: * @see #purge
143: */
144: private transient ReferenceQueue queue = new ReferenceQueue();
145:
146: /**
147: * The hash table. Its length is always a power of two.
148: */
149: private transient Entry[] table;
150:
151: /**
152: * Number of mappings in this map.
153: */
154: private transient int size;
155:
156: /**
157: * When size reaches threshold, the map is resized.
158: * @see #resize
159: */
160: private transient int threshold;
161:
162: /**
163: * Number of times this map has been modified.
164: */
165: private transient volatile int modCount;
166:
167: /**
168: * Cached key set. May be null if key set is never accessed.
169: */
170: private transient Set keySet;
171:
172: /**
173: * Cached entry set. May be null if entry set is never accessed.
174: */
175: private transient Set entrySet;
176:
177: /**
178: * Cached values. May be null if values() is never accessed.
179: */
180: private transient Collection values;
181:
182: /**
183: * Note: I originally marked this field as final, but then this class
184: * didn't compile under JDK1.2.2.
185: */
186: private boolean useSystemIdentity;
187:
188: /**
189: * Constructs a new <Code>ReferenceMap</Code> that will
190: * use hard references to keys and soft references to values.
191: */
192: public ReferenceMap() {
193: this (HARD, SOFT);
194: }
195:
196: /**
197: * Constructs a new <Code>ReferenceMap</Code> that will
198: * use the specified types of references.
199: *
200: * @param keyType the type of reference to use for keys;
201: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
202: * @param valueType the type of reference to use for values;
203: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
204: */
205: public ReferenceMap(int keyType, int valueType) {
206: this (keyType, valueType, 16, 0.75f, false);
207: }
208:
209: /**
210: * Constructs a new <Code>ReferenceMap</Code> with the
211: * specified reference types, load factor and initial
212: * capacity.
213: *
214: * @param keyType the type of reference to use for keys;
215: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
216: * @param valueType the type of reference to use for values;
217: * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
218: * @param capacity the initial capacity for the map
219: * @param loadFactor the load factor for the map
220: * @param useSystemIdentity if true System.identityHashCode() and comparision operator (==) will be used
221: * in place of Object.hashCode and Object.equals (Please read {@link java.util.WeakHashMap}
222: * java doc for more information).
223: */
224: public ReferenceMap(int keyType, int valueType, int capacity,
225: float loadFactor, boolean useSystemIdentity) {
226: super ();
227:
228: verify("keyType", keyType);
229: verify("valueType", valueType);
230:
231: this .useSystemIdentity = useSystemIdentity;
232:
233: if (capacity <= 0) {
234: throw new IllegalArgumentException(
235: "capacity must be positive");
236: }
237: if ((loadFactor <= 0.0f) || (loadFactor >= 1.0f)) {
238: throw new IllegalArgumentException(
239: "Load factor must be greater than 0 and less than 1.");
240: }
241:
242: this .keyType = keyType;
243: this .valueType = valueType;
244:
245: int v = 1;
246: while (v < capacity)
247: v *= 2;
248:
249: this .table = new Entry[v];
250: this .loadFactor = loadFactor;
251: this .threshold = (int) (v * loadFactor);
252: }
253:
254: // used by constructor
255: private static void verify(String name, int type) {
256: if ((type < HARD) || (type > WEAK)) {
257: throw new IllegalArgumentException(name
258: + " must be HARD, SOFT, WEAK.");
259: }
260: }
261:
262: /**
263: * Writes this object to the given output stream.
264: *
265: * @param out the output stream to write to
266: * @throws java.io.IOException if the stream raises it
267: */
268: private void writeObject(ObjectOutputStream out) throws IOException {
269: out.defaultWriteObject();
270: out.writeInt(table.length);
271:
272: // Have to use null-terminated list because size might shrink
273: // during iteration
274:
275: for (Iterator iter = entrySet().iterator(); iter.hasNext();) {
276: Map.Entry entry = (Map.Entry) iter.next();
277: out.writeObject(entry.getKey());
278: out.writeObject(entry.getValue());
279: }
280: out.writeObject(null);
281: }
282:
283: /**
284: * Reads the contents of this object from the given input stream.
285: *
286: * @param inp the input stream to read from
287: * @throws java.io.IOException if the stream raises it
288: * @throws java.lang.ClassNotFoundException if the stream raises it
289: */
290: private void readObject(ObjectInputStream inp) throws IOException,
291: ClassNotFoundException {
292: inp.defaultReadObject();
293: table = new Entry[inp.readInt()];
294: threshold = (int) (table.length * loadFactor);
295: queue = new ReferenceQueue();
296: Object key = inp.readObject();
297: while (key != null) {
298: Object value = inp.readObject();
299: put(key, value);
300: key = inp.readObject();
301: }
302: }
303:
304: /**
305: * Constructs a reference of the given type to the given
306: * referent. The reference is registered with the queue
307: * for later purging.
308: *
309: * @param type HARD, SOFT or WEAK
310: * @param referent the object to refer to
311: * @param hash the hash code of the <I>key</I> of the mapping;
312: * this number might be different from referent.hashCode() if
313: * the referent represents a value and not a key
314: */
315: private Object toReference(int type, Object referent, int hash) {
316: switch (type) {
317: case HARD:
318: return referent;
319: case SOFT:
320: return new SoftRef(hash, referent, queue);
321: case WEAK:
322: return new WeakRef(hash, referent, queue);
323: default:
324: throw new Error();
325: }
326: }
327:
328: /**
329: * Returns the entry associated with the given key.
330: *
331: * @param key the key of the entry to look up
332: * @return the entry associated with that key, or null
333: * if the key is not in this map
334: */
335: private Entry getEntry(Object key) {
336: if (key == null)
337: return null;
338: int hash = hashCode(key);
339: int index = indexFor(hash);
340: for (Entry entry = table[index]; entry != null; entry = entry.next) {
341: if ((entry.hash == hash) && equals(key, entry.getKey())) {
342: return entry;
343: }
344: }
345: return null;
346: }
347:
348: /**
349: * Converts the given hash code into an index into the
350: * hash table.
351: */
352: private int indexFor(int hash) {
353: // mix the bits to avoid bucket collisions...
354: hash += ~(hash << 15);
355: hash ^= (hash >>> 10);
356: hash += (hash << 3);
357: hash ^= (hash >>> 6);
358: hash += ~(hash << 11);
359: hash ^= (hash >>> 16);
360: return hash & (table.length - 1);
361: }
362:
363: /**
364: * Resizes this hash table by doubling its capacity.
365: * This is an expensive operation, as entries must
366: * be copied from the old smaller table to the new
367: * bigger table.
368: */
369: private void resize() {
370: Entry[] old = table;
371: table = new Entry[old.length * 2];
372:
373: for (int i = 0; i < old.length; i++) {
374: Entry next = old[i];
375: while (next != null) {
376: Entry entry = next;
377: next = next.next;
378: int index = indexFor(entry.hash);
379: entry.next = table[index];
380: table[index] = entry;
381: }
382: old[i] = null;
383: }
384: threshold = (int) (table.length * loadFactor);
385: }
386:
387: /**
388: * Purges stale mappings from this map.<P>
389: *
390: * Ordinarily, stale mappings are only removed during
391: * a write operation; typically a write operation will
392: * occur often enough that you'll never need to manually
393: * invoke this method.<P>
394: *
395: * Note that this method is not synchronized! Special
396: * care must be taken if, for instance, you want stale
397: * mappings to be removed on a periodic basis by some
398: * background thread.
399: */
400: private void purge() {
401: Reference ref = queue.poll();
402: while (ref != null) {
403: purge(ref);
404: ref = queue.poll();
405: }
406: }
407:
408: private void purge(Reference ref) {
409: // The hashCode of the reference is the hashCode of the
410: // mapping key, even if the reference refers to the
411: // mapping value...
412: int hash = ref.hashCode(); // note: hashCode() is referined
413: int index = indexFor(hash);
414: Entry previous = null;
415: Entry entry = table[index];
416: while (entry != null) {
417: if (entry.purge(ref)) {
418: if (previous == null)
419: table[index] = entry.next;
420: else
421: previous.next = entry.next;
422: this .size--;
423: return;
424: }
425: previous = entry;
426: entry = entry.next;
427: }
428:
429: }
430:
431: /**
432: * Returns the size of this map.
433: *
434: * @return the size of this map
435: */
436: public int size() {
437: purge();
438: return size;
439: }
440:
441: /**
442: * Returns <Code>true</Code> if this map is empty.
443: *
444: * @return <Code>true</Code> if this map is empty
445: */
446: public boolean isEmpty() {
447: purge();
448: return size == 0;
449: }
450:
451: /**
452: * Returns <Code>true</Code> if this map contains the given key.
453: *
454: * @return true if the given key is in this map
455: */
456: public boolean containsKey(Object key) {
457: purge();
458: Entry entry = getEntry(key);
459: if (entry == null)
460: return false;
461: return entry.getValue() != null;
462: }
463:
464: /**
465: * Returns the value associated with the given key, if any.
466: *
467: * @return the value associated with the given key, or <Code>null</Code>
468: * if the key maps to no value
469: */
470: public Object get(Object key) {
471: purge();
472: Entry entry = getEntry(key);
473: if (entry == null)
474: return null;
475: return entry.getValue();
476: }
477:
478: /**
479: * Associates the given key with the given value.<P>
480: * Neither the key nor the value may be null.
481: *
482: * @param key the key of the mapping
483: * @param value the value of the mapping
484: * @return the last value associated with that key, or
485: * null if no value was associated with the key
486: * @throws java.lang.NullPointerException if either the key or value
487: * is null
488: */
489: public Object put(Object key, Object value) {
490: if (key == null)
491: throw new NullPointerException("null keys not allowed");
492: if (value == null)
493: throw new NullPointerException("null values not allowed");
494:
495: purge();
496: if (size + 1 > threshold)
497: resize();
498:
499: int hash = hashCode(key);
500: int index = indexFor(hash);
501: Entry entry = table[index];
502: while (entry != null) {
503: if ((hash == entry.hash) && equals(key, entry.getKey())) {
504: Object result = entry.getValue();
505: entry.setValue(value);
506: return result;
507: }
508: entry = entry.next;
509: }
510: this .size++;
511: modCount++;
512: key = toReference(keyType, key, hash);
513: value = toReference(valueType, value, hash);
514: table[index] = new Entry(key, hash, value, table[index]);
515: return null;
516: }
517:
518: /**
519: * Removes the key and its associated value from this map.
520: *
521: * @param key the key to remove
522: * @return the value associated with that key, or null if
523: * the key was not in the map
524: */
525: public Object remove(Object key) {
526: if (key == null)
527: return null;
528: purge();
529: int hash = hashCode(key);
530: int index = indexFor(hash);
531: Entry previous = null;
532: Entry entry = table[index];
533: while (entry != null) {
534: if ((hash == entry.hash) && equals(key, entry.getKey())) {
535: if (previous == null)
536: table[index] = entry.next;
537: else
538: previous.next = entry.next;
539: this .size--;
540: modCount++;
541: return entry.getValue();
542: }
543: previous = entry;
544: entry = entry.next;
545: }
546: return null;
547: }
548:
549: /**
550: * Clears this map.
551: */
552: public void clear() {
553: Arrays.fill(table, null);
554: size = 0;
555: while (queue.poll() != null) {
556: // drain the queue
557: }
558: }
559:
560: /**
561: * Returns a set view of this map's entries.
562: *
563: * @return a set view of this map's entries
564: */
565: public Set entrySet() {
566: if (entrySet != null)
567: return entrySet;
568: entrySet = new AbstractSet() {
569: public int size() {
570: return ReferenceMap.this .size();
571: }
572:
573: public void clear() {
574: ReferenceMap.this .clear();
575: }
576:
577: public boolean contains(Object o) {
578: if (o == null)
579: return false;
580: if (!(o instanceof Map.Entry))
581: return false;
582: Map.Entry e = (Map.Entry) o;
583: Entry e2 = getEntry(e.getKey());
584: return (e2 != null) && e.equals(e2);
585: }
586:
587: public boolean remove(Object o) {
588: boolean r = contains(o);
589: if (r) {
590: Map.Entry e = (Map.Entry) o;
591: ReferenceMap.this .remove(e.getKey());
592: }
593: return r;
594: }
595:
596: public Iterator iterator() {
597: return new EntryIterator();
598: }
599:
600: public Object[] toArray() {
601: return toArray(new Object[0]);
602: }
603:
604: public Object[] toArray(Object[] arr) {
605: ArrayList list = new ArrayList();
606: Iterator iterator = iterator();
607: while (iterator.hasNext()) {
608: Entry e = (Entry) iterator.next();
609: list.add(new DefaultMapEntry(e.getKey(), e
610: .getValue()));
611: }
612: return list.toArray(arr);
613: }
614: };
615: return entrySet;
616: }
617:
618: /**
619: * Returns a set view of this map's keys.
620: *
621: * @return a set view of this map's keys
622: */
623: public Set keySet() {
624: if (keySet != null)
625: return keySet;
626: keySet = new AbstractSet() {
627: public int size() {
628: return size;
629: }
630:
631: public Iterator iterator() {
632: return new KeyIterator();
633: }
634:
635: public boolean contains(Object o) {
636: return containsKey(o);
637: }
638:
639: public boolean remove(Object o) {
640: Object r = ReferenceMap.this .remove(o);
641: return r != null;
642: }
643:
644: public void clear() {
645: ReferenceMap.this .clear();
646: }
647:
648: };
649: return keySet;
650: }
651:
652: /**
653: * Returns a collection view of this map's values.
654: *
655: * @return a collection view of this map's values.
656: */
657: public Collection values() {
658: if (values != null)
659: return values;
660: values = new AbstractCollection() {
661: public int size() {
662: return size;
663: }
664:
665: public void clear() {
666: ReferenceMap.this .clear();
667: }
668:
669: public Iterator iterator() {
670: return new ValueIterator();
671: }
672: };
673: return values;
674: }
675:
676: // If getKey() or getValue() returns null, it means
677: // the mapping is stale and should be removed.
678: private class Entry implements Map.Entry {
679:
680: Object key;
681: Object value;
682: int hash;
683: Entry next;
684:
685: public Entry(Object key, int hash, Object value, Entry next) {
686: this .key = key;
687: this .hash = hash;
688: this .value = value;
689: this .next = next;
690: }
691:
692: public Object getKey() {
693: return (keyType > HARD) ? ((Reference) key).get() : key;
694: }
695:
696: public Object getValue() {
697: return (valueType > HARD) ? ((Reference) value).get()
698: : value;
699: }
700:
701: public Object setValue(Object object) {
702: Object old = getValue();
703: if (valueType > HARD)
704: ((Reference) value).clear();
705: value = toReference(valueType, object, hash);
706: return old;
707: }
708:
709: public boolean equals(Object o) {
710: if (o == null)
711: return false;
712: if (o == this )
713: return true;
714: if (!(o instanceof Map.Entry))
715: return false;
716:
717: Map.Entry entry = (Map.Entry) o;
718: Object _key = entry.getKey();
719: Object _value = entry.getValue();
720: if ((_key == null) || (_value == null))
721: return false;
722: return ReferenceMap.this .equals(_key, getKey())
723: && ReferenceMap.this .equals(_value, getValue());
724: }
725:
726: public int hashCode() {
727: Object v = getValue();
728: return hash ^ ((v == null) ? 0 : v.hashCode());
729: }
730:
731: public String toString() {
732: return getKey() + "=" + getValue();
733: }
734:
735: boolean purge(Reference ref) {
736: boolean r = (keyType > HARD) && (key == ref);
737: r = r || ((valueType > HARD) && (value == ref));
738: if (r) {
739: if (keyType > HARD)
740: ((Reference) key).clear();
741: if (valueType > HARD)
742: ((Reference) value).clear();
743: }
744: return r;
745: }
746: }
747:
748: private class EntryIterator implements Iterator {
749: // These fields keep track of where we are in the table.
750: int index;
751: Entry entry;
752: Entry previous;
753:
754: // These Object fields provide hard references to the
755: // current and next entry; this assures that if hasNext()
756: // returns true, next() will actually return a valid element.
757: Object nextKey, nextValue;
758: Object currentKey, currentValue;
759:
760: int expectedModCount;
761:
762: public EntryIterator() {
763: index = (size() != 0 ? table.length : 0);
764: // have to do this here! size() invocation above
765: // may have altered the modCount.
766: expectedModCount = modCount;
767: }
768:
769: public boolean hasNext() {
770: checkMod();
771: while (nextNull()) {
772: Entry e = entry;
773: int i = index;
774: while ((e == null) && (i > 0)) {
775: i--;
776: e = table[i];
777: }
778: entry = e;
779: index = i;
780: if (e == null) {
781: currentKey = null;
782: currentValue = null;
783: return false;
784: }
785: nextKey = e.getKey();
786: nextValue = e.getValue();
787: if (nextNull())
788: entry = entry.next;
789: }
790: return true;
791: }
792:
793: private void checkMod() {
794: if (modCount != expectedModCount) {
795: throw new ConcurrentModificationException();
796: }
797: }
798:
799: private boolean nextNull() {
800: return (nextKey == null) || (nextValue == null);
801: }
802:
803: protected Entry nextEntry() {
804: checkMod();
805: if (nextNull() && !hasNext())
806: throw new NoSuchElementException();
807: previous = entry;
808: entry = entry.next;
809: currentKey = nextKey;
810: currentValue = nextValue;
811: nextKey = null;
812: nextValue = null;
813: return previous;
814: }
815:
816: public Object next() {
817: return nextEntry();
818: }
819:
820: public void remove() {
821: checkMod();
822: if (previous == null)
823: throw new IllegalStateException();
824: ReferenceMap.this .remove(currentKey);
825: previous = null;
826: currentKey = null;
827: currentValue = null;
828: expectedModCount = modCount;
829: }
830:
831: }
832:
833: private class ValueIterator extends EntryIterator {
834: public Object next() {
835: return nextEntry().getValue();
836: }
837: }
838:
839: private class KeyIterator extends EntryIterator {
840: public Object next() {
841: return nextEntry().getKey();
842: }
843: }
844:
845: // These two classes store the hashCode of the key of
846: // of the mapping, so that after they're dequeued a quick
847: // lookup of the bucket in the table can occur.
848:
849: private static class SoftRef extends SoftReference {
850: private int hash;
851:
852: public SoftRef(int hash, Object r, ReferenceQueue q) {
853: super (r, q);
854: this .hash = hash;
855: }
856:
857: public int hashCode() {
858: return hash;
859: }
860: }
861:
862: private static class WeakRef extends WeakReference {
863: private int hash;
864:
865: public WeakRef(int hash, Object r, ReferenceQueue q) {
866: super (r, q);
867: this .hash = hash;
868: }
869:
870: public int hashCode() {
871: return hash;
872: }
873: }
874:
875: private int hashCode(Object obj) {
876: return useSystemIdentity ? System.identityHashCode(obj) : obj
877: .hashCode(); // null keys|values are not supported
878: }
879:
880: private boolean equals(Object obj1, Object obj2) {
881: return (obj1 == obj2)
882: || (!useSystemIdentity && obj1.equals(obj2)); // null keys|values are not supported
883: }
884:
885: //***********************************************
886: // inner class
887: //***********************************************
888: /** A default implementation of {@link java.util.Map.Entry}
889: *
890: * @since 1.0
891: * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
892: * @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
893: */
894:
895: public class DefaultMapEntry implements Map.Entry {
896:
897: private Object key;
898: private Object value;
899:
900: /**
901: * Constructs a new <Code>DefaultMapEntry</Code> with a null key
902: * and null value.
903: */
904: public DefaultMapEntry() {
905: }
906:
907: /**
908: * Constructs a new <Code>DefaultMapEntry</Code> with the given
909: * key and given value.
910: *
911: * @param key the key for the entry, may be null
912: * @param value the value for the entyr, may be null
913: */
914: public DefaultMapEntry(Object key, Object value) {
915: this .key = key;
916: this .value = value;
917: }
918:
919: /**
920: * Implemented per API documentation of
921: * {@link java.util.Map.Entry#equals(java.lang.Object)}
922: **/
923: public boolean equals(Object o) {
924: if (o == null)
925: return false;
926: if (o == this )
927: return true;
928:
929: if (!(o instanceof Map.Entry))
930: return false;
931: Map.Entry e2 = (Map.Entry) o;
932: return ((getKey() == null ? e2.getKey() == null : getKey()
933: .equals(e2.getKey())) && (getValue() == null ? e2
934: .getValue() == null : getValue().equals(
935: e2.getValue())));
936: }
937:
938: /**
939: * Implemented per API documentation of
940: * {@link java.util.Map.Entry#hashCode()}
941: **/
942: public int hashCode() {
943: return ((getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0
944: : getValue().hashCode()));
945: }
946:
947: // Map.Entry interface
948: //-------------------------------------------------------------------------
949:
950: /**
951: * Returns the key.
952: *
953: * @return the key
954: */
955: public Object getKey() {
956: return key;
957: }
958:
959: /**
960: * Returns the value.
961: *
962: * @return the value
963: */
964: public Object getValue() {
965: return value;
966: }
967:
968: // Properties
969: //-------------------------------------------------------------------------
970:
971: /**
972: * Sets the key. This method does not modify any map.
973: *
974: * @param key the new key
975: */
976: public void setKey(Object key) {
977: this .key = key;
978: }
979:
980: /** Note that this method only sets the local reference inside this object and
981: * does not modify the original Map.
982: *
983: * @return the old value of the value
984: * @param value the new value
985: */
986: public Object setValue(Object value) {
987: Object answer = this.value;
988: this.value = value;
989: return answer;
990: }
991:
992: }
993: }
|