
package jde.debugger;

import java.util.*;

import com.sun.jdi.*;

/**
 * ObjectStore.java
 * <p>
 *
 * The main function of this class is to keep a store of all the currently
 * referenced objects. Any time jdebug sends an object ID across, it stores
 * the ObjectReference itself in {@link #objectMap}, mapped to the id that
 * will identify this object. This id is the ObjectReference.uniqueID().
 * <p>
 * We need to do this because if we don't keep a link to the ObjectReference
 * <i>some</i>where, it might get garbage collected, and the id used to
 * identify it (ie the uniqueID) reused. If the user then requests info
 * about that ID, he'll be sent information about the new object, which is
 * obviously wrong.
 * <p>
 * When jde wants to know more about the object, it sends across the id,
 * which is used to reference the ObjectReference in the Map
 * <p>
 * Since this is done with each object that's ever reported to jde, the list
 * can get pretty huge, and needs to be refreshed from time to time. For
 * this purpose, we maintain the variable {@link #maximumLimit}.
 * <p>
 * Objects keep getting added to the list, until we
 * reach {@link #maximumLimit}. At this point, a notification is sent to the
 * jde side requesting for a list of all the object references (ie, the ids)
 * that it is currently interested in. 
 * <p>
 * When this list is obtained, the {@link #objectMap} is scanned and entries
 * <i>not</i> in this list removed. {@link #maximumLimit} is then set to
 * 2 times the current size of the list, or the old maximumLimit, whichever
 * is larger. This is to ensure we don't keep sending the request over and
 * over again very frequently.
 * <p>
 * Note that we keep adding objects to the Map even after sending the
 * notification to jde: any reducing the size of the list is only done
 * when jde responds.
 * <p>
 * <b>Note:</b> Question: Should we disable garbage collection of objects
 * in the debugee VM once we put their corresponding ObjectReference in the
 * debugger VM in the objectstore? And maybe enable the gc once they're
 * removed from the store? This way we'll never get the ObjectCollected
 * exception, and we can use the object as long as its displayed on the
 * emacs side. The only thing is, we'd need the emacs side to be pretty
 * frequent about the list of things it is displaying so we don't encumber
 * the debuggee VM too much with objects it's unable to collect.
 * <p>
 * Created: Thu Jul 29 10:38:06 1999
 * 
 * @author Amit Kumar
 * @since 0.1
 */

public class ObjectStore implements Protocol {

  /** my very own process! */
  final DebuggeeProcess proc;

  final Integer procID;

  JDE jde = JDE.getJDE();

  /** maps object_id -> ObjectReference */
  private Map objectMap;

  /** maximum number of objects before we send a notification to jde */
  private long maximumLimit = 8;

  /** keep track of if our request has been met yet */
  private boolean requestPending = false;

  /** Create a new object map for a new process. */
  public ObjectStore(DebuggeeProcess proc) {
    this.proc = proc;
    procID = proc.getId();
    objectMap = new HashMap();
  }

  /** Register that an object is being sent to the jde side */
  public void put(ObjectReference ref) {
    long size;
    synchronized (this) {
      objectMap.put(new Long(ref.uniqueID()), ref);
      size = objectMap.size();
    }
    if (size > maximumLimit) {
      if (!requestPending) {
	jde.signal(procID, REPORT_IDS_IN_USE, null);
	requestPending = true;
      }
    }
  }

  /**
   * jde sent us a list of objects it is currently interested in. Trim
   * objectMap based on this list
   */
  public void trim(List objectIDs) {
    Map newMap = new HashMap();
    Iterator it = objectIDs.iterator();
    while (it.hasNext()) {
      Long id = (Long)it.next();
      synchronized (this) {
	if (objectMap.containsKey(id)) {
	  newMap.put(id, objectMap.get(id));
	}
      }
    }
    maximumLimit = 2*newMap.size();
    synchronized (this) {
      objectMap = newMap;
    }
    requestPending = false;
  }

  /** Returns the object corresponding to the id, or null */
  public ObjectReference get(Object id) {
    synchronized (this) {
      return (ObjectReference)objectMap.get(id);
    }
  }

} // ObjectStore
