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 <a href="http://jheer.org">jeffrey heer</a>
* @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() {
/*
* Prefuse Bug ID #1708926
* The fix ("Contribution") has not been tested and/or validated for release as or in products,
* combinations with products or other commercial use.
* Any use of the Contribution is entirely made at the user's own responsibility and the user can
* not rely on any features, functionalities or performances Alcatel-Lucent has attributed to the Contribution.
* THE CONTRIBUTION BY ALCATEL-LUCENT (...) IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, COMPLIANCE, NON-INTERFERENCE AND/OR INTERWORKING WITH THE SOFTWARE
* TO WHICH THE CONTRIBUTION HAS BEEN MADE, TITLE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL ALCATEL-LUCENT (...) BE LIABLE FOR ANY DAMAGES OR OTHER LIABLITY,
* WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE CONTRIBUTION
* OR THE USE OR OTHER DEALINGS IN THE CONTRIBUTION,
* WHETHER TOGETHER WITH THE SOFTWARE TO WHICH THE CONTRIBUTION RELATES OR ON A STAND ALONE BASIS.
*/
ActivityManager.cancelActivity(this);
}
/**
* 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<a.length; ++i ) {
((ActivityListener)a[i]).activityScheduled(this);
}
}
protected void fireActivityStarted() {
if ( m_listeners == null ) return;
Object[] a = m_listeners.getArray();
for ( int i=0; i<a.length; ++i ) {
((ActivityListener)a[i]).activityStarted(this);
}
}
protected void fireActivityStepped() {
if ( m_listeners == null ) return;
Object[] a = m_listeners.getArray();
for ( int i=0; i<a.length; ++i ) {
((ActivityListener)a[i]).activityStepped(this);
}
}
protected void fireActivityFinished() {
if ( m_listeners == null ) return;
Object[] a = m_listeners.getArray();
for ( int i=0; i<a.length; ++i ) {
((ActivityListener)a[i]).activityFinished(this);
}
}
protected void fireActivityCancelled() {
if ( m_listeners == null ) return;
Object[] a = m_listeners.getArray();
for ( int i=0; i<a.length; ++i ) {
((ActivityListener)a[i]).activityCancelled(this);
}
}
// ------------------------------------------------------------------------
// Accessor / Mutator Methods
/**
* Returns a value between 0 and 1 inclusive, indicating the current
* position in an animation or other similarly parameterized activity.
* The returned value is determined by consulting this Activity's
* pacing function.
* @param elapsedTime the time in milliseconds since the start of this
* Activity.
* @return a value between 0 and 1 indicating the current position in
* an animation.
* @see prefuse.activity.Activity#getPacingFunction()
*/
public double getPace(long elapsedTime) {
long duration = getDuration();
double frac = (duration == 0L ? 0.0 : ((double)elapsedTime)/duration);
frac = Math.min(1, Math.max(0, frac));
return m_pacer!=null ? m_pacer.pace(frac) : frac;
}
/**
* Returns the pacing function associated with this Activity. Pacing
* functions are used to control the pace of animations.
* @return this Activity's pacing function. A value of null indicates a
* basic, linear pace is used, moving from 0 to 1 uniformly over time.
*/
public synchronized Pacer getPacingFunction() {
return m_pacer;
}
/**
* Sets the pacing function associated with this Activity. Pacing
* functions are used to control the pace of animations.
* @param pfunc this Activity's new pacing function, or null to
* indicate a basic, linear pace moving from 0 to 1 uniformly
* over time.
*/
public synchronized void setPacingFunction(Pacer pfunc) {
m_pacer = pfunc;
}
/**
* Get the time at which this activity should complete.
* @return the stopping time for this activity, or Long.MAX_VALUE
* if this activity should run indefinitely.
*/
public long getStopTime() {
if (m_duration == -1) {
return Long.MAX_VALUE;
}
return m_startTime + m_duration;
}
/**
* Get the time at which this activity should be run next.
* @return the time this activity should run next
*/
public long getNextTime() {
return m_nextTime;
}
/**
* Returns the duration of this activity
* @return the duration of this activity, in milliseconds
*/
public long getDuration() {
return m_duration;
}
/**
* Set the duration of this activity
* @param duration The new duration, in milliseconds, for this activity.
* A value of {@link #INFINITY} indicates that this activity should run
* indefinitely.
*/
public void setDuration(long duration) {
this.m_duration = duration;
}
/**
* Returns this activity's start time
* @return the starting time for this activity
*/
public long getStartTime() {
return m_startTime;
}
/**
* Sets this activity's start time
* @param time the new starting time for this activity
*/
public void setStartTime(long time) {
m_startTime = time;
}
/**
* Returns the delay between runs for this activity
* @return the step time between runs of this activity
*/
public long getStepTime() {
return m_stepTime;
}
/**
* Sets the delay between runs for this activity
* @param time the new step time between runs of this activity
*/
public void setStepTime(long time) {
m_stepTime = time;
}
/**
* Indicates whether or not this activity is currently enabled.
* @return true if enabled, false otherwise
*/
public boolean isEnabled() {
return m_enabled;
}
/**
* Sets whether this component is enabled.
* @param s true to enable component, false to disable it
*/
public void setEnabled(boolean s) {
m_enabled = s;
}
} // end of class Activity