package prefuse.activity;
import prefuse.util.collections.CopyOnWriteArrayList;
/**
* Represents an activity that can be scheduled and run. This could include
* data processing, animation, and time-sensitive operations.
*
* @author jeffrey heer
* @see prefuse.activity.ActivityManager
* @see prefuse.action.Action
*/
public abstract class Activity {
public static final long INFINITY = -1L; // specifies infinite duration
public static final long DEFAULT_STEP_TIME = 15L;
private boolean m_enabled = true;
private Pacer m_pacer;
private long m_startTime = -1L;
private long m_duration = -1L;
private long m_stepTime = -1L;
private long m_nextTime = -1L;
private boolean m_isRunning = false;
private boolean m_isScheduled = false;
private CopyOnWriteArrayList m_listeners;
/**
* Creates a new Activity.
* @param duration the length of this activity.
* A value of {@link #INFINITY} indicates an infinite running time.
* @see prefuse.activity.Activity#Activity(long, long, long)
*/
public Activity(long duration) {
this(duration, DEFAULT_STEP_TIME);
}
/**
* Creates a new Activity.
* @param duration the length of this activity.
* A value of {@link #INFINITY} indicates an infinite running time.
* @param stepTime the delay time between steps of this activity
* @see prefuse.activity.Activity#Activity(long, long, long)
*/
public Activity(long duration, long stepTime) {
this(duration, stepTime, System.currentTimeMillis());
}
/**
* Creates a new Activity.
* @param duration the length of this activity.
* A value of {@link #INFINITY} indicates an infinite running time.
* @param stepTime the delay time between steps of this activity
* @param startTime the time at which this activity should begin
*/
public Activity(long duration, long stepTime, long startTime) {
m_startTime = startTime;
m_nextTime = startTime;
m_duration = duration;
m_stepTime = stepTime;
}
/**
* Schedules this Activity to start immediately.
*/
public void run() {
ActivityManager.scheduleNow(this);
}
/**
* Schedules this Activity for the specified startTime, overwriting the
* Activity's currently set startTime.
* @param startTime the time at which the activity should run
*/
public void runAt(long startTime) {
ActivityManager.scheduleAt(this,startTime);
}
/**
* Schedules this Activity to start immediately after another Activity.
* This Activity will be scheduled to start immediately after the
* first one finishes, overwriting any previously set startTime. If the
* first Activity is cancelled, this one will not run.
*
* This functionality is provided by using an ActivityListener to monitor
* the first Activity. The listener is removed upon completion or
* cancellation of the first Activity.
*
* This method does not in any way affect the scheduling of the first
* Activity. If the first Activity is never scheduled, this Activity
* will correspondingly never be run unless scheduled by a separate
* scheduling call.
* @param before the Activity that must finish before this one starts
*/
public void runAfter(Activity before) {
ActivityManager.scheduleAfter(before, this);
}
/**
* Schedules this Activity to start immediately after another Activity.
* This Activity will be scheduled to start immediately after the
* first one finishes, overwriting any previously set startTime. If the
* first Activity is cancelled, this one will not run.
*
* This functionality is provided by using an ActivityListener to monitor
* the first Activity. The listener will persist across mulitple runs,
* meaning the second Activity will always be evoked upon a successful
* finish of the first.
*
* This method does not in any way affect the scheduling of the first
* Activity. If the first Activity is never scheduled, this Activity
* will correspondingly never be run unless scheduled by a separate
* scheduling call.
* @param before the Activity that must finish before this one starts
*/
public void alwaysRunAfter(Activity before) {
ActivityManager.alwaysScheduleAfter(before, this);
}
/**
* Run this activity one step. Subclasses should override this method to
* specify the actions this activity should perform.
* @param elapsedTime the time elapsed since the start of the activity.
*/
protected abstract void run(long elapsedTime);
/**
* Run this activity for a single step. This method is called by the
* ActivityManager -- outside code should have no need to call or
* override this method. To implement custom activities, override the
* run() method instead.
* @param currentTime the time at which this step is being run.
* @return the time (in milliseconds) when this activity should be
* run again. A return value of -1 indicates this activity is finished.
*/
long runActivity(long currentTime) {
if (currentTime < m_startTime) {
return m_startTime - currentTime;
}
long elapsedTime = currentTime - m_startTime;
if ( m_duration == 0 || currentTime >= getStopTime() )
{
if ( !setRunning(true) ) {
fireActivityStarted();
}
if ( m_enabled ) {
run(elapsedTime);
fireActivityStepped();
}
setRunning(false);
fireActivityFinished();
return -1;
}
else if ( currentTime >= m_nextTime )
{
if ( !setRunning(true) )
fireActivityStarted();
if ( m_enabled ) {
run(elapsedTime);
fireActivityStepped();
}
m_nextTime = currentTime + m_stepTime;
}
return (m_nextTime-currentTime);
}
/**
* Cancels this activity, if scheduled. This will stop a
* running activity, and will remove the activity from
* the ActivityManager's schedule.
*/
public void cancel() {
boolean fire = false;
synchronized ( this ) {
if ( isScheduled() ) {
// attempt to remove this activity, if the remove fails,
// this activity is not currently scheduled with the manager
ActivityManager.removeActivity(this);
fire = true;
}
setRunning(false);
}
if ( fire )
fireActivityCancelled();
}
/**
* Indicates if this activity is currently scheduled
* with the ActivityManager
* @return true if scheduled, false otherwise
*/
public synchronized boolean isScheduled() {
return m_isScheduled;
}
/**
* Sets whether or not this Activity has been scheduled. This method should
* only be called by the ActivityManager.
* @param s the scheduling state of this Activity
*/
void setScheduled(boolean s) {
boolean fire;
synchronized ( this ) {
fire = (s && !m_isScheduled);
m_isScheduled = s;
}
if ( fire )
fireActivityScheduled();
}
/**
* Sets a flag indicating whether or not this activity is currently running
* @param s the new running state of this activity
*/
synchronized boolean setRunning(boolean s) {
boolean b = m_isRunning;
m_isRunning = s;
return b;
}
/**
* Indicates if this activity is currently running.
* @return true if running, false otherwise
*/
public synchronized boolean isRunning() {
return m_isRunning;
}
// ------------------------------------------------------------------------
// Activity Listeners
/**
* Add an ActivityListener to monitor this activity's events.
* @param l the ActivityListener to add
*/
public void addActivityListener(ActivityListener l) {
if ( m_listeners == null )
m_listeners = new CopyOnWriteArrayList();
if ( !m_listeners.contains(l) )
m_listeners.add(l);
}
/**
* Remove a registered ActivityListener
* @param l the ActivityListener to remove
*/
public void removeActivityListener(ActivityListener l) {
if ( m_listeners == null )
return;
m_listeners.remove(l);
if ( m_listeners.size() == 0 )
m_listeners = null;
}
protected void fireActivityScheduled() {
if ( m_listeners == null ) return;
Object[] a = m_listeners.getArray();
for ( int i=0; i