Staging
v0.5.1
v0.5.1
https://repo1.maven.org/maven2/org/prefuse/prefuse
ActivityManager.java
package prefuse.activity;
import java.util.ArrayList;
import prefuse.util.PrefuseConfig;
/**
* <p>The ActivityManager is responsible for scheduling and running timed
* activities that perform data processing and animation.</p>
*
* <p>The AcivityManager runs in its own separate thread of execution, and
* one instance is used to schedule activities from any number of currently
* active visualizations. The class is implemented as a singleton; the single
* instance of this class is interacted with through static methods. These
* methods are called by an Activity's run methods, and so are made only
* package visible here.</p>
*
* <p>Activity instances can be scheduled by using their
* {@link prefuse.activity.Activity#run()},
* {@link prefuse.activity.Activity#runAt(long)}, and
* {@link prefuse.activity.Activity#runAfter(Activity)}
* methods. These will automatically call the
* appropriate methods with the ActivityManager.</p>
*
* <p>For {@link prefuse.action.Action} instances, one can also register
* the actions with a {@link prefuse.Visualization} and use the
* visualizations provided run methods to launch Actions in a
* convenient fashion. The interface, which is backed by an {@link ActivityMap}
* instance, also provides a useful level of indirection, allowing actions
* to be changed dynamically without changes to code in other locations.
* </p>
*
* @author <a href="http://jheer.org">jeffrey heer</a>
* @see Activity
* @see prefuse.action.Action
*/
public class ActivityManager extends Thread {
private static ActivityManager s_instance;
private ArrayList m_activities;
private ArrayList m_tmp;
private long m_nextTime;
private boolean m_run;
/**
* Returns the active ActivityManager instance.
* @return the ActivityManager
*/
private synchronized static ActivityManager getInstance() {
if ( s_instance == null || !s_instance.isAlive() ) {
s_instance = new ActivityManager();
}
return s_instance;
}
/**
* Create a new ActivityManger.
*/
private ActivityManager() {
super("prefuse_ActivityManager");
m_activities = new ArrayList();
m_tmp = new ArrayList();
m_nextTime = Long.MAX_VALUE;
int priority = PrefuseConfig.getInt("activity.threadPriority");
if ( priority >= Thread.MIN_PRIORITY &&
priority <= Thread.MAX_PRIORITY )
{
this.setPriority(priority);
}
this.setDaemon(true);
this.start();
}
/**
* Stops the activity manager thread. All scheduled actvities are
* canceled, and then the thread is then notified to stop running.
*/
public static void stopThread() {
ActivityManager am;
synchronized ( ActivityManager.class ) {
am = s_instance;
}
if ( am != null )
am._stop();
}
/**
* Schedules an Activity with the manager.
* @param a the Activity to schedule
*/
static void schedule(Activity a) {
getInstance()._schedule(a, a.getStartTime());
}
/**
* Schedules an Activity to start immediately, overwriting the
* Activity's currently set startTime.
* @param a the Activity to schedule
*/
static void scheduleNow(Activity a) {
getInstance()._schedule(a, System.currentTimeMillis());
}
/**
* Schedules an Activity at the specified startTime, overwriting the
* Activity's currently set startTime.
* @param a the Activity to schedule
* @param startTime the time at which the activity should run
*/
static void scheduleAt(Activity a, long startTime) {
getInstance()._schedule(a, startTime);
}
/**
* Schedules an Activity to start immediately after another Activity.
* The second Activity will be scheduled to start immediately after the
* first one finishes, overwriting any previously set startTime. If the
* first Activity is cancelled, the second 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 effect the scheduling of the first Activity.
* @param before the first Activity to run
* @param after the Activity to run immediately after the first
*/
static void scheduleAfter(Activity before, Activity after) {
getInstance()._scheduleAfter(before, after);
}
/**
* Schedules an Activity to start immediately after another Activity.
* The second Activity will be scheduled to start immediately after the
* first one finishes, overwriting any previously set startTime. If the
* first Activity is cancelled, the second 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 otherwise effect the scheduling of the first Activity.
* @param before the first Activity to run
* @param after the Activity to run immediately after the first
*/
static void alwaysScheduleAfter(Activity before, Activity after) {
getInstance()._alwaysScheduleAfter(before, after);
}
/**
* Removes an Activity from this manager, called by an
* Activity when it finishes or is cancelled. Application
* code should not call this method! Instead, use
* Activity.cancel() to stop a sheduled or running Activity.
* @param a
* @return true if the activity was found and removed, false
* if the activity is not scheduled with this manager.
*/
static void removeActivity(Activity a) {
getInstance()._removeActivity(a);
}
/**
* Returns the number of scheduled activities
* @return the number of scheduled activities
*/
public static int activityCount() {
return getInstance()._activityCount();
}
/**
* Stops the activity manager thread. All scheduled actvities are
* canceled, and then the thread is then notified to stop running.
*/
private synchronized void _stop() {
while ( m_activities.size() > 0 ) {
Activity a = (Activity)m_activities.get(m_activities.size()-1);
a.cancel();
}
_setRunning(false);
notify();
}
/**
* Schedules an Activity with the manager.
* @param a the Activity to schedule
*/
private void _schedule(Activity a, long startTime) {
if ( a.isScheduled() ) {
return; // already scheduled, do nothing
}
a.setStartTime(startTime);
synchronized ( this ) {
m_activities.add(a);
a.setScheduled(true);
if ( startTime < m_nextTime ) {
m_nextTime = startTime;
notify();
}
}
}
/**
* Schedules an Activity to start immediately after another Activity.
* The second Activity will be scheduled to start immediately after the
* first one finishes, overwriting any previously set startTime. If the
* first Activity is cancelled, the second 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 effect the scheduling of the first Activity.
* @param before the first Activity to run
* @param after the Activity to run immediately after the first
*/
private void _scheduleAfter(Activity before, Activity after) {
before.addActivityListener(new ScheduleAfterActivity(after,true));
}
/**
* Schedules an Activity to start immediately after another Activity.
* The second Activity will be scheduled to start immediately after the
* first one finishes, overwriting any previously set startTime. If the
* first Activity is cancelled, the second 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 otherwise effect the scheduling of the first Activity.
* @param before the first Activity to run
* @param after the Activity to run immediately after the first
*/
private void _alwaysScheduleAfter(Activity before, Activity after) {
before.addActivityListener(new ScheduleAfterActivity(after,false));
}
/**
* Removes an Activity from this manager, called by an
* Activity when it finishes or is cancelled. Application
* code should not call this method! Instead, use
* Activity.cancel() to stop a sheduled or running Activity.
* @param a
* @return true if the activity was found and removed, false
* if the activity is not scheduled with this manager.
*/
private boolean _removeActivity(Activity a) {
boolean r;
synchronized ( this ) {
r = m_activities.remove(a);
if ( r ) {
if ( m_activities.size() == 0 ) {
m_nextTime = Long.MAX_VALUE;
}
}
}
if ( r ) {
a.setScheduled(false);
}
return r;
}
/**
* Returns the number of scheduled activities
* @return the number of scheduled activities
*/
private synchronized int _activityCount() {
return m_activities.size();
}
/**
* Sets the running flag for the ActivityManager instance.
*/
private synchronized void _setRunning(boolean b) {
m_run = b;
}
/**
* Used by the activity loop to determine if the ActivityManager
* thread should keep running or exit.
*/
private synchronized boolean _keepRunning() {
return m_run;
}
/**
* Main scheduling thread loop. This is automatically started upon
* initialization of the ActivityManager.
*/
public void run() {
_setRunning(true);
while ( _keepRunning() ) {
if ( _activityCount() > 0 ) {
long currentTime = System.currentTimeMillis();
long t = -1;
synchronized (this) {
// copy content of activities, as new activities might
// be added while we process the current ones
for ( int i=0; i<m_activities.size(); i++ ) {
Activity a = (Activity)m_activities.get(i);
m_tmp.add(a);
// remove activities that won't be run again
if ( currentTime >= a.getStopTime() )
{
m_activities.remove(i--);
a.setScheduled(false);
}
}
// if no activities left, reflect that in the next time
if ( m_activities.size() == 0 ) {
m_nextTime = Long.MAX_VALUE;
}
}
for ( int i=0; i<m_tmp.size(); i++ ) {
// run the activity - the activity will check for
// itself if it should perform any action or not
Activity a = (Activity)m_tmp.get(i);
long s = a.runActivity(currentTime);
// compute minimum time for next activity cycle
t = (s<0 ? t : t<0 ? s : Math.min(t,s));
}
// clear the temporary list
m_tmp.clear();
if ( t == -1 ) continue;
// determine the next time we should run
try {
synchronized (this) { wait(t); }
} catch (InterruptedException e) { }
} else {
// nothing to do, chill out until notified
try {
synchronized (this) { wait(); }
} catch (InterruptedException e) { }
}
}
}
public class ScheduleAfterActivity extends ActivityAdapter {
Activity after;
boolean remove;
public ScheduleAfterActivity(Activity after, boolean remove) {
this.after = after;
this.remove = remove;
}
public void activityFinished(Activity a) {
if ( remove ) a.removeActivityListener(this);
scheduleNow(after);
}
public void activityCancelled(Activity a) {
if ( remove ) a.removeActivityListener(this);
}
} // end of inner class ScheduleAfterActivity
} // end of class ActivityManager