Coverage Report - org.apache.ojb.broker.util.ReferenceMap
 
Classes in this File Line Coverage Branch Coverage Complexity
ReferenceMap
N/A
N/A
2.676
ReferenceMap$1
N/A
N/A
2.676
ReferenceMap$2
N/A
N/A
2.676
ReferenceMap$3
N/A
N/A
2.676
ReferenceMap$DefaultMapEntry
N/A
N/A
2.676
ReferenceMap$Entry
N/A
N/A
2.676
ReferenceMap$EntryIterator
N/A
N/A
2.676
ReferenceMap$KeyIterator
N/A
N/A
2.676
ReferenceMap$SoftRef
N/A
N/A
2.676
ReferenceMap$ValueIterator
N/A
N/A
2.676
ReferenceMap$WeakRef
N/A
N/A
2.676
 
 1  
 package org.apache.ojb.broker.util;
 2  
 
 3  
 /* Copyright 2004-2005 The Apache Software Foundation
 4  
  *
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * You may obtain a copy of the License at
 8  
  *
 9  
  *     http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 import java.io.IOException;
 19  
 import java.io.ObjectInputStream;
 20  
 import java.io.ObjectOutputStream;
 21  
 import java.lang.ref.Reference;
 22  
 import java.lang.ref.ReferenceQueue;
 23  
 import java.lang.ref.SoftReference;
 24  
 import java.lang.ref.WeakReference;
 25  
 import java.util.AbstractCollection;
 26  
 import java.util.AbstractMap;
 27  
 import java.util.AbstractSet;
 28  
 import java.util.ArrayList;
 29  
 import java.util.Arrays;
 30  
 import java.util.Collection;
 31  
 import java.util.ConcurrentModificationException;
 32  
 import java.util.Iterator;
 33  
 import java.util.Map;
 34  
 import java.util.NoSuchElementException;
 35  
 import java.util.Set;
 36  
 
 37  
 /**
 38  
  * <p>
 39  
  * Modified version of {@link org.apache.commons.collections.ReferenceMap}, using
 40  
  * object identity for key comparison (). This class
 41  
  * simply extended {@link org.apache.commons.collections.ReferenceMap} with an extra field
 42  
  * "useSystemIdentity" which is initialized in constructor and is used every time we
 43  
  * want to compare (or hash) keys or values.
 44  
  * </p>
 45  
  * <p>
 46  
  * Javadoc of ReferenceMap starts here:
 47  
  * <br/>
 48  
  * Hashtable-based {@link java.util.Map} implementation that allows
 49  
  *  mappings to be removed by the garbage collector.
 50  
  * </p>
 51  
  * <P>
 52  
  *  When you construct a <Code>ReferenceMap</Code>, you can
 53  
  *  specify what kind of references are used to store the
 54  
  *  map's keys and values.  If non-hard references are
 55  
  *  used, then the garbage collector can remove mappings
 56  
  *  if a key or value becomes unreachable, or if the
 57  
  *  JVM's memory is running low.  For information on how
 58  
  *  the different reference types behave, see
 59  
  *  {@link java.lang.ref.Reference}.<P>
 60  
  *
 61  
  *  Different types of references can be specified for keys
 62  
  *  and values.  The keys can be configured to be weak but
 63  
  *  the values hard, in which case this class will behave
 64  
  *  like a <A HREF="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
 65  
  *  <Code>WeakHashMap</Code></A>.  However, you
 66  
  *  can also specify hard keys and weak values, or any other
 67  
  *  combination.  The default constructor uses hard keys
 68  
  *  and soft values, providing a memory-sensitive cache.<P>
 69  
  *
 70  
  *  The algorithms used are basically the same as those
 71  
  *  in {@link java.util.HashMap}.  In particular, you
 72  
  *  can specify a load factor and capacity to suit your
 73  
  *  needs.  All optional {@link java.util.Map} operations are
 74  
  *  supported.<P>
 75  
  *
 76  
  *  However, this {@link java.util.Map} implementation does <I>not</I>
 77  
  *  allow null elements.  Attempting to add a null key or
 78  
  *  or a null value to the map will raise a
 79  
  *  <Code>NullPointerException</Code>.<P>
 80  
  *
 81  
  *  As usual, this implementation is not synchronized.  You
 82  
  *  can use {@link java.util.Collections#synchronizedMap} to
 83  
  *  provide synchronized access to a <Code>ReferenceMap</Code>.
 84  
  *
 85  
  * @deprecated use {@link org.apache.commons.collections.map.ReferenceIdentityMap} instead.
 86  
  *  @author Andy Malakov
 87  
  *  @version $Id: ReferenceMap.java,v 1.1 2007-08-24 22:17:36 ewestfal Exp $
 88  
  */
 89  
 public class ReferenceMap extends AbstractMap
 90  
 {
 91  
 
 92  
     /**
 93  
      *  For serialization.
 94  
      */
 95  
     final private static long serialVersionUID = -3370601314380922368L;
 96  
 
 97  
 
 98  
     /**
 99  
      *  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  
 }