/* * Licensed Materials - Property of Perforce Software, Inc. * © Copyright Perforce Software, Inc. 2014, 2021 * © 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; } } }