From 1d99762c7965d351510cfb5e08eac25e48d96038 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 6 Jun 2025 08:02:52 +0300 Subject: Modernize project structure, update Maven config, move sources, add logging config, update README and .gitignore --- src/main/java/simulator/VSCreateTask.java | 179 ++ src/main/java/simulator/VSLogging.java | 201 +++ src/main/java/simulator/VSMain.java | 70 + src/main/java/simulator/VSMenuItemStates.java | 109 ++ src/main/java/simulator/VSSimulator.java | 1536 +++++++++++++++++ src/main/java/simulator/VSSimulatorFrame.java | 628 +++++++ .../java/simulator/VSSimulatorVisualization.java | 1814 ++++++++++++++++++++ 7 files changed, 4537 insertions(+) create mode 100644 src/main/java/simulator/VSCreateTask.java create mode 100644 src/main/java/simulator/VSLogging.java create mode 100644 src/main/java/simulator/VSMain.java create mode 100644 src/main/java/simulator/VSMenuItemStates.java create mode 100644 src/main/java/simulator/VSSimulator.java create mode 100644 src/main/java/simulator/VSSimulatorFrame.java create mode 100644 src/main/java/simulator/VSSimulatorVisualization.java (limited to 'src/main/java/simulator') diff --git a/src/main/java/simulator/VSCreateTask.java b/src/main/java/simulator/VSCreateTask.java new file mode 100644 index 0000000..9a6426b --- /dev/null +++ b/src/main/java/simulator/VSCreateTask.java @@ -0,0 +1,179 @@ +package simulator; + +import core.VSInternalProcess; +import core.VSTask; +import events.VSAbstractEvent; +import events.VSRegisteredEvents; +import events.internal.VSProtocolEvent; + +/** + * The class VSCreateTask, an object of this class represents how new + * VSTask objects are to be created using JComboBox selections of the + * GUI editor.. + * + * @author Paul C. Buetow + */ +public class VSCreateTask { + /** The event classname. */ + private String eventClassname; + + /** The create task menu string. */ + private String menuText; + + /** The protocol classname. */ + private String protocolClassname; + + /** The shortname. */ + private String shortname; + + /** The task is a protocol activation. */ + private boolean isProtocolActivation; + + /** The task is a protocol deactivation. */ + private boolean isProtocolDeactivation; + + /** The task is a client protocol. */ + private boolean isClientProtocol; + + /** True, if the task is a client request. false, if the task is a + * server request + */ + private boolean isRequest; + + /** + * Instantiates a new VSCreateTask object. + * + * @param menuText the menu text + * @param eventClassname the event classname + */ + public VSCreateTask(String menuText, String eventClassname) { + this.menuText = menuText; + this.eventClassname = eventClassname; + } + + /** + * Instantiates a new VSCreateTask dummy object. + * + * @param menuText the menu text + */ + public VSCreateTask(String menuText) { + this.menuText = menuText; + this.eventClassname = null; + } + + /** + * Sets if it is a protocol activation task. + * + * @param isProtocolActivation true, if it is a protocol activation + * task. + */ + public void isProtocolActivation(boolean isProtocolActivation) { + this.isProtocolActivation = isProtocolActivation; + + if (isProtocolActivation) + isProtocolDeactivation(false); + } + + /** + * Sets if it is a protocol deactivation task. + * + * @param isProtocolDeactivation true, if it is a protocol deactivation + * task. + */ + public void isProtocolDeactivation(boolean isProtocolDeactivation) { + this.isProtocolDeactivation = isProtocolDeactivation; + + if (isProtocolDeactivation) + isProtocolActivation(false); + } + + /** + * Sets if it is a client protocol. + * + * @param isClientProtocol the is client protocol + */ + public void isClientProtocol(boolean isClientProtocol) { + this.isClientProtocol = isClientProtocol; + } + + /** + * Sets if it is a client request. + * + * @param isRequest the is client request + */ + public void isRequest(boolean isRequest) { + this.isRequest = isRequest; + } + + /** + * Checks if it is a dummy object.. + * + * @return true, if dummy + */ + public boolean isDummy() { + return eventClassname == null; + } + + /** + * Sets the protocol classname. + * + * @param protocolClassname the protocol classname + */ + public void setProtocolClassname(String protocolClassname) { + this.protocolClassname = protocolClassname; + } + + /** + * Sets the shortname. + * + * @param shortname the shortname + */ + public void setShortname(String shortname) { + this.shortname = shortname; + } + + /** + * Gets the create tasks menu text. + * + * @return The text + */ + public String getMenuText() { + return menuText; + } + + /** + * Creates the task. + * + * @param process the process + * @param time the time + * @param localTimedTask the local timed task + * + * @return the new task + */ + public VSTask createTask(VSInternalProcess process, long time, + boolean localTimedTask) { + VSAbstractEvent event = null; + + if (isRequest) { + event = process.getProtocolObject(eventClassname); + + } else { + event = VSRegisteredEvents.createEventInstanceByClassname( + eventClassname, process); + } + + event.init(process); + if (shortname != null) + event.setShortname(shortname); + + if (isProtocolActivation || isProtocolDeactivation) { + VSProtocolEvent protocolEvent = (VSProtocolEvent) event; + protocolEvent.setProtocolClassname(protocolClassname); + protocolEvent.isProtocolActivation(isProtocolActivation); + protocolEvent.isClientProtocol(isClientProtocol); + } + + return new VSTask(time, process, event, localTimedTask); + } +} + diff --git a/src/main/java/simulator/VSLogging.java b/src/main/java/simulator/VSLogging.java new file mode 100644 index 0000000..66cb563 --- /dev/null +++ b/src/main/java/simulator/VSLogging.java @@ -0,0 +1,201 @@ +package simulator; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.swing.JTextArea; + +import utils.VSTools; + +/** + * The class VSLogging, an object of this class is responsible for the loging + * of text messages into the simulator's loging window. + * + * @author Paul C. Buetow + */ +public class VSLogging { + /** The loging area. */ + private JTextArea logingArea; + + /** The filter text. */ + private String filterText; + + /** The pause lines. Used for cacheing the loging if the loging is + * deactivated for a while + */ + private ArrayList pauseLines; + + /** The loging lines. */ + private ArrayList logingLines; + + /** The simulator canvas. */ + private VSSimulatorVisualization simulatorVisualization; + + /** The loging messages are filtered. */ + private boolean isFiltered; + + /** The loging is paused. */ + private boolean isPaused; + + /** The filter pattern. */ + private Pattern filterPattern; + + /** + * Instantiates a new VSLogging object. + */ + public VSLogging() { + logingArea = new JTextArea(0, 0); + logingArea.setEditable(false); + logingArea.setLineWrap(true); + logingArea.setWrapStyleWord(true); + logingLines = new ArrayList(); + pauseLines = new ArrayList(); + filterText = ""; + } + + /** + * Sets the simulator canvas. + * + * @param sv the simulator canvas + */ + public void setSimulatorCanvas(VSSimulatorVisualization sv) { + this.simulatorVisualization = sv; + } + + /** + * Gets the loging area. + * + * @return the loging area + */ + public JTextArea getLoggingArea() { + return logingArea; + } + + /** + * Loggs a message using the global time. + * + * @param message the message + */ + public void log(String message) { + if (simulatorVisualization == null) + log(message, 0); + else + log(message, simulatorVisualization.getTime()); + } + + /** + * Loggs a message using the specified time. + * + * @param message the message + * @param time the time + */ + public synchronized void log(String message, long time) { + StringBuffer buffer = new StringBuffer(); + buffer.append(VSTools.getTimeString(time)); + buffer.append(": "); + buffer.append(message); + + if (isPaused) + pauseLines.add(buffer); + else + logFiltered(buffer); + } + + /** + * Sets if the loging is paused. + * + * @param isPaused true, if the loging is paused + */ + public synchronized void isPaused(boolean isPaused) { + this.isPaused = isPaused; + + if (!isPaused) { + for (StringBuffer buffer : pauseLines) + logFiltered(buffer); + + pauseLines.clear(); + } + } + + /** + * If the loging is filtered, it's using the pattern matching. + * + * @param buffer the loging buffer to filter + */ + private void logFiltered(StringBuffer buffer) { + logingLines.add(buffer); + if (!isFiltered) { + logingArea.append(buffer.toString()+"\n"); + logingArea.setCaretPosition( + logingArea.getDocument().getLength()); + + } else if (filterPattern != null && + filterPattern.matcher(buffer).find()) { + logingArea.append(buffer.toString()+"\n"); + logingArea.setCaretPosition( + logingArea.getDocument().getLength()); + } + } + + /** + * Checks if the loging is filtered. + * + * @param isFiltered true, if the loging is filtered + */ + public synchronized void isFiltered(boolean isFiltered) { + this.isFiltered = isFiltered; + + if (!isFiltered) + setFilterText(""); + else + filter(); + } + + /** + * Sets the filter text. + * + * @param filterText the new filter text + */ + public synchronized void setFilterText(String filterText) { + this.filterText = filterText; + filter(); + } + + /** + * Clears the loging. + */ + public synchronized void clear() { + logingLines.clear(); + pauseLines.clear(); + logingArea.setText(""); + } + + /** + * Filters the loging. + */ + private void filter() { + try { + filterPattern = Pattern.compile(filterText); + StringBuffer buffer = new StringBuffer(); + + for (StringBuffer line : logingLines) { + if (isFiltered) { + Matcher matcher = filterPattern.matcher(line); + if (matcher.find()) { + buffer.append(line); + buffer.append("\n"); + } + } else { + buffer.append(line); + buffer.append("\n"); + } + } + logingArea.setText(buffer.toString()); + + } catch (Exception e) { + filterPattern = null; + logingArea.setText(""); + } + } +} diff --git a/src/main/java/simulator/VSMain.java b/src/main/java/simulator/VSMain.java new file mode 100644 index 0000000..34a21b7 --- /dev/null +++ b/src/main/java/simulator/VSMain.java @@ -0,0 +1,70 @@ +package simulator; + +import java.awt.Component; +import java.util.Locale; + +import javax.swing.UIManager; + +import events.VSRegisteredEvents; +import prefs.VSDefaultPrefs; +import prefs.VSPrefs; + +/** + * The class VSMain. This class contains the static main method. The simulator + * starts here! + * + * @author Paul C. Buetow + */ +public class VSMain { + /** The global preferences */ + public static VSPrefs prefs; + + /** + * Instantiates a new VSMain object. + * + * @param prefs the prefs + */ + public VSMain(VSPrefs prefs) { + init(prefs, null); + } + + /** + * Instantiates a new VSMain object + * + * @param prefs the prefs + * @param relativeTo the component to open the window relative to + */ + public VSMain(VSPrefs prefs, Component relativeTo) { + init(prefs, relativeTo); + } + + /** + * Inits the VSMain object. + * + * @param prefs the prefs + * @param relativeTo the component to open the window relative to + */ + private void init(VSPrefs prefs, Component relativeTo) { + //VSSimulatorFrame simulatorFrame = + VSMain.prefs = prefs; + new VSSimulatorFrame(prefs, relativeTo); + } + + /** + * The main method. + * + * @param args the arguments + */ + public static void main(String[] args) { + try { + UIManager.setLookAndFeel( + UIManager.getCrossPlatformLookAndFeelClassName()); + } catch (Exception e) { } + + Locale.setDefault(Locale.GERMAN); + javax.swing.JPopupMenu.setDefaultLightWeightPopupEnabled(false); + VSPrefs prefs = VSDefaultPrefs.init(); + VSRegisteredEvents.init(prefs); + new VSMain(prefs); + } +} diff --git a/src/main/java/simulator/VSMenuItemStates.java b/src/main/java/simulator/VSMenuItemStates.java new file mode 100644 index 0000000..941dbf5 --- /dev/null +++ b/src/main/java/simulator/VSMenuItemStates.java @@ -0,0 +1,109 @@ +package simulator; + +/** + * The class VSMenuItemStates. Used by the VSSimulator to update the + * "simulator" bar of the VSSimulatorFrame. + * + * @author Paul C. Buetow + */ +public class VSMenuItemStates { + /** The pause state. */ + private volatile boolean pause; + + /** The replay state. */ + private volatile boolean replay; + + /** The reset state. */ + private volatile boolean reset; + + /** The start state. */ + private volatile boolean start; + + /** + * Instantiates a new VSMenuItemStates object. + * + * @param pause the pause state + * @param replay the replay state + * @param reset the reset state + * @param start the start state + */ + public VSMenuItemStates(boolean pause, boolean replay, boolean reset, + boolean start) { + this.pause = pause; + this.replay = replay; + this.reset = reset; + this.start = start; + } + + /** + * Sets the pause state. + * + * @param pause the new pause state + */ + public void setPause(boolean pause) { + this.pause = pause; + } + + /** + * Sets the replay state. + * + * @param replay the new replay state + */ + public void setReplay(boolean replay) { + this.replay = replay; + } + + /** + * Sets the reset state. + * + * @param reset the new reset state + */ + public void setReset(boolean reset) { + this.reset = reset; + } + + /** + * Sets the start state. + * + * @param start the new start state + */ + public void setStart(boolean start) { + this.start = start; + } + + /** + * Gets the pause state. + * + * @return the pause state + */ + public boolean getPause() { + return pause; + } + + /** + * Gets the replay state. + * + * @return the replay state + */ + public boolean getReplay() { + return replay; + } + + /** + * Gets the reset state. + * + * @return the reset state + */ + public boolean getReset() { + return reset; + } + + /** + * Gets the start state. + * + * @return the start state + */ + public boolean getStart() { + return start; + } +} diff --git a/src/main/java/simulator/VSSimulator.java b/src/main/java/simulator/VSSimulator.java new file mode 100644 index 0000000..4965a71 --- /dev/null +++ b/src/main/java/simulator/VSSimulator.java @@ -0,0 +1,1536 @@ +package simulator; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Vector; + +import javax.swing.AbstractButton; +import javax.swing.AbstractCellEditor; +import javax.swing.BoxLayout; +import javax.swing.ButtonModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableColumn; + +import core.VSInternalProcess; +import core.VSTask; +import core.VSTaskManager; +import events.VSRegisteredEvents; +import exceptions.VSNegativeNumberException; +import prefs.VSPrefs; +import prefs.editors.VSProcessEditor; +import serialize.VSSerializable; +import serialize.VSSerialize; + +/** + * The class VSSimulator, an object of this class represents a whole simulator. + * It may be, that several parallel simulators exist. They are independent + * fron each other. + * + * @author Paul C. Buetow + */ +public class VSSimulator extends JPanel implements VSSerializable { + /** The serial version uid */ + private static final long serialVersionUID = 1L; + + /** The global text fields. */ + private ArrayList globalTextFields; + + /** The local text fields. */ + private ArrayList localTextFields; + + /** The create tasks array list. */ + private ArrayList createTasks; + + /** The filter active check box. */ + private JCheckBox filterActiveCheckBox; + + /** The lamport active check box. */ + private JCheckBox lamportActiveCheckBox; + + /** The vector time active check box. */ + private JCheckBox vectorTimeActiveCheckBox; + + /** The global pid combo box. */ + private JComboBox globalPIDComboBox; + + /** The local pid combo box. */ + private JComboBox localPIDComboBox; + + /** The processes combo box. */ + private JComboBox processesComboBox; + + /** The local add panel. */ + private JPanel localAddPanel; + + /** The local panel. */ + private JPanel localPanel; + + /** The loging panel. */ + private JPanel logingPanel; + + /** The split pane1. */ + private JSplitPane splitPane1; + + /** The split pane h. */ + private JSplitPane splitPaneH; + + /** The split pane v. */ + private JSplitPane splitPaneV; + + /** The tabbed pane. */ + private JTabbedPane tabbedPane; + + /** The loging area. */ + private JTextArea logingArea; + + /** The filter text field. */ + private JTextField filterTextField; + + /** The global text field. */ + private JTextField globalTextField; + + /** The local text field. */ + private JTextField localTextField; + + /** The thread. */ + private Thread thread; + + /** The loging. */ + private VSLogging loging; + + /** The menu item states. */ + private VSMenuItemStates menuItemStates; + + /** The prefs. */ + private VSPrefs prefs; + + /** The simulator canvas. */ + private VSSimulatorVisualization simulatorVisualization; + + /** The simulator frame. */ + private VSSimulatorFrame simulatorFrame; + + /** The task manager. */ + private VSTaskManager taskManager; + + /** The task manager global model. */ + private VSTaskManagerTableModel taskManagerGlobalModel; + + /** The task manager local model. */ + private VSTaskManagerTableModel taskManagerLocalModel; + + /** The task manager global editor. */ + private VSTaskManagerCellEditor taskManagerGlobalEditor; + + /** The task manager local editor. */ + private VSTaskManagerCellEditor taskManagerLocalEditor; + + /** The last selected process num. */ + private int lastSelectedProcessNum; + + /** The last expert state. */ + private boolean lastExpertState; + + /** The simulator counter. */ + private static int simulatorCounter; + + /** The simulator num. */ + private static volatile int simulatorNum; + + /** + * The class VSTaskManagerTableModel, an object of this class handles + * the task manager's JTable. + */ + private class VSTaskManagerTableModel extends AbstractTableModel + implements MouseListener { + /** the serial version uid */ + private static final long serialVersionUID = 1l; + + /** The Constant LOCAL. */ + public static final boolean LOCAL = true; + + /** The Constant GLOBAL. */ + public static final boolean GLOBAL = false; + + /** The Constant ALL_PROCESSES. */ + // public static final boolean ALL_PROCESSES = true; + + /** The Constant ONE_PROCESS. */ + public static final boolean ONE_PROCESS = false; + + /** The tasks. */ + private ArrayList tasks; + + /** The column names. */ + private String columnNames[]; + + /** The num columns. */ + private int numColumns; + + /** The table. */ + //private JTable table; + + /** The editor. */ + //private VSTaskManagerCellEditor editor; + + /** + * Instantiates a new VSTaskManagerTableModel object + * + * @param process the process + * @param localTask true, if this table manages the local task. false, + * if this table manages the global tasks. + */ + public VSTaskManagerTableModel(VSInternalProcess process, + boolean localTask) { + tasks = new ArrayList(); + set(process, localTask, ONE_PROCESS); + columnNames = new String[3]; + columnNames[0]= prefs.getString("lang.time") + " (ms)"; + columnNames[1] = prefs.getString("lang.process.id"); + columnNames[2] = prefs.getString("lang.event"); + numColumns = 3; + } + + /** + * Sets the table. + * + * @param table the table + */ + public void setTable(JTable table) { + /* Maybe needed for future usage */ + //this.table = table; + } + + /** + * Sets the editor. + * + * @param editor the editor + */ + public void setEditor(VSTaskManagerCellEditor editor) { + /* Maybe needed for future usage */ + //this.editor = editor; + } + + /** + * Sets new values. + * + * @param process the process + * @param localTasks true, if this table manages the local tasks. false + * if this table manages the global tasks. + * @param allProcesses true, if this table shows tasks of all processes. + * false, if this table only shows tasks of the specified process. + */ + public void set(VSInternalProcess process, boolean localTasks, + boolean allProcesses) { + + if (allProcesses) { + this.tasks = localTasks + ? taskManager.getLocalTasks() + : taskManager.getGlobalTasks(); + } else { + this.tasks = localTasks + ? taskManager.getProcessLocalTasks(process) + : taskManager.getProcessGlobalTasks(process); + } + + Collections.sort(tasks); + fireTableDataChanged(); + } + + /* (non-Javadoc) + * @see javax.swing.table.AbstractTableModel#getColumnName(int) + */ + public String getColumnName(int col) { + return columnNames[col]; + } + + /* (non-Javadoc) + * @see javax.swing.table.TableModel#getRowCount() + */ + public int getRowCount() { + return tasks == null ? 0 : tasks.size(); + } + + /* (non-Javadoc) + * @see javax.swing.table.TableModel#getColumnCount() + */ + public int getColumnCount() { + return numColumns; + } + + /* (non-Javadoc) + * @see javax.swing.table.TableModel#getValueAt(int, int) + */ + public Object getValueAt(int row, int col) { + VSTask task = tasks.get(row); + + switch (col) { + case 0: + return task.getTaskTime(); + case 1: + return task.getProcess().getProcessID(); + } + + return task.getEvent().getShortname(); + } + + /* (non-Javadoc) + * @see javax.swing.table.AbstractTableModel#isCellEditable(int, int) + */ + public boolean isCellEditable(int row, int col) { + if (col == 2) + return false; + + return true; + } + + /* (non-Javadoc) + * @see javax.swing.table.AbstractTableModel#setValueAt( + * java.lang.Object, int, int) + */ + public void setValueAt(Object value, int row, int col) { + } + + /** + * Adds the task. + * + * @param task the task + */ + public void addTask(VSTask task) { + tasks.add(task); + Collections.sort(tasks); + fireTableDataChanged(); + } + + /** + * Removes the task at a specified row. + * + * @param row the row + * @return The removed task + */ + public VSTask removeTaskAtRow(int row) { + VSTask task = tasks.get(row); + tasks.remove(task); + taskManager.removeTask(task); + fireTableDataChanged(); + return task; + } + + /** + * Checks if a specific row exists + * + * @param row the row + * @return True, if the row exists. False, if not + */ + public boolean rowExists(int row) { + if (row < 0) + return false; + + if (tasks.size() <= row) + return false; + + return true; + } + + /** + * Gets the index of a specific task + * + * @param task The task + * @return The index of the task + */ + public int getIndexOf(VSTask task) { + return tasks.indexOf(task); + } + + /** + * Copies the tasks at a specified rows. + * + * @param rows the rows + */ + private void copyTasksAtRows(int rows[]) { + ArrayList copiedTasks = new ArrayList(); + + for (int row : rows) + /* Use the copy constructor */ + copiedTasks.add(new VSTask(tasks.get(row))); + + for (VSTask task : copiedTasks) { + taskManager.addTask(task, VSTaskManager.PROGRAMMED); + addTask(task); + } + + fireTableDataChanged(); + } + + /* (non-Javadoc) + * @see java.awt.event.MouseListener#mouseClicked( + * java.awt.event.MouseEvent) + */ + public void mouseClicked(MouseEvent me) { + final JTable source = (JTable) me.getSource(); + //final int row = source.rowAtPoint(me.getPoint()); + //final int col = source.columnAtPoint(me.getPoint()); + + if (SwingUtilities.isRightMouseButton(me)) { + ActionListener actionListener = new ActionListener() { + public void actionPerformed(ActionEvent ae) { + String command = ae.getActionCommand(); + int rows[] = source.getSelectedRows(); + + if (command.equals(prefs.getString("lang.remove"))) { + for (int i = rows.length - 1; i >= 0; --i) + removeTaskAtRow(rows[i]); + + } else if (command.equals( + prefs.getString("lang.copy"))) { + copyTasksAtRows(rows); + } + } + }; + + JPopupMenu popup = new JPopupMenu(); + JMenuItem item = new JMenuItem(prefs.getString("lang.remove")); + item.addActionListener(actionListener); + popup.add(item); + + item = new JMenuItem(prefs.getString("lang.copy")); + item.addActionListener(actionListener); + popup.add(item); + + popup.show(me.getComponent(), me.getX(), me.getY()); + } + } + + /* (non-Javadoc) + * @see java.awt.event.MouseListener#mouseEntered( + * java.awt.event.MouseEvent) + */ + public void mouseEntered(MouseEvent me) { } + + /* (non-Javadoc) + * @see java.awt.event.MouseListener#mouseExited( + * java.awt.event.MouseEvent) + */ + public void mouseExited(MouseEvent me) { } + + /* (non-Javadoc) + * @see java.awt.event.MouseListener#mousePressed( + * java.awt.event.MouseEvent) + */ + public void mousePressed(MouseEvent me) { } + + /* (non-Javadoc) + * @see java.awt.event.MouseListener#mouseReleased( + * java.awt.event.MouseEvent) + */ + public void mouseReleased(MouseEvent me) { } + } + + /** + * The class VSTaskManagerCellEditor, an object of this class handles + * the task manager's JTable editor + */ + private class VSTaskManagerCellEditor extends AbstractCellEditor + implements TableCellEditor { + /** the serial version uid */ + private static final long serialVersionUID = 1L; + + /** The JTable model */ + private VSTaskManagerTableModel model; + + /** + * Instantiates a new VSTaskManagerCellEditor object. + * + * @param model the model + */ + public VSTaskManagerCellEditor(VSTaskManagerTableModel model) { + this.model = model; + model.setEditor(this); + } + + /** + * Stops editing + */ + public void stopEditing() { + fireEditingStopped(); + } + + /** + /* (non-Javadoc) + * @see javax.swing.table.TableCellEditor#getTableCellEditorComponent( + * javax.swing.JTable, java.lang.Object, boolean, int, int) + */ + public Component getTableCellEditorComponent(final JTable table, + Object object, + boolean isSelected, + final int row, + final int col) { + switch (col) { + case 0: + Long val = (Long) model.getValueAt(row, col); + final JTextField valField = new JTextField(val.toString()); + valField.setBackground(Color.WHITE); + valField.setBorder(null); + valField.addActionListener(new ActionListener() { + private boolean isRed = false; + public void actionPerformed(ActionEvent ae) { + try { + Long val = Long.valueOf(valField.getText()); + if (val.longValue() < 0) + throw new VSNegativeNumberException(); + VSTask task = model.removeTaskAtRow(row); + task.setTaskTime(val.longValue()); + taskManager.addTask(task, VSTaskManager.PROGRAMMED); + model.addTask(task); + if (isRed) { + valField.setBackground(Color.WHITE); + isRed = false; + } + int index = model.getIndexOf(task); + ListSelectionModel selectionModel = + table.getSelectionModel(); + selectionModel.setSelectionInterval(index, index); + fireEditingStopped(); + + } catch (NumberFormatException exc) { + valField.setBackground(Color.RED); + isRed = true; + + } catch (VSNegativeNumberException exc) { + valField.setBackground(Color.RED); + isRed = true; + } + } + }); + return valField; + case 1: + Integer current[] = { (Integer) model.getValueAt(row, col) }; + final JComboBox comboBox = new JComboBox<>(current); + + Integer pids[] = simulatorVisualization.getProcessIDs(); + for (Integer pid : pids) + comboBox.addItem(pid); + + comboBox.setSelectedIndex(0); + comboBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + int index = comboBox.getSelectedIndex() - 1; + if (model.rowExists(row)) { + VSTask task = model.removeTaskAtRow(row); + VSInternalProcess process = + simulatorVisualization.getProcess(index); + task.setProcess(process); + taskManager.addTask(task, VSTaskManager.PROGRAMMED); + if (allProcessesAreSelected()) + model.addTask(task); + } + + fireEditingStopped(); + } + }); + + return comboBox; + case 2: + break; + } + + return null; + } + + /* (non-Javadoc) + * @see javax.swing.CellEditor#getCellEditorValue() + */ + public Object getCellEditorValue() { + return new String(""); + } + } + + + /** + * Instantiates a new VSSimulator object. + * + * @param prefs the prefs + * @param simulatorFrame the simulator frame + */ + public VSSimulator(VSPrefs prefs, VSSimulatorFrame simulatorFrame) { + init(prefs, simulatorFrame); + } + + /** + * inits the VSSimulator object. + * + * @param prefs the prefs + * @param simulatorFrame the simulator frame + */ + private void init(VSPrefs prefs, VSSimulatorFrame simulatorFrame) { + this.prefs = prefs; + this.simulatorFrame = simulatorFrame; + simulatorNum = ++simulatorCounter; + this.menuItemStates = new VSMenuItemStates(false, false, false, true); + this.localTextFields = new ArrayList(); + this.globalTextFields = new ArrayList(); + + /* Not null if init has been called from the deserialization */ + if (this.loging == null) + this.loging = new VSLogging(); + + loging.log(prefs.getString("lang.simulator.new")); + + fillContentPane(); + updateFromPrefs(); + + splitPaneH.setDividerLocation( + prefs.getInteger("div.window.splitsize")); + + splitPaneV.setDividerLocation( + prefs.getInteger("div.window.ysize") + - prefs.getInteger("div.window.logsize")); + + splitPane1.setDividerLocation((int) (getPaintSize()/2) - 20); + + int numProcesses = simulatorVisualization.getNumProcesses(); + for (int i = 0; i <= numProcesses; ++i) { + localTextFields.add("0000"); + globalTextFields.add("0000"); + } + + processesComboBox.setSelectedIndex(0); + localPIDComboBox.setSelectedIndex(0); + globalPIDComboBox.setSelectedIndex(0); + + thread = new Thread(simulatorVisualization); + thread.start(); + } + + /** + * Fills the content pane. + */ + private void fillContentPane() { + logingArea = loging.getLoggingArea(); + + splitPaneH = new JSplitPane(); + splitPaneV = new JSplitPane(); + + /* Not null if init has been called from the deserialization */ + if (this.simulatorVisualization == null) + simulatorVisualization = new VSSimulatorVisualization( + prefs, this, loging); + + taskManager = simulatorVisualization.getTaskManager(); + loging.setSimulatorCanvas(simulatorVisualization); + + JPanel canvasPanel = new JPanel(); + canvasPanel.setLayout(new GridLayout(1, 1, 3, 3)); + canvasPanel.add(simulatorVisualization); + canvasPanel.setMinimumSize(new Dimension(0, 0)); + canvasPanel.setMaximumSize(new Dimension(0, 0)); + + logingPanel = new JPanel(new BorderLayout()); + logingPanel.add(new JScrollPane(logingArea), BorderLayout.CENTER); + logingPanel.add(createToolsPanel(), BorderLayout.SOUTH); + logingPanel.setPreferredSize(new Dimension(200, 1)); + + splitPaneH.setOrientation(JSplitPane.HORIZONTAL_SPLIT); + splitPaneH.setLeftComponent(createProcessPanel()); + splitPaneH.setRightComponent(canvasPanel); + splitPaneH.setContinuousLayout(true); + splitPaneH.setOneTouchExpandable(true); + + splitPaneV.setOrientation(JSplitPane.VERTICAL_SPLIT); + splitPaneV.setTopComponent(splitPaneH); + splitPaneV.setBottomComponent(logingPanel); + splitPaneV.setContinuousLayout(true); + + this.add(splitPaneV); + } + + /** + * Creates the tools panel. + * + * @return the panel + */ + private JPanel createToolsPanel() { + JPanel toolsPanel = new JPanel(); + boolean expertMode = prefs.getBoolean("sim.mode.expert"); + + toolsPanel.setLayout(new BoxLayout(toolsPanel, BoxLayout.X_AXIS)); + JCheckBox expertActiveCheckBox = + new JCheckBox(prefs.getString("lang.mode.expert")); + + expertActiveCheckBox.setSelected(expertMode); + expertActiveCheckBox.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent ce) { + AbstractButton abstractButton = + (AbstractButton) ce.getSource(); + ButtonModel buttonModel = abstractButton.getModel(); + boolean newState = buttonModel.isSelected(); + if (lastExpertState != newState) { + lastExpertState = newState; + prefs.setBoolean("sim.mode.expert", newState); + fireExpertModeChanged(); + } + } + }); + toolsPanel.add(expertActiveCheckBox); + + if (expertMode) { + lamportActiveCheckBox = new JCheckBox( + prefs.getString("lang.time.lamport")); + lamportActiveCheckBox.setSelected(false); + lamportActiveCheckBox.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent ce) { + AbstractButton abstractButton = + (AbstractButton) ce.getSource(); + ButtonModel buttonModel = abstractButton.getModel(); + simulatorVisualization.showLamport( + buttonModel.isSelected()); + if (buttonModel.isSelected()) + vectorTimeActiveCheckBox.setSelected(false); + } + }); + toolsPanel.add(lamportActiveCheckBox); + + vectorTimeActiveCheckBox = new JCheckBox( + prefs.getString("lang.time.vector")); + vectorTimeActiveCheckBox.setSelected(false); + vectorTimeActiveCheckBox.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent ce) { + AbstractButton abstractButton = + (AbstractButton) ce.getSource(); + ButtonModel buttonModel = abstractButton.getModel(); + simulatorVisualization.showVectorTime( + buttonModel.isSelected()); + if (buttonModel.isSelected()) + lamportActiveCheckBox.setSelected(false); + } + }); + toolsPanel.add(vectorTimeActiveCheckBox); + + JCheckBox antiAliasing = new JCheckBox( + prefs.getString("lang.antialiasing")); + antiAliasing.setSelected(false); + antiAliasing.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent ce) { + AbstractButton abstractButton = + (AbstractButton) ce.getSource(); + ButtonModel buttonModel = abstractButton.getModel(); + simulatorVisualization.isAntiAliased( + buttonModel.isSelected()); + } + }); + toolsPanel.add(antiAliasing); + } + + JCheckBox logingActiveCheckBox = new JCheckBox( + prefs.getString("lang.loging.active")); + logingActiveCheckBox.setSelected(true); + logingActiveCheckBox.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent ce) { + AbstractButton abstractButton = + (AbstractButton) ce.getSource(); + ButtonModel buttonModel = abstractButton.getModel(); + loging.isPaused(!buttonModel.isSelected()); + } + }); + toolsPanel.add(logingActiveCheckBox); + + if (expertMode) { + filterActiveCheckBox = new JCheckBox( + prefs.getString("lang.filter")); + filterActiveCheckBox.setSelected(false); + filterActiveCheckBox.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent ce) { + AbstractButton abstractButton = + (AbstractButton) ce.getSource(); + ButtonModel buttonModel = abstractButton.getModel(); + loging.isFiltered(buttonModel.isSelected()); + if (buttonModel.isSelected()) + loging.setFilterText(filterTextField.getText()); + } + }); + toolsPanel.add(filterActiveCheckBox); + + filterTextField = new JTextField(); + filterTextField.getDocument().addDocumentListener( + new DocumentListener() { + public void insertUpdate(DocumentEvent de) { + loging.setFilterText(filterTextField.getText()); + } + public void removeUpdate(DocumentEvent de) { + loging.setFilterText(filterTextField.getText()); + } + public void changedUpdate(DocumentEvent de) { + loging.setFilterText(filterTextField.getText()); + } + }); + toolsPanel.add(filterTextField); + + JButton clearButton = new JButton( + prefs.getString("lang.loging.clear")); + clearButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + String command = ae.getActionCommand(); + if (command.equals( + prefs.getString("lang.loging.clear"))) { + loging.clear(); + } + } + }); + toolsPanel.add(clearButton); + } + + return toolsPanel; + } + + /** + * Creates the process panel. + * + * @return the panel + */ + private JPanel createProcessPanel() { + JPanel editPanel = new JPanel(new GridBagLayout()); + boolean expertMode = prefs.getBoolean("sim.mode.expert"); + editPanel.setLayout(new BoxLayout(editPanel, BoxLayout.Y_AXIS)); + + processesComboBox = new JComboBox<>(); + localPIDComboBox = new JComboBox<>(); + globalPIDComboBox = new JComboBox<>(); + + lastSelectedProcessNum = 0; + int numProcesses = simulatorVisualization.getNumProcesses(); + String processString = prefs.getString("lang.process"); + + for (int i = 0; i < numProcesses; ++i) { + int pid = simulatorVisualization.getProcess(i).getProcessID(); + processesComboBox.addItem(processString + " " + pid); + localPIDComboBox.addItem("PID: " + pid); + globalPIDComboBox.addItem("PID: " + pid); + } + + processesComboBox.addItem(prefs.getString("lang.processes.all")); + localPIDComboBox.addItem(prefs.getString("lang.all")); + globalPIDComboBox.addItem(prefs.getString("lang.all")); + + tabbedPane = new JTabbedPane(JTabbedPane.TOP, + JTabbedPane.WRAP_TAB_LAYOUT); + localPanel = createTaskLabel(VSTaskManagerTableModel.LOCAL); + JPanel globalPanel = createTaskLabel(VSTaskManagerTableModel.GLOBAL); + + splitPane1 = new JSplitPane(); + splitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT); + splitPane1.setTopComponent(localPanel); + splitPane1.setBottomComponent(globalPanel); + splitPane1.setOneTouchExpandable(true); + + if (expertMode) + tabbedPane.addTab(prefs.getString("lang.events"), splitPane1); + + else + tabbedPane.addTab(prefs.getString("lang.events"), localPanel); + + processesComboBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + localTextFields.set(lastSelectedProcessNum, + localTextField.getText()); + globalTextFields.set(lastSelectedProcessNum, + globalTextField.getText()); + updateTaskManagerTable(); + + int processNum = getSelectedProcessNum(); + localTextField.setText(localTextFields.get(processNum)); + globalTextField.setText(globalTextFields.get(processNum)); + localTextField.setBackground(Color.WHITE); + globalTextField.setBackground(Color.WHITE); + lastSelectedProcessNum = processNum; + + localPIDComboBox.setSelectedIndex(processNum); + globalPIDComboBox.setSelectedIndex(processNum); + + if (processNum == simulatorVisualization.getNumProcesses()) { + tabbedPane.setEnabledAt(1, false); + if (tabbedPane.getSelectedIndex() == 1) + tabbedPane.setSelectedIndex(0); + + } else if (!tabbedPane.isEnabledAt(1)) { + tabbedPane.setEnabledAt(1, true); + } + + if (processNum != simulatorVisualization.getNumProcesses()) { + VSInternalProcess process = getSelectedProcess(); + VSProcessEditor processEditor = + new VSProcessEditor(prefs, process); + tabbedPane.setComponentAt(1, + processEditor.getContentPane()); + } + } + }); + + tabbedPane.add(prefs.getString("lang.variables"), null); + + editPanel.add(processesComboBox); + editPanel.add(tabbedPane); + + return editPanel; + } + + /** + * Creates the label panel. + * + * @param text the text + * + * @return the panel + */ + private JPanel createLabelPanel(String text) { + JPanel panel = new JPanel(); + JLabel label = new JLabel(text); + panel.add(label); + + return panel; + } + + /** + * Creates the task label. + * + * @param localTasks true, if the local task label has to get created. + * false, if the global task label has to get created. + * + * @return the panel + */ + private JPanel createTaskLabel(boolean localTasks) { + JPanel panel = new JPanel(new GridBagLayout()); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + + if (localTasks) + panel.add(createLabelPanel(prefs.getString("lang.timed.local"))); + else + panel.add(createLabelPanel(prefs.getString("lang.timed.global"))); + + JScrollPane scrollPane = new JScrollPane(createTaskTable(localTasks)); + panel.add(scrollPane); + + if (localTasks) + localAddPanel = initAddPanel(panel, localTasks); + else + /*globalAddPanel = */ initAddPanel(panel, localTasks); + + return panel; + } + + /** + * Creates the task table. + * + * @param localTasks true, if the local task label has to get created. + * false, if the global task label has to get created. + * + * @return the table + */ + private JTable createTaskTable(boolean localTasks) { + VSInternalProcess process = getSelectedProcess(); + VSTaskManagerTableModel model = + new VSTaskManagerTableModel(process, localTasks); + VSTaskManagerCellEditor cellEditor = + new VSTaskManagerCellEditor(model); + + if (localTasks) { + taskManagerLocalModel = model; + taskManagerLocalEditor = cellEditor; + } else { + taskManagerGlobalModel = model; + taskManagerGlobalEditor = cellEditor; + } + + JTable table = new JTable(model); + table.setDefaultEditor(Object.class, cellEditor); + model.setTable(table); + + table.addMouseListener(model); + + TableColumn col = table.getColumnModel().getColumn(0); + col.setMaxWidth(62); + col.setResizable(false); + + col = table.getColumnModel().getColumn(1); + col.setMaxWidth(40); + col.setResizable(false); + + col = table.getColumnModel().getColumn(2); + col.sizeWidthToFit(); + table.setBackground(Color.WHITE); + + return table; + } + + /** + * Inits the add panel. + * + * @param panel the panel + * @param localTasks true, if the local task label has to get created. + * false, if the global task label has to get created. + * + * @return the panel + */ + private JPanel initAddPanel(JPanel panel, final boolean localTasks) { + JPanel addPanel = new JPanel(); + addPanel.setLayout(new BoxLayout(addPanel, BoxLayout.X_AXIS)); + boolean expertMode = prefs.getBoolean("sim.mode.expert"); + + final JTextField textField = new JTextField(); + if (localTasks) + localTextField = textField; + else + globalTextField = textField; + + textField.setText("0000"); + textField.setBackground(Color.WHITE); + addPanel.add(textField); + + addPanel.add(new JLabel(" ms ")); + + if (localTasks) { + if (expertMode) + addPanel.add(localPIDComboBox); + } else { + addPanel.add(globalPIDComboBox); + } + + final JComboBox comboBox = new JComboBox<>(); + JButton takeoverButton = new JButton(prefs.getString("lang.takeover")); + takeoverButton.setMnemonic(prefs.getInteger("keyevent.takeover")); + takeoverButton.addActionListener(new ActionListener() { + private boolean isRed; + public void actionPerformed(ActionEvent ae) { + String textValue = textField.getText(); + Long longValue = null; + + try { + longValue = Long.valueOf(textValue); + + if (longValue.longValue() < 0) { + makeRed(); + return; + } + + if (isRed) { + makeWhite(); + } + + } catch (NumberFormatException e) { + makeRed(); + } + + if (longValue == null) + return; + + if (takeover(longValue.longValue())) { + if (isRed) + makeWhite(); + + } else { + makeRed(); + } + } + + private void makeWhite() { + textField.setBackground(Color.WHITE); + isRed = false; + } + + private void makeRed() { + textField.setBackground(Color.RED); + isRed = true; + } + + private boolean takeover(long time) { + VSInternalProcess selectedProcess = getSelectedProcess(); + int index = comboBox.getSelectedIndex(); + VSCreateTask createTask = createTasks.get(index); + + if (createTask.isDummy()) + return false; + + ArrayList processes = + getConcernedProcesses(localTasks); + + for (VSInternalProcess process : processes) { + VSTask task = createTask.createTask(process, time, + localTasks); + taskManager.addTask(task, VSTaskManager.PROGRAMMED); + + if (selectedProcess == null || + process.equals(selectedProcess)) { + if (localTasks) + taskManagerLocalModel.addTask(task); + else + taskManagerGlobalModel.addTask(task); + } + } + + return true; + } + }); + + addPanel.add(takeoverButton); + + boolean createTaskFlag = createTasks == null; + if (createTaskFlag) createTasks = new ArrayList(); + + Vector eventClassnames = + VSRegisteredEvents.getNonProtocolClassnames(); + + comboBox.setMaximumRowCount(20); + String menuText = prefs.getString("lang.events.process"); + comboBox.addItem("----- " + menuText + " -----"); + + if (createTaskFlag) + createTasks.add(new VSCreateTask(menuText)); + + for (String eventClassname : eventClassnames) { + String eventShortname = + VSRegisteredEvents.getShortnameByClassname(eventClassname); + menuText = eventShortname; + comboBox.addItem(menuText); + if (createTaskFlag) + createTasks.add(new VSCreateTask(menuText, eventClassname)); + } + + String activate = prefs.getString("lang.activate"); + String client = prefs.getString("lang.client"); + String clientRequest = prefs.getString("lang.clientrequest.start"); + String deactivate = prefs.getString("langactivate"); + String server = prefs.getString("lang.server"); + String serverRequest = prefs.getString("lang.serverrequest.start"); + String protocol = prefs.getString("lang.protocol"); + + String protocolEventClassname = "events.internal.VSProtocolEvent"; + eventClassnames = VSRegisteredEvents.getProtocolClassnames(); + + for (String eventClassname : eventClassnames) { + String eventShortname_ = + VSRegisteredEvents.getShortnameByClassname(eventClassname); + String eventShortname = null; + + menuText = eventShortname_ + " " + protocol; + comboBox.addItem("----- " + menuText + " -----"); + + if (createTaskFlag) + createTasks.add(new VSCreateTask(menuText)); + + if (VSRegisteredEvents.isOnServerStartProtocol(eventClassname)) + eventShortname = eventShortname_ + " " + serverRequest; + else + eventShortname = eventShortname_ + " " + clientRequest; + + menuText = eventShortname; + comboBox.addItem(menuText); + if (createTaskFlag) { + VSCreateTask createTask = new VSCreateTask(menuText, + eventClassname); + createTask.setShortname(eventShortname); + createTask.isRequest(true); + createTasks.add(createTask); + } + + eventShortname = eventShortname_ + " " + client + " " + activate; + menuText = eventShortname; + comboBox.addItem(menuText); + if (createTaskFlag) { + VSCreateTask createTask = + new VSCreateTask(menuText, protocolEventClassname); + createTask.isProtocolActivation(true); + createTask.isClientProtocol(true); + createTask.setProtocolClassname(eventClassname); + createTask.setShortname(eventShortname); + createTasks.add(createTask); + } + + eventShortname = eventShortname_ + " " + client + " " + deactivate; + menuText = eventShortname; + comboBox.addItem(menuText); + if (createTaskFlag) { + VSCreateTask createTask = + new VSCreateTask(menuText, protocolEventClassname); + createTask.isProtocolDeactivation(true); + createTask.isClientProtocol(true); + createTask.setProtocolClassname(eventClassname); + createTask.setShortname(eventShortname); + createTasks.add(createTask); + } + + eventShortname = eventShortname_ + " " + server + " " + activate; + menuText = eventShortname; + comboBox.addItem(menuText); + if (createTaskFlag) { + VSCreateTask createTask = + new VSCreateTask(menuText, protocolEventClassname); + createTask.isProtocolActivation(true); + createTask.isClientProtocol(false); + createTask.setProtocolClassname(eventClassname); + createTask.setShortname(eventShortname); + createTasks.add(createTask); + } + + eventShortname = eventShortname_ + " " + server + " " + deactivate; + menuText = eventShortname; + comboBox.addItem(menuText); + if (createTaskFlag) { + VSCreateTask createTask = + new VSCreateTask(menuText, protocolEventClassname); + createTask.isProtocolDeactivation(true); + createTask.isClientProtocol(false); + createTask.setProtocolClassname(eventClassname); + createTask.setShortname(eventShortname); + createTasks.add(createTask); + } + } + + panel.add(comboBox); + panel.add(addPanel); + + return addPanel; + } + + /** + * Gets the split size. + * + * @return the split size + */ + public synchronized int getSplitSize() { + return splitPaneH.getDividerLocation(); + } + + /** + * Gets the paint size. + * + * @return the paint size + */ + public synchronized int getPaintSize() { + return splitPaneV.getDividerLocation(); + } + + /** + * Gets the selected process num. + * + * @return the selected process num + */ + private int getSelectedProcessNum() { + return processesComboBox.getSelectedIndex(); + } + + /** + * Checks if 'all processes' is selected + * + * @return True, if 'all processes' are selected, else false + */ + private boolean allProcessesAreSelected() { + return processesComboBox.getSelectedIndex() + 1 + == processesComboBox.getItemCount(); + } + + /** + * Gets the selected process. + * + * @return the selected process + */ + private VSInternalProcess getSelectedProcess() { + int processNum = getSelectedProcessNum(); + return simulatorVisualization.getProcess(processNum); + } + + /** + * Gets the concerned processes. + * + * @param localTasks true, if this table manages the local tasks. false + * if this table manages the global tasks. + * + * @return the concerned processes + */ + private ArrayList getConcernedProcesses( + boolean localTasks) { + int processNum = localTasks + ? localPIDComboBox.getSelectedIndex() + : globalPIDComboBox.getSelectedIndex(); + + if (processNum == simulatorVisualization.getNumProcesses()) + return simulatorVisualization.getProcessesArray(); + + ArrayList arr = new ArrayList(); + arr.add(simulatorVisualization.getProcess(processNum)); + + return arr; + } + + /** + * Update task manager table. + */ + public synchronized void updateTaskManagerTable() { + VSInternalProcess process = getSelectedProcess(); + boolean allProcesses = process == null; + + taskManagerLocalEditor.stopEditing(); + taskManagerGlobalEditor.stopEditing(); + + taskManagerLocalModel.set(process, + VSTaskManagerTableModel.LOCAL, + allProcesses); + + taskManagerGlobalModel.set(process, + VSTaskManagerTableModel.GLOBAL, + allProcesses); + } + + /** + * Update the processes combo box + */ + private void updateProcessesComboBox() { + int numProcesses = simulatorVisualization.getNumProcesses(); + String processString = prefs.getString("lang.process"); + + for (int i = 0; i < numProcesses; ++i) { + int processID = simulatorVisualization.getProcess(i).getProcessID(); + + processesComboBox.removeItemAt(i); + localPIDComboBox.removeItemAt(i); + globalPIDComboBox.removeItemAt(i); + + processesComboBox.insertItemAt(processString + " " + processID, i); + localPIDComboBox.insertItemAt("PID: " + processID, i); + globalPIDComboBox.insertItemAt("PID: " + processID, i); + } + } + + /** + * The simulator has finished. + */ + public synchronized void finish() { + menuItemStates.setStart(false); + menuItemStates.setPause(false); + menuItemStates.setReset(true); + menuItemStates.setReplay(true); + simulatorFrame.updateSimulatorMenu(); + } + + /** + * Gets the simulator num. + * + * @return the simulator num + */ + public synchronized int getSimulatorNum() { + return simulatorNum; + } + + /** + * Gets the menu item states. + * + * @return the menu item states + */ + public synchronized VSMenuItemStates getMenuItemStates() { + return menuItemStates; + } + + /** + * Gets the simulator canvas. + * + * @return the simulator canvas + */ + public synchronized VSSimulatorVisualization getSimulatorCanvas() { + return simulatorVisualization; + } + + /** + * Gets the simulator frame. + * + * @return the simulator frame + */ + public synchronized VSSimulatorFrame getSimulatorFrame() { + return simulatorFrame; + } + + /** + * Update from prefs. + */ + public synchronized void updateFromPrefs() { + simulatorVisualization.setBackground(prefs.getColor("col.background")); + simulatorVisualization.updateFromPrefs(); + } + + /** + * Removes the process at a specified index. + * + * @param index the index + */ + public synchronized void removedAProcessAtIndex(int index) { + if (lastSelectedProcessNum > index) + --lastSelectedProcessNum; + + globalTextFields.remove(index); + localTextFields.remove(index); + + globalPIDComboBox.removeItemAt(index); + localPIDComboBox.removeItemAt(index); + + processesComboBox.removeItemAt(index); + simulatorFrame.updateEditMenu(); + + updateTaskManagerTable(); + } + + /** + * Adds the process at a specified index. + * + * @param index the index + */ + public synchronized void addProcessAtIndex(int index) { + int processID = simulatorVisualization.getProcess(index).getProcessID(); + String processString = prefs.getString("lang.process"); + + localTextFields.add(index, "0000"); + globalTextFields.add(index, "0000"); + + localPIDComboBox.insertItemAt("PID: " + processID, index); + globalPIDComboBox.insertItemAt("PID: " + processID, index); + + processesComboBox.insertItemAt(processString + " " + processID, index); + simulatorFrame.updateEditMenu(); + } + + /** + * Fire expert mode changed. Tell, that the expert mode has changed. + */ + public synchronized void fireExpertModeChanged() { + boolean expertMode = prefs.getBoolean("sim.mode.expert"); + + /* Update the Task Manager GUI */ + int selectedIndex = tabbedPane.getSelectedIndex(); + + if (expertMode) { + tabbedPane.remove(localPanel); + tabbedPane.insertTab(prefs.getString("lang.events"), null, + splitPane1, null, 0); + splitPane1.setTopComponent(localPanel); + //splitPane1.setDividerLocation((int) (getPaintSize()/2) - 20); + + /* addPanel */ + localAddPanel.add(localPIDComboBox, 2); + + } else { + tabbedPane.remove(splitPane1); + tabbedPane.insertTab(prefs.getString("lang.events"), null, + localPanel, null, 0); + + /* addPanel */ + localAddPanel.remove(2); + } + + tabbedPane.setSelectedIndex(selectedIndex); + + /* Update the 'Variables tab' */ + if (getSelectedProcessNum() != + simulatorVisualization.getNumProcesses()) { + VSInternalProcess process = getSelectedProcess(); + VSProcessEditor editor = new VSProcessEditor(prefs, process); + tabbedPane.setComponentAt(1, editor.getContentPane()); + } + + /* Update the tools panel */ + logingPanel.remove(1); + logingPanel.add(createToolsPanel(), BorderLayout.SOUTH); + updateUI(); + } + + /** + * Gets the prefs. + * + * @return the prefs + */ + public synchronized VSPrefs getPrefs() { + return prefs; + } + + /** + * Gets the create tasks objects. Those objects are for creating new tasks + * via the task manager GUI or via right click on the paint area of the + * simulator canvas! + * + * @return The create tasks objects + */ + ArrayList getCreateTaskObjects() { + return createTasks; + } + + /* (non-Javadoc) + * @see serialize.VSSerializable#serialize(serialize.VSSerialize, + * java.io.ObjectOutputStream) + */ + public synchronized void serialize(VSSerialize serialize, + ObjectOutputStream objectOutputStream) + throws IOException { + /** For later backwards compatibility, to add more stuff */ + objectOutputStream.writeObject(Boolean.valueOf(false)); + + simulatorVisualization.serialize(serialize, objectOutputStream); + + /** For later backwards compatibility, to add more stuff */ + objectOutputStream.writeObject(Boolean.valueOf(false)); + + } + + /* (non-Javadoc) + * @see serialize.VSSerializable#deserialize(serialize.VSSerialize, + * java.io.ObjectInputStream) + */ + public synchronized void deserialize(VSSerialize serialize, + ObjectInputStream objectInputStream) + throws IOException, ClassNotFoundException { + if (VSSerialize.DEBUG) + System.out.println("Deserializing: VSSimulator"); + + serialize.setObject("simulator", this); + serialize.setObject("loging", loging); + + /** For later backwards compatibility, to add more stuff */ + objectInputStream.readObject(); + + simulatorVisualization.deserialize(serialize, objectInputStream); + + /** For later backwards compatibility, to add more stuff */ + objectInputStream.readObject(); + + updateFromPrefs(); + updateTaskManagerTable(); + updateProcessesComboBox(); + processesComboBox.setSelectedIndex(processesComboBox.getItemCount()-1); + } +} diff --git a/src/main/java/simulator/VSSimulatorFrame.java b/src/main/java/simulator/VSSimulatorFrame.java new file mode 100644 index 0000000..3cf4e91 --- /dev/null +++ b/src/main/java/simulator/VSSimulatorFrame.java @@ -0,0 +1,628 @@ +package simulator; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Vector; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JTabbedPane; +import javax.swing.JToolBar; +import javax.swing.KeyStroke; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import core.VSInternalProcess; +import prefs.VSDefaultPrefs; +import prefs.VSPrefs; +import prefs.editors.VSEditorFrame; +import prefs.editors.VSSimulatorEditor; +import serialize.VSSerialize; +import utils.VSAboutFrame; +import utils.VSFrame; + +/** + * The class VSSimulatorFrame, an object of this class represents a window + * of the simulator. The window can have several tabs. Each tab contains + * an independent simulator. + * + * @author Paul C. Buetow + */ +public class VSSimulatorFrame extends VSFrame { + /** The serial version uid */ + private static final long serialVersionUID = 1L; + + /** The pause item. */ + private JMenuItem pauseItem; + + /** The replay item. */ + private JMenuItem replayItem; + + /** The reset item. */ + private JMenuItem resetItem; + + /** The start item. */ + private JMenuItem startItem; + + /** The pause button. */ + private JButton pauseButton; + + /** The replay button. */ + private JButton replayButton; + + /** The reset button. */ + private JButton resetButton; + + /** The start button. */ + private JButton startButton; + + /** The menu edit. */ + private JMenu menuEdit; + + /** The menu file. */ + private JMenu menuFile; + + /** The close item. */ + private JMenuItem closeItem; + + /** The save item. */ + private JMenuItem saveItem; + + /** The save as item. */ + private JMenuItem saveAsItem; + + /** The menu simulator. */ + private JMenu menuSimulator; + + /** The tool bar. */ + private JToolBar toolBar; + + /** The prefs. */ + private VSPrefs prefs; + + /** The simulators. */ + private Vector simulators; + + /** The current simulator. */ + private VSSimulator currentSimulator; + + /** The tabbed pane. */ + private JTabbedPane tabbedPane; + + /** The action listener */ + private ActionListener actionListener; + + /** + * Instantiates a new VSSimulatorFrame object. + * + * @param prefs the prefs + * @param relativeTo the component to open the window relative to + */ + public VSSimulatorFrame(VSPrefs prefs, Component relativeTo) { + super(prefs.getString("lang.name"), relativeTo); + this.prefs = prefs; + this.simulators = new Vector(); + + final VSPrefs finalPrefs = this.prefs; + actionListener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + Object source = e.getSource(); + String sourceText = null; + + if (source instanceof JMenuItem) + sourceText = ((JMenuItem) source).getText(); + else + sourceText = ((ImageIcon) ((JButton) source).getIcon()). + getDescription(); + + if (sourceText.equals( + finalPrefs.getString("lang.simulator.close"))) { + removeCurrentSimulator(); + + } else if (sourceText.equals( + finalPrefs.getString("lang.simulator.new"))) { + VSPrefs newPrefs = VSDefaultPrefs.init(); + VSSimulatorEditor simulatorEditor = + new VSSimulatorEditor(newPrefs, VSSimulatorFrame.this, + VSSimulatorEditor.OPENED_NEW_TAB); + new VSEditorFrame(newPrefs, VSSimulatorFrame.this, + simulatorEditor); + + } else if (sourceText.equals( + finalPrefs.getString("lang.window.new"))) { + new VSMain(VSDefaultPrefs.init(), + VSSimulatorFrame.this); + + } else if (sourceText.equals( + finalPrefs.getString("lang.window.close"))) { + dispose(); + + } else if (sourceText.equals( + finalPrefs.getString("lang.open"))) { + VSSerialize serialize = new VSSerialize(); + serialize.openSimulator(VSSimulatorFrame.this); + + } else if (sourceText.equals( + finalPrefs.getString("lang.save"))) { + VSSimulatorVisualization simulatorVisualization = + currentSimulator.getSimulatorCanvas(); + boolean flag = !simulatorVisualization.isPaused() + && !simulatorVisualization.isResetted() + && !simulatorVisualization.hasFinished(); + + if (flag) + pauseCurrentSimulator(); + + VSSerialize serialize = new VSSerialize(); + serialize.saveSimulator(VSSerialize.LAST_FILENAME, + currentSimulator); + if (flag) + startCurrentSimulator(); + + } else if (sourceText.equals( + finalPrefs.getString("lang.saveas"))) { + VSSimulatorVisualization simulatorVisualization = + currentSimulator.getSimulatorCanvas(); + boolean flag = !simulatorVisualization.isPaused() + && !simulatorVisualization.isResetted() + && !simulatorVisualization.hasFinished(); + if (flag) + pauseCurrentSimulator(); + + VSSerialize serialize = new VSSerialize(); + serialize.saveSimulator(currentSimulator); + + if (flag) + startCurrentSimulator(); + + } else if (sourceText.equals( + finalPrefs.getString("lang.about"))) { + new VSAboutFrame(finalPrefs, VSSimulatorFrame.this); + + } else if (sourceText.equals( + finalPrefs.getString("lang.quit"))) { + System.exit(0); + + } else if (sourceText.equals( + finalPrefs.getString("lang.start"))) { + startCurrentSimulator(); + + } else if (sourceText.equals( + finalPrefs.getString("lang.pause"))) { + pauseCurrentSimulator(); + + } else if (sourceText.equals( + finalPrefs.getString("lang.reset"))) { + resetCurrentSimulator(); + + } else if (sourceText.equals( + finalPrefs.getString("lang.replay"))) { + VSMenuItemStates menuItemState = + currentSimulator.getMenuItemStates(); + menuItemState.setStart(false); + menuItemState.setPause(true); + menuItemState.setReset(false); + menuItemState.setReplay(true); + currentSimulator.getSimulatorCanvas().reset(); + currentSimulator.getSimulatorCanvas().play(); + updateSimulatorMenu(); + } + } + }; + + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + + setSize(prefs.getInteger("div.window.xsize"), + prefs.getInteger("div.window.ysize")); + + setJMenuBar(createMenuBar()); + setLayout(new BorderLayout()); + setContentPane(createContentPane()); + setVisible(true); + + pauseButton.setEnabled(false); + replayButton.setEnabled(false); + resetButton.setEnabled(false); + startButton.setEnabled(false); + menuEdit.setEnabled(false); + //menuFile.setEnabled(false); + closeItem.setEnabled(false); + saveItem.setEnabled(false); + saveAsItem.setEnabled(false); + menuSimulator.setEnabled(false); + } + + /** + * Creates the menu bar. + * + * @return the j menu bar + */ + private JMenuBar createMenuBar() { + /* File menu */ + menuFile = new JMenu(prefs.getString("lang.file")); + menuFile.setMnemonic(prefs.getInteger("keyevent.file")); + JMenuItem menuItem; + + menuItem = new JMenuItem(prefs.getString("lang.simulator.new")); + menuItem.setAccelerator(KeyStroke.getKeyStroke( + prefs.getInteger("keyevent.new"), + ActionEvent.ALT_MASK)); + menuItem.addActionListener(actionListener); + menuFile.add(menuItem); + + closeItem = new JMenuItem( + prefs.getString("lang.simulator.close")); + closeItem.setAccelerator(KeyStroke.getKeyStroke( + prefs.getInteger("keyevent.close"), + ActionEvent.ALT_MASK)); + closeItem.addActionListener(actionListener); + menuFile.add(closeItem); + + menuFile.addSeparator(); + + menuItem = new JMenuItem(prefs.getString("lang.window.new")); + menuItem.addActionListener(actionListener); + menuFile.add(menuItem); + + menuItem = new JMenuItem(prefs.getString("lang.window.close")); + menuItem.addActionListener(actionListener); + menuFile.add(menuItem); + + menuFile.addSeparator(); + + menuItem = new JMenuItem(prefs.getString("lang.open")); + menuItem.setAccelerator(KeyStroke.getKeyStroke( + prefs.getInteger("keyevent.open"), + ActionEvent.ALT_MASK)); + menuItem.addActionListener(actionListener); + menuFile.add(menuItem); + + saveItem = new JMenuItem(prefs.getString("lang.save")); + saveItem.setAccelerator(KeyStroke.getKeyStroke( + prefs.getInteger("keyevent.save"), + ActionEvent.ALT_MASK)); + saveItem.addActionListener(actionListener); + menuFile.add(saveItem); + + saveAsItem = new JMenuItem(prefs.getString("lang.saveas")); + saveAsItem.setAccelerator(KeyStroke.getKeyStroke( + prefs.getInteger("keyevent.saveas"), + ActionEvent.ALT_MASK)); + saveAsItem.addActionListener(actionListener); + menuFile.add(saveAsItem); + + menuFile.addSeparator(); + + menuItem = new JMenuItem(prefs.getString("lang.about")); + menuItem.addActionListener(actionListener); + menuFile.add(menuItem); + + menuItem = new JMenuItem(prefs.getString("lang.quit")); + menuItem.addActionListener(actionListener); + menuFile.add(menuItem); + + /* Edit menu */ + menuEdit = new JMenu( + prefs.getString("lang.edit")); + menuEdit.setMnemonic(prefs.getInteger("keyevent.edit")); + updateEditMenu(); + + /* Simulator menu */ + toolBar = new JToolBar(); + menuSimulator = new JMenu( + prefs.getString("lang.simulator")); + menuSimulator.setMnemonic(prefs.getInteger("keyevent.simulator")); + + resetItem = new JMenuItem(prefs.getString("lang.reset")); + resetItem.setAccelerator(KeyStroke.getKeyStroke( + prefs.getInteger("keyevent.reset"), + ActionEvent.ALT_MASK)); + resetItem.addActionListener(actionListener); + resetItem.setEnabled(false); + menuSimulator.add(resetItem); + resetButton = new JButton(getImageIcon("reset.png", + prefs.getString("lang.reset"))); + resetButton.addActionListener(actionListener); + toolBar.add(resetButton); + + replayItem = new JMenuItem( + prefs.getString("lang.replay")); + replayItem.setAccelerator(KeyStroke.getKeyStroke( + prefs.getInteger("keyevent.replay"), + ActionEvent.ALT_MASK)); + replayItem.addActionListener(actionListener); + replayItem.setEnabled(false); + menuSimulator.add(replayItem); + replayButton = new JButton( + getImageIcon("replay.png", prefs.getString("lang.replay"))); + replayButton.addActionListener(actionListener); + toolBar.add(replayButton); + + pauseItem = new JMenuItem(prefs.getString("lang.pause")); + pauseItem.setAccelerator(KeyStroke.getKeyStroke( + prefs.getInteger("keyevent.pause"), + ActionEvent.ALT_MASK)); + pauseItem.addActionListener(actionListener); + menuSimulator.add(pauseItem); + pauseItem.setEnabled(false); + pauseButton = new JButton(getImageIcon("pause.png", + prefs.getString("lang.pause"))); + pauseButton.addActionListener(actionListener); + toolBar.add(pauseButton); + + startItem = new JMenuItem(prefs.getString("lang.start")); + startItem.setAccelerator(KeyStroke.getKeyStroke( + prefs.getInteger("keyevent.start"), + ActionEvent.ALT_MASK)); + startItem.addActionListener(actionListener); + menuSimulator.add(startItem); + startButton = new JButton(getImageIcon("start.png", + prefs.getString("lang.start"))); + startButton.addActionListener(actionListener); + toolBar.add(startButton); + + + JMenuBar mainMenuBar = new JMenuBar(); + mainMenuBar.add(menuFile); + mainMenuBar.add(menuEdit); + mainMenuBar.add(menuSimulator); + + return mainMenuBar; + } + + /** + * Creates the content pane. + * + * @return the container + */ + private Container createContentPane() { + Container pane = getContentPane(); + tabbedPane = new JTabbedPane(JTabbedPane.BOTTOM, + JTabbedPane.SCROLL_TAB_LAYOUT); + + tabbedPane.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent ce) { + JTabbedPane pane = (JTabbedPane) ce.getSource(); + currentSimulator = (VSSimulator) pane.getSelectedComponent(); + if (currentSimulator != null) { + currentSimulator.getSimulatorCanvas().paint(); + updateEditMenu(); + updateSimulatorMenu(); + } + } + }); + + pane.add(toolBar, BorderLayout.PAGE_START); + pane.add(tabbedPane, BorderLayout.CENTER); + + return pane; + } + + /** + * Updates the edit menu. Called if another simulator tab has been selected + * or if processes have been added or removed. + */ + public void updateEditMenu() { + menuEdit.removeAll(); + + JMenuItem globalPrefsItem = new JMenuItem( + prefs.getString("lang.prefs")); + + globalPrefsItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + VSPrefs simulatorPrefs = currentSimulator.getPrefs(); + VSSimulatorEditor.TAKEOVER_BUTTON = true; + VSSimulatorEditor simulatorEditor = new VSSimulatorEditor( + simulatorPrefs, VSSimulatorFrame.this, currentSimulator); + new VSEditorFrame(prefs, VSSimulatorFrame.this, + simulatorEditor); + } + }); + + menuEdit.add(globalPrefsItem); + menuEdit.addSeparator(); + + if (currentSimulator == null) + return; + + String processString = prefs.getString("lang.process"); + ArrayList arr = + currentSimulator.getSimulatorCanvas().getProcessesArray(); + + //int numProcesses = arr.size(); + int processNum = 0; + + for (VSInternalProcess process : arr) { + int processID = process.getProcessID(); + JMenuItem processItem = new JMenuItem(processString + " " + + processID); + if (processNum < 9) + processItem.setAccelerator( + KeyStroke.getKeyStroke(0x31+processNum, + ActionEvent.ALT_MASK)); + final int finalProcessNum = processNum++; + processItem.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + currentSimulator.getSimulatorCanvas().editProcess( + finalProcessNum); + } + }); + menuEdit.add(processItem); + } + } + + /** + * Updates the simulator menu. Called if the simulator state has changed + * (e.g. start/play/stop/replay etc) + */ + public synchronized void updateSimulatorMenu() { + VSMenuItemStates menuItemState = currentSimulator.getMenuItemStates(); + + pauseItem.setEnabled(menuItemState.getPause()); + replayItem.setEnabled(menuItemState.getReplay()); + resetItem.setEnabled(menuItemState.getReset()); + startItem.setEnabled(menuItemState.getStart()); + + pauseButton.setEnabled(menuItemState.getPause()); + replayButton.setEnabled(menuItemState.getReplay()); + resetButton.setEnabled(menuItemState.getReset()); + startButton.setEnabled(menuItemState.getStart()); + } + + /* (non-Javadoc) + * @see java.awt.Window#dispose() + */ + public void dispose() { + synchronized (simulators) { + for (VSSimulator simulator : simulators) + simulator.getSimulatorCanvas().stopThread(); + } + super.dispose(); + } + + /** + * Adds the simulator. + * + * @param simulator the simulator + */ + public void addSimulator(VSSimulator simulator) { + simulator.setLayout(new GridLayout(1, 1, 3, 3)); + simulator.setMinimumSize(new Dimension(0, 0)); + simulator.setMaximumSize(new Dimension(0, 0)); + + simulators.add(simulator); + tabbedPane.addTab(prefs.getString("lang.simulator") + + " " + simulator.getSimulatorNum(), simulator); + tabbedPane.setSelectedComponent(simulator); + + if (simulators.size() == 1) { + menuEdit.setEnabled(true); + //menuFile.setEnabled(true); + closeItem.setEnabled(true); + saveItem.setEnabled(true); + saveAsItem.setEnabled(true); + menuSimulator.setEnabled(true); + } + } + + /** + * Removes the simulator. + * + * @param simulatorToRemove the simulator to remove + */ + public void removeSimulator(VSSimulator simulatorToRemove) { + if (simulators.size() == 1) { + pauseButton.setEnabled(false); + replayButton.setEnabled(false); + resetButton.setEnabled(false); + startButton.setEnabled(false); + menuEdit.setEnabled(false); + //menuFile.setEnabled(false); + closeItem.setEnabled(false); + saveItem.setEnabled(false); + saveAsItem.setEnabled(false); + menuSimulator.setEnabled(false); + } + + simulators.remove(simulatorToRemove); + tabbedPane.remove(simulatorToRemove); + simulatorToRemove.getSimulatorCanvas().stopThread(); + } + + /** + * Removes the current simulator. + */ + private void removeCurrentSimulator() { + removeSimulator(currentSimulator); + } + + /** + * Gets the current simulator. + * + * @return the current simulator + */ + public VSSimulator getCurrentSimulator() { + return currentSimulator; + } + + /** + * Resets the current simulator + */ + public void resetCurrentSimulator() { + if (currentSimulator == null) + return; + + VSMenuItemStates menuItemState = + currentSimulator.getMenuItemStates(); + menuItemState.setStart(true); + menuItemState.setPause(false); + menuItemState.setReset(false); + menuItemState.setReplay(false); + currentSimulator.getSimulatorCanvas().reset(); + updateSimulatorMenu(); + } + + /** + * Starts the current simulator + */ + public void startCurrentSimulator() { + VSMenuItemStates menuItemState = + currentSimulator.getMenuItemStates(); + menuItemState.setStart(false); + menuItemState.setPause(true); + menuItemState.setReset(false); + menuItemState.setReplay(true); + currentSimulator.getSimulatorCanvas().play(); + updateSimulatorMenu(); + } + + /** + * Pauses the current simulator + */ + public void pauseCurrentSimulator() { + VSMenuItemStates menuItemState = + currentSimulator.getMenuItemStates(); + menuItemState.setStart(true); + menuItemState.setPause(false); + menuItemState.setReset(true); + menuItemState.setReplay(true); + currentSimulator.getSimulatorCanvas().pause(); + updateSimulatorMenu(); + } + + /** + * Gets the image icon. + * + * @param name the name + * @param descr the descr + * + * @return the image icon + */ + private ImageIcon getImageIcon(String name, String descr) { + java.net.URL imageURL = getClass().getResource("/icons/"+name); + + if (imageURL == null) + return new ImageIcon("icons/"+name, descr); + + return new ImageIcon(imageURL, descr); + } + + /** + * Gets the prefs. + * + * @return the prefs + */ + public VSPrefs getPrefs() { + return prefs; + } +} diff --git a/src/main/java/simulator/VSSimulatorVisualization.java b/src/main/java/simulator/VSSimulatorVisualization.java new file mode 100644 index 0000000..2dc4a64 --- /dev/null +++ b/src/main/java/simulator/VSSimulatorVisualization.java @@ -0,0 +1,1814 @@ +package simulator; + +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.HierarchyBoundsListener; +import java.awt.event.HierarchyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.image.BufferStrategy; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.LinkedList; + +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; + +import core.VSInternalProcess; +import core.VSMessage; +import core.VSTask; +import core.VSTaskManager; +import core.time.VSTime; +import events.VSAbstractEvent; +import events.implementations.VSProcessCrashEvent; +import events.implementations.VSProcessRecoverEvent; +import events.internal.VSMessageReceiveEvent; +import prefs.VSPrefs; +import prefs.editors.VSEditorFrame; +import prefs.editors.VSProcessEditor; +import serialize.VSSerializable; +import serialize.VSSerialize; + +/** + * The class VSSimulatorVisualization. An instance of this object represents the + * graphical paint area of a simulator. It contains all graphic calculations. + * Also the simulator thread takes place in this class in a loop! This class + * is probably the most cryptic of the whole simulator source code. This is + * this way in order to gain more performance of the painting area! + * + * @author Paul C. Buetow + */ +public class VSSimulatorVisualization extends Canvas + implements Runnable, VSSerializable { + + /** The serial version uid */ + private static final long serialVersionUID = 1L; + + /** The highlighted process. */ + private VSInternalProcess highlightedProcess; + + /** The simulator. */ + private VSSimulator simulator; + + /** The prefs. */ + private VSPrefs prefs; + + /** The loging. */ + private VSLogging loging; + + /** The num processes. */ + private volatile int numProcesses; + + /** The seconds spaceing. */ + private int secondsSpaceing; + + /** The thread sleep. */ + private int threadSleep; + + /** The until time. Until then goes the simulator? */ + private long untilTime; + + /** The simulator is paused. */ + private volatile boolean isPaused = true; + + /** The simulator thread is stopped. */ + private volatile boolean hasThreadStopped = false; + + /** The simulator is finished. */ + private volatile boolean hasFinished = false; + + /** The simulator is resetted. */ + private volatile boolean isResetted = false; + + /** The simulator is anti aliased. */ + private volatile boolean isAntiAliased = false; + + /** The simulator's anti aliasing has changed. */ + private volatile boolean isAntiAliasedChanged = false; + + /** The simulator shows the lamport time. */ + private volatile boolean showLamport = false; + + /** The simulator shows the vector time. */ + private volatile boolean showVectorTime = false; + + /** The pause time. */ + private volatile long pauseTime; + + /** The start time. */ + private volatile long startTime; + + /** The global time. */ + private volatile long time; + + /** The last global time. */ + private volatile long lastTime; + + /** The task manager. */ + private VSTaskManager taskManager; + + /** The message lines. */ + private LinkedList messageLines; + + /** The message lines to remove. */ + private LinkedList messageLinesToRemove; + + /** The processes. */ + private ArrayList processes; + + /** The clock speed. */ + private double clockSpeed; + + /** The clock offset. */ + private double clockOffset; + + /** The simulator time. */ + private long simulatorTime; + + /** The x paint size. */ + double xPaintSize; + + /** The paint size. */ + double paintSize; + + /** The y distance. */ + double yDistance; + + /** The global time x position. */ + double globalTimeXPosition; + + /** The xoffset_plus_xpaintsize. */ + int xoffset_plus_xpaintsize; + + /** The xpaintsize_dividedby_untiltime. */ + double xpaintsize_dividedby_untiltime; + + /** The paint processes offset. */ + int paintProcessesOffset; + + /** The paint secondlines seconds. */ + int paintSecondlinesSeconds; + + /** The paint secondlines line. */ + int paintSecondlinesLine[] = new int[4]; + + /** The paint secondlines y string pos1. */ + int paintSecondlinesYStringPos1; + + /** The paint secondlines y string pos2. */ + int paintSecondlinesYStringPos2; + + /** The paint global time y position. */ + int paintGlobalTimeYPosition; + + /* GFX buffering */ + /** The strategy for buffering. */ + private BufferStrategy strategy; + + /** The graphics object to paint at. */ + private Graphics2D g; + + /** The Constant LINE_WIDTH. */ + private static final int LINE_WIDTH = 5; + + /** The Constant SEPLINE_WIDTH. */ + private static final int SEPLINE_WIDTH = 2; + + /** The Constant XOFFSET. */ + private static final int XOFFSET = 50; + + /** The Constant YOFFSET. */ + private static final int YOFFSET = 30; + + /** The Constant YOUTER_SPACEING. */ + private static final int YOUTER_SPACEING = 15; + + /** The Constant YSEPLINE_SPACEING. */ + private static final int YSEPLINE_SPACEING = 20; + + /** The Constant TEXT_SPACEING. */ + private static final int TEXT_SPACEING = 10; + + /** The Constant ROW_HEIGHT. */ + private static final int ROW_HEIGHT = 14; + + /* Constats, which have to get calculated once after start */ + /** The processline color. */ + private Color processlineColor; + + /** The process secondline color. */ + private Color processSecondlineColor; + + /** The process sepline color. */ + private Color processSeplineColor; + + /** The message arrived color. */ + private Color messageArrivedColor; + + /** The message sending color. */ + private Color messageSendingColor; + + /** The message lost color. */ + private Color messageLostColor; + + /** The background color. */ + private Color backgroundColor; + + /** The message line counter. */ + private long messageLineCounter; + + /** The process counter. Needed for the unique process id's. */ + private int processCounter; + + /** + * The class VSMessageLine, an object of this class represents a message + * line drawn into the painting area. + * + * @author Paul C. Buetow + */ + public class VSMessageLine { + /** The receiver process. */ + private VSInternalProcess receiverProcess; + + /** The color. */ + private Color color; + + /** The send time. */ + private long sendTime; + + /** The recv time. */ + private long recvTime; + + /** The sender num. */ + private int senderNum; + + /** The receiver num. */ + private int receiverNum; + + /** The offset1. */ + private int offset1; + + /** The offset2. */ + private int offset2; + + /** The message has arrived. */ + private boolean isArrived; + + /** The message is lost. */ + private boolean isLost; + + /** The x1. */ + private double x1; + + /** The y1. */ + private double y1; + + /** The x2. */ + private double x2; + + /** The y2. */ + private double y2; + + /** The x. */ + private double x; + + /** The y. */ + private double y; + + /** The outage time. */ + private long outageTime; + + /** The z. */ + private long z; + + /** The message line num. */ + private long messageLineNum; + + /** The task. */ + private VSTask task; + + /** + * Instantiates a new VSMessageLine object. + * + * @param receiverProcess the receiver process + * @param sendTime the send time + * @param recvTime the recv time + * @param outageTime the outage time + * @param senderNum the sender num + * @param receiverNum the receiver num + * @param task the task + */ + public VSMessageLine(VSInternalProcess receiverProcess, long sendTime, + long recvTime, long outageTime, int senderNum, + int receiverNum, VSTask task) { + this.receiverProcess = receiverProcess; + this.sendTime = sendTime; + this.recvTime = recvTime; + this.outageTime = outageTime; + this.senderNum = senderNum; + this.receiverNum = receiverNum; + this.isArrived = false; + this.isLost = false; + this.messageLineNum = ++messageLineCounter; + this.task = task; + + if (senderNum > receiverNum) { + //offset1 = 1; + offset2 = LINE_WIDTH; + } else { + offset1 = LINE_WIDTH - 1; + //offset2 = 1; + } + + /* Needed if the message gets lost after 0ms */ + this.x = getTimeXPosition(sendTime); + this.y = getProcessYPosition(senderNum+1) + offset1; + + recalcOnChange(); + paint(); + } + + /** + * Recalc on change. + */ + public void recalcOnChange() { + x1 = getTimeXPosition(sendTime); + y1 = getProcessYPosition(senderNum+1) + offset1; + x2 = getTimeXPosition(recvTime); + y2 = getProcessYPosition(receiverNum+1) + offset2; + + if (isLost) { + x = getTimeXPosition(z); + y = y1 + ( ( (y2-y1) / (x2-x1)) * (x-x1)); + } + + } + + /** + * Draws the message line. + * + * @param g the grpahics object to draw at + * @param globalTime the global time + */ + public void draw(final Graphics2D g, final long globalTime) { + if (isArrived) { + g.setColor(color); + g.drawLine((int) x1, (int) y1, (int) x2, (int) y2); + + } else if (isLost) { + g.setColor(messageLostColor); + g.drawLine((int) x1, (int) y1, (int) x, (int) y); + + } else if (globalTime >= recvTime) { + checkIfMessageIsRelevant(); + isArrived = true; + + if (receiverProcess.isCrashed()) + color = messageLostColor; + else + color = messageArrivedColor; + + draw(g, globalTime); + + } else if (outageTime >= 0 && outageTime <= globalTime) { + checkIfMessageIsRelevant(); + isLost = true; + draw(g, globalTime);; + + } else { + z = globalTime; + x = globalTimeXPosition; + y = y1 + ( ( (y2-y1) / (x2-x1)) * (x-x1)); + g.setColor(messageSendingColor); + g.drawLine((int) x1, (int) y1, (int) x, (int) y); + } + } + + /** + * Checks if the message is relevant. If it's not relevant, then it will + * get removed. + */ + private void checkIfMessageIsRelevant() { + if (prefs.getBoolean("sim.messages.relevant")) { + VSMessageReceiveEvent event = + (VSMessageReceiveEvent) task.getEvent(); + event.init(receiverProcess); + if (!event.isRelevantMessage()) + removeMessageLine(this); + } + } + + /** + * Called if a process within the simulator has been removed at a + * specified index. + * + * @param index the index + * + * @return true, if the sender or the receiver of the message has been + * removed from the simulator. Else false is returned. + */ + public boolean removedAProcessAtIndex(int index) { + if (index == receiverNum || index == senderNum) + return true; + + if (index < receiverNum) + --receiverNum; + + if (index < senderNum) + --senderNum; + + recalcOnChange(); + + return false; + } + + /** + * Gets the message line num. + * + * @return the message line num + */ + public long getMessageLineNum() { + return messageLineNum; + } + + /** + * Checks one line to another if they equal (have the same message line + * id) + * + * @param line the line to compare against + * + * @return true, if they equal + */ + public boolean equals(VSMessageLine line) { + return messageLineNum == line.getMessageLineNum(); + } + + /** + * Gets the task. + * + * @return the task + */ + public VSTask getTask() { + return task; + } + } + + /** + * Instantiates a new VSSimulatorVisualization object. + * + * @param prefs the prefs + * @param simulator the simulator + * @param loging the loging + */ + public VSSimulatorVisualization(VSPrefs prefs, VSSimulator simulator, + VSLogging loging) { + init(prefs, simulator, loging); + } + + /** + * Instantiates inits the VSSimulatorVisualization object. + * + * @param prefs the prefs + * @param simulator the simulator + * @param loging the loging + */ + private void init(VSPrefs prefs, VSSimulator simulator, + VSLogging loging) { + this.prefs = prefs; + this.simulator = simulator; + this.loging = loging; + this.messageLines = new LinkedList(); + this.messageLinesToRemove = new LinkedList(); + + /* May be not null if called from deserialization */ + if (this.taskManager == null) + this.taskManager = new VSTaskManager(prefs, this); + + /* May be not null if called from deserialization */ + if (this.processes == null) { + this.processes = new ArrayList(); + + numProcesses = prefs.getInteger("sim.process.num"); + for (int i = 0; i < numProcesses; ++i) + processes.add(createProcess(i)); + } + + updateFromPrefs(); + + final VSPrefs finalPrefs = prefs; + final VSSimulator finalSimulator = simulator; + + addMouseListener(new MouseListener() { + public void mouseClicked(MouseEvent me) { + final VSInternalProcess process = getProcessAtYPos(me.getY()); + + if (SwingUtilities.isRightMouseButton(me)) { + ActionListener actionListener = new ActionListener() { + public void actionPerformed(ActionEvent ae) { + String command = ae.getActionCommand(); + if (command.equals( + finalPrefs.getString("lang.process.edit"))) { + editProcess(process); + + } else if (command.equals( + finalPrefs.getString("lang.process.crash"))) { + VSAbstractEvent event = + new VSProcessCrashEvent(); + + taskManager.addTask(new VSTask( + process.getGlobalTime(), + process, event, + VSTask.GLOBAL)); + + } else if (command.equals( + finalPrefs.getString("lang.process.recover"))) { + VSAbstractEvent event = + new VSProcessRecoverEvent(); + + taskManager.addTask(new VSTask( + process.getGlobalTime(), + process, event, + VSTask.GLOBAL)); + + } else if (command.equals( + finalPrefs.getString("lang.process.remove"))) { + removeProcess(process); + + } else if (command.equals( + finalPrefs.getString("lang.process.add.new"))) { + addProcess(); + } + } + }; + + JPopupMenu popup = new JPopupMenu(); + JMenuItem item = null; + + if (process != null) + item = new JMenuItem( + finalPrefs.getString("lang.process.selected") + + ": " + process.getProcessID()); + else + item = new JMenuItem( + finalPrefs.getString("lang.process.not.selected")); + + item.setEnabled(false); + popup.add(item); + popup.addSeparator(); + + item = new JMenuItem( + finalPrefs.getString("lang.process.edit")); + if (process == null) + item.setEnabled(false); + else + item.addActionListener(actionListener); + popup.add(item); + + item = new JMenuItem( + finalPrefs.getString("lang.process.crash")); + + if (process == null || process.isCrashed() || isPaused || + time == 0 || hasFinished) + item.setEnabled(false); + else + item.addActionListener(actionListener); + popup.add(item); + + item = new JMenuItem( + finalPrefs.getString("lang.process.recover")); + + if (process == null || !process.isCrashed() || isPaused || + time == 0 || hasFinished) + item.setEnabled(false); + else + item.addActionListener(actionListener); + popup.add(item); + + item = new JMenuItem( + finalPrefs.getString("lang.process.remove")); + + if (process == null) + item.setEnabled(false); + else + item.addActionListener(actionListener); + popup.add(item); + + popup.addSeparator(); + + final long xPosTime = getXPositionTime(me.getX()); + String timeString = finalPrefs.getString( + "lang.event.add.time") + + " " + xPosTime + "ms"; + + JMenu subMenu = new JMenu( + finalPrefs.getString("lang.event.add.local") + + " " + timeString); + + ArrayList createTasks = + finalSimulator.getCreateTaskObjects(); + + if (process == null) { + subMenu.setEnabled(false); + } else { + JMenu subSubMenu = null; + for (final VSCreateTask createTask : createTasks) { + if (createTask.isDummy()) { + if (subSubMenu != null) + subMenu.add(subSubMenu); + subSubMenu = new JMenu( + createTask.getMenuText()); + } else { + item = new JMenuItem(createTask.getMenuText()); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + VSTask task = + createTask.createTask(process, + xPosTime, + true); + taskManager.addTask( + task, VSTaskManager.PROGRAMMED); + finalSimulator.updateTaskManagerTable(); + } + }); + subSubMenu.add(item); + } + } + } + + popup.add(subMenu); + + subMenu = new JMenu( + finalPrefs.getString("lang.event.add.global") + + " " + timeString); + if (process == null) { + subMenu.setEnabled(false); + } else { + JMenu subSubMenu = null; + for (final VSCreateTask createTask : createTasks) { + if (createTask.isDummy()) { + if (subSubMenu != null) + subMenu.add(subSubMenu); + subSubMenu = new JMenu( + createTask.getMenuText()); + } else { + item = new JMenuItem(createTask.getMenuText()); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + VSTask task = + createTask.createTask(process, + xPosTime, + false); + taskManager.addTask( + task, VSTaskManager.PROGRAMMED); + finalSimulator.updateTaskManagerTable(); + } + }); + subSubMenu.add(item); + } + } + } + + if (finalPrefs.getBoolean("sim.mode.expert")) + popup.add(subMenu); + + popup.addSeparator(); + + item = new JMenuItem( + finalPrefs.getString("lang.process.add.new")); + + item.addActionListener(actionListener); + popup.add(item); + + + popup.show(me.getComponent(), me.getX(), me.getY()); + + } else { + editProcess(process); + } + } + + public void mouseExited(MouseEvent e) { + if (highlightedProcess != null) { + highlightedProcess.highlightOff(); + highlightedProcess = null; + paint(); + } + } + + public void mouseEntered(MouseEvent e) { } + + public void mousePressed(MouseEvent e) { } + + public void mouseReleased(MouseEvent e) { } + + }); + addMouseMotionListener(new MouseMotionListener() { + public void mouseDragged(MouseEvent e) { } + + public void mouseMoved(MouseEvent e) { + VSInternalProcess p = getProcessAtYPos(e.getY()); + + if (p == null) { + if (highlightedProcess != null) { + highlightedProcess.highlightOff(); + highlightedProcess = null; + } + + if (isPaused) + paint(); + + return; + } + + if (highlightedProcess != null) { + if (highlightedProcess.getProcessID() != p.getProcessID()) { + highlightedProcess.highlightOff(); + highlightedProcess = p; + p.highlightOn(); + } + } else { + highlightedProcess = p; + p.highlightOn(); + } + + if (isPaused) + paint(); + } + + }); + + addHierarchyBoundsListener(new HierarchyBoundsListener() { + public void ancestorMoved(HierarchyEvent e) { } + + public void ancestorResized(HierarchyEvent e) { + recalcOnChange(); + } + }); + } + + /** + * This method gets called if the window border of the simulator canvas + * has changed. This method contains very ugly code. But this has to be in + * order to gain performance! + */ + private void recalcOnChange() { + synchronized (processes) { + if (processes.size() == 0) + return; + } + + processlineColor = prefs.getColor("col.process.line"); + processSecondlineColor = prefs.getColor("col.process.secondline"); + processSeplineColor = prefs.getColor("col.process.sepline"); + messageArrivedColor = prefs.getColor("col.message.arrived"); + messageSendingColor = prefs.getColor("col.message.sending"); + messageLostColor = prefs.getColor("col.message.lost"); + backgroundColor = prefs.getColor("col.background"); + + paintSize = simulator.getPaintSize(); + xPaintSize = simulator.getWidth() - + (3 * XOFFSET + simulator.getSplitSize()); + yDistance = (simulator.getPaintSize() - + 2 * (YOFFSET + YOUTER_SPACEING))/ numProcesses; + xpaintsize_dividedby_untiltime = xPaintSize / (double) untilTime; + + + synchronized (messageLines) { + for (VSMessageLine messageLine : messageLines) + messageLine.recalcOnChange(); + } + + /* paintProcesses optimization, precalc things */ + { + xoffset_plus_xpaintsize = XOFFSET + (int) xPaintSize; + if (numProcesses > 1) + paintProcessesOffset = + (int) ((paintSize-2* (YOFFSET+ + YOUTER_SPACEING+YSEPLINE_SPACEING))/ + (numProcesses-1)); + else + paintProcessesOffset = + (int) ((paintSize-2*(YOFFSET+ + YOUTER_SPACEING+YSEPLINE_SPACEING))); + } + + /* paintSecondlines optimization, precalc things */ + { + int yMax = YOFFSET + YOUTER_SPACEING + + (int) (numProcesses * yDistance); + paintSecondlinesSeconds = (int) untilTime / 1000; + paintSecondlinesLine[1] = YOFFSET; + paintSecondlinesLine[3] = yMax; + paintSecondlinesYStringPos1 = paintSecondlinesLine[1] - 5; + paintSecondlinesYStringPos2 = paintSecondlinesLine[3] + 15; + } + + /* paitnGlobalTime optimization, precalc things */ + { + paintGlobalTimeYPosition = YOFFSET + YOUTER_SPACEING + + (int) (numProcesses * yDistance); + } + + if (strategy != null) { + synchronized (strategy) { + g = (Graphics2D) strategy.getDrawGraphics(); + g.setColor(backgroundColor); + if (isAntiAliased) + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } + } + } + + /** + * Updates the simulator. + * + * @param globalTime the global time + * @param lastGlobalTime the last global time + */ + private void updateSimulator(long globalTime, long lastGlobalTime) { + if (isPaused || hasFinished) + return; + + long lastSimulatorTime = simulatorTime; + long offset = globalTime - lastGlobalTime; + + clockOffset += offset * clockSpeed; + + while (clockOffset >= 1) { + --clockOffset; + ++simulatorTime; + } + + if (simulatorTime > untilTime) + simulatorTime = untilTime; + + offset = simulatorTime - lastSimulatorTime; + + for (long l = 0; l < offset; ++l) + taskManager.runTasks(l, offset, lastSimulatorTime); + + synchronized (processes) { + for (VSInternalProcess process : processes) + process.syncTime(simulatorTime); + } + } + + /** + * Paints the simulator. + */ + public void paint() { + while (getBufferStrategy() == null) { + createBufferStrategy(3); + strategy = getBufferStrategy(); + + if (strategy != null) { + g = (Graphics2D) strategy.getDrawGraphics(); + g.setColor(backgroundColor); + } + } + + synchronized (strategy) { + if (isAntiAliasedChanged) { + if (isAntiAliased) + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + else + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + isAntiAliasedChanged = false; + } + + g.fillRect(0, 0, getWidth(), getHeight()); + long globalTime = simulatorTime; + + globalTimeXPosition = getTimeXPosition(globalTime); + paintSecondlines(g); + paintProcesses(g, globalTime); + paintGlobalTime(g, globalTime); + + synchronized (messageLines) { + synchronized (messageLinesToRemove) { + if (messageLinesToRemove.size() > 0) { + for (VSMessageLine removeThis : messageLinesToRemove) + messageLines.remove(removeThis); + messageLinesToRemove.clear(); + } + } + + for (VSMessageLine line : messageLines) + line.draw(g, globalTime); + } + + g.setColor(backgroundColor); + strategy.show(); + } + } + + /** + * Paints the processes. + * + * @param g the graphics object + * @param globalTime the global time + */ + private void paintProcesses(Graphics2D g, long globalTime) { + /* First paint the horizontal process timelines + * Second paint the processes + */ + final int yOffset = YOFFSET + YOUTER_SPACEING + YSEPLINE_SPACEING; + final int xPoints[] = { XOFFSET, xoffset_plus_xpaintsize, + xoffset_plus_xpaintsize, XOFFSET, XOFFSET + }; + final int yPoints[] = { yOffset, yOffset, yOffset + LINE_WIDTH, + yOffset + LINE_WIDTH, yOffset + }; + + synchronized (processes) { + for (VSInternalProcess process : processes) { + final long localTime = process.getTime(); + + g.setColor(process.getColor()); + g.fillPolygon(xPoints, yPoints, 5); + + if (process.hasCrashed()) { + g.setColor(process.getCrashedColor()); + final Long crashHistory[] = process.getCrashHistoryArray(); + final int length = crashHistory.length; + + for (int i = 0; i < length; i += 2) { + final int crashStartPos = + (int) getTimeXPosition( + crashHistory[i].longValue()); + int crashEndPos; + + if (i == length - 1) + crashEndPos = xoffset_plus_xpaintsize; + else + crashEndPos = (int) getTimeXPosition( + crashHistory[i+1].longValue()); + + final int xPointsCrashed[] = { + crashStartPos, crashEndPos, + crashEndPos, crashStartPos, crashStartPos + }; + g.fillPolygon(xPointsCrashed, yPoints, 5); + } + } + + g.setColor(process.getColor()); + g.drawString("P" + process.getProcessID() + ":", XOFFSET - 30, + yPoints[0] + LINE_WIDTH); + + final long tmp = localTime > untilTime ? untilTime : localTime; + final int xPos = 1 + (int) getTimeXPosition(tmp); + final int yStart = yPoints[0] - 14; + final int yEnd = yPoints[0]; + + g.setColor(processlineColor); + g.drawLine(xPos, yStart, xPos, yEnd); + g.drawString(localTime+"ms", xPos + 2, yStart + TEXT_SPACEING); + + if (showLamport) + paintTime(g, process.getLamportTimeArray(), process, + yStart, 25); + else if (showVectorTime) + paintTime(g, process.getVectorTimeArray(), process, + yStart, 20 * numProcesses); + + for (int i = 0; i < 5; ++i) + yPoints[i] += paintProcessesOffset; + } + } + } + + /** + * Paints the time. (e.g. lamport time or vector time) + * + * @param g the graphics object + * @param times the times + * @param process the process + * @param yStart the y start + * @param distance the distance + */ + private void paintTime(final Graphics2D g, final VSTime times[], + final VSInternalProcess process, final int yStart, + final int distance) { + + final int lastPos[] = { -1, -1, -1, -1 }; + + for (VSTime time : times) { + int xPos = (int) getTimeXPosition(time.getGlobalTime()); + int bestRow[] = { -1, -1 }; + + for (int i = 0; i < 4; ++i) { + if (lastPos[i] != -1) { + int diff = xPos - lastPos[i]; + if (diff > distance) { + bestRow[0] = i; + bestRow[1] = -1; + break; + } else if (bestRow[0] == -1) { + bestRow[0] = i; + bestRow[1] = diff; + } else if (diff > bestRow[1]) { + bestRow[0] = i; + bestRow[1] = diff; + } + } else { + bestRow[0] = i; + bestRow[1] = -1; + break; + } + } + + final int row = bestRow[0]; + if (bestRow[1] != -1) + xPos += distance - bestRow[1]; + + g.drawString(time.toString(), xPos + 2, yStart + 3 * + TEXT_SPACEING + row * ROW_HEIGHT); + lastPos[row] = xPos; + } + } + + /** + * Paint the second lines. + * + * @param g the graphics object + */ + private void paintSecondlines(Graphics2D g) { + g.setColor(processSecondlineColor); + + int i; + for (i = 0; i <= paintSecondlinesSeconds; i += secondsSpaceing) { + paintSecondlinesLine[0] = paintSecondlinesLine[2] = + (int) getTimeXPosition(i*1000); + g.drawLine(paintSecondlinesLine[0], paintSecondlinesLine[1], + paintSecondlinesLine[2], paintSecondlinesLine[3]); + + final int xStringPos = paintSecondlinesLine[0] - 5; + g.drawString(i+"s", xStringPos, paintSecondlinesYStringPos1); + if (!showVectorTime && !showLamport) + g.drawString(i+"s", xStringPos, paintSecondlinesYStringPos2); + } + + if (i > paintSecondlinesSeconds) { + paintSecondlinesLine[0] = paintSecondlinesLine[2] = + (int) getTimeXPosition(untilTime); + g.drawLine(paintSecondlinesLine[0], paintSecondlinesLine[1], + paintSecondlinesLine[2], paintSecondlinesLine[3]); + } + } + + /** + * Paints the global time. + * + * @param g the graphics object + * @param globalTime the global time + */ + private void paintGlobalTime(Graphics2D g, long globalTime) { + g.setColor(processSeplineColor); + final int xOffset = (int) globalTimeXPosition; + + final int xPoints[] = { + xOffset, xOffset + SEPLINE_WIDTH, + xOffset + SEPLINE_WIDTH, xOffset, xOffset + }; + final int yOffset = YOFFSET - 8; + final int yPoints[] = { + yOffset, yOffset, paintGlobalTimeYPosition, + paintGlobalTimeYPosition, yOffset + }; + + g.fillPolygon(xPoints, yPoints, 5); + g.drawString(globalTime+"ms", xPoints[1]+1, yPoints[0]+TEXT_SPACEING); + } + + /** + * Gets the process at a specified y pos. + * + * @param yPos the y pos + * + * @return the process at y pos + */ + private VSInternalProcess getProcessAtYPos(int yPos) { + final int reachDistance = (int) (yDistance/3); + int y = YOFFSET + YOUTER_SPACEING + YSEPLINE_SPACEING; + + int yOffset = numProcesses > 1 + ? (int) ((paintSize-2* + (YOFFSET+YOUTER_SPACEING+YSEPLINE_SPACEING))/ + (numProcesses-1)) + : (int) ((paintSize-2* + (YOFFSET+YOUTER_SPACEING+YSEPLINE_SPACEING))); + + for (int i = 0; i < numProcesses; ++i) { + if (yPos < y + reachDistance && yPos > y - reachDistance - + LINE_WIDTH) { + VSInternalProcess process = null; + synchronized (processes) { + process = processes.get(i); + } + return process; + } + y += yOffset; + } + + return null; + } + + /** + * Gets the x position of the given time. + * + * @param time the time + * + * @return the time x position + */ + private double getTimeXPosition(long time) { + return XOFFSET + xpaintsize_dividedby_untiltime * time; + } + + /** + * Gets the time of a given x position + * + * @param xPos the x position + * + * @return the time + */ + private long getXPositionTime(double xPos) { + xPos -= XOFFSET; + + if (xPos <= 0) + return 0; + + else if (xPos >= xPaintSize) + return untilTime; + + return (long) ((untilTime/xPaintSize) * xPos); + } + + /** + * Gets the process y position. + * + * @param i the process num + * + * @return the process y position + */ + private int getProcessYPosition(int i) { + int y; + + if (numProcesses > 1) + y = (int) ((paintSize - + 2 * (YOFFSET + YOUTER_SPACEING + YSEPLINE_SPACEING))/ + (numProcesses-1)); + else + y = (int) ((paintSize - + 2 * (YOFFSET + YOUTER_SPACEING + YSEPLINE_SPACEING))); + + return y * (i - 1) + YOFFSET + YOUTER_SPACEING + YSEPLINE_SPACEING; + } + + /** + * Gets the time. + * + * @return the time + */ + public long getTime() { + return simulatorTime; + } + + /** + * Gets the until time. + * + * @return the until time + */ + public long getUntilTime() { + return untilTime; + } + + /** + * Gets the start time. + * + * @return the start time + */ + public long getStartTime() { + return startTime; + } + + /** + * Gets the next process id. + * + * @return the next process id + */ + public int processIDCount() { + return ++processCounter; + } + + /** + * Gets the task manager. + * + * @return the task manager + */ + public VSTaskManager getTaskManager() { + return taskManager; + } + + /** + * Gets the num of processes. + * + * @return the num of processes + */ + public int getNumProcesses() { + return numProcesses; + } + + /** + * Gets the specified process. + * + * @param processNum the process num to get the process of + * + * @return the process + */ + public VSInternalProcess getProcess(int processNum) { + synchronized (processes) { + if (processNum >= processes.size()) + return null; + + return processes.get(processNum); + } + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + public void run() { + while (true) { + while (!hasThreadStopped && (isPaused || hasFinished || + isResetted)) { + try { + Thread.sleep(100); + paint(); + + } catch (Exception e) { + System.out.println(e); + } + } + + if (hasThreadStopped) + break; /* Exit the thread */ + + while (!isPaused && !hasThreadStopped) { + try { + Thread.sleep(threadSleep); + } catch (Exception e) { + System.out.println(e); + } + + updateSimulator(time, lastTime); + + if (simulatorTime == untilTime) { + finish(); + break; + } + + paint(); + lastTime = time; + time = System.currentTimeMillis() - startTime; + } + + updateSimulator(time, lastTime); + paint(); + } + } + + /** + * Starts/plays the simulator. + */ + public void play() { + loging.log(prefs.getString("lang.simulator.started")); + final long currentTime = System.currentTimeMillis(); + + synchronized (processes) { + for (VSInternalProcess p : processes) + p.play(); + } + + if (isResetted) + isResetted = false; + + else if (hasFinished) + hasFinished = false; + + if (isPaused) { + isPaused = false; + startTime += currentTime - pauseTime; + time = currentTime - startTime; + + } else { + startTime = currentTime; + time = 0; + } + + paint(); + } + + /** + * Called if the simulator has finished. + */ + public void finish() { + synchronized (processes) { + for (VSInternalProcess p : processes) + p.finish(); + } + + simulator.finish(); + hasFinished = true; + loging.log(prefs.getString("lang.simulator.finished")); + paint(); + + if (prefs.getBoolean("sim.periodic")) { + VSSimulatorFrame simulatorFrame = simulator.getSimulatorFrame(); + simulatorFrame.resetCurrentSimulator(); + simulatorFrame.startCurrentSimulator(); + } + } + + /** + * Call this, in order to pause the simulator. + */ + public void pause() { + isPaused = true; + synchronized (processes) { + for (VSInternalProcess p : processes) + p.pause(); + } + + pauseTime = System.currentTimeMillis(); + loging.log(prefs.getString("lang.simulator.paused")); + paint(); + } + + /** + * Call this, in order to reset the simulator. + */ + public void reset() { + if (!isResetted) { + loging.log(prefs.getString("lang.simulator.resetted")); + + isResetted = true; + isPaused = false; + hasFinished = false; + startTime = System.currentTimeMillis(); + time = 0; + lastTime = 0; + clockOffset = 0; + simulatorTime = 0; + + synchronized (processes) { + for (VSInternalProcess process : processes) + process.reset(); + } + + /* Reset the task manager AFTER the processes, for the programmed + tasks */ + taskManager.reset(); + + synchronized (processes) { + for (VSInternalProcess process : processes) + process.createRandomCrashTask(); + } + + synchronized (messageLines) { + messageLines.clear(); + } + + synchronized (messageLinesToRemove) { + messageLinesToRemove.clear(); + } + + paint(); + loging.clear(); + } + } + + /** + * Stops the thread of the simulator. + */ + public void stopThread() { + hasThreadStopped = true; + } + + /** + * Checks if the thread has been stopped. + * + * @return true, if is thread has stopped + */ + public boolean hasThreadStopped() { + return hasThreadStopped; + } + + /** + * Sets, if the the lamport time should be shown. It implicitly disables + * the vector time. + * + * @param showLamport true, if the lamport time should be shown + */ + public void showLamport(boolean showLamport) { + this.showLamport = showLamport; + if (isPaused) + paint(); + } + + /** + * Sets, if the vector time should be shown. It implicitly disables the + * lamport time. + * + * @param showVectorTime true, if the vector time should be shown + */ + public void showVectorTime(boolean showVectorTime) { + this.showVectorTime = showVectorTime; + if (isPaused) + paint(); + } + + /** + * Sets if the simulator graphics are anti aliased. + * + * @param isAntiAliased true, if the simulator is anti aliased + */ + public void isAntiAliased(boolean isAntiAliased) { + this.isAntiAliased = isAntiAliased; + this.isAntiAliasedChanged = true; + if (isPaused) + paint(); + } + + /** + * Sends a message. + * + * @param message the message + */ + public void sendMessage(VSMessage message) { + VSTask task = null; + VSAbstractEvent receiveEvent = null; + VSInternalProcess sendingProcess = (VSInternalProcess) + message.getSendingProcess(); + long deliverTime, outageTime, durationTime; + boolean recvOwn = prefs.getBoolean("sim.message.own.recv"); + + synchronized (processes) { + for (VSInternalProcess receiverProcess : processes) { + if (receiverProcess.equals(sendingProcess)) { + if (recvOwn) { + deliverTime = sendingProcess.getGlobalTime(); + receiveEvent = new VSMessageReceiveEvent(message); + task = new VSTask(deliverTime, receiverProcess, + receiveEvent, VSTask.GLOBAL); + taskManager.addTask(task); + } + + } else { + durationTime = sendingProcess.getDurationTime(); + + if (prefs.getBoolean("sim.message.sendingtime.mean")) { + durationTime += receiverProcess.getDurationTime(); + durationTime /= 2; + } + + deliverTime = sendingProcess.getGlobalTime() + + durationTime; + + if (prefs.getBoolean("sim.message.prob.mean")) + outageTime = sendingProcess.getARandomMessageOutageTime( + durationTime, receiverProcess); + else + outageTime = sendingProcess.getARandomMessageOutageTime( + durationTime, null); + + receiveEvent = new VSMessageReceiveEvent(message); + task = new VSTask(deliverTime, receiverProcess, + receiveEvent, VSTask.GLOBAL); + + /* Only add a 'receiving message' task if the message will + not get lost! */ + if (outageTime == -1) + taskManager.addTask(task); + + synchronized (messageLines) { + messageLines.add( + new VSMessageLine(receiverProcess, + sendingProcess.getGlobalTime(), + deliverTime, outageTime, + sendingProcess.getProcessNum(), + receiverProcess.getProcessNum(), + task)); + } + } + } + } + } + + /** + * Edits the process. + * + * @param processNum the process num + */ + public void editProcess(int processNum) { + synchronized (processes) { + VSInternalProcess process = processes.get(processNum); + /* May be null if another thread changed the processes arraylist + before this process actually called editProcess */ + if (process != null) + editProcess(process); + } + } + + /** + * Edits the process. + * + * @param process the process + */ + public void editProcess(VSInternalProcess process) { + if (process != null) { + process.updatePrefs(); + new VSEditorFrame(prefs, simulator.getSimulatorFrame(), + new VSProcessEditor(prefs, process)); + } + } + + /** + * Gets the processes array. + * + * @return the processes array + */ + public ArrayList getProcessesArray() { + ArrayList arr = new ArrayList(); + + synchronized (processes) { + for (VSInternalProcess process : processes) + arr.add(process); + } + + return arr; + } + + /** + * Gets the processes IDs. + * + * @return the processes IDs + */ + public Integer[] getProcessIDs() { + Integer pids[] = null; + + synchronized (processes) { + pids = new Integer[numProcesses]; + for (int i = 0; i < numProcesses; ++i) + pids[i] = Integer.valueOf(processes.get(i).getProcessID()); + } + + return pids; + } + + /** + * Gets the processes. + * + * @return the processes + */ + public ArrayList getProcesses() { + return processes; + } + + /** + * Updates from the prefs. Called by the VSSimulatorEditor if values + * have been saved. + */ + public void updateFromPrefs() { + untilTime = prefs.getInteger("sim.seconds") * 1000; + clockSpeed = prefs.getFloat("sim.clock.speed"); + + secondsSpaceing = (int) (untilTime / 15000); + if (secondsSpaceing == 0) + secondsSpaceing = 1; + + threadSleep = (int) (untilTime / 7500); + if (threadSleep == 0) + threadSleep = 1; + + recalcOnChange(); + } + + /** + * Removes a specific message line from the painting area. + * + * @param messageLine the message line to remove + */ + private void removeMessageLine(VSMessageLine messageLine) { + synchronized (messageLinesToRemove) { + messageLinesToRemove.add(messageLine); + } + } + + /** + * Removes a process from the simulator. + * + * @param process the process + */ + private void removeProcess(VSInternalProcess process) { + if (numProcesses == 1) { + simulator.getSimulatorFrame().removeSimulator(simulator); + + } else { + int index = 0; + synchronized (processes) { + index = processes.indexOf(process); + processes.remove(index); + + for (VSInternalProcess p : processes) + p.removedAProcessAtIndex(index); + + numProcesses = processes.size(); + } + + taskManager.removeTasksOf(process); + simulator.removedAProcessAtIndex(index); + recalcOnChange(); + + ArrayList removeThose = + new ArrayList(); + + synchronized (messageLines) { + for (VSMessageLine line : messageLines) + if (line.removedAProcessAtIndex(index)) + removeThose.add(line); + + for (VSMessageLine line : removeThose) { + VSTask deliverTask = line.getTask(); + + if (deliverTask != null) + taskManager.removeTask(deliverTask); + + messageLines.remove(line); + } + } + } + } + + /** + * Creates a process with the specified num. + * + * @param processNum the process num + * + * @return the new process + */ + private VSInternalProcess createProcess(int processNum) { + VSInternalProcess process = + new VSInternalProcess(prefs, processNum, this, loging); + loging.log(prefs.getString("lang.process.new") + "; " + process); + return process; + } + + /** + * Adds a new process to the simulator. + * + * @return The process which has been added + */ + private VSInternalProcess addProcess() { + VSInternalProcess newProcess = null; + //int foo = -1; + //System.out.println("ADD " + ++foo); + synchronized (processes) { + //System.out.println("ADD " + ++foo); + numProcesses = processes.size() + 1; + //System.out.println("ADD " + ++foo); + newProcess = createProcess(processes.size()); + //System.out.println("ADD " + ++foo); + //System.out.println("ADD " + ++foo); + } + + //System.out.println("ADD " + ++foo); + addProcess(newProcess); + //System.out.println("ADD " + ++foo); + return newProcess; + } + + /** + * Adds a the given process to the simulator. + * + * @newProcess The process to add + */ + private void addProcess(VSInternalProcess newProcess) { + //int foo = -1; + //System.out.println("ADD_ " + ++foo); + synchronized (processes) { + //System.out.println("ADD_ " + ++foo); + processes.add(newProcess); + + for (VSInternalProcess process : processes) + if (!process.equals(newProcess)) + process.addedAProcess(); + //System.out.println("ADD_ " + ++foo); + } + + //System.out.println("ADD_ " + ++foo); + recalcOnChange(); + //System.out.println("ADD_ " + ++foo); + simulator.addProcessAtIndex(processes.size()-1); + //System.out.println("ADD_ " + ++foo); + } + + /** + * Checks if the simulation is paused. + * + * @return true, if the simulation is paused + */ + boolean isPaused() { + return isPaused; + } + + /** + * Checks if the simulation is resetted. + * + * @return true, if the simulation is resetted + */ + boolean isResetted() { + return isResetted; + } + + /** + * Checks if the simulation has finished + * + * @return true, if the simulation has finished + */ + boolean hasFinished() { + return hasFinished; + } + + /* (non-Javadoc) + * @see serialize.VSSerializable#serialize(serialize.VSSerialize, + * java.io.ObjectOutputStream) + */ + public synchronized void serialize(VSSerialize serialize, + ObjectOutputStream objectOutputStream) + throws IOException { + /** For later backwards compatibility, to add more stuff */ + objectOutputStream.writeObject(Boolean.valueOf(false)); + + objectOutputStream.writeObject(Integer.valueOf(processCounter)); + + synchronized (processes) { + objectOutputStream.writeObject(Integer.valueOf(numProcesses)); + for (VSInternalProcess process : processes) + process.serialize(serialize, objectOutputStream); + } + + taskManager.serialize(serialize, objectOutputStream); + + /** For later backwards compatibility, to add more stuff */ + objectOutputStream.writeObject(Boolean.valueOf(false)); + } + + /* (non-Javadoc) + * @see serialize.VSSerializable#deserialize(serialize.VSSerialize, + * java.io.ObjectInputStream) + */ + public synchronized void deserialize(VSSerialize serialize, + ObjectInputStream objectInputStream) + throws IOException, ClassNotFoundException { + if (VSSerialize.DEBUG) + System.out.println("Deserializing: VSSimulatorVisualization"); + + /** For later backwards compatibility, to add more stuff */ + objectInputStream.readObject(); + + processCounter = ((Integer) objectInputStream.readObject()).intValue(); + + int num = ((Integer) objectInputStream.readObject()).intValue(); + loging.clear(); + + if (num > numProcesses) { + for (int i = numProcesses; i < num; ++i) + addProcess(); + } else { + int oldNum = numProcesses; + for (int i = num; i < oldNum; ++i) + removeProcess(getProcess(0)); + } + + for (int i = 0; i < num; ++i) + processes.get(i).deserialize(serialize, objectInputStream); + + taskManager.deserialize(serialize, objectInputStream); + + /** For later backwards compatibility, to add more stuff */ + objectInputStream.readObject(); + } +} -- cgit v1.2.3