//
// $Id: Interval.java,v 1.5 2004/02/25 13:20:44 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
// 
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.samskivert.util;

import java.util.Timer;
import java.util.TimerTask;

import com.samskivert.Log;

/**
 * An interface for doing operations after some delay.
 */
public abstract class Interval
{
    /**
     * Create a simple interval that does not use a RunQueue to run
     * the expire() method.
     */
    public Interval ()
    {
    }

    /**
     * Create an Interval that uses the specified RunQueue to run
     * the expire() method.
     */
    public Interval (RunQueue runQueue)
    {
        if (runQueue == null) {
            throw new NullPointerException("RunQueue cannot be null, " +
                "use other constructor if you want a simple Interval.");
        }
        _runQueue = runQueue;
    }

    /**
     *
     * The main method where your interval should do its work.
     *
     */
    public abstract void expired ();

    /**
     * Schedule the interval to execute once, after the specified delay.
     */
    public final void schedule (long delay)
    {
        schedule(delay, 0L);
    }

    /**
     * Schedule the interval to execute repeatedly, with the same delay.
     */
    public final void schedule (long delay, boolean repeat)
    {
        schedule(delay, repeat ? delay : 0L);
    }

    /**
     * Schedule the interval to execute repeatedly with the specified
     * initial delay and repeat delay.
     */
    public final void schedule (long initialDelay, long repeatDelay)
    {
        cancel();
        _task = new IntervalTask();

        if (repeatDelay == 0L) {
            _timer.schedule(_task, initialDelay);
        } else {
            _timer.scheduleAtFixedRate(_task, initialDelay, repeatDelay);
        }
    }

    /**
     * Cancel the Interval, and ensure that any expirations that are queued
     * up but have not yet run do not run.
     */
    public final void cancel ()
    {
        if (_task != null) {
            try {
                _task.cancel();
                _task = null;
            } catch (NullPointerException npe) {
                // This could happen if two threads call this method
                // simultaneously. I could prevent it by synchronizing
                // this method and schedule(), but this is less taxing
                // in the common case.
                // (Also, it's impossible to NPE inside task.cancel())
            }
        }
    }

    /**
     * Safely expire the interval.
     */
    protected final void safelyExpire (TimerTask task)
    {
        // only expire the interval if the task is still valid
        if (_task == task) {
            try {
                expired();
            } catch (Throwable t) {
                Log.warning("Interval broken in expired(): " + t);
                Log.logStackTrace(t);
            }
        }
    }

    /**
     * The task that schedules actually runs the interval.
     */
    protected class IntervalTask extends TimerTask
    {
        // documentation inherited
        public void run () {
            if (_runQueue == null || _runQueue.isDispatchThread()) {
                safelyExpire(this);
            } else {
                _runQueue.postRunnable(this);
            }
        }
    }

    /** If non-null, the RunQueue used to run the expired() method for each
     * Interval. */
    protected RunQueue _runQueue;

    /** The task that actually schedules our execution with the static Timer. */
    protected volatile TimerTask _task;

    /** The daemon timer used to schedule all Intervals. */
    protected static final Timer _timer =
        new Timer(/*JDK1.5 "samskivert Interval Timer",*/ true);
}

@@ -3483,32 +3483,89 @@
           try %7B%0A
+                System.err.println(%22Expiring Interval%22);%0A
                 
@@ -3875,24 +3875,293 @@
 rTask%0A    %7B%0A
+        %7B // initializer%0A            if (_runQueue != null) %7B%0A                _runner = new Runnable() %7B%0A                    public void run () %7B%0A                        safelyExpire(IntervalTask.this);%0A                    %7D%0A                %7D;%0A            %7D%0A        %7D%0A%0A
         // d
@@ -4249,40 +4249,8 @@
 null
- %7C%7C _runQueue.isDispatchThread()
 ) %7B%0A
@@ -4345,20 +4345,23 @@
 unnable(
-this
+_runner
 );%0A     
@@ -4370,32 +4370,140 @@
      %7D%0A        %7D
+%0A%0A        /** If we are using a RunQueue, the Runnable we post to it. */%0A        protected Runnable _runner;
 %0A    %7D%0A%0A    /** 

Create a special Runnable for posting to the RunQueue, rather than checking to see if we're on the RunQueue twice each firing.