/*
 * 
 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source
 * License v3.0/Jini Technology Specific Attachment v 1.0 (the
 * "License"). You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 * http://wwws.sun.com/software/jini/licensing/SCSL3_JiniTSA1.html.
 * 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 Reference Code is Jini Technology Core Platform code, v2.0. The
 * Developer of the Reference Code is Sun Microsystems, Inc.
 * 
 * Contributor(s): Sun Microsystems, Inc.
 * 
 * The contents of this file comply with the Jini Technology Core
 * Platform Compatibility Kit, v2.0A.
 * 
 * Tester(s): Sun Microsystems, Inc.
 * 
 * Test Platform(s):
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Solaris(TM)SPARC/x86
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Linux (Intel x86)
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Windows (Intel Platform)
 * 
 */
package com.sun.jini.outrigger;

/**
 * Base class for objects that represent interest in particular entry
 * visibility transitions.  Each <code>TransitionWatcher</code> has a
 * time stamp and when compared <code>TransitionWatcher</code>s with a
 * lower time stamp are considered less than those with a larger time
 * stamp. If two <code>TransitionWatcher</code> have the same time
 * stamp their tie breaker values are used.
 * <p>
 * Unless otherwise noted, the <code>setTemplateHandle</code> method should
 * be called before any other method.
 * The <code>setTemplateHandle</code> method should only be called
 * once.
 * <p>
 * Unless otherwise noted, all the package protected methods
 * are thread safe.  
 */
abstract class TransitionWatcher implements Comparable {
    /** The time stamp for this object */
    final private long timestamp;

    /** The tie breaker for this object */
    final private long tiebreaker;

    /**
     * The transitions with higher ordinals occurred
     * after the operation associated with this
     * watcher was started. Package protected
     * but should only be accessed by subclasses.
     */
    final long startOrdinal;

    /**
     * The <code>TemplateHandle</code> associated with this
     * watcher. Package protected, but should only be accessed
     * by subclasses.
     */
    TemplateHandle owner;

    /** Next tiebreaker to use */
    private static long nextTiebreaker = 0;

    /** Lock for nextTiebreaker */
    private static Object nextTiebreakerLock = new Object();

    /**
     * Create a new <code>TransitionWatcher</code>. 
     * @param timestamp the value that is used
     *        to sort <code>TransitionWatcher</code>s.
     * @param startOrdinal the highest ordinal associated
     *        with operations that are considered to have occurred 
     *        before the operation associated with this watcher.
     */
    TransitionWatcher(long timestamp, long startOrdinal) {
	this.timestamp = timestamp;
	this.startOrdinal = startOrdinal;

	synchronized (nextTiebreakerLock) {
	    tiebreaker = nextTiebreaker++;
	}

    }

    /**
     * Associate a <code>TemplateHandle</code> with this 
     * object. This method should be called at most once
     * on any given instance.
     * @param h The <code>TemplateHandle</code> associated
     *          with this watcher.
     * @throws NullPointerException if <code>h</code> is 
     *        <code>null</code>
     */
    void setTemplateHandle(TemplateHandle h) {
	if (h == null) 
	    throw 
		new NullPointerException("TemplateHandle must be non-null");

	assert owner == null : "Can only call setTemplateHandle once";
	owner = h;
    }

    /**
     * Compares this object with another. If the other object is not a
     * <code>TransitionWatcher</code> a
     * <code>ClassCastException</code> will be thrown. The object with
     * the lower time stamp will be considered less than the other. If they 
     * both have the same time stamp the object will the lower
     * tie breaker will be less than the other. If both have the same
     * tie breaker and time stamp they will be considered equal.
     * <p>
     * This method may be called before <code>setTemplateHandle</code> 
     * is called.
     *
     * @return a negative integer, zero, or a positive integer as this
     *         object is less than, equal to, or greater than the specified
     *         object.
     * @throws ClassCastException if <code>o</code> is not
     *         a <code>TransitionWatcher</code>.  
     */
    public int compareTo(Object o) {
	final TransitionWatcher other = (TransitionWatcher)o;
	if (timestamp < other.timestamp)
	    return -1;

	if (timestamp > other.timestamp)
	    return 1;

	if (tiebreaker < other.tiebreaker)
	    return -1;

	if (tiebreaker > other.tiebreaker)
	    return 1;

	assert o == this : 
	    "Two TransitionWatchers with the same tiebreaker"; 
	return 0;
    }

    /* Since every TransitionWatcher has its own tie breaker
     * we can use the default hashCode and equals.
     */

    /**
     * Get the expiration time of this object.  This method may be
     * called before <code>setTemplateHandle</code> is called.
     * Assumes locking is handled by the caller.
     * @return The time (in milliseconds since the beginning of
     *         the epoch) when this watcher is no longer relevant.
     */
    abstract public long getExpiration();

    /**
     * Remove this watcher from the system if it
     * has expired. Responsible
     * for calling the <code>remove</code> method
     * of the associated <code>TemplateHandle</code>.
     * This method can be called more than once,
     * though second and subsequent calls may have no effect.
     * @param now An estimate of the current time that must be
     *            less than or equal to the current time.
     */
    abstract void removeIfExpired(long now);

    /**
     * Return <code>true</code> if this watcher cares about a given
     * visibility transition. Assumes the transitioning entry matches
     * the template in the <code>TemplateHandle</code> associated with
     * this watcher. This method should return a value even if the
     * expiration time has been reached or <code>remove</code> has
     * been called. This call should not obtain any locks.
     * @param transition A <code>EntryTransition</code> that
     *              describes the transition and what
     *              entry is transitioning. This method
     *              will assume that <code>transition.getHandle</code>
     *              returns a non-null value.
     * @param ordinal The ordinal associated with <code>transition</code>.
     * @return <code>true</code> if this watcher is interested
     *         in the indicated transition and <code>false</code> 
     *         otherwise.  
     * @throws NullPointerException if <code>transition</code> is 
     *         <code>null</code>.  
     */
    abstract boolean isInterested(EntryTransition transition, long ordinal);

    /**
     * Process the given transition. Assumes the passed entry matches the
     * template in the <code>TemplateHandle</code> associated with this
     * watcher and that <code>isInterested</code> returned
     * <code>true</code>. This call may make changes to the passed
     * <code>EntryHandle</code> that will prevent it from being used by
     * other watchers. If <code>remove</code> has been called or the
     * expiration time of this watcher has passed, this call should have no
     * effect. This call may cause the watcher to be removed.
     *
     * @param transition A <code>EntryTransition</code> that
     *              describes the transition and what
     *              entry is transitioning. This method
     *              will assume that <code>transition.getHandle</code>
     *              returns a non-null value.
     * @param now   An estimate of the current time (not the time
     *              when the event occured).
     * @throws NullPointerException if <code>transition</code> is 
     *         <code>null</code>.  
     */
    abstract void process(EntryTransition transition, long now);
} 
