/*
* 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.
*/
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.SimpleBeanInfo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ResourceBundle;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.Timer;
import ilog.views.IlvGrapher;
import ilog.views.IlvGraphic;
import ilog.views.IlvGraphicEnumeration;
import ilog.views.diagrammer.IlvDiagrammer;
import ilog.views.diagrammer.application.IlvDiagrammerAction;
import ilog.views.sdm.IlvSDMEngine;
import ilog.views.util.IlvImageUtil;
/**
* A base class for "running" a diagram, that is, dynamically changing
* properties of a diagram's nodes and/or links to simulate a real-time data
* feed.
*/
public abstract class DiagramRunner extends Timer implements ActionListener {
// The diagram component that we want to animate.
//
IlvDiagrammer diagrammer;
private boolean looping;
private static final String runnerProperty = "_IlvDiagrammer_DiagramRunner";
private static final String loopProperty = "_IlvDiagrammer_DiagramRunner_Loop";
/**
* Creates a new diagram runner.
*
* @param delay
* The delay between two animation steps.
* @param diagrammer
* The diagrammer to be animated.
*/
public DiagramRunner(IlvDiagrammer diagrammer, int delay) {
super(delay, null);
setInitialDelay(0);
addActionListener(this);
this.diagrammer = diagrammer;
diagrammer.putClientProperty(runnerProperty, this);
diagrammer.getEngine().applyStyle(this, "DiagramRunner", null);
if (Boolean.TRUE.equals(diagrammer.getClientProperty(loopProperty))) {
setLooping(true);
}
diagrammer.putClientProperty(loopProperty, null);
}
/**
* Returns the diagram runner attached to an IlvDiagrammer object.
*
* @param diagrammer
* The diagram component.
*/
public static DiagramRunner getRunner(IlvDiagrammer diagrammer) {
return (DiagramRunner) diagrammer.getClientProperty(runnerProperty);
}
/**
* Returns true if the animation loops continuously.
*/
public boolean isLooping() {
return looping;
}
/**
* If looping is true, the animation loops continuously.
*/
public void setLooping(boolean looping) {
this.looping = looping;
}
/**
* Calls the "step" method inside an adjustment sequence.
*/
Override
public void actionPerformed(ActionEvent e) {
diagrammer.setAdjusting(true);
step();
diagrammer.setAdjusting(false);
}
/**
* Called on all objects when the animation is started.
*/
protected abstract void init(Object object);
/**
* This method is called at every step of the animation.
*/
protected abstract void step();
Override
public void start() {
super.start();
diagrammer.setAdjusting(true);
Iterator<?> it = diagrammer.getAllObjects();
while (it.hasNext()) {
init(it.next());
}
diagrammer.setAdjusting(false);
}
Override
public void stop() {
super.stop();
if (looping) {
setInitialDelay(getDelay());
start();
} else {
IlvDiagrammerAction.updateActions(diagrammer.getRootPane(), diagrammer);
}
}
/**
* A subclass of 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;
public Sequence(IlvDiagrammer diagrammer, int delay) {
super(diagrammer, delay);
}
/**
* Calls the "next" method to determine which object to modify next. Then
* calls "leave" on the previous object, and "enter" on the new object.
*/
Override
protected void step() {
Object next = next(current);
if (current != null) {
leave(current);
}
if (next != null) {
current = next;
enter(current);
} else {
stop();
}
}
Override
public void start() {
current = null;
super.start();
}
Override
public void stop() {
super.stop();
if (current != null) {
leave(current);
current = null;
}
}
/**
* Returns the next object to animate. If this method returns null, the
* animation stops.
*
* @param previous
* The current object.
*/
protected abstract Object next(Object previous);
/**
* Called at each animation step on the previous current object.
*
* @param previous
* The previous current object.
*/
protected abstract void leave(Object previous);
/**
* Called at each animation step on the new current object.
*
* @param next
* The new current object.
*/
protected abstract void enter(Object next);
}
/**
* Bean info class
*/
public class SequenceBeanInfo extends SimpleBeanInfo {
/**
* Constructor
*/
public SequenceBeanInfo() {
}
}
public static class Flow extends Sequence {
public Flow(IlvDiagrammer diagrammer, int delay) {
super(diagrammer, delay);
}
/**
* Returns the next object, following the flow of the diagram: - The first
* object is the one that has no incoming links. - The next object of a link
* is its destination node. - When a node has several outgoing links, one of
* the links is chosen randomly, otherwise the next object is the outgoing
* link.
*
* @param previous
* The current object.
*/
Override
protected Object next(Object previous) {
if (previous == null) {
Iterator<?> selection = diagrammer.getSelectedObjects();
if (selection.hasNext()) {
return selection.next();
} else {
Iterator<?> objs = diagrammer.getAllObjects();
while (objs.hasNext()) {
Object o = objs.next();
if (getLinks(o, false).size() == 0) {
return o;
}
}
objs = diagrammer.getAllObjects();
if (objs.hasNext())
return objs.next();
else
return null;
}
} else if (diagrammer.isLink(previous)) {
return diagrammer.getTargetNode(previous);
} else {
List<Object> links = getLinks(previous, true);
if (links.size() == 0) {
return null;
} else if (links.size() == 1) {
return links.get(0);
} else {
// See if there is a "preferred" path.
//
for (int i = 0; i < links.size(); i++) {
Object link = links.get(i);
if (diagrammer.getObjectProperty(link, "sdm:preferred") != null) {
return link;
}
}
// Otherwise choose randomly.
//
return links.get((int) Math.round(Math.random() * (links.size() - 1)));
}
}
}
private List<Object> getLinks(Object node, boolean from) {
IlvSDMEngine engine = diagrammer.getEngine();
IlvGrapher grapher = engine.getGrapher();
IlvGraphic nodeGraphic = engine.getGraphic(node, false);
IlvGraphicEnumeration linksEnum = from ? grapher.getLinksFrom(nodeGraphic) : grapher.getLinksTo(nodeGraphic);
List<Object> links = new ArrayList<Object>();
while (linksEnum.hasMoreElements()) {
links.add(engine.getObject(linksEnum.nextElement()));
}
return links;
}
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", "");
}
/**
* Selects the next object.
*/
Override
protected void enter(Object next) {
diagrammer.setSelected(next, true);
diagrammer.setObjectProperty(next, "status", "active");
diagrammer.scrollToObject(next);
}
/**
* Deselects the previous object.
*/
Override
protected void leave(Object previous) {
diagrammer.setSelected(previous, false);
diagrammer.setObjectProperty(previous, "status", "done");
}
}
/**
* Bean info class
*/
public class FlowBeanInfo extends SimpleBeanInfo {
/**
* Constructor
*/
public FlowBeanInfo() {
}
}
/**
* An action that starts/stops a diagram runner.
*/
public static abstract class Run extends IlvDiagrammerAction.ToggleAction {
public Run() {
super("Run", ResourceBundle.getBundle("runner"));
}
Override
protected boolean isSelected(IlvDiagrammer diagrammer) throws Exception {
return diagrammer != null && DiagramRunner.getRunner(diagrammer) != null
&& DiagramRunner.getRunner(diagrammer).isRunning();
}
Override
protected void setSelected(IlvDiagrammer diagrammer, boolean selected) throws Exception {
if (diagrammer != null) {
DiagramRunner runner = DiagramRunner.getRunner(diagrammer);
if (selected) {
if (runner == null) {
runner = createRunner(diagrammer);
}
runner.start();
} else if (runner != null) {
boolean oldLooping = runner.isLooping();
runner.setLooping(false);
runner.stop();
runner.setLooping(oldLooping);
setIcon(Action.SMALL_ICON);
}
}
}
Override
protected void setSelected(boolean selected) {
super.setSelected(selected);
if (selected) {
setIcon("StopIcon");
} else {
setIcon(Action.SMALL_ICON);
}
}
private void setIcon(String key) {
try {
String s = getResourceBundle().getString(getPrefix() + "." + key);
Image image = null;
image = IlvImageUtil.getImageFromFile(getClass(), s);
putValue(Action.SMALL_ICON, new ImageIcon(image));
} catch (IOException e) {
e.printStackTrace();
}
}
Override
protected boolean isEnabled(IlvDiagrammer diagrammer) throws Exception {
return true;
}
protected abstract DiagramRunner createRunner(IlvDiagrammer diagrammer);
}
/**
* An action that toggle continuous looping of the animation.
*/
public static class Loop extends IlvDiagrammerAction.ToggleAction {
public Loop() {
super("Loop", ResourceBundle.getBundle("runner"));
}
Override
protected boolean isSelected(IlvDiagrammer diagrammer) throws Exception {
return diagrammer != null
&& ((DiagramRunner.getRunner(diagrammer) != null && DiagramRunner.getRunner(diagrammer).isLooping())
|| Boolean.TRUE.equals(diagrammer.getClientProperty(loopProperty)));
}
Override
protected void setSelected(IlvDiagrammer diagrammer, boolean selected) throws Exception {
if (diagrammer != null) {
DiagramRunner runner = DiagramRunner.getRunner(diagrammer);
if (runner != null) {
runner.setLooping(selected);
} else {
diagrammer.putClientProperty(loopProperty, new Boolean(selected));
}
}
}
Override
protected boolean isEnabled(IlvDiagrammer diagrammer) throws Exception {
return true;
}
}
}