/*
* Licensed Materials - Property of Rogue Wave Software, Inc.
* © Copyright Rogue Wave Software, Inc. 2014, 2017
* © Copyright IBM Corp. 2009, 2014
* © Copyright ILOG 1996, 2009
* All Rights Reserved.
*
* Note to U.S. Government Users Restricted Rights:
* The Software and Documentation were developed at private expense and
* are "Commercial Items" as that term is defined at 48 CFR 2.101,
* consisting of "Commercial Computer Software" and
* "Commercial Computer Software Documentation", as such terms are
* used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
* as applicable.
*/
package demo.runner;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import javax.servlet.http.HttpServletRequest;
import ilog.views.IlvGrapher;
import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.IlvManagerView;
import ilog.views.IlvRect;
import ilog.views.diagrammer.IlvDiagrammer;
import ilog.views.sdm.IlvSDMEngine;
/**
* A base class to <i>run</i> a diagram, which means to simulate real-time data
* feed by dynamically changing properties of the nodes and links of an
* {@link IlvDiagrammer}.
*/
public abstract class DiagramRunner {
// The diagram component that contains a data model to be modified
protected IlvDiagrammer _diagrammer;
// Whether the simulation should run constantly, or if it should stop
// at the end of the diagram
private boolean _looping;
// Update interval between each simulation step
private int _period = 10000;
// The Timer scheduling the simulation steps on a background thread
private Timer _timer;
// The TimerTask representing the simulation steps, executed
// repeatedly by the Timer
private TimerTask _task;
// Property name to store the DiagramRunner inside the IlvDiagrammer
private static final String runnerProperty = "_IlvDiagrammer_DiagramRunner";
/**
* Constructor.
*
* @param diagrammer
* The diagrammer to be <i>run</i>.
*/
public DiagramRunner(IlvDiagrammer diagrammer) {
// Keep a reference to the IlvDiagrammer
_diagrammer = diagrammer;
// Add this DiagramRunner into the IlvDiagrammer as a property
_diagrammer.putClientProperty(runnerProperty, this);
// Apply the StyleSheet (CSS) to this IlvDiagrammer
diagrammer.getEngine().applyStyle(this, "DiagramRunner", null);
}
/**
* Returns the {@link DiagramRunner} attached to the given
* {@link IlvDiagrammer} instance.
*
* @param diagrammer
* The {@link IlvDiagrammer} instance.
*/
public static DiagramRunner getRunner(IlvDiagrammer diagrammer) {
// Tries to retrieve the DiagramRunner from the IlvDiagrammer (as a
// property)
return (DiagramRunner) diagrammer.getClientProperty(runnerProperty);
}
/**
* Returns whether the simulation should repeat continuously or not.
*/
public boolean isLooping() {
return _looping;
}
/**
* Defines whether the simulation should repeat continuously or not.
*
* @param looping
* If <code>true</code>, the simulation runs continuously.
*/
public void setLooping(boolean looping) {
_looping = looping;
}
/**
* Returns the time interval between each simulation step, in milliseconds.
*/
int getPeriod() {
return _period;
}
/**
* Defines the time interval between each simulation step, in milliseconds.
*
* @param period
* The new time interval.
*/
void setPeriod(int period) {
_period = period;
}
/**
* Abstract method called on all objects when the simulation is started.
*
* @param object
* The object being initialized
*/
protected abstract void init(Object object);
/**
* Abstract method to return the manager bounding box according to the current
* simulation state.
*
* @param req
* The client request
* @param view
* The manager view associated with the {@link IlvDiagrammer}
*
* @return The bounding box corresponding to the current simulation state.
*/
protected abstract IlvRect getManagerBBox(HttpServletRequest req, IlvManagerView view);
/**
* Abstratct method that is called at every step of the simulation.
*/
protected abstract void step();
/**
* Returns a new <code>TimerTask</code>, which invokes {@link #step()} when
* executed.
*/
private TimerTask newTask() {
return new TimerTask() {
Override
public void run() {
step();
}
};
}
/**
* Restarts the simulation.
*/
public void restart() {
if (null != _task) {
try {
// Tries to cancel the current task
_task.cancel();
} catch (Exception e) {
System.err.println(e.toString());
}
// Instantiates new Timer and TimerTask
_timer = new Timer();
_task = newTask();
// Schedule TimerTask for execution
try {
System.err.println("schedule runnable " + getPeriod());
_timer.schedule(_task, 0, getPeriod());
} catch (Exception e) {
System.err.println(e.toString());
}
}
}
/**
* Starts the simulation
*/
public void start() {
// Invoke 'init()' method for every object
_diagrammer.setAdjusting(true);
Iterator<?> it = _diagrammer.getAllObjects();
while (it.hasNext()) {
init(it.next());
}
_diagrammer.setAdjusting(false);
// Instantiates new Timer and TimerTask
_timer = new Timer();
_task = newTask();
// Schedule TimerTask for execution
try {
System.err.println("schedule runnable " + getPeriod());
_timer.schedule(_task, 0, getPeriod());
} catch (Exception e) {
System.err.println(e.toString());
}
}
/**
* Stops the simulation
*/
public void stop() {
if (null != _timer) {
// Cancels Timer
_timer.cancel();
_task.cancel();
}
_task = null;
_timer = null;
}
/**
* A subclass of {@link DiagramRunner} that modifies some properties of one
* object after another.
*/
public static abstract class Sequence extends DiagramRunner {
// The current object, that is, the last object that was modified.
private Object _current;
/**
* Constructor
*
* @param diagrammer
* The {@link IlvDiagrammer} to be <i>run</i>
*/
public Sequence(IlvDiagrammer diagrammer) {
super(diagrammer);
}
/**
* Method to return the manager bounding box around the current object being
* updated.
*
* @param req
* The client request
* @param view
* The manager view associated with the {@link IlvDiagrammer}
*
* @return The bounding box corresponding to the current simulation state.
*/
Override
protected IlvRect getManagerBBox(HttpServletRequest req, IlvManagerView view) {
if (null == _current) {
return null;
}
// Get the graphic corresponding to the current object
IlvGraphic g = _diagrammer.getEngine().getGraphic(_current, false);
// Get the bounding box of the graphic
IlvRect rect = new IlvRect(g.boundingBox());
// Expand the bounding box to two times the largest dimension
rect.expand(2 * Math.max(rect.width + 5, rect.height + 5));
return rect;
}
/**
* <p>
* Calls the {@link #next(Object)} method to determine what is the next
* object to be modified.
* </p>
*
* <p>
* Then calls {@link #leave(Object)} on the current object.
* </p>
*
* <p>
* And finally calls {@link #enter(Object)} on the new object.
* </p>
*/
Override
protected void step() {
Object next = next(_current);
if (null != _current) {
leave(_current);
}
if (null != next) {
_current = next;
enter(_current);
} else {
stop();
}
}
/**
* Overrides method to clean the current object.
*/
Override
public void start() {
_current = null;
super.start();
}
/**
* Overrides method to clean the current object.
*/
Override
public void stop() {
super.stop();
if (null != _current) {
leave(_current);
_current = null;
}
if (isLooping()) {
start();
}
}
/**
* Abstract method to compute the next object to be updated in the
* simulation. The simulation stops if this method returns
* <code>null</code>.
*
* @param current
* The current object.
*
* @return The next object to be updated in the simulation, or
* <code>null</code>.
*/
protected abstract Object next(Object current);
/**
* Abstract method called at each simulation step to indicate that the
* updates on the current object is about to be completed.
*
* @param current
* The current object being finalized.
*/
protected abstract void leave(Object current);
/**
* Abstract method called at each animation step on the next object to be
* updated by the simulation.
*
* @param next
* The object that will become the new current object
*/
protected abstract void enter(Object next);
}
/**
* Implementation of {@link Sequence} that follows a flow diagram in an
* {@link IlvDiagrammer}.
*/
public static class Flow extends Sequence {
/**
* Constructor.
*
* @param diagrammer
* The diagrammer to be <i>run</i>.
*/
public Flow(IlvDiagrammer diagrammer) {
super(diagrammer);
}
/**
* <p>
* Returns the next object, following the flow of the diagram:
* </p>
* <ul>
* <li>The first object is the one that has no incoming links.</li>
* <li>The next object of a link is its destination node.</li>
* <li>When a node has several outgoing links, one of the links is chosen
* randomly, otherwise the next object is the outgoing link.</li>
* </ul>
*
* @param current
* The current object.
*/
Override
protected Object next(Object current) {
if (null == current) {
// There is no 'current' object, tries to start from the
// currently selected object
Iterator<?> selection = _diagrammer.getSelectedObjects();
if (selection.hasNext()) {
return selection.next();
}
// No object is selected, try to find an object with
// no incoming links
Iterator<?> objs = _diagrammer.getAllObjects();
while (objs.hasNext()) {
Object o = objs.next();
if (getLinks(o, false).size() == 0) {
return o;
}
}
// Fallback option, return any object in the diagram
objs = _diagrammer.getAllObjects();
if (objs.hasNext()) {
return objs.next();
}
// No objects in the diagram
return null;
} else if (_diagrammer.isLink(current)) {
// The current object is a link, return its 'to' endpoint
return _diagrammer.getTargetNode(current);
} else {
// The current object is a node, try to get a 'from' link
ArrayList<?> links = getLinks(current, true);
if (links.size() == 0) {
// No 'from' links, the simulation is over
return null;
} else if (links.size() == 1) {
// Just one 'from' link, use it
return links.get(0);
} else {
// Multiple 'from' links, see if there is a "preferred" path.
for (Object link : links) {
if (_diagrammer.getObjectProperty(link, "sdm:preferred") != null) {
return link;
}
}
// Otherwise choose randomly.
return links.get((int) Math.round(Math.random() * (links.size() - 1)));
}
}
}
/**
* Method invoked on every object in the model when the simulation starts
*/
/**
* <p>
* Method called on all objects when the simulation is started.
* </p>
*
* <p>
* Ensures that the node is <b>not</b> selected, and sets the object
* property <i>status</i> to <i>empty</i>.
*
* @param object
* The object being initialized
*/
Override
protected void init(Object object) {
_diagrammer.setSelected(object, false);
// Simply set the property value to empty
// This will ensure that the active nor the
// done state is represented
_diagrammer.setObjectProperty(object, "status", "");
}
/**
* <p>
* Method called at each animation step on the next object to be updated by
* the simulation.
* </p>
*
* <p>
* Ensure that the object is selected, then set the object property
* <i>status</i> to <i>active</i>.
* </p>
*
* @param next
* The object that will become the new current object
*/
Override
protected void enter(Object next) {
_diagrammer.setSelected(next, true);
_diagrammer.setObjectProperty(next, "status", "active");
}
/**
* <p>
* Method called at each simulation step to indicate that the updates on the
* current object is about to be completed.
* </p>
*
* <p>
* Ensures that the object is not selected, then set the object property
* <i>status</i> to <i>done</i>.
* </p>
*
* @param current
* The current object being finalized.
*/
Override
protected void leave(Object previous) {
_diagrammer.setSelected(previous, false);
_diagrammer.setObjectProperty(previous, "status", "done");
}
/**
* Private method to retrieve either the links ending on a node (<i>to</i>)
* or the links starting on a node (<i>from</i>).
*
* @param node
* The node connected to links
* @param from
* Whether the <i>from</i> or <i>to</i> links should be returned
*
* @return A list of links either starting (<code>from</code> parameter is
* <code>true</code>) or ending in the given node.
*/
private ArrayList<?> getLinks(Object node, boolean from) {
// Get the SDM engine from the IlvDiagrammer
IlvSDMEngine engine = _diagrammer.getEngine();
// Get the graphic representing the given node
IlvGraphic nodeGraphic = engine.getGraphic(node, false);
// Get the grapher containing all objects
IlvGrapher grapher = engine.getGrapher();
// Gets either the 'from' or 'to' links connected to the node
IlvGraphicEnumeration linksEnum = from ? grapher.getLinksFrom(nodeGraphic) : grapher.getLinksTo(nodeGraphic);
// Converts the enumeration into an ArrayList
ArrayList<Object> links = new ArrayList<Object>();
while (linksEnum.hasMoreElements()) {
links.add(engine.getObject(linksEnum.nextElement()));
}
return links;
}
}
}