/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.util.cache;

import java.lang.ref.*;
import java.util.*;

/**
 * An entry from the <CODE>CacheMap</CODE> class.  This object holds the key, a <CODE>SoftReference</CODE>
 * to the value, the timestamp of its last access, and the number of times this element has been accessed.
 * The latter two values are frequently used by an object implementing the <CODE>CacheMapStrategy</CODE>
 * interface to compute a "figure of merit" for the entry that decides whether or not it gets discarded.
 *
 * @author Eric J. Bowersox &lt;erbo@silcom.com&gt;
 * @version X
 * @see CacheMap
 * @see CacheMapStrategy
 * @see java.lang.ref.SoftReference
 */
public final class CacheMapEntry implements Map.Entry
{
  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private Object key;               // the key for this entry
  private SoftReference value;      // reference to (discardable) value
  private int hits = 0;             // number of hits on this entry
  private long timestamp;           // timestamp of this entry

  /*--------------------------------------------------------------------------------
   * Constructors
   *--------------------------------------------------------------------------------
   */

  /**
   * Creates a new <CODE>CacheMapEntry</CODE>.
   *
   * @param key The key for this entry.
   * @param value The value to be held in this entry.
   */
  CacheMapEntry(Object key, Object value)
  {
    this.key = key;
    this.value = new SoftReference(value);
    this.timestamp = System.currentTimeMillis();

  } // end constructor

  /**
   * Creates a new <CODE>CacheMapEntry</CODE>.
   *
   * @param key The key for this entry.
   * @param value The value to be held in this entry.
   * @param q The <CODE>ReferenceQueue</CODE> to add the new reference generated by this constructor to.
   */
  CacheMapEntry(Object key, Object value, ReferenceQueue q)
  {
    this.key = key;
    this.value = new SoftReference(value,q);
    this.timestamp = System.currentTimeMillis();

  } // end constructor

  /*--------------------------------------------------------------------------------
   * finalize() function
   *--------------------------------------------------------------------------------
   */

  /**
   * Called by the garbage collector on an object when garbage collection determines that there are no
   * more references to the object.
   */
  protected void finalize()
  {
    key = null;
    value.clear();
    value = null;

  } // end finalize

  /*--------------------------------------------------------------------------------
   * Implementations from interface Map.Entry
   *--------------------------------------------------------------------------------
   */

  /**
   * Returns the key corresponding to this entry.
   *
   * @return The key corresponding to this entry.
   */
  public final Object getKey()
  {
    return key;

  } // end getKey

  /**
   * Returns the value corresponding to this entry.  If the value has already been reclaimed by the
   * garbage collector, the return value is <CODE>null</CODE>.
   *
   * @return The value corresponding to this entry.
   */
  public final Object getValue()
  {
    return value.get();

  } // end getValue

  /**
   * Replaces the value corresponding to this entry with the specified value.
   *
   * @param o The new value to be stored in this entry.
   * @return The former value stored in this entry.  If the value has already been reclaimed by the
   *         garbage collector, the return value is <CODE>null</CODE>.
   */
  public final Object setValue(Object o)
  {
    Object rc = value.get();
    value.clear();
    value = new SoftReference(o);
    return rc;

  } // end setValue

  /**
   * Compares the specified object with this object for equality.  The <CODE>CacheMap</CODE> considers
   * two entries to be equivalent if they have the same key.  (The value is not considered because it
   * might already have been garbage-collected.)
   *
   * @param o The other object to compare to.
   * @return <CODE>true</CODE> if the specified object is equal to this entry.
   */
  public final boolean equals(Object o)
  {
    // make sure the other element is a Map.Entry
    if ((o==null) || !(o instanceof Map.Entry))
      return false;
    Map.Entry other = (Map.Entry)o;

    // compare the keys only (the values may change)
    if (key==null)
      return (other.getKey()==null);
    else
      return ((other.getKey()!=null) && key.equals(other.getKey()));

  } // end equals

  /**
   * Returns the hash code value for this map entry, defined to be the hash code value of the key.
   *
   * @return The hash code value for this map entry.
   */
  public final int hashCode()
  {
    return ((key==null) ? 0 : key.hashCode());

  } // end hashCode

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  /**
   * Replaces the value corresponding to this entry with the specified value.
   *
   * @param o The new value to be stored in this entry.
   * @param q The <CODE>ReferenceQueue</CODE> to add the freshly-generated reference to.
   * @return The former value stored in this entry.  If the value has already been reclaimed by the
   *         garbage collector, the return value is <CODE>null</CODE>.
   */
  final Object setValue(Object o, ReferenceQueue q)
  {
    Object rc = value.get();
    value.clear();
    value = new SoftReference(o,q);
    return rc;

  } // end setValue

  /**
   * Returns <CODE>true</CODE> if the value in this map entry has been cleared by the garbage collector.
   *
   * @return <CODE>true</CODE> if the value has been cleared, <CODE>false</CODE> if not.
   */
  public final boolean isCleared()
  {
    return (value.get()==null);

  } // end isCleared

  /**
   * Returns <CODE> true if the specified reference is the one contained in this map entry.
   *
   * @param r The reference we're trying to match.
   * @return <CODE>true/CODE> if this reference is the same as the one contained in the map entry,
   *         <CODE>false</CODE> if not.
   */
  final boolean matchReference(Reference r)
  {
    if (r instanceof SoftReference)
      return (value==(SoftReference)r);
    else
      return false;

  } // end matchReference

  /**
   * Clears out all references held by this map entry.
   */
  final void discard()
  {
    key = null;
    value.clear();
    value = null;

  } // end discard

  /**
   * Returns the number of times this map entry has been referenced.
   *
   * @return The number of times this map entry has been referenced.
   */
  public final int getHits()
  {
    return hits;

  } // end getHits

  /**
   * Returns the timestamp of the most recent reference to this map entry.
   *
   * @return The timestamp of the most recent reference to this map entry.
   */
  public final long getTimestamp()
  {
    return timestamp;

  } // end getTimestamp

  /**
   * Returns the number of milliseconds since this map entry was last referenced.
   *
   * @param tick The current time tick of the system in milliseconds.
   * @return The number of milliseconds since this map entry was last referenced.
   */
  public final long getAge(long tick)
  {
    return (tick - timestamp);

  } // end getAge

  /**
   * References this entry, which increments its "hit" counter and updates its current time stamp.
   */
  final void touch()
  {
    hits++;
    timestamp = System.currentTimeMillis();

  } // end touch

} // end class CacheMapEntry
