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.1 2007-08-24 22:17:36 ewestfal Exp $
088     */
089    public class ReferenceMap extends AbstractMap
090    {
091    
092        /**
093         *  For serialization.
094         */
095        final private static long serialVersionUID = -3370601314380922368L;
096    
097    
098        /**
099         *  Constant indicating that hard references should be used.
100         */
101        final public static int HARD = 0;
102    
103    
104        /**
105         *  Constant indiciating that soft references should be used.
106         */
107        final public static int SOFT = 1;
108    
109    
110        /**
111         *  Constant indicating that weak references should be used.
112         */
113        final public static int WEAK = 2;
114    
115    
116        // --- serialized instance variables:
117    
118    
119        /**
120         *  The reference type for keys.  Must be HARD, SOFT, WEAK.
121         *  Note: I originally marked this field as final, but then this class
122         *   didn't compile under JDK1.2.2.
123         *  @serial
124         */
125        private int keyType;
126    
127    
128        /**
129         *  The reference type for values.  Must be HARD, SOFT, WEAK.
130         *  Note: I originally marked this field as final, but then this class
131         *   didn't compile under JDK1.2.2.
132         *  @serial
133         */
134        private int valueType;
135    
136    
137        /**
138         *  The threshold variable is calculated by multiplying
139         *  table.length and loadFactor.
140         *  Note: I originally marked this field as final, but then this class
141         *   didn't compile under JDK1.2.2.
142         *  @serial
143         */
144        private float loadFactor;
145    
146    
147        // -- Non-serialized instance variables
148    
149        /**
150         *  ReferenceQueue used to eliminate stale mappings.
151         *  @see #purge
152         */
153        private transient ReferenceQueue queue = new ReferenceQueue();
154    
155    
156        /**
157         *  The hash table.  Its length is always a power of two.
158         */
159        private transient Entry[] table;
160    
161    
162        /**
163         *  Number of mappings in this map.
164         */
165        private transient int size;
166    
167    
168        /**
169         *  When size reaches threshold, the map is resized.
170         *  @see #resize
171         */
172        private transient int threshold;
173    
174    
175        /**
176         *  Number of times this map has been modified.
177         */
178        private transient volatile int modCount;
179    
180    
181        /**
182         *  Cached key set.  May be null if key set is never accessed.
183         */
184        private transient Set keySet;
185    
186    
187        /**
188         *  Cached entry set.  May be null if entry set is never accessed.
189         */
190        private transient Set entrySet;
191    
192    
193        /**
194         *  Cached values.  May be null if values() is never accessed.
195         */
196        private transient Collection values;
197    
198            /**
199              *  Note: I originally marked this field as final, but then this class
200              *   didn't compile under JDK1.2.2.
201              */
202        private boolean useSystemIdentity;
203    
204        /**
205         *  Constructs a new <Code>ReferenceMap</Code> that will
206         *  use hard references to keys and soft references to values.
207         */
208        public ReferenceMap()
209        {
210            this(HARD, SOFT);
211        }
212    
213    
214        /**
215         *  Constructs a new <Code>ReferenceMap</Code> that will
216         *  use the specified types of references.
217         *
218         *  @param keyType  the type of reference to use for keys;
219         *   must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
220         *  @param valueType  the type of reference to use for values;
221         *   must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
222         */
223        public ReferenceMap(int keyType, int valueType)
224        {
225            this(keyType, valueType, 16, 0.75f, false);
226        }
227    
228    
229        /**
230         *  Constructs a new <Code>ReferenceMap</Code> with the
231         *  specified reference types, load factor and initial
232         *  capacity.
233         *
234         *  @param keyType  the type of reference to use for keys;
235         *   must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
236         *  @param valueType  the type of reference to use for values;
237         *   must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
238         *  @param capacity  the initial capacity for the map
239         *  @param loadFactor  the load factor for the map
240         *  @param useSystemIdentity if true System.identityHashCode() and comparision operator (==) will be used
241         *         in place of Object.hashCode and Object.equals (Please read {@link java.util.WeakHashMap}
242         *         java doc for more information).
243         */
244        public ReferenceMap(int keyType, int valueType, int capacity, float loadFactor, boolean useSystemIdentity)
245        {
246            super();
247    
248            verify("keyType", keyType);
249            verify("valueType", valueType);
250    
251            this.useSystemIdentity = useSystemIdentity;
252    
253            if (capacity <= 0)
254            {
255                throw new IllegalArgumentException("capacity must be positive");
256            }
257            if ((loadFactor <= 0.0f) || (loadFactor >= 1.0f))
258            {
259                throw new IllegalArgumentException("Load factor must be greater than 0 and less than 1.");
260            }
261    
262            this.keyType = keyType;
263            this.valueType = valueType;
264    
265            int v = 1;
266            while (v < capacity) v *= 2;
267    
268            this.table = new Entry[v];
269            this.loadFactor = loadFactor;
270            this.threshold = (int) (v * loadFactor);
271        }
272    
273    
274        // used by constructor
275        private static void verify(String name, int type)
276        {
277            if ((type < HARD) || (type > WEAK))
278            {
279                throw new IllegalArgumentException(name +
280                        " must be HARD, SOFT, WEAK.");
281            }
282        }
283    
284    
285        /**
286         *  Writes this object to the given output stream.
287         *
288         *  @param out  the output stream to write to
289         *  @throws java.io.IOException  if the stream raises it
290         */
291        private void writeObject(ObjectOutputStream out) throws IOException
292        {
293            out.defaultWriteObject();
294            out.writeInt(table.length);
295    
296            // Have to use null-terminated list because size might shrink
297            // during iteration
298    
299            for (Iterator iter = entrySet().iterator(); iter.hasNext();)
300            {
301                Map.Entry entry = (Map.Entry) iter.next();
302                out.writeObject(entry.getKey());
303                out.writeObject(entry.getValue());
304            }
305            out.writeObject(null);
306        }
307    
308    
309        /**
310         *  Reads the contents of this object from the given input stream.
311         *
312         *  @param inp  the input stream to read from
313         *  @throws java.io.IOException  if the stream raises it
314         *  @throws java.lang.ClassNotFoundException  if the stream raises it
315         */
316        private void readObject(ObjectInputStream inp) throws IOException, ClassNotFoundException
317        {
318            inp.defaultReadObject();
319            table = new Entry[inp.readInt()];
320            threshold = (int) (table.length * loadFactor);
321            queue = new ReferenceQueue();
322            Object key = inp.readObject();
323            while (key != null)
324            {
325                Object value = inp.readObject();
326                put(key, value);
327                key = inp.readObject();
328            }
329        }
330    
331    
332        /**
333         *  Constructs a reference of the given type to the given
334         *  referent.  The reference is registered with the queue
335         *  for later purging.
336         *
337         *  @param type  HARD, SOFT or WEAK
338         *  @param referent  the object to refer to
339         *  @param hash  the hash code of the <I>key</I> of the mapping;
340         *    this number might be different from referent.hashCode() if
341         *    the referent represents a value and not a key
342         */
343        private Object toReference(int type, Object referent, int hash)
344        {
345            switch (type)
346            {
347                case HARD:
348                    return referent;
349                case SOFT:
350                    return new SoftRef(hash, referent, queue);
351                case WEAK:
352                    return new WeakRef(hash, referent, queue);
353                default:
354                    throw new Error();
355            }
356        }
357    
358        /**
359         *  Returns the entry associated with the given key.
360         *
361         *  @param key  the key of the entry to look up
362         *  @return  the entry associated with that key, or null
363         *    if the key is not in this map
364         */
365        private Entry getEntry(Object key)
366        {
367            if (key == null) return null;
368            int hash = hashCode(key);
369            int index = indexFor(hash);
370            for (Entry entry = table[index]; entry != null; entry = entry.next)
371            {
372                if ((entry.hash == hash) && equals(key, entry.getKey()))
373                {
374                    return entry;
375                }
376            }
377            return null;
378        }
379    
380    
381        /**
382         *  Converts the given hash code into an index into the
383         *  hash table.
384         */
385        private int indexFor(int hash)
386        {
387            // mix the bits to avoid bucket collisions...
388            hash += ~(hash << 15);
389            hash ^= (hash >>> 10);
390            hash += (hash << 3);
391            hash ^= (hash >>> 6);
392            hash += ~(hash << 11);
393            hash ^= (hash >>> 16);
394            return hash & (table.length - 1);
395        }
396    
397    
398        /**
399         *  Resizes this hash table by doubling its capacity.
400         *  This is an expensive operation, as entries must
401         *  be copied from the old smaller table to the new
402         *  bigger table.
403         */
404        private void resize()
405        {
406            Entry[] old = table;
407            table = new Entry[old.length * 2];
408    
409            for (int i = 0; i < old.length; i++)
410            {
411                Entry next = old[i];
412                while (next != null)
413                {
414                    Entry entry = next;
415                    next = next.next;
416                    int index = indexFor(entry.hash);
417                    entry.next = table[index];
418                    table[index] = entry;
419                }
420                old[i] = null;
421            }
422            threshold = (int) (table.length * loadFactor);
423        }
424    
425    
426        /**
427         *  Purges stale mappings from this map.<P>
428         *
429         *  Ordinarily, stale mappings are only removed during
430         *  a write operation; typically a write operation will
431         *  occur often enough that you'll never need to manually
432         *  invoke this method.<P>
433         *
434         *  Note that this method is not synchronized!  Special
435         *  care must be taken if, for instance, you want stale
436         *  mappings to be removed on a periodic basis by some
437         *  background thread.
438         */
439        private void purge()
440        {
441            Reference ref = queue.poll();
442            while (ref != null)
443            {
444                purge(ref);
445                ref = queue.poll();
446            }
447        }
448    
449    
450        private void purge(Reference ref)
451        {
452            // The hashCode of the reference is the hashCode of the
453            // mapping key, even if the reference refers to the
454            // mapping value...
455            int hash = ref.hashCode(); // note: hashCode() is referined
456            int index = indexFor(hash);
457            Entry previous = null;
458            Entry entry = table[index];
459            while (entry != null)
460            {
461                if (entry.purge(ref))
462                {
463                    if (previous == null)
464                        table[index] = entry.next;
465                    else
466                        previous.next = entry.next;
467                    this.size--;
468                    return;
469                }
470                previous = entry;
471                entry = entry.next;
472            }
473    
474        }
475    
476    
477        /**
478         *  Returns the size of this map.
479         *
480         *  @return  the size of this map
481         */
482        public int size()
483        {
484            purge();
485            return size;
486        }
487    
488    
489        /**
490         *  Returns <Code>true</Code> if this map is empty.
491         *
492         *  @return <Code>true</Code> if this map is empty
493         */
494        public boolean isEmpty()
495        {
496            purge();
497            return size == 0;
498        }
499    
500    
501        /**
502         *  Returns <Code>true</Code> if this map contains the given key.
503         *
504         *  @return true if the given key is in this map
505         */
506        public boolean containsKey(Object key)
507        {
508            purge();
509            Entry entry = getEntry(key);
510            if (entry == null) return false;
511            return entry.getValue() != null;
512        }
513    
514    
515        /**
516         *  Returns the value associated with the given key, if any.
517         *
518         *  @return the value associated with the given key, or <Code>null</Code>
519         *   if the key maps to no value
520         */
521        public Object get(Object key)
522        {
523            purge();
524            Entry entry = getEntry(key);
525            if (entry == null) return null;
526            return entry.getValue();
527        }
528    
529    
530        /**
531         *  Associates the given key with the given value.<P>
532         *  Neither the key nor the value may be null.
533         *
534         *  @param key  the key of the mapping
535         *  @param value  the value of the mapping
536         *  @return  the last value associated with that key, or
537         *   null if no value was associated with the key
538         *  @throws java.lang.NullPointerException if either the key or value
539         *   is null
540         */
541        public Object put(Object key, Object value)
542        {
543            if (key == null) throw new NullPointerException("null keys not allowed");
544            if (value == null) throw new NullPointerException("null values not allowed");
545    
546            purge();
547            if (size + 1 > threshold) resize();
548    
549            int hash = hashCode(key);
550            int index = indexFor(hash);
551            Entry entry = table[index];
552            while (entry != null)
553            {
554                if ((hash == entry.hash) && equals(key, entry.getKey()))
555                {
556                    Object result = entry.getValue();
557                    entry.setValue(value);
558                    return result;
559                }
560                entry = entry.next;
561            }
562            this.size++;
563            modCount++;
564            key = toReference(keyType, key, hash);
565            value = toReference(valueType, value, hash);
566            table[index] = new Entry(key, hash, value, table[index]);
567            return null;
568        }
569    
570    
571        /**
572         *  Removes the key and its associated value from this map.
573         *
574         *  @param key  the key to remove
575         *  @return the value associated with that key, or null if
576         *   the key was not in the map
577         */
578        public Object remove(Object key)
579        {
580            if (key == null) return null;
581            purge();
582            int hash = hashCode(key);
583            int index = indexFor(hash);
584            Entry previous = null;
585            Entry entry = table[index];
586            while (entry != null)
587            {
588                if ((hash == entry.hash) && equals(key, entry.getKey()))
589                {
590                    if (previous == null)
591                        table[index] = entry.next;
592                    else
593                        previous.next = entry.next;
594                    this.size--;
595                    modCount++;
596                    return entry.getValue();
597                }
598                previous = entry;
599                entry = entry.next;
600            }
601            return null;
602        }
603    
604    
605        /**
606         *  Clears this map.
607         */
608        public void clear()
609        {
610            Arrays.fill(table, null);
611            size = 0;
612            while (queue.poll() != null)
613            {
614                // drain the queue
615            }
616        }
617    
618    
619        /**
620         *  Returns a set view of this map's entries.
621         *
622         *  @return a set view of this map's entries
623         */
624        public Set entrySet()
625        {
626            if (entrySet != null) return entrySet;
627            entrySet = new AbstractSet()
628            {
629                public int size()
630                {
631                    return ReferenceMap.this.size();
632                }
633    
634    
635                public void clear()
636                {
637                    ReferenceMap.this.clear();
638                }
639    
640    
641                public boolean contains(Object o)
642                {
643                    if (o == null) return false;
644                    if (!(o instanceof Map.Entry)) return false;
645                    Map.Entry e = (Map.Entry) o;
646                    Entry e2 = getEntry(e.getKey());
647                    return (e2 != null) && e.equals(e2);
648                }
649    
650    
651                public boolean remove(Object o)
652                {
653                    boolean r = contains(o);
654                    if (r)
655                    {
656                        Map.Entry e = (Map.Entry) o;
657                        ReferenceMap.this.remove(e.getKey());
658                    }
659                    return r;
660                }
661    
662    
663                public Iterator iterator()
664                {
665                    return new EntryIterator();
666                }
667    
668                public Object[] toArray()
669                {
670                    return toArray(new Object[0]);
671                }
672    
673    
674                public Object[] toArray(Object[] arr)
675                {
676                    ArrayList list = new ArrayList();
677                    Iterator iterator = iterator();
678                    while (iterator.hasNext())
679                    {
680                        Entry e = (Entry) iterator.next();
681                        list.add(new DefaultMapEntry(e.getKey(), e.getValue()));
682                    }
683                    return list.toArray(arr);
684                }
685            };
686            return entrySet;
687        }
688    
689    
690        /**
691         *  Returns a set view of this map's keys.
692         *
693         *  @return a set view of this map's keys
694         */
695        public Set keySet()
696        {
697            if (keySet != null) return keySet;
698            keySet = new AbstractSet()
699            {
700                public int size()
701                {
702                    return size;
703                }
704    
705                public Iterator iterator()
706                {
707                    return new KeyIterator();
708                }
709    
710                public boolean contains(Object o)
711                {
712                    return containsKey(o);
713                }
714    
715    
716                public boolean remove(Object o)
717                {
718                    Object r = ReferenceMap.this.remove(o);
719                    return r != null;
720                }
721    
722                public void clear()
723                {
724                    ReferenceMap.this.clear();
725                }
726    
727            };
728            return keySet;
729        }
730    
731    
732        /**
733         *  Returns a collection view of this map's values.
734         *
735         *  @return a collection view of this map's values.
736         */
737        public Collection values()
738        {
739            if (values != null) return values;
740            values = new AbstractCollection()
741            {
742                public int size()
743                {
744                    return size;
745                }
746    
747                public void clear()
748                {
749                    ReferenceMap.this.clear();
750                }
751    
752                public Iterator iterator()
753                {
754                    return new ValueIterator();
755                }
756            };
757            return values;
758        }
759    
760    
761        // If getKey() or getValue() returns null, it means
762        // the mapping is stale and should be removed.
763        private class Entry implements Map.Entry
764        {
765    
766            Object key;
767            Object value;
768            int hash;
769            Entry next;
770    
771    
772            public Entry(Object key, int hash, Object value, Entry next)
773            {
774                this.key = key;
775                this.hash = hash;
776                this.value = value;
777                this.next = next;
778            }
779    
780    
781            public Object getKey()
782            {
783                return (keyType > HARD) ? ((Reference) key).get() : key;
784            }
785    
786    
787            public Object getValue()
788            {
789                return (valueType > HARD) ? ((Reference) value).get() : value;
790            }
791    
792    
793            public Object setValue(Object object)
794            {
795                Object old = getValue();
796                if (valueType > HARD) ((Reference) value).clear();
797                value = toReference(valueType, object, hash);
798                return old;
799            }
800    
801    
802            public boolean equals(Object o)
803            {
804                if (o == null) return false;
805                if (o == this) return true;
806                if (!(o instanceof Map.Entry)) return false;
807    
808                Map.Entry entry = (Map.Entry) o;
809                Object _key = entry.getKey();
810                Object _value = entry.getValue();
811                if ((_key == null) || (_value == null)) return false;
812                return ReferenceMap.this.equals(_key, getKey()) &&
813                        ReferenceMap.this.equals(_value, getValue());
814            }
815    
816    
817            public int hashCode()
818            {
819                Object v = getValue();
820                return hash ^ ((v == null) ? 0 : v.hashCode());
821            }
822    
823    
824            public String toString()
825            {
826                return getKey() + "=" + getValue();
827            }
828    
829    
830            boolean purge(Reference ref)
831            {
832                boolean r = (keyType > HARD) && (key == ref);
833                r = r || ((valueType > HARD) && (value == ref));
834                if (r)
835                {
836                    if (keyType > HARD) ((Reference) key).clear();
837                    if (valueType > HARD) ((Reference) value).clear();
838                }
839                return r;
840            }
841        }
842    
843    
844        private class EntryIterator implements Iterator
845        {
846            // These fields keep track of where we are in the table.
847            int index;
848            Entry entry;
849            Entry previous;
850    
851            // These Object fields provide hard references to the
852            // current and next entry; this assures that if hasNext()
853            // returns true, next() will actually return a valid element.
854            Object nextKey, nextValue;
855            Object currentKey, currentValue;
856    
857            int expectedModCount;
858    
859    
860            public EntryIterator()
861            {
862                index = (size() != 0 ? table.length : 0);
863                // have to do this here!  size() invocation above
864                // may have altered the modCount.
865                expectedModCount = modCount;
866            }
867    
868    
869            public boolean hasNext()
870            {
871                checkMod();
872                while (nextNull())
873                {
874                    Entry e = entry;
875                    int i = index;
876                    while ((e == null) && (i > 0))
877                    {
878                        i--;
879                        e = table[i];
880                    }
881                    entry = e;
882                    index = i;
883                    if (e == null)
884                    {
885                        currentKey = null;
886                        currentValue = null;
887                        return false;
888                    }
889                    nextKey = e.getKey();
890                    nextValue = e.getValue();
891                    if (nextNull()) entry = entry.next;
892                }
893                return true;
894            }
895    
896    
897            private void checkMod()
898            {
899                if (modCount != expectedModCount)
900                {
901                    throw new ConcurrentModificationException();
902                }
903            }
904    
905    
906            private boolean nextNull()
907            {
908                return (nextKey == null) || (nextValue == null);
909            }
910    
911            protected Entry nextEntry()
912            {
913                checkMod();
914                if (nextNull() && !hasNext()) throw new NoSuchElementException();
915                previous = entry;
916                entry = entry.next;
917                currentKey = nextKey;
918                currentValue = nextValue;
919                nextKey = null;
920                nextValue = null;
921                return previous;
922            }
923    
924    
925            public Object next()
926            {
927                return nextEntry();
928            }
929    
930    
931            public void remove()
932            {
933                checkMod();
934                if (previous == null) throw new IllegalStateException();
935                ReferenceMap.this.remove(currentKey);
936                previous = null;
937                currentKey = null;
938                currentValue = null;
939                expectedModCount = modCount;
940            }
941    
942        }
943    
944    
945        private class ValueIterator extends EntryIterator
946        {
947            public Object next()
948            {
949                return nextEntry().getValue();
950            }
951        }
952    
953    
954        private class KeyIterator extends EntryIterator
955        {
956            public Object next()
957            {
958                return nextEntry().getKey();
959            }
960        }
961    
962    
963    
964        // These two classes store the hashCode of the key of
965        // of the mapping, so that after they're dequeued a quick
966        // lookup of the bucket in the table can occur.
967    
968    
969        private static class SoftRef extends SoftReference
970        {
971            private int hash;
972    
973    
974            public SoftRef(int hash, Object r, ReferenceQueue q)
975            {
976                super(r, q);
977                this.hash = hash;
978            }
979    
980    
981            public int hashCode()
982            {
983                return hash;
984            }
985        }
986    
987    
988        private static class WeakRef extends WeakReference
989        {
990            private int hash;
991    
992    
993            public WeakRef(int hash, Object r, ReferenceQueue q)
994            {
995                super(r, q);
996                this.hash = hash;
997            }
998    
999    
1000            public int hashCode()
1001            {
1002                return hash;
1003            }
1004        }
1005    
1006        private int hashCode(Object obj)
1007        {
1008            return useSystemIdentity ? System.identityHashCode(obj) : obj.hashCode(); // null keys|values are not supported
1009        }
1010    
1011        private boolean equals(Object obj1, Object obj2)
1012        {
1013            return (obj1 == obj2) ||
1014                    (!useSystemIdentity && obj1.equals(obj2)); // null keys|values are not supported
1015        }
1016    
1017        //***********************************************
1018        // inner class
1019        //***********************************************
1020        /** A default implementation of {@link java.util.Map.Entry}
1021          *
1022          * @since 1.0
1023          * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
1024          * @author <a href="mailto:mas@apache.org">Michael A. Smith</a>
1025          */
1026    
1027        public class DefaultMapEntry implements Map.Entry {
1028    
1029            private Object key;
1030            private Object value;
1031    
1032            /**
1033             *  Constructs a new <Code>DefaultMapEntry</Code> with a null key
1034             *  and null value.
1035             */
1036            public DefaultMapEntry() {
1037            }
1038    
1039            /**
1040             *  Constructs a new <Code>DefaultMapEntry</Code> with the given
1041             *  key and given value.
1042             *
1043             *  @param key  the key for the entry, may be null
1044             *  @param value  the value for the entyr, may be null
1045             */
1046            public DefaultMapEntry(Object key, Object value) {
1047                this.key = key;
1048                this.value = value;
1049            }
1050    
1051            /**
1052             *  Implemented per API documentation of
1053             *  {@link java.util.Map.Entry#equals(java.lang.Object)}
1054             **/
1055            public boolean equals(Object o) {
1056                if( o == null ) return false;
1057                if( o == this ) return true;
1058    
1059                if ( ! (o instanceof Map.Entry ) )
1060                    return false;
1061                Map.Entry e2 = (Map.Entry)o;
1062                return ((getKey() == null ?
1063                         e2.getKey() == null : getKey().equals(e2.getKey())) &&
1064                        (getValue() == null ?
1065                         e2.getValue() == null : getValue().equals(e2.getValue())));
1066            }
1067    
1068    
1069            /**
1070             *  Implemented per API documentation of
1071             *  {@link java.util.Map.Entry#hashCode()}
1072             **/
1073            public int hashCode() {
1074                return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^
1075                         ( getValue() == null ? 0 : getValue().hashCode() ) );
1076            }
1077    
1078    
1079    
1080            // Map.Entry interface
1081            //-------------------------------------------------------------------------
1082    
1083            /**
1084             *  Returns the key.
1085             *
1086             *  @return the key
1087             */
1088            public Object getKey() {
1089                return key;
1090            }
1091    
1092    
1093            /**
1094             *  Returns the value.
1095             *
1096             *  @return the value
1097             */
1098            public Object getValue() {
1099                return value;
1100            }
1101    
1102            // Properties
1103            //-------------------------------------------------------------------------
1104    
1105            /**
1106             *  Sets the key.  This method does not modify any map.
1107             *
1108             *  @param key  the new key
1109             */
1110            public void setKey(Object key) {
1111                this.key = key;
1112            }
1113    
1114            /** Note that this method only sets the local reference inside this object and
1115              * does not modify the original Map.
1116              *
1117              * @return the old value of the value
1118              * @param value the new value
1119              */
1120            public Object setValue(Object value) {
1121                Object answer = this.value;
1122                this.value = value;
1123                return answer;
1124            }
1125    
1126        }
1127    }