/* * Licensed Materials - Property of Rogue Wave Software, Inc. * © Copyright Rogue Wave Software, Inc. 2014, 2015 * © 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 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; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.beans.SimpleBeanInfo; import java.util.ArrayList; import java.util.Iterator; import java.util.ResourceBundle; import java.io.IOException; /** * 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. */ 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(); public void start() { super.start(); diagrammer.setAdjusting(true); Iterator it = diagrammer.getAllObjects(); while (it.hasNext()) { init(it.next()); } diagrammer.setAdjusting(false); } 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. */ protected void step() { Object next = next(current); if(current != null){ leave(current); } if(next != null){ current = next; enter(current); } else { stop(); } } public void start() { current = null; super.start(); } 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. */ 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 { ArrayList 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 ArrayList 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); ArrayList links = new ArrayList(); while(linksEnum.hasMoreElements()){ links.add(engine.getObject(linksEnum.nextElement())); } return links; } 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. */ protected void enter(Object next) { diagrammer.setSelected(next, true); diagrammer.setObjectProperty(next, "status", "active"); diagrammer.scrollToObject(next); } /** * Deselects the previous object. */ 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")); } protected boolean isSelected(IlvDiagrammer diagrammer) throws Exception { return diagrammer != null && DiagramRunner.getRunner(diagrammer) != null && DiagramRunner.getRunner(diagrammer).isRunning(); } 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); } } } 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(); } } 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")); } protected boolean isSelected(IlvDiagrammer diagrammer) throws Exception { return diagrammer != null && ((DiagramRunner.getRunner(diagrammer) != null && DiagramRunner.getRunner(diagrammer).isLooping()) || Boolean.TRUE.equals(diagrammer.getClientProperty(loopProperty))); } 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)); } } } protected boolean isEnabled(IlvDiagrammer diagrammer) throws Exception { return true; } } }