/**
  * Returns the value to which the specified key is mapped in this hashtable.
  *
  * @param key a key in the hashtable.
  * @return the value to which the key is mapped in this hashtable; <code>null</code> if the key is
  *     not mapped to any value in this hashtable.
  * @see java.util.LRUCache#put(java.lang.Object, java.lang.Object)
  * @since JDK1.0
  */
 public Object get(Object key) {
   LRUCacheEntry tab[] = table;
   int hash = key.hashCode();
   int index = (hash & 0x7FFFFFFF) % tab.length;
   for (LRUCacheEntry e = tab[index]; e != null; e = e.next) {
     if ((e.hash == hash) && e.key.equals(key)) {
       if (expirationTime > 0) e.expirationTime = System.currentTimeMillis() + expirationTime;
       // LoggingService.getDefaultLogger().logDebug(this, "===>"+table.length);
       return e.value;
     }
   }
   return null;
 }
 /**
  * Removes the key (and its corresponding value) from this hashtable. This method does nothing if
  * the key is not in the hashtable.
  *
  * @param key the key that needs to be removed.
  * @return the value to which the key had been mapped in this hashtable, or <code>null</code> if
  *     the key did not have a mapping.
  * @since JDK1.0
  */
 public Object remove(Object key) {
   LRUCacheEntry tab[] = table;
   int hash = key.hashCode();
   int index = (hash & 0x7FFFFFFF) % tab.length;
   for (LRUCacheEntry e = tab[index], prev = null; e != null; prev = e, e = e.next) {
     if ((e.hash == hash) && e.key.equals(key)) {
       if (prev != null) {
         prev.next = e.next;
       } else {
         tab[index] = e.next;
       }
       count--;
       return e.value;
     }
   }
   return null;
 }
 protected Object clone() {
   LRUCacheEntry entry = new LRUCacheEntry();
   entry.hash = hash;
   entry.key = key;
   entry.value = value;
   entry.expirationTime = expirationTime;
   entry.next = (next != null) ? (LRUCacheEntry) next.clone() : null;
   return entry;
 }
  /**
   * Rehashes the contents of the hashtable into a hashtable with a larger capacity. This method is
   * called automatically when the number of keys in the hashtable exceeds this hashtable's capacity
   * and load factor.
   *
   * @since JDK1.0
   */
  protected void rehash() {
    int oldCapacity = table.length;
    LRUCacheEntry oldTable[] = table;

    int newCapacity = oldCapacity * 2 + 1;
    LRUCacheEntry newTable[] = new LRUCacheEntry[newCapacity];

    threshold = (int) (newCapacity * loadFactor);
    table = newTable;

    // LoggingService.getDefaultLogger().logDebug(this, "rehash old=" + oldCapacity + ", new=" +
    // newCapacity + ", thresh=" + threshold + ", count=" + count);

    for (int i = oldCapacity; i-- > 0; ) {
      for (LRUCacheEntry old = oldTable[i]; old != null; ) {
        LRUCacheEntry e = old;
        old = old.next;

        int index = (e.hash & 0x7FFFFFFF) % newCapacity;
        e.next = newTable[index];
        newTable[index] = e;
      }
    }
  }
  /**
   * Maps the specified <code>key</code> to the specified <code>value</code> in this hashtable.
   * Neither the key nor the value can be <code>null</code>.
   *
   * <p>The value can be retrieved by calling the <code>get</code> method with a key that is equal
   * to the original key.
   *
   * @param key the hashtable key.
   * @param value the value.
   * @return the previous value of the specified key in this hashtable, or <code>null</code> if it
   *     did not have one.
   * @exception NullPointerException if the key or value is <code>null</code>.
   * @see java.lang.Object#equals(java.lang.Object)
   * @see java.util.LRUCache#get(java.lang.Object)
   * @since JDK1.0
   */
  public Object put(Object key, Object value) {
    // Make sure the value is not null
    if (value == null) {
      throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    LRUCacheEntry tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    // LoggingService.getDefaultLogger().logDebug(this, "=====================>Index: "+index );
    for (LRUCacheEntry e = tab[index]; e != null; e = e.next) {
      if ((e.hash == hash) && e.key.equals(key)) {
        Object old = e.value;
        e.value = value;
        return old;
      }
    }

    if (count >= threshold) {
      // Rehash the table if the threshold is exceeded
      rehash();
      return put(key, value);
    }

    // Creates the new entry.
    LRUCacheEntry e = new LRUCacheEntry();
    e.hash = hash;
    e.key = key;
    e.value = value;
    e.expirationTime = (expirationTime > 0) ? (System.currentTimeMillis() + expirationTime) : -1;
    e.next = tab[index];
    tab[index] = e;
    count++;
    if (cacheMonitorThread == null || !cacheMonitorThread.isAlive()) {
      cacheMonitorThread = new CacheMonitor(); // new Thread(new CacheMonitor() );
      cacheMonitorThread.setDaemon(true);
      cacheMonitorThread.setPriority(Thread.NORM_PRIORITY - 1);
      cacheMonitorThread.start();
    }
    return null;
  }