1 /*
   2  * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.misc;
  27 
  28 import java.lang.ref.SoftReference;
  29 import java.util.Dictionary;
  30 import java.util.Enumeration;
  31 import java.util.NoSuchElementException;
  32 
  33 /**
  34  * Caches the collision list.
  35  */
  36 class CacheEntry {
  37     int hash;
  38     Object key;
  39     CacheEntry next;
  40     SoftReference<Object> value;
  41 
  42     public CacheEntry() {
  43         value = null;
  44     }
  45 
  46     public CacheEntry(Object o) {
  47         value = new SoftReference<>(o);
  48     }
  49 
  50     public Object get() {
  51         return value.get();
  52     }
  53 
  54     public void setThing(Object thing) {
  55         value = new SoftReference<>(thing);
  56     }
  57 }
  58 
  59 /**
  60  * The Cache class. Maps keys to values. Any object can be used as
  61  * a key and/or value.  This is very similar to the Hashtable
  62  * class, except that after putting an object into the Cache,
  63  * it is not guaranteed that a subsequent get will return it.
  64  * The Cache will automatically remove entries if memory is
  65  * getting tight and if the entry is not referenced from outside
  66  * the Cache.<p>
  67  *
  68  * To sucessfully store and retrieve objects from a hash table the
  69  * object used as the key must implement the hashCode() and equals()
  70  * methods.<p>
  71  *
  72  * This example creates a Cache of numbers. It uses the names of
  73  * the numbers as keys:
  74  * <pre>
  75  *      Cache numbers = new Cache();
  76  *      numbers.put("one", new Integer(1));
  77  *      numbers.put("two", new Integer(1));
  78  *      numbers.put("three", new Integer(1));
  79  * </pre>
  80  * To retrieve a number use:
  81  * <pre>
  82  *      Integer n = (Integer)numbers.get("two");
  83  *      if (n != null) {
  84  *          System.out.println("two = " + n);
  85  *      }
  86  * </pre>
  87  *
  88  * @see java.lang.Object#hashCode
  89  * @see java.lang.Object#equals
  90  * @deprecated Consider {@link java.util.LinkedHashMap} for LRU caches.
  91  */
  92 @Deprecated
  93 public
  94     class Cache extends Dictionary<Object, Object> {
  95     /**
  96      * The hash table data.
  97      */
  98     private CacheEntry table[];
  99 
 100     /**
 101      * The total number of entries in the hash table.
 102      */
 103     private int count;
 104 
 105     /**
 106      * Rehashes the table when count exceeds this threshold.
 107      */
 108     private int threshold;
 109 
 110     /**
 111      * The load factor for the hashtable.
 112      */
 113     private float loadFactor;
 114 
 115     private void init(int initialCapacity, float loadFactor) {
 116         if ((initialCapacity <= 0) || (loadFactor <= 0.0)) {
 117             throw new IllegalArgumentException();
 118         }
 119         this.loadFactor = loadFactor;
 120         table = new CacheEntry[initialCapacity];
 121         threshold = (int) (initialCapacity * loadFactor);
 122     }
 123 
 124     /**
 125      * Constructs a new, empty Cache with the specified initial
 126      * capacity and the specified load factor.
 127      * @param initialCapacity the initial number of buckets
 128      * @param loadFactor a number between 0.0 and 1.0, it defines
 129      *          the threshold for rehashing the Cache into
 130      *          a bigger one.
 131      * @exception IllegalArgumentException If the initial capacity
 132      * is less than or equal to zero.
 133      * @exception IllegalArgumentException If the load factor is
 134      * less than or equal to zero.
 135      */
 136     public Cache (int initialCapacity, float loadFactor) {
 137         init(initialCapacity, loadFactor);
 138     }
 139 
 140     /**
 141      * Constructs a new, empty Cache with the specified initial
 142      * capacity.
 143      * @param initialCapacity the initial number of buckets
 144      */
 145     public Cache (int initialCapacity) {
 146         init(initialCapacity, 0.75f);
 147     }
 148 
 149     /**
 150      * Constructs a new, empty Cache. A default capacity and load factor
 151      * is used. Note that the Cache will automatically grow when it gets
 152      * full.
 153      */
 154     public Cache () {
 155         try {
 156             init(101, 0.75f);
 157         } catch (IllegalArgumentException ex) {
 158             // This should never happen
 159             throw new Error("panic");
 160         }
 161     }
 162 
 163     /**
 164      * Returns the number of elements contained within the Cache.
 165      */
 166     public int size() {
 167         return count;
 168     }
 169 
 170     /**
 171      * Returns true if the Cache contains no elements.
 172      */
 173     public boolean isEmpty() {
 174         return count == 0;
 175     }
 176 
 177     /**
 178      * Returns an enumeration of the Cache's keys.
 179      * @see Cache#elements
 180      * @see Enumeration
 181      */
 182     public synchronized Enumeration<Object> keys() {
 183         return new CacheEnumerator(table, true);
 184     }
 185 
 186     /**
 187      * Returns an enumeration of the elements. Use the Enumeration methods
 188      * on the returned object to fetch the elements sequentially.
 189      * @see Cache#keys
 190      * @see Enumeration
 191      */
 192     public synchronized Enumeration<Object> elements() {
 193         return new CacheEnumerator(table, false);
 194     }
 195 
 196     /**
 197      * Gets the object associated with the specified key in the Cache.
 198      * @param key the key in the hash table
 199      * @returns the element for the key or null if the key
 200      *          is not defined in the hash table.
 201      * @see Cache#put
 202      */
 203     public synchronized Object get(Object key) {
 204         CacheEntry tab[] = table;
 205         int hash = key.hashCode();
 206         int index = (hash & 0x7FFFFFFF) % tab.length;
 207         for (CacheEntry e = tab[index]; e != null; e = e.next) {
 208             if ((e.hash == hash) && e.key.equals(key)) {
 209                 return e.get();
 210             }
 211         }
 212         return null;
 213     }
 214 
 215     /**
 216      * Rehashes the contents of the table into a bigger table.
 217      * This is method is called automatically when the Cache's
 218      * size exceeds the threshold.
 219      */
 220     protected void rehash() {
 221         int oldCapacity = table.length;
 222         CacheEntry oldTable[] = table;
 223 
 224         int newCapacity = oldCapacity * 2 + 1;
 225         CacheEntry newTable[] = new CacheEntry[newCapacity];
 226 
 227         threshold = (int) (newCapacity * loadFactor);
 228         table = newTable;
 229 
 230         // System.out.println("rehash old=" + oldCapacity + ", new=" +
 231         // newCapacity + ", thresh=" + threshold + ", count=" + count);
 232 
 233         for (int i = oldCapacity; i-- > 0;) {
 234             for (CacheEntry old = oldTable[i]; old != null;) {
 235                 CacheEntry e = old;
 236                 old = old.next;
 237                 if (e.get() != null) {
 238                     int index = (e.hash & 0x7FFFFFFF) % newCapacity;
 239                     e.next = newTable[index];
 240                     newTable[index] = e;
 241                 } else
 242                     count--;    /* remove entries that have disappeared */
 243             }
 244         }
 245     }
 246 
 247     /**
 248      * Puts the specified element into the Cache, using the specified
 249      * key.  The element may be retrieved by doing a get() with the same
 250      * key.  The key and the element cannot be null.
 251      * @param key the specified hashtable key
 252      * @param value the specified element
 253      * @return the old value of the key, or null if it did not have one.
 254      * @exception NullPointerException If the value of the specified
 255      * element is null.
 256      * @see Cache#get
 257      */
 258     public synchronized Object put(Object key, Object value) {
 259         // Make sure the value is not null
 260         if (value == null) {
 261             throw new NullPointerException();
 262         }
 263         // Makes sure the key is not already in the cache.
 264         CacheEntry tab[] = table;
 265         int hash = key.hashCode();
 266         int index = (hash & 0x7FFFFFFF) % tab.length;
 267         CacheEntry ne = null;
 268         for (CacheEntry e = tab[index]; e != null; e = e.next) {
 269             if ((e.hash == hash) && e.key.equals(key)) {
 270                 Object old = e.get();
 271                 e.setThing(value);
 272                 return old;
 273             } else if (e.get() == null)
 274                 ne = e;         /* reuse old flushed value */
 275         }
 276 
 277         if (count >= threshold) {
 278             // Rehash the table if the threshold is exceeded
 279             rehash();
 280             return put(key, value);
 281         }
 282         // Creates the new entry.
 283         if (ne == null) {
 284             ne = new CacheEntry ();
 285             ne.next = tab[index];
 286             tab[index] = ne;
 287             count++;
 288         }
 289         ne.hash = hash;
 290         ne.key = key;
 291         ne.setThing(value);
 292         return null;
 293     }
 294 
 295     /**
 296      * Removes the element corresponding to the key. Does nothing if the
 297      * key is not present.
 298      * @param key the key that needs to be removed
 299      * @return the value of key, or null if the key was not found.
 300      */
 301     public synchronized Object remove(Object key) {
 302         CacheEntry tab[] = table;
 303         int hash = key.hashCode();
 304         int index = (hash & 0x7FFFFFFF) % tab.length;
 305         for (CacheEntry e = tab[index], prev = null; e != null; prev = e, e = e.next) {
 306             if ((e.hash == hash) && e.key.equals(key)) {
 307                 if (prev != null) {
 308                     prev.next = e.next;
 309                 } else {
 310                     tab[index] = e.next;
 311                 }
 312                 count--;
 313                 return e.get();
 314             }
 315         }
 316         return null;
 317     }
 318 }
 319 
 320 /**
 321  * A Cache enumerator class.  This class should remain opaque
 322  * to the client. It will use the Enumeration interface.
 323  */
 324 class CacheEnumerator implements Enumeration<Object> {
 325     boolean keys;
 326     int index;
 327     CacheEntry table[];
 328     CacheEntry entry;
 329 
 330     CacheEnumerator (CacheEntry table[], boolean keys) {
 331         this.table = table;
 332         this.keys = keys;
 333         this.index = table.length;
 334     }
 335 
 336     public boolean hasMoreElements() {
 337         while (index >= 0) {
 338             while (entry != null)
 339                 if (entry.get() != null)
 340                     return true;
 341                 else
 342                     entry = entry.next;
 343             while (--index >= 0 && (entry = table[index]) == null) ;
 344         }
 345         return false;
 346     }
 347 
 348     public Object nextElement() {
 349         while (index >= 0) {
 350             if (entry == null)
 351                 while (--index >= 0 && (entry = table[index]) == null) ;
 352             if (entry != null) {
 353                 CacheEntry e = entry;
 354                 entry = e.next;
 355                 if (e.get() != null)
 356                     return keys ? e.key : e.get();
 357             }
 358         }
 359         throw new NoSuchElementException("CacheEnumerator");
 360     }
 361 
 362 }