All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.jbotsim.core.Topology Maven / Gradle / Ivy

There is a newer version: 1.2.0
Show newest version
/*
 * Copyright 2008 - 2019, Arnaud Casteigts and the JBotSim contributors 
 *
 *
 * This file is part of JBotSim.
 *
 * JBotSim is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JBotSim is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JBotSim.  If not, see .
 *
 */
package io.jbotsim.core;

import io.jbotsim.core.Link.Mode;
import io.jbotsim.core.Link.Type;
import io.jbotsim.core.event.CommandListener;
import io.jbotsim.core.event.*;
import io.jbotsim.io.FileManager;
import io.jbotsim.io.TopologySerializer;
import io.jbotsim.io.format.plain.PlainTopologySerializer;

import java.util.*;

/**
 * 

The {@link Topology} object is the main entry point of JBotSim.

* * It provides several features and convenience accessors, but at its core, it contains a set of {@link Node} objects * which can be linked two by two with a set of {@link Link} objects. */ public class Topology extends Properties implements ClockListener { public static final int DEFAULT_WIDTH = 600; public static final int DEFAULT_HEIGHT = 400; public static final double DEFAULT_COMMUNICATION_RANGE = 100; public static final double DEFAULT_SENSING_RANGE = 0; /** * The name under which the default Node model is stored. */ public static final String DEFAULT_NODE_MODEL_NAME = "default"; ClockManager clockManager; List cxUndirectedListeners = new ArrayList<>(); List cxDirectedListeners = new ArrayList<>(); List topologyListeners = new ArrayList<>(); List movementListeners = new ArrayList<>(); List messageListeners = new ArrayList<>(); List selectionListeners = new ArrayList<>(); List startListeners = new ArrayList<>(); MessageEngine messageEngine = null; Scheduler scheduler; List nodes = new ArrayList<>(); List arcs = new ArrayList<>(); List edges = new ArrayList<>(); HashMap> nodeModels = new HashMap>(); boolean isWirelessEnabled = true; double communicationRange = DEFAULT_COMMUNICATION_RANGE; double sensingRange = DEFAULT_SENSING_RANGE; int width; int height; LinkResolver linkResolver = new LinkResolver(); Node selectedNode = null; ArrayList toBeUpdated = new ArrayList<>(); private boolean step = false; private boolean isStarted = false; private int nextID = 0; private FileManager fileManager = new FileManager(); private TopologySerializer topologySerializer = new PlainTopologySerializer(); public enum RefreshMode {CLOCKBASED, EVENTBASED} RefreshMode refreshMode = RefreshMode.EVENTBASED; /** * Creates a topology. */ public Topology() { this(DEFAULT_WIDTH, DEFAULT_HEIGHT); } /** * Creates a topology of given dimensions. * @param width the {@link Topology}'s width, as an integer. * @param height the {@link Topology}'s height, as an integer. */ public Topology(int width, int height) { setMessageEngine(new MessageEngine()); setScheduler(new Scheduler()); setDimensions(width, height); clockManager = new ClockManager(this); } /** * Returns the node class corresponding to that name. * @param modelName a {@link String} identifying the node model. * @return the node model registered for the provided modelName. */ public Class getNodeModel(String modelName) { if(nodeModels == null || nodeModels.isEmpty()) return Node.class; return nodeModels.get(modelName); } /** * Returns the default node model, * all properties assigned to this virtual node will be given to further nodes created * without explicit model name. * @return the default node model. */ public Class getDefaultNodeModel() { return getNodeModel(DEFAULT_NODE_MODEL_NAME); } /** * Adds the given node instance as a model. * * @param modelName a {@link String} identifying the node model. * @param nodeClass the node model: a {@link Class} object a class extending the {@link Node} class. */ public void setNodeModel(String modelName, Class nodeClass) { nodeModels.put(modelName, nodeClass); } /** * Sets the default node model to the given node instance. * * @param nodeClass the default node model: a {@link Class} object a class extending the {@link Node} class. */ public void setDefaultNodeModel(Class nodeClass) { setNodeModel(DEFAULT_NODE_MODEL_NAME, nodeClass); } /** * Returns the set registered node classes. * @return the {@link Set} of known model names. */ public Set getModelsNames() { return nodeModels.keySet(); } /** * Create a new instance of this type of node. * * @param modelName a {@link String} identifying the node model. * @return a new instance of this type of node */ public Node newInstanceOfModel(String modelName) { try { return getNodeModel(modelName).newInstance(); } catch (IllegalAccessException e) { e.printStackTrace(); System.err.println("(is your class of node public?)"); } catch (NullPointerException e) { e.printStackTrace(); System.err.println("(does your Node belong to a Topology?)"); } catch (Exception e) { e.printStackTrace(); } return new Node(); } public boolean isStarted() { // FIXME Ambiguous for the user return isStarted; } /** * Sets the updates (links, sensed objects, etc.) to be instantaneous (EVENTBASED), * or periodic after each round (CLOCKBASED). * @param refreshMode the {@link RefreshMode} to use. */ public void setRefreshMode(RefreshMode refreshMode) { this.refreshMode = refreshMode; } /** * Returns the current refresh mode (CLOCKBASED or EVENTBASED). * @return the current {@link RefreshMode}. */ public RefreshMode getRefreshMode() { return refreshMode; } /** * Enables this node's wireless capabilities. */ public void enableWireless() { setWirelessStatus(true); } /** * Disables this node's wireless capabilities. */ public void disableWireless() { setWirelessStatus(false); } /** * Set wireless capabilities status * @param enabled the new wireless status: true to enable, * false otherwise. */ public void setWirelessStatus(boolean enabled) { if (enabled == isWirelessEnabled) return; isWirelessEnabled = enabled; for (Node node : nodes) node.setWirelessStatus(enabled); } /** * Returns true if wireless links are enabled. * @return true if the wireless links are enabled, * false otherwise. */ public boolean getWirelessStatus() { return isWirelessEnabled; } /** * Returns the default communication range. * * @return the default communication range */ public double getCommunicationRange() { return communicationRange; } /** * Sets the default communication range. * If the topology already has some nodes, their range is changed. * * @param communicationRange The communication range */ public void setCommunicationRange(double communicationRange) { this.communicationRange = communicationRange; for (Node node : nodes) node.setCommunicationRange(communicationRange); setProperty("communicationRange", communicationRange); // for notification purpose } /** * Returns the default sensing range, * * @return the default sensing range */ public double getSensingRange() { return sensingRange; } /** * Sets the default sensing range. * If the topology already has some nodes, their range is changed. * * @param sensingRange The sensing range */ public void setSensingRange(double sensingRange) { this.sensingRange = sensingRange; for (Node node : nodes) node.setSensingRange(sensingRange); setProperty("sensingRange", sensingRange); // for notification purpose } /** * Gets a reference on the message engine of this topology. * @return the current {@link MessageEngine}. */ public MessageEngine getMessageEngine() { return messageEngine; } /** * Sets the message engine of this topology. * @param messageEngine the new {@link MessageEngine}. */ public void setMessageEngine(MessageEngine messageEngine) { this.messageEngine = messageEngine; messageEngine.setTopology(this); } /** * Gets a reference on the scheduler. * @return the current {@link Scheduler}. */ public Scheduler getScheduler() { return scheduler; } /** * Sets the scheduler of this topology. * @param scheduler the new {@link Scheduler}. */ public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; } /** * Returns the global duration of a round in this topology (in millisecond). * * @return The duration */ public int getTimeUnit() { return clockManager.getTimeUnit(); } /** * Sets the global duration of a round in this topology (in millisecond). * * @param period The desired duration */ public void setTimeUnit(int period) { clockManager.setTimeUnit(period); } /** * Returns the clock model currently in use. * @return the current clock model. */ public Class getClockModel() { return clockManager.getClockModel(); } /** * Sets the clock model (to be instantiated automatically). * * @param clockModel A class that extends JBotSim's abstract Clock */ public void setClockModel(Class clockModel) { clockManager.setClockModel(clockModel); } /** * Returns the current time (current round number) * @return the current time. */ public int getTime() { return clockManager.currentTime(); } /** * Indicates whether the internal clock is currently running or in pause. * * @return true if running, false if paused. */ public boolean isRunning() { return clockManager.isRunning(); } /** * Pauses the clock (or increments the pause counter). */ public void pause() { clockManager.pause(); } /** * Resumes the clock (or decrements the pause counter). */ public void resume() { clockManager.resume(); } /** * Reset the round number to 0. */ public void resetTime() { clockManager.reset(); } /** * Sets the topology dimensions as indicated. * @param width the {@link Topology}'s width, as an integer. * @param height the {@link Topology}'s height, as an integer. */ public void setDimensions(int width, int height) { this.width = width; this.height = height; } /** * Returns the width of this topology. * @return the width, as an integer. */ public int getWidth() { return width; } /** * Returns the height of this topology. * @return the height, as an integer. */ public int getHeight() { return height; } /** * Initializes the clock. */ public void start() { clockManager.start(); isStarted = true; restart(); } /** * (Re)init the nodes through their onStart() method (and notifies StartListeners as well) */ public void restart() { pause(); resetTime(); clearMessages(); for (Node n : nodes) n.onStart(); for (StartListener listener : startListeners) listener.onStart(); resume(); } /** * Removes all the nodes (and links) of this topology. */ public void clear() { while (!nodes.isEmpty()) removeNode(nodes.get(nodes.size() - 1)); nextID = 0; } /** * Removes all the links of this topology. */ public void clearLinks() { while (!edges.isEmpty()) removeLink(edges.get(edges.size() - 1)); } /** * Removes all the ongoing messages in this topology. */ public void clearMessages() { for (Node n : nodes) { n.sendQueue.clear(); n.mailBox.clear(); } } /** * Performs a single round, then switch to pause state. */ public void step() { resume(); step = true; if(!isStarted()) start(); } /** * Adds the specified node to this topology. The location of the node * in the topology will be its current inherent location (or (0,0) * if no location was prealably given to it). * * @param n The node to be added. */ public void addNode(Node n) { addNode(n.getX(), n.getY(), n); } /** * Adds a new node to this topology at the specified location. * * @param x The abscissa of the location. * @param y The ordinate of the location. */ public void addNode(double x, double y) { addNode(x, y, newInstanceOfModel(DEFAULT_NODE_MODEL_NAME)); } /** * Adds the specified node to this topology at the specified location. * * @param x The abscissa of the location. * @param y The ordinate of the location. * @param n The node to be added. */ public void addNode(double x, double y, Node n) { pause(); if (x == -1) x = Math.random() * width; if (y == -1) y = Math.random() * height; if (n.getX() == 0 && n.getY() == 0) n.setLocation(x, y); if (n.communicationRange == null) n.setCommunicationRange(communicationRange); if (n.sensingRange == null) n.setSensingRange(sensingRange); if (isWirelessEnabled == false) n.disableWireless(); if (n.getID() == -1) n.setID(nextID++); nodes.add(n); n.topo = this; notifyNodeAdded(n); if (isStarted) n.onStart(); touch(n); resume(); } /** * Removes the specified node from this topology. All adjacent links will * be automatically removed. * * @param n The node to be removed. */ public void removeNode(Node n) { pause(); n.onStop(); for (Link l : n.getLinks(true)) removeLink(l); notifyNodeRemoved(n); nodes.remove(n); for (Node n2 : nodes) { if (n2.sensedNodes.contains(n)) { n2.sensedNodes.remove(n); n2.onSensingOut(n); } } n.topo = null; resume(); } /** * Selects the specified {@link Node} in this {@link Topology}. * * @param n The {@link Node} to be selected. */ public void selectNode(Node n) { selectedNode = n; n.onSelection(); notifyNodeSelected(n); } /** * Adds the specified link to this topology. Calling this method makes * sense only for wired links, since wireless links are automatically * managed as per the nodes' communication ranges. * * @param l The link to be added. */ public void addLink(Link l) { addLink(l, false); } /** * Adds the specified link to this topology without notifying the listeners * (if silent is true). Calling this method makes sense only for wired * links, since wireless links are automatically managed as per the nodes' * communication ranges. * * @param l The link to be added. * @param silent true to disable notifications of this adding. */ public void addLink(Link l, boolean silent) { if (l.type == Type.DIRECTED) { arcs.add(l); l.source.outLinks.put(l.destination, l); if (l.destination.outLinks.containsKey(l.source)) { Link edge = new Link(l.source, l.destination, Type.UNDIRECTED, l.mode); edges.add(edge); if (!silent) notifyLinkAdded(edge); } } else { // UNDIRECTED Link arc1 = l.source.outLinks.get(l.destination); Link arc2 = l.destination.outLinks.get(l.source); if (arc1 == null) { arc1 = new Link(l.source, l.destination, Type.DIRECTED); arcs.add(arc1); arc1.source.outLinks.put(arc1.destination, arc1); if (!silent) notifyLinkAdded(arc1); } else { arc1.mode = l.mode; } if (arc2 == null) { arc2 = new Link(l.destination, l.source, Type.DIRECTED); arcs.add(arc2); arc2.source.outLinks.put(arc2.destination, arc2); if (!silent) notifyLinkAdded(arc2); } else { arc2.mode = l.mode; } edges.add(l); } if (!silent) notifyLinkAdded(l); } /** * Removes the specified link from this topology. Calling this method makes * sense only for wired links, since wireless links are automatically * managed as per the nodes' communication ranges. * * @param l The link to be removed. */ public void removeLink(Link l) { if (l.type == Type.DIRECTED) { arcs.remove(l); l.source.outLinks.remove(l.destination); Link edge = getLink(l.source, l.destination, false); if (edge != null) { edges.remove(edge); notifyLinkRemoved(edge); } } else { Link arc1 = getLink(l.source, l.destination, true); Link arc2 = getLink(l.destination, l.source, true); arcs.remove(arc1); arc1.source.outLinks.remove(arc1.destination); notifyLinkRemoved(arc1); arcs.remove(arc2); arc2.source.outLinks.remove(arc2.destination); notifyLinkRemoved(arc2); edges.remove(l); } notifyLinkRemoved(l); } /** * Returns true if this topology has at least one directed link. * @return true if the {@link Topology} has at least one directed link, false otherwise. */ public boolean hasDirectedLinks() { return arcs.size() > 2 * edges.size(); } /** * Returns a list containing all the nodes in this topology. The returned * ArrayList can be subsequently modified without effect on the topology. * @return the {@link List} of {@link Node}s. */ public List getNodes() { return new ArrayList<>(nodes); } /** * Returns the first node found with this ID. * @param id an integer identifying the {@link Node}. * @return the corresponding {@link Node}, null if not found. */ public Node findNodeById(int id) { for (Node node : nodes) if (node.getID() == id) return node; return null; } /** * Shuffles the IDs of the nodes in this topology. */ public void shuffleNodeIds() { List Ids = new ArrayList<>(); for (Node node : nodes) Ids.add(node.getID()); Collections.shuffle(Ids); for (int i = 0; i < nodes.size(); i++) nodes.get(i).setID(Ids.get(i)); } /** * Returns a list containing all undirected links in this topology. The * returned ArrayList can be subsequently modified without effect on the * topology. * @return the {@link List} of {@link Link}s. */ public List getLinks() { return getLinks(false); } /** * Returns a list containing all links of the specified type in this * topology. The returned ArrayList can be subsequently modified without * effect on the topology. * * @param directed true for directed links, false for * undirected links. * @return the {@link List} of {@link Link}s. */ public List getLinks(boolean directed) { return new ArrayList<>(directed ? arcs : edges); } List getLinks(boolean directed, Node n, int pos) { List result = new ArrayList<>(); List allLinks = (directed) ? arcs : edges; for (Link l : allLinks) switch (pos) { case 0: if (l.source == n || l.destination == n) result.add(l); break; case 1: if (l.source == n) result.add(l); break; case 2: if (l.destination == n) result.add(l); break; } return result; } /** * Returns the undirected link shared the specified nodes, if any. * * @param n1 the first {@link Node}. * @param n2 the second {@link Node}. * @return The requested link, if such a link exists, null * otherwise. */ public Link getLink(Node n1, Node n2) { return getLink(n1, n2, false); } /** * Returns the link of the specified type between the specified nodes, if * any. * @param from the source {@link Node}. * @param to the destination {@link Node}. * @param directed true if the searched {@link Link} is directed, false otherwise. * * @return The requested link, if such a link exists, null * otherwise. */ public Link getLink(Node from, Node to, boolean directed) { if (directed) { return from.outLinks.get(to); //Link l=new Link(from, to,Link.Type.DIRECTED); //int pos=arcs.indexOf(l); //return (pos != -1)?arcs.get(pos):null; } else { Link l = new Link(from, to, Type.UNDIRECTED); int pos = edges.indexOf(l); return (pos != -1) ? edges.get(pos) : null; } } /** * Replaces the default Wireless Link Resolver by a custom one. * * @param linkResolver An object that implements LinkResolver. */ public void setLinkResolver(LinkResolver linkResolver) { this.linkResolver = linkResolver; } /** * Return the current LinkResolver. * @return the current {@link LinkResolver}. */ public LinkResolver getLinkResolver() { return linkResolver; } /** * Registers the specified topology listener to this topology. The listener * will be notified whenever an undirected link is added or removed. * * @param listener The listener to add. */ public void addConnectivityListener(ConnectivityListener listener) { cxUndirectedListeners.add(listener); } /** * Registers the specified connectivity listener to this topology. The * listener will be notified whenever a link of the specified type is * added or removed. * * @param listener The listener to register. * @param directed The type of links to be listened (true for * directed, false for undirected). */ public void addConnectivityListener(ConnectivityListener listener, boolean directed) { if (directed) cxDirectedListeners.add(listener); else cxUndirectedListeners.add(listener); } /** * Unregisters the specified connectivity listener from the 'undirected' * listeners. * * @param listener The listener to unregister. */ public void removeConnectivityListener(ConnectivityListener listener) { cxUndirectedListeners.remove(listener); } /** * Unregisters the specified connectivity listener from the listeners * of the specified type. * * @param listener The listener to unregister. * @param directed The type of links that this listener was listening * (true for directed, false for undirected). */ public void removeConnectivityListener(ConnectivityListener listener, boolean directed) { if (directed) cxDirectedListeners.remove(listener); else cxUndirectedListeners.remove(listener); } /** * Registers the specified movement listener to this topology. The * listener will be notified every time the location of a node changes. * * @param listener The movement listener. */ public void addMovementListener(MovementListener listener) { movementListeners.add(listener); } /** * Unregisters the specified movement listener for this topology. * * @param listener The movement listener. */ public void removeMovementListener(MovementListener listener) { movementListeners.remove(listener); } /** * Registers the specified topology listener to this topology. The listener * will be notified whenever a node is added or removed. * * @param listener The listener to register. */ public void addTopologyListener(TopologyListener listener) { topologyListeners.add(listener); } /** * Unregisters the specified topology listener. * * @param listener The listener to unregister. */ public void removeTopologyListener(TopologyListener listener) { topologyListeners.remove(listener); } /** * Registers the specified message listener to this topology. The listener * will be notified every time a message is received at any node. * * @param listener The message listener. */ public void addMessageListener(MessageListener listener) { messageListeners.add(listener); } /** * Unregisters the specified message listener for this topology. * * @param listener The message listener. */ public void removeMessageListener(MessageListener listener) { messageListeners.remove(listener); } /** * Registers the specified selection listener to this topology. The listener * will be notified every time a node is selected. * * @param listener The selection listener. */ public void addSelectionListener(SelectionListener listener) { selectionListeners.add(listener); } /** * Unregisters the specified selection listener for this topology. * * @param listener The selection listener. */ public void removeSelectionListener(SelectionListener listener) { selectionListeners.remove(listener); } /** * Registers the specified start listener to this topology. The listener * will be notified every time a (re)start is requested on the topology. * * @param listener The start listener. */ public void addStartListener(StartListener listener) { startListeners.add(listener); } /** * Unregisters the specified selection listener for this topology. * * @param listener The start listener. */ public void removeStartListener(StartListener listener) { startListeners.remove(listener); } /** * Registers the specified listener to the events of the clock. * * @param listener The listener to register. * @param period The number of rounds between consecutive onClock() events, * in time units. */ public void addClockListener(ClockListener listener, int period) { clockManager.addClockListener(listener, period); } /** * Registers the specified listener to the events of the clock. * * @param listener The listener to register. */ public void addClockListener(ClockListener listener) { clockManager.addClockListener(listener); } /** * Unregisters the specified listener. (The onClock() method of this * listener will not longer be called.) * * @param listener The listener to unregister. */ public void removeClockListener(ClockListener listener) { clockManager.removeClockListener(listener); } public void setFileManager(FileManager fileManager) { this.fileManager = fileManager; } public FileManager getFileManager() { return fileManager; } /** * Provides a {@link TopologySerializer}. * @return a {@link TopologySerializer}. */ public TopologySerializer getSerializer() { return topologySerializer; } /** * Sets the new {@link TopologySerializer} to use. * @param topologySerializer the new {@link TopologySerializer} to use. */ public void setSerializer(TopologySerializer topologySerializer) { this.topologySerializer = topologySerializer; } protected void notifyLinkAdded(Link l) { if (l.type == Type.DIRECTED) { l.endpoint(0).onDirectedLinkAdded(l); l.endpoint(1).onDirectedLinkAdded(l); for (ConnectivityListener cl : cxDirectedListeners) cl.onLinkAdded(l); } else { l.endpoint(0).onLinkAdded(l); l.endpoint(1).onLinkAdded(l); for (ConnectivityListener cl : cxUndirectedListeners) cl.onLinkAdded(l); } } protected void notifyLinkRemoved(Link l) { if (l.type == Type.DIRECTED) { l.endpoint(0).onDirectedLinkRemoved(l); l.endpoint(1).onDirectedLinkRemoved(l); for (ConnectivityListener cl : cxDirectedListeners) cl.onLinkRemoved(l); } else { l.endpoint(0).onLinkRemoved(l); l.endpoint(1).onLinkRemoved(l); for (ConnectivityListener cl : cxUndirectedListeners) cl.onLinkRemoved(l); } } protected void notifyNodeAdded(Node node) { for (TopologyListener tl : new ArrayList<>(topologyListeners)) tl.onNodeAdded(node); } protected void notifyNodeRemoved(Node node) { for (TopologyListener tl : new ArrayList<>(topologyListeners)) tl.onNodeRemoved(node); } protected void notifyNodeSelected(Node node) { for (SelectionListener tl : new ArrayList<>(selectionListeners)) tl.onSelection(node); } @Override public void onClock() { if (step) { pause(); step = false; } if (refreshMode == RefreshMode.CLOCKBASED) { for (Node node : toBeUpdated) update(node); toBeUpdated.clear(); } removeDyingNodes(); } private void removeDyingNodes() { List dyingNodes = new ArrayList<>(); for (Node node : nodes) if(node.isDying()) dyingNodes.add(node); for (Node node : dyingNodes) removeNode(node); } void touch(Node n) { if (refreshMode == RefreshMode.CLOCKBASED) toBeUpdated.add(n); else update(n); } void update(Node n) { for (Node n2 : nodes) if (n2 != n) { updateWirelessLink(n, n2); updateWirelessLink(n2, n); } for (Node n2 : new ArrayList<>(nodes)) { if (n2 != n) { updateSensedNodes(n, n2); updateSensedNodes(n2, n); } } } void updateWirelessLink(Node n1, Node n2) { Link l = n1.getOutLinkTo(n2); boolean linkExisted = (l == null) ? false : true; boolean linkExists = linkResolver.isHeardBy(n1, n2); if (!linkExisted && linkExists) addLink(new Link(n1, n2, Type.DIRECTED, Mode.WIRELESS)); else if (linkExisted && l.isWireless() && !linkExists) removeLink(l); } void updateSensedNodes(Node from, Node to) { if (from.distance(to) < from.sensingRange) { if (!from.sensedNodes.contains(to)) { from.sensedNodes.add(to); from.onSensingIn(to); } } else if (from.sensedNodes.contains(to)) { from.sensedNodes.remove(to); from.onSensingOut(to); } } @Override public String toString() { return super.toString(); } // region Command management protected ArrayList commandListeners = new ArrayList<>(); protected ArrayList commands = new ArrayList(); protected boolean defaultCommandsEnabled = true; /** * Registers the specified action listener to this {@link Topology}. * * @param al The listener to add. */ public void addCommandListener(CommandListener al) { commandListeners.add(al); } /** * Unregisters the specified action listener to this {@link Topology}. * * @param al The listener to remove. */ public void removeCommandListener(CommandListener al) { commandListeners.remove(al); } /** * Adds the specified action command to this {@link Topology}. * * @param command The command name to add. */ public void addCommand(String command) { ((List) commands).add(command); } /** * Removes the specified action command from this {@link Topology}. * * @param command The command name to remove. */ public void removeCommand(String command) { commands.remove(command); } /** * Disables the set of default commands provided by the {@link Topology} when using {@link #getCommands()}. */ public void disableDefaultCommands() { defaultCommandsEnabled = false; } /** * Enables the set of default commands provided by the {@link Topology} when using {@link #getCommands()}. */ public void enableDefaultCommands() { defaultCommandsEnabled = true; } /** *

Recompute the current list of commands.

*

This list contains:

*
    *
  • The list of default commands managed by the {@link Topology}. * Please use {@link #disableDefaultCommands()} to if you dont want them.
  • *
  • The list of commands which have been added by {@link #addCommand(String)}.
  • *
* @return the current list of commands */ public Iterable getCommands() { List retList = new ArrayList<>(); retList = addDefaultCommands(retList); retList.addAll(commands); return retList; } private List addDefaultCommands(List commands) { if(!defaultCommandsEnabled) return commands; if(!isStarted()) { commands.add(DefaultCommands.START_EXECUTION); commands.add(DefaultCommands.EXECUTE_A_SINGLE_STEP); } else { if (isRunning()) commands.add(DefaultCommands.PAUSE_EXECUTION); else { commands.add(DefaultCommands.RESUME_EXECUTION); commands.add(DefaultCommands.EXECUTE_A_SINGLE_STEP); } commands.add(DefaultCommands.RESTART_NODES); } commands.add(COMMAND_SEPARATOR); return commands; } /** * The character used as command separator. */ public static final String COMMAND_SEPARATOR = "-"; public class DefaultCommands { public static final String START_EXECUTION = "Start execution"; public static final String PAUSE_EXECUTION = "Pause execution"; public static final String RESUME_EXECUTION = "Resume execution"; public static final String EXECUTE_A_SINGLE_STEP = "Execute a single step"; public static final String RESTART_NODES = "Restart nodes"; } /** * Removes all commands from this JTopology. */ public void removeAllCommands() { commands.clear(); } /** * Executes a command. * * @param command the command to be executed */ public void executeCommand(String command) { if(defaultCommandsEnabled) { if (command.equals(DefaultCommands.START_EXECUTION)) { if (!isStarted()) start(); } else if (command.equals(DefaultCommands.PAUSE_EXECUTION)) { if (isStarted() && isRunning()) pause(); } else if (command.equals(DefaultCommands.RESUME_EXECUTION)) { if (isStarted() && !isRunning()) resume(); } else if (command.equals(DefaultCommands.RESTART_NODES)) { if (isStarted()) restart(); } else if (command.equals(DefaultCommands.EXECUTE_A_SINGLE_STEP)) { if (!isStarted() || (isStarted() && !isRunning())) step(); } } for (CommandListener cl : commandListeners) cl.onCommand(command); } // endregion }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy