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