Released GUI version.

This commit is contained in:
Marcin Piątkowski
2025-06-04 09:47:12 +02:00
parent 9aa34aca51
commit 8e7ea9dd96
8 changed files with 422 additions and 268 deletions

View File

@@ -1,5 +1,6 @@
package pl.umk.mat.martinp.reactics;
import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.layout.StaticLayout;
@@ -29,11 +30,13 @@ import java.io.PrintWriter;
import java.util.*;
public class ContextAutomatonEditor extends JPanel {
public class ContextAutomatonEditor extends JPanel implements RSObserver {
private enum EditorState {STATE, EDGE, DELETE, CLEAR, NONE}
private static final long serialVersionUID = 1L;
private ReactionSystem rs;
//---------------------------------------------------------------
// Visual configuration settings
//---------------------------------------------------------------
@@ -79,6 +82,8 @@ public class ContextAutomatonEditor extends JPanel {
createGraphEditor();
createModeButtonsPanel();
createContextMenus();
rs = ReactionSystem.getInstance();
}
public boolean isModified() { return graph.isModified(); }
@@ -99,6 +104,18 @@ public class ContextAutomatonEditor extends JPanel {
graph.loadFromXML(input);
}
public void recalculateCoordinates() {
Layout<CAState, CAEdge> autoLayout = new FRLayout<CAState, CAEdge>(graph, graphViewer.getSize());
autoLayout.initialize();
for (CAState state : graph.getVertices()) {
Point2D pos = autoLayout.transform(state);
state.updateLocation(pos);
}
}
public void clear() {
graph.clear();
@@ -349,7 +366,6 @@ public class ContextAutomatonEditor extends JPanel {
"Error", JOptionPane.ERROR_MESSAGE);
}
else {
//TODO: State description should be unique
idOk = true;
state.setLabel(newLabel);
}
@@ -393,6 +409,10 @@ public class ContextAutomatonEditor extends JPanel {
}
}
public void onRSUpdate() {
repaint();
}
//---------------------------------------------------------------------------------------------
@@ -525,10 +545,16 @@ public class ContextAutomatonEditor extends JPanel {
edge = pickSupport.getEdge(layout, clickPt.getX(), clickPt.getY());
if (node != null) {
Collection<CAEdge> edges = graph.getIncidentEdges(node);
for (CAEdge e : edges)
rs.removeCAEdge(e);
graph.removeState(node);
}
else if (edge != null) {
graph.removeEdge(edge);
rs.removeCAEdge(edge);
}
break;

View File

@@ -254,8 +254,11 @@ class ContextAutomatonGraph extends DirectedSparseGraph<CAState, CAEdge> {
public void addEdge(CAState from, CAState to, Collection<Transition> transitionList) {
CAEdge edge = this.findEdge(from, to);
if (edge == null)
this.addEdge(new CAEdge(transitionList), from, to);
if (edge == null) {
CAEdge caEdge = ReactionSystem.getInstance().createCAEdge();
caEdge.addTransitions(transitionList);
this.addEdge(caEdge, from, to);
}
else
edge.addTransitions(transitionList);
@@ -378,9 +381,9 @@ class CAEdge {
this.id = nextId++;
}
public CAEdge(Collection<Transition> transitionList) {
this.transitions.addAll(transitionList);
}
// public CAEdge(Collection<Transition> transitionList) {
// this.transitions.addAll(transitionList);
// }
Vector<Transition> getTransitions() {
return transitions;

View File

@@ -17,9 +17,10 @@ import java.util.Collection;
import java.util.Vector;
public class FormulaEditor extends JPanel {
public class FormulaEditor extends JPanel implements RSObserver {
private FormulaTableModel ftModel;
private JTable formTable;
private ReactionSystem rs;
private Vector<Formula> formulaList;
boolean modified = false;
@@ -34,8 +35,9 @@ public class FormulaEditor extends JPanel {
static final Border selectedBorder = BorderFactory.createLineBorder(Color.RED, 2);
public FormulaEditor() {
formulaList = new Vector<Formula>();
public FormulaEditor(ReactionSystem rs) {
this.rs = rs;
formulaList = rs.formulas;
// Create the table for displaying formulas. Prevent default selection modes.
ftModel = new FormulaTableModel();
@@ -106,7 +108,7 @@ public class FormulaEditor extends JPanel {
ftModel.fireTableDataChanged();
}
private boolean showFormulaEditDialog(Formula ff) {
private boolean showFormulaEditDialog(JFrame parent, Formula ff) {
final int Select = 0;
final int Approve = 1;
@@ -149,7 +151,7 @@ public class FormulaEditor extends JPanel {
labelInput.setText(labelStr);
formulaInput.setText(formulaStr);
int result = JOptionPane.showConfirmDialog(null, myPanel,
int result = JOptionPane.showConfirmDialog(parent, myPanel,
"Formula details", JOptionPane.OK_CANCEL_OPTION);
if (result != JOptionPane.OK_OPTION) {
@@ -159,8 +161,6 @@ public class FormulaEditor extends JPanel {
labelStr = labelInput.getText();
formulaStr = formulaInput.getText();
//TODO: Validate the data entered to the text fields
ff.label = labelStr;
ff.formula = formulaStr;
@@ -171,10 +171,10 @@ public class FormulaEditor extends JPanel {
return true;
}
public void addFormula() {
public void addFormula(JFrame parent) {
Formula ff = new Formula();
if (showFormulaEditDialog(ff)) {
if (showFormulaEditDialog(parent, ff)) {
formulaList.add(ff);
modified = true;
ftModel.fireTableDataChanged();
@@ -182,11 +182,11 @@ public class FormulaEditor extends JPanel {
}
// If more than one formula is selected throw an exception only the first can be edited
public void editFormula() {
public void editFormula(JFrame parent) {
int[] selected = formTable.getSelectedRows();
if (selected.length != 1) {
JOptionPane.showMessageDialog(this, "Select a single formula to edit.", "Select formula", JOptionPane.WARNING_MESSAGE);
JOptionPane.showMessageDialog(parent, "Select a single formula to edit.", "Select formula", JOptionPane.WARNING_MESSAGE);
return;
}
@@ -195,7 +195,7 @@ public class FormulaEditor extends JPanel {
if (fEdt == null)
return;
showFormulaEditDialog(fEdt);
showFormulaEditDialog(parent, fEdt);
fEdt.status = Formula.FormulaStatus.None;
modified = true;
ftModel.fireTableDataChanged();
@@ -240,6 +240,10 @@ public class FormulaEditor extends JPanel {
ftModel.fireTableDataChanged();
}
public void onRSUpdate() {
resetFormulasStatus();
refreshStatus();
}
static class FormulaColumnModel extends DefaultTableColumnModel {
int colno = 0;

View File

@@ -17,10 +17,8 @@ import java.util.*;
class ProcessEditor extends JPanel {
private JTable reactionList;
private RSTableModel reactionsModel;
private Vector<Reaction> reactions;
private final int id;
private String label;
private RSProcess rsProcess;
private boolean modified = false;
private JCheckBox selectBox;
@@ -30,32 +28,12 @@ class ProcessEditor extends JPanel {
private final Color selectedBorderColor = new Color(150, 10, 10);
private static final int borderThickness = 4;
private static int nextId = 1;
public ProcessEditor() {
id = nextId;
++nextId;
label = new String("Process_" + id);
public ProcessEditor(RSProcess rsProc) {
rsProcess = rsProc;
init();
}
ProcessEditor(String name) {
id = nextId;
++nextId;
label = name;
init();
}
ProcessEditor(ProcessEditor procEdt) {
id = nextId;
++nextId;
label = "Copy_of_" + procEdt.label;
init();
reactions.addAll(procEdt.reactions);
reactionsModel.fireTableDataChanged();
modified = procEdt.modified;
}
public RSProcess getProcess() { return rsProcess; }
public boolean isSelected() {
return selectBox.isSelected();
@@ -66,17 +44,12 @@ class ProcessEditor extends JPanel {
public void clearModificationStatus() { modified = false; }
public String getLabel() {
return label;
}
public void addReaction(Reaction rr) {
reactions.add(rr);
reactionsModel.fireTableDataChanged();
return rsProcess.label;
}
private void init() {
// Create border for the component
outBorder = BorderFactory.createTitledBorder(label);
outBorder = BorderFactory.createTitledBorder(rsProcess.label);
Font font = outBorder.getTitleFont();
Font newFont = new Font (font.getFamily(), Font.BOLD, font.getSize() + 2);
outBorder.setTitleFont(new Font (font.getFamily(), Font.BOLD, font.getSize() + 2));
@@ -85,7 +58,6 @@ class ProcessEditor extends JPanel {
setBorder(outBorder);
// Create reactions list component
reactions = new Vector<Reaction>();
reactionsModel = new RSTableModel();
reactionList = new JTable(reactionsModel);
@@ -122,14 +94,6 @@ class ProcessEditor extends JPanel {
});
buttonPanel.add(rmButton);
JButton optButton = new JButton("Options");
optButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
editOptions();
}
});
buttonPanel.add(optButton);
selectBox = new JCheckBox("Select");
selectBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
@@ -145,7 +109,7 @@ class ProcessEditor extends JPanel {
setPreferredSize(new Dimension(-1, 200));
}
private boolean showReactionEditDialog(Reaction rr) {
private boolean showReactionEditDialog(JComponent parent, Reaction rr) {
final int Select = 0;
final int Approve = 1;
@@ -221,7 +185,7 @@ class ProcessEditor extends JPanel {
inhibitorsInput.setText(inhibitorsStr);
productsInput.setText(productsStr);
int result = JOptionPane.showConfirmDialog(null, myPanel,
int result = JOptionPane.showConfirmDialog(parent, myPanel,
"Reaction details", JOptionPane.OK_CANCEL_OPTION);
if (result != JOptionPane.OK_OPTION) {
@@ -251,8 +215,6 @@ class ProcessEditor extends JPanel {
continue;
}
//TODO: Validate entities names against a regular expression
rr.reactants.clear();
rr.reactants.addAll(reactantsSet);
rr.inhibitors.clear();
@@ -260,8 +222,6 @@ class ProcessEditor extends JPanel {
rr.products.clear();
rr.products.addAll(productsSet);
opStatus = Approve;
}
while(opStatus == Select);
@@ -272,25 +232,25 @@ class ProcessEditor extends JPanel {
public Set<String> getReactantsSet() {
HashSet<String> rset = new HashSet<String>();
for (Reaction rr : reactions)
for (Reaction rr : rsProcess.reactions)
rset.addAll(rr.getReactantsSet());
return rset;
}
public void exportToXML(PrintWriter output) {
output.println(" <process name=\"" + this.label + "\">");
output.println(" <process name=\"" + rsProcess.label + "\">");
for (Reaction rr : reactions)
for (Reaction rr : rsProcess.reactions)
rr.exportToXML(output);
output.println(" </process>");
}
public String toRSSLString() {
StringBuilder rsslString = new StringBuilder(" " + this.label + " {\n");
StringBuilder rsslString = new StringBuilder(" " + rsProcess.label + " {\n");
for (Reaction rr : reactions) {
for (Reaction rr : rsProcess.reactions) {
rsslString.append(rr.toRSSLString());
}
@@ -299,42 +259,25 @@ class ProcessEditor extends JPanel {
return rsslString.toString();
}
private void editOptions() {
String idRegex = "[a-zA-Z][a-zA-Z_0-9:-]*";
boolean idOk = false;
void rename(String newLabel) {
rsProcess.label = newLabel;
outBorder.setTitle(rsProcess.label);
do {
String newLabel = JOptionPane.showInputDialog(this, "Process name", label);
if (newLabel == null)
return;
newLabel = newLabel.trim();
if (!newLabel.matches(idRegex)) {
JOptionPane.showMessageDialog(this,
"<html>Process name should start with a letter and may contain only:<br/>" +
"letters, numbers, colons (:), underscores (_) and hyphens (-)</html>",
"Error", JOptionPane.ERROR_MESSAGE);
}
else {
idOk = true;
label = newLabel;
outBorder.setTitle(label);
}
}
while (!idOk);
modified = true;
this.repaint();
}
// Add new reaction
public void addReaction(Reaction rr) {
rsProcess.reactions.add(rr);
reactionsModel.fireTableDataChanged();
}
private void addReaction() {
Reaction rr = new Reaction();
if (!showReactionEditDialog(rr))
if (!showReactionEditDialog(this, rr))
return;
reactions.add(rr);
rsProcess.reactions.add(rr);
modified = true;
reactionsModel.fireTableDataChanged();
}
@@ -348,13 +291,13 @@ class ProcessEditor extends JPanel {
return;
}
Reaction rr = new Reaction(reactions.get(rIdx));
Reaction rr = new Reaction(rsProcess.reactions.get(rIdx));
reactionList.clearSelection();
if (!showReactionEditDialog(rr)) {
if (!showReactionEditDialog(this, rr)) {
return;
}
reactions.setElementAt(rr, rIdx);
rsProcess.reactions.setElementAt(rr, rIdx);
modified = true;
reactionsModel.fireTableDataChanged();
}
@@ -368,7 +311,7 @@ class ProcessEditor extends JPanel {
return;
}
reactions.remove(rIdx);
rsProcess.reactions.remove(rIdx);
reactionList.clearSelection();
reactionsModel.fireTableDataChanged();
modified = true;
@@ -398,7 +341,7 @@ class ProcessEditor extends JPanel {
}
public int getRowCount() {
return reactions.size();
return rsProcess.reactions.size();
}
public int getColumnCount() {
@@ -406,7 +349,7 @@ class ProcessEditor extends JPanel {
}
public Object getValueAt(int row, int column) {
Reaction rr = reactions.get(row);
Reaction rr = rsProcess.reactions.get(row);
return switch (column) {
case 0 -> rr.reactants;

View File

@@ -13,8 +13,10 @@ import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Collection;
import java.util.TreeSet;
@@ -26,21 +28,26 @@ public class ReacticsGUI extends JFrame {
private static final String defaultConfigFileName = "reactics.conf";
private JFrame window;
private JTabbedPane modulePane;
private ReactantSetPanel reactantSetPanel;
private ReactionSystemEditor reactionSystem;
private ContextAutomatonEditor contextAutomaton;
private TransitionSystemViewer transitionSystem;
private TransitionSystemViewer compressedTransitionSystem;
private ReactionSystemEditor reactionSystemEditor;
private ContextAutomatonEditor contextAutomatonEditor;
private TransitionSystemViewer transitionSystemViewer;
private TransitionSystemViewer compressedTransitionSystemViewer;
private FormulaEditor formulaEditor;
private FileSelector fileSelector;
private ReactionSystem reactionSystem;
private ReacticsRuntime reactics;
public ReacticsGUI() {
super(ApplicationName);
reactionSystem = ReactionSystem.getInstance();
try {
reactics = ReacticsRuntime.getInstance();
}
@@ -74,6 +81,8 @@ public class ReacticsGUI extends JFrame {
"however not model checking will be possible.",
"Reactics Runtime Error", JOptionPane.ERROR_MESSAGE);
}
window = this;
}
private void createMenuBar() {
@@ -104,7 +113,6 @@ public class ReacticsGUI extends JFrame {
importFromRSSL();
}
});
rsslImportItem.setEnabled(false);
fileMenu.add(rsslImportItem);
JMenuItem rsslExportItem = new JMenuItem("Save to RSSL file");
@@ -156,23 +164,28 @@ public class ReacticsGUI extends JFrame {
// Reaction System Editor
//-----------------------------------------------------------------------------------------
reactionSystem = new ReactionSystemEditor();
modulePane.addTab("Reaction System", reactionSystem);
reactionSystemEditor = new ReactionSystemEditor();
modulePane.addTab("Reaction System", reactionSystemEditor);
reactionSystem.addObserver(reactionSystemEditor);
//-----------------------------------------------------------------------------------------
// Context Automaton Editor
//-----------------------------------------------------------------------------------------
contextAutomaton = new ContextAutomatonEditor(getSize());
modulePane.addTab("Context Automaton", contextAutomaton);
contextAutomatonEditor = new ContextAutomatonEditor(getSize());
modulePane.addTab("Context Automaton", contextAutomatonEditor);
reactionSystem.addObserver(contextAutomatonEditor);
//-----------------------------------------------------------------------------------------
// Transition System Viewer
//-----------------------------------------------------------------------------------------
JPanel transitionSystemPanel = new JPanel(new BorderLayout());
transitionSystem = new TransitionSystemViewer(getSize(), false);
transitionSystemPanel.add(transitionSystem, BorderLayout.CENTER);
transitionSystemViewer = new TransitionSystemViewer(getSize(), false);
transitionSystemPanel.add(transitionSystemViewer, BorderLayout.CENTER);
reactionSystem.addObserver(transitionSystemViewer);
JPanel tsCtrlPanel = new JPanel();
JButton tsUpdateBtn = new JButton("Update transition system");
@@ -184,7 +197,7 @@ public class ReacticsGUI extends JFrame {
JCheckBox tsLockCBox = new JCheckBox("Lock relative nodes positions");
tsLockCBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
transitionSystem.lockNodesPositions(tsLockCBox.isSelected());
transitionSystemViewer.lockNodesPositions(tsLockCBox.isSelected());
}
});
tsCtrlPanel.add(tsLockCBox);
@@ -197,8 +210,9 @@ public class ReacticsGUI extends JFrame {
//-----------------------------------------------------------------------------------------
JPanel compressedTransitionSystemPanel = new JPanel(new BorderLayout());
compressedTransitionSystem = new TransitionSystemViewer(getSize(), true);
compressedTransitionSystemPanel.add(compressedTransitionSystem, BorderLayout.CENTER);
compressedTransitionSystemViewer = new TransitionSystemViewer(getSize(), true);
compressedTransitionSystemPanel.add(compressedTransitionSystemViewer, BorderLayout.CENTER);
reactionSystem.addObserver(compressedTransitionSystemViewer);
JPanel ctsCtrlPanel = new JPanel();
JButton ctsUpdateBtn = new JButton("Update transition system");
@@ -210,7 +224,7 @@ public class ReacticsGUI extends JFrame {
JCheckBox ctsLockCBox = new JCheckBox("Lock relative nodes positions");
ctsLockCBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
compressedTransitionSystem.lockNodesPositions(ctsLockCBox.isSelected());
compressedTransitionSystemViewer.lockNodesPositions(ctsLockCBox.isSelected());
}
});
ctsCtrlPanel.add(ctsLockCBox);
@@ -224,20 +238,21 @@ public class ReacticsGUI extends JFrame {
JPanel formulaEditorPanel = new JPanel();
formulaEditorPanel.setLayout(new BorderLayout());
formulaEditor = new FormulaEditor();
formulaEditor = new FormulaEditor(reactionSystem);
reactionSystem.addObserver(formulaEditor);
formulaEditorPanel.add(formulaEditor, BorderLayout.CENTER);
JPanel formulaCtrlPanel = new JPanel();
JButton addButton = new JButton("Add formula");
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) { formulaEditor.addFormula(); }
public void actionPerformed(ActionEvent actionEvent) { formulaEditor.addFormula(window); }
});
formulaCtrlPanel.add(addButton);
JButton edtButton = new JButton("Edit formula");
edtButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
formulaEditor.editFormula();
formulaEditor.editFormula(window);
}
});
formulaCtrlPanel.add(edtButton);
@@ -304,15 +319,14 @@ public class ReacticsGUI extends JFrame {
}
private boolean isModified() {
return reactionSystem.isModified() || contextAutomaton.isModified() || formulaEditor.isModified();
return reactionSystemEditor.isModified() || contextAutomatonEditor.isModified();
}
private void clearModificationStatus() {
reactionSystem.clearModificationStatus();
contextAutomaton.clearModificationStatus();
formulaEditor.clearModificationStatus();
transitionSystem.clearModificationStatus();
compressedTransitionSystem.clearModificationStatus();
reactionSystemEditor.clearModificationStatus();
contextAutomatonEditor.clearModificationStatus();
transitionSystemViewer.clearModificationStatus();
compressedTransitionSystemViewer.clearModificationStatus();
}
private void updateReactionSystem() {
@@ -335,18 +349,16 @@ public class ReacticsGUI extends JFrame {
"Error", JOptionPane.ERROR_MESSAGE);
}
reactionSystem.notifyObservers();
updateReactantSet();
transitionSystem.clear();
compressedTransitionSystem.clear();
formulaEditor.resetFormulasStatus();
clearModificationStatus();
}
private void updateReactantSet() {
TreeSet<String> rset = new TreeSet<String>();
rset.addAll(reactionSystem.getReactantsSet());
rset.addAll(contextAutomaton.getReactantsSet());
rset.addAll(reactionSystemEditor.getReactantsSet());
rset.addAll(contextAutomatonEditor.getReactantsSet());
rset.remove("");
reactantSetPanel.updateReactants(rset);
}
@@ -355,23 +367,24 @@ public class ReacticsGUI extends JFrame {
if (isModified()) {
updateReactionSystem();
}
else if (transitionSystem.isComputed()) {
else if (transitionSystemViewer.isComputed()) {
// Skip re-computing transition system structure
return;
}
try {
String tsStructure = reactics.getTransitionSystemStructure();
transitionSystem.updateTransitionSystemStructure(tsStructure);
compressedTransitionSystem.updateTransitionSystemStructure(tsStructure);
transitionSystemViewer.updateTransitionSystemStructure(tsStructure);
compressedTransitionSystemViewer.updateTransitionSystemStructure(tsStructure);
}
catch (IOException ioe) {
System.out.println("IOException: " + ioe.getMessage());
System.out.println("[ReactICS] I/O Error: " + ioe.getMessage());
JOptionPane.showMessageDialog(this,
"Could not write reaction system structure to a temporary file\n" + ioe.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
}
catch (ReacticsRuntimeException rre) {
System.out.println("[ReactICS] Runtime Error: " + rre.getMessage());
JOptionPane.showMessageDialog(this,
rre.getMessage(),
"Reactics Runtime Error", JOptionPane.ERROR_MESSAGE);
@@ -379,15 +392,29 @@ public class ReacticsGUI extends JFrame {
}
private void evaluateFormulas() {
Collection<Formula> formulas = formulaEditor.getSelectedFormulas();
if (isModified())
updateReactionSystem();
Collection<Formula> formulas = formulaEditor.getSelectedFormulas();
Path rsslFile = reactics.getRSSLFile();
long originalSize = -1;
try {
originalSize = Files.size(rsslFile);
}
catch (IOException ioe) {
System.err.println("[ReactICS] Error: " + ioe.getMessage());
JOptionPane.showMessageDialog(this, ioe.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
}
for (Formula ff : formulas) {
try {
System.out.println("[ReactICS] Evaluate formula: " + ff.label);
Files.writeString(rsslFile, ff.toRSSLString() + "\n", StandardCharsets.UTF_8, StandardOpenOption.APPEND);
ReacticsRuntime.EvalResult result = reactics.evaluateFormula(ff.label);
ff.status = result.result();
ff.mcTime = result.mcTime() + " s";
@@ -405,6 +432,19 @@ public class ReacticsGUI extends JFrame {
JOptionPane.showMessageDialog(this,
rre.getMessage(),
"Reactics Runtime Error", JOptionPane.ERROR_MESSAGE);
ff.status = Formula.FormulaStatus.Invalid;
}
finally {
try {
RandomAccessFile raf = new RandomAccessFile(rsslFile.toFile(), "rw");
raf.setLength(originalSize);
}
catch (IOException ioe) {
System.err.println("[ReactICS] Error: " + ioe.getMessage());
JOptionPane.showMessageDialog(this, ioe.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
}
}
}
}
@@ -421,8 +461,8 @@ public class ReacticsGUI extends JFrame {
try {
PrintWriter xmlOut = new PrintWriter(new FileWriter(outFile));
xmlOut.println("<xml version=\"1.0\">");
reactionSystem.exportToXML(xmlOut);
contextAutomaton.exportToXML(xmlOut);
reactionSystemEditor.exportToXML(xmlOut);
contextAutomatonEditor.exportToXML(xmlOut);
formulaEditor.exportToXML(xmlOut);
xmlOut.println("</xml>");
xmlOut.flush();
@@ -435,86 +475,16 @@ public class ReacticsGUI extends JFrame {
}
private void loadFromXML() {
FileNameExtensionFilter rpnFilter = new FileNameExtensionFilter("Reaction System (XML)", "xml");
FileNameExtensionFilter fileTypeFilter = new FileNameExtensionFilter("Reaction System (XML)", "xml");
JFileChooser fc = new JFileChooser();
fc.addChoosableFileFilter(rpnFilter);
fc.addChoosableFileFilter(fileTypeFilter);
fc.setAcceptAllFileFilterUsed(true);
fc.setCurrentDirectory(new File(System.getProperty("user.dir")));
if(fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
File xmlFile = fc.getSelectedFile();
System.out.println("[ReactICS] Load from XML file: " + xmlFile.getName());
reactionSystem.clear();
contextAutomaton.clear();
reactantSetPanel.clear();
transitionSystem.clear();
compressedTransitionSystem.clear();
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xmlFile);
doc.getDocumentElement().normalize();
reactionSystem.loadFromXML(doc);
contextAutomaton.loadFromXML(doc);
formulaEditor.loadFromXML(doc);
updateReactionSystem();
}
catch(IOException ioe) {
System.err.println("[ReactICS] Error: " + ioe.getMessage());
JOptionPane.showMessageDialog(this, "Error reading file: " + xmlFile.getName() + "\n" + ioe.getMessage(),
"Input error", JOptionPane.ERROR_MESSAGE);
reactionSystem.clear();
contextAutomaton.clear();
reactantSetPanel.clear();
}
catch(ParserConfigurationException pce) {
System.err.println("[ReactICS] Parse Configuration Error: " + pce.getMessage());
JOptionPane.showMessageDialog(this, pce.getMessage(),
"Parse Configuration Error", JOptionPane.ERROR_MESSAGE);
reactionSystem.clear();
contextAutomaton.clear();
reactantSetPanel.clear();
}
catch(SAXException se) {
System.err.println("[ReactICS] SAXException: " + se.getMessage());
JOptionPane.showMessageDialog(this, se.getMessage(),
"Parse error", JOptionPane.ERROR_MESSAGE);
reactionSystem.clear();
contextAutomaton.clear();
reactantSetPanel.clear();
}
catch(FileStructureError err) {
System.err.println("[ReactICS]: File Structure Error: " + err.getMessage());
JOptionPane.showMessageDialog(this, err.getMessage(),
"XML file structure error", JOptionPane.ERROR_MESSAGE);
reactionSystem.clear();
contextAutomaton.clear();
reactantSetPanel.clear();
}
catch(Exception e) {
System.out.println("[ReactICS] Error: " + e.getMessage());
JOptionPane.showMessageDialog(this, "Unexpected error reading file: " + xmlFile.getName() +
" " + e.getMessage(),
"Input error", JOptionPane.ERROR_MESSAGE);
reactionSystem.clear();
contextAutomaton.clear();
reactantSetPanel.clear();
}
loadFromXMLFile(xmlFile);
setTitle("ReactICS GUI : " + xmlFile.getName());
}
@@ -522,6 +492,109 @@ public class ReacticsGUI extends JFrame {
clearModificationStatus();
}
private void loadFromXMLFile(File xmlFile) {
reactionSystemEditor.clear();
contextAutomatonEditor.clear();
reactantSetPanel.clear();
transitionSystemViewer.clear();
compressedTransitionSystemViewer.clear();
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xmlFile);
doc.getDocumentElement().normalize();
reactionSystemEditor.loadFromXML(doc);
contextAutomatonEditor.loadFromXML(doc);
formulaEditor.loadFromXML(doc);
updateReactionSystem();
}
catch(IOException ioe) {
System.err.println("[ReactICS] Error: " + ioe.getMessage());
JOptionPane.showMessageDialog(this, "Error reading file: " + xmlFile.getName() + "\n" + ioe.getMessage(),
"Input error", JOptionPane.ERROR_MESSAGE);
reactionSystemEditor.clear();
contextAutomatonEditor.clear();
reactantSetPanel.clear();
}
catch(ParserConfigurationException pce) {
System.err.println("[ReactICS] Parse Configuration Error: " + pce.getMessage());
JOptionPane.showMessageDialog(this, pce.getMessage(),
"Parse Configuration Error", JOptionPane.ERROR_MESSAGE);
reactionSystemEditor.clear();
contextAutomatonEditor.clear();
reactantSetPanel.clear();
}
catch(SAXException se) {
System.err.println("[ReactICS] SAXException: " + se.getMessage());
JOptionPane.showMessageDialog(this, se.getMessage(),
"Parse error", JOptionPane.ERROR_MESSAGE);
reactionSystemEditor.clear();
contextAutomatonEditor.clear();
reactantSetPanel.clear();
}
catch(FileStructureError err) {
System.err.println("[ReactICS]: File Structure Error: " + err.getMessage());
JOptionPane.showMessageDialog(this, err.getMessage(),
"XML file structure error", JOptionPane.ERROR_MESSAGE);
reactionSystemEditor.clear();
contextAutomatonEditor.clear();
reactantSetPanel.clear();
}
catch(Exception e) {
System.out.println("[ReactICS] Error: " + e.getMessage());
JOptionPane.showMessageDialog(this, "Unexpected error reading file: " + xmlFile.getName() +
" " + e.getMessage(),
"Input error", JOptionPane.ERROR_MESSAGE);
reactionSystemEditor.clear();
contextAutomatonEditor.clear();
reactantSetPanel.clear();
}
}
private void importFromRSSL() {
FileNameExtensionFilter fileTypeFilter = new FileNameExtensionFilter("Reaction System Specification Language (rs)", "rs");
JFileChooser fc = new JFileChooser();
fc.addChoosableFileFilter(fileTypeFilter);
fc.setAcceptAllFileFilterUsed(true);
fc.setCurrentDirectory(new File(System.getProperty("user.dir")));
if(fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
File rsslFile = fc.getSelectedFile();
try {
File xmlFile = reactics.convertToXMLFile(rsslFile);
loadFromXMLFile(xmlFile);
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
e.printStackTrace();
} catch (ReacticsRuntimeException e) {
System.err.println("ReacticsRuntimeException: " + e.getMessage());
e.printStackTrace();
}
setTitle("ReactICS GUI : " + rsslFile.getName());
contextAutomatonEditor.recalculateCoordinates();
}
modulePane.setSelectedIndex(0);
clearModificationStatus();
}
private void exportToRSSLFile() {
File outFile = fileSelector.selectOutputFile(this, new File(System.getProperty("user.dir")),
new FileNameExtensionFilter("Reaction System Specification Language (RSSL)", "rs"));
@@ -543,13 +616,18 @@ public class ReacticsGUI extends JFrame {
}
}
private void importFromRSSL() {
//TODO: Import reaction system structure from RSSL file
}
private void exportToISPLFile() {
updateReactionSystem();
if (!reactionSystem.isplTranslationAllowed()) {
JOptionPane.showMessageDialog(this,
"Context automaton contains guards for some transitions.\n" +
"Current version of ReactICS does not support translation to ISPL format in this case.\n",
"Translation is not possible", JOptionPane.INFORMATION_MESSAGE);
return;
}
File outFile = fileSelector.selectOutputFile(this, new File(System.getProperty("user.dir")),
new FileNameExtensionFilter("Interpreted System Programming Language (ISPL)", "ispl"));
@@ -592,9 +670,9 @@ public class ReacticsGUI extends JFrame {
private void printRSStructure(PrintWriter outStream) {
outStream.println("options { use-context-automaton; make-progressive; };\n");
outStream.println(reactionSystem.toRSSLString());
outStream.println(contextAutomaton.toRSSLString());
outStream.println(formulaEditor.toRSSLString());
outStream.println(reactionSystemEditor.toRSSLString());
outStream.println(contextAutomatonEditor.toRSSLString());
// outStream.println(formulaEditor.toRSSLString());
}
private void showAboutWindow() {

View File

@@ -153,6 +153,29 @@ public class ReacticsRuntime {
throw new ReacticsRuntimeException(errMsg.toString());
}
public File convertToXMLFile(File rsslInputFile) throws IOException, ReacticsRuntimeException {
File xmlFile = Files.createTempFile("_" + rsslInputFile.getName(), ".xml").toFile();
xmlFile.deleteOnExit();
ProcessBuilder procBuilder = new ProcessBuilder(rctPath + rctRuntime, "-y", rsslInputFile.getAbsolutePath());
procBuilder.redirectOutput(new File(xmlFile.getAbsolutePath()));
Process proc = procBuilder.start();
String line;
StringBuilder errMsg = new StringBuilder();
BufferedReader procErrStr = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
while ((line = procErrStr.readLine()) != null) {
errMsg.append(line).append("\n");
}
if (!errMsg.isEmpty())
throw new ReacticsRuntimeException(errMsg.toString());
return xmlFile;
}
record EvalResult(Formula.FormulaStatus result, String mcTime, String totalTime, String memory) {}
}

View File

@@ -22,13 +22,15 @@ import java.util.Vector;
* A distributed reaction system contains a number of processes.
* Each process contains a list of reactions.
*/
public class ReactionSystemEditor extends JPanel {
private Vector<ProcessEditor> processes = new Vector<ProcessEditor>();
public class ReactionSystemEditor extends JPanel implements RSObserver {
ReactionSystem rs;
private Vector<ProcessEditor> procEditors = new Vector<ProcessEditor>();
private JPanel processPanel = new JPanel();
private boolean modified = false;
public ReactionSystemEditor() {
rs = ReactionSystem.getInstance();
processPanel.setLayout(new BoxLayout(processPanel, BoxLayout.Y_AXIS));
final Dimension buttonSize = new Dimension(150, 25);
@@ -63,6 +65,16 @@ public class ReactionSystemEditor extends JPanel {
}
});
JButton renameButton = new JButton("Rename");
renameButton.setMaximumSize(buttonSize);
renameButton.setPreferredSize(buttonSize);
renameButton.setMaximumSize(buttonSize);
renameButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
renameProcess();
}
});
JButton upButton = new JButton("Move up");
upButton.setMaximumSize(buttonSize);
upButton.setPreferredSize(buttonSize);
@@ -89,6 +101,7 @@ public class ReactionSystemEditor extends JPanel {
buttonPanel.add(addButton);
buttonPanel.add(rmButton);
buttonPanel.add(copyButton);
buttonPanel.add(renameButton);
buttonPanel.add(upButton);
buttonPanel.add(downButton);
@@ -102,23 +115,23 @@ public class ReactionSystemEditor extends JPanel {
public boolean isModified() {
boolean procModified = false;
for (ProcessEditor pe : processes)
for (ProcessEditor pe : procEditors)
procModified |= pe.isModified();
return modified | procModified;
return rs.modified | procModified;
}
public void clearModificationStatus() {
for (ProcessEditor pe : processes)
for (ProcessEditor pe : procEditors)
pe.clearModificationStatus();
modified = false;
rs.modified = false;
}
public Set<String> getReactantsSet() {
HashSet<String> rset = new HashSet<String>();
for (ProcessEditor pe : processes)
for (ProcessEditor pe : procEditors)
rset.addAll(pe.getReactantsSet());
return rset;
@@ -127,20 +140,20 @@ public class ReactionSystemEditor extends JPanel {
public void exportToXML(PrintWriter output) {
output.println(" <reaction-system>");
for (ProcessEditor process : processes)
for (ProcessEditor process : procEditors)
process.exportToXML(output);
output.println(" </reaction-system>");
}
public void loadFromXML(Document input) throws ReactionSystemStructureError {
NodeList rsSextions = input.getElementsByTagName("reaction-system");
NodeList rsSections = input.getElementsByTagName("reaction-system");
if (rsSextions.getLength() == 0 || rsSextions.getLength() > 1) {
if (rsSections.getLength() == 0 || rsSections.getLength() > 1) {
throw new ReactionSystemStructureError("An XML file should contain a single reaction system description.");
}
NodeList processList = rsSextions.item(0).getChildNodes();
NodeList processList = rsSections.item(0).getChildNodes();
for (int idx=0; idx<processList.getLength(); ++idx) {
Node processNode = processList.item(idx);
@@ -149,10 +162,10 @@ public class ReactionSystemEditor extends JPanel {
continue;
String procName = processNode.getAttributes().getNamedItem("name").getNodeValue();
ProcessEditor processEditor = new ProcessEditor(procName);
ProcessEditor processEditor = new ProcessEditor(rs.createProcess(procName));
//----------------------------------------------------------------------------------------------------------
// Reactins details
// Reactions details
//----------------------------------------------------------------------------------------------------------
NodeList reactionList = processNode.getChildNodes();
@@ -194,7 +207,7 @@ public class ReactionSystemEditor extends JPanel {
processEditor.addReaction(reaction);
}
processes.add(processEditor);
procEditors.add(processEditor);
processPanel.add(processEditor);
}
@@ -204,7 +217,7 @@ public class ReactionSystemEditor extends JPanel {
public String toRSSLString() {
StringBuilder rsString = new StringBuilder("reactions {\n");
for (ProcessEditor pe : processes) {
for (ProcessEditor pe : procEditors) {
rsString.append(pe.toRSSLString());
}
@@ -215,116 +228,177 @@ public class ReactionSystemEditor extends JPanel {
public void clear() {
processPanel.removeAll();
processes.clear();
procEditors.clear();
rs.clearProcesses();
}
private void createProcess() {
ProcessEditor newProcess = new ProcessEditor();
processes.add(newProcess);
processPanel.add(newProcess);
modified = true;
ProcessEditor newProcEdt = new ProcessEditor(rs.createProcess());
procEditors.add(newProcEdt);
processPanel.add(newProcEdt);
rs.modified = true;
revalidate();
}
private void createProcess(String label) {
ProcessEditor newProcess = new ProcessEditor(label);
processes.add(newProcess);
processPanel.add(newProcess);
modified = true;
ProcessEditor newProcEdt = new ProcessEditor(rs.createProcess(label));
procEditors.add(newProcEdt);
processPanel.add(newProcEdt);
rs.modified = true;
revalidate();
}
private void removeProcesses() {
Vector<ProcessEditor> toRemove = new Vector<ProcessEditor>();
for (ProcessEditor edt : processes) {
for (ProcessEditor edt : procEditors) {
if (edt.isSelected()) {
toRemove.add(edt);
}
}
if (toRemove.size() == processes.size()) {
if (toRemove.size() == procEditors.size()) {
JOptionPane.showMessageDialog(this, "There should be at least a single process in the system.",
"Warning", JOptionPane.WARNING_MESSAGE);
return;
}
for (ProcessEditor edt : toRemove) {
processPanel.remove(edt);
processes.remove(edt);
if (rs.removeProcess(edt.getProcess())) {
processPanel.remove(edt);
procEditors.remove(edt);
}
else {
JOptionPane.showMessageDialog(this, "Process " + edt.getLabel() +
" appears in a formula or a context automaton guard.\n" +
"Update them before removing the process.",
"Warning", JOptionPane.WARNING_MESSAGE);
}
}
modified = true;
rs.modified = true;
revalidate();
}
private void copyProcesses() {
Vector<ProcessEditor> toBeCopied = new Vector<ProcessEditor>();
for (ProcessEditor edt : processes) {
for (ProcessEditor edt : procEditors) {
if (edt.isSelected()) {
toBeCopied.add(edt);
}
}
for (ProcessEditor edt : toBeCopied) {
ProcessEditor newEdt = new ProcessEditor(edt);
ProcessEditor newEdt = new ProcessEditor(rs.copyProcess(edt.getProcess()));
processPanel.add(newEdt);
processes.add(newEdt);
procEditors.add(newEdt);
}
modified = true;
rs.modified = true;
revalidate();
}
private void renameProcess() {
ProcessEditor toRename = null;
boolean fail = false;
for (ProcessEditor edt : procEditors) {
if (edt.isSelected()) {
if (toRename == null) {
toRename = edt;
}
else {
fail = true;
break;
}
}
}
if (fail || toRename == null) {
JOptionPane.showMessageDialog(this, "Select a single process to rename.", "Select process", JOptionPane.WARNING_MESSAGE);
return;
}
String idRegex = "[a-zA-Z][a-zA-Z_0-9:-]*";
boolean idOk = false;
do {
String newLabel = JOptionPane.showInputDialog(this, "Process name", toRename.getProcess().label);
if (newLabel == null)
return;
newLabel = newLabel.trim();
if (!newLabel.matches(idRegex)) {
JOptionPane.showMessageDialog(this,
"<html>Process name should start with a letter and may contain only:<br/>" +
"letters, numbers, colons (:), underscores (_) and hyphens (-)</html>",
"Error", JOptionPane.ERROR_MESSAGE);
}
else {
idOk = true;
rs.renameProcess(toRename.getProcess(), newLabel);
toRename.rename(newLabel);
rs.notifyObservers();
}
}
while (!idOk);
}
private void moveSelectedUp() {
Vector<Integer> toBeMovedIds = new Vector<Integer>();
for (int i=0; i<processes.size(); ++i) {
if (processes.elementAt(i).isSelected())
for (int i = 0; i< procEditors.size(); ++i) {
if (procEditors.elementAt(i).isSelected())
toBeMovedIds.add(i);
}
for (int pos : toBeMovedIds) {
if (pos == 0 || processes.elementAt(pos-1).isSelected())
if (pos == 0 || procEditors.elementAt(pos-1).isSelected())
continue;
Collections.swap(processes, pos, pos-1);
Collections.swap(procEditors, pos, pos-1);
}
processPanel.removeAll();
for (ProcessEditor edt : processes) {
for (ProcessEditor edt : procEditors) {
processPanel.add(edt);
}
modified = true;
rs.modified = true;
revalidate();
}
private void moveSelectedDown() {
Vector<Integer> toBeMovedIds = new Vector<Integer>();
for (int i=processes.size()-1; i>=0; --i) {
if (processes.elementAt(i).isSelected())
for (int i = procEditors.size()-1; i>=0; --i) {
if (procEditors.elementAt(i).isSelected())
toBeMovedIds.add(i);
}
for (int pos : toBeMovedIds) {
if (pos == processes.size()-1 || processes.elementAt(pos+1).isSelected())
if (pos == procEditors.size()-1 || procEditors.elementAt(pos+1).isSelected())
continue;
Collections.swap(processes, pos, pos+1);
Collections.swap(procEditors, pos, pos+1);
}
processPanel.removeAll();
for (ProcessEditor edt : processes) {
for (ProcessEditor edt : procEditors) {
processPanel.add(edt);
}
modified = true;
rs.modified = true;
revalidate();
}
public void onRSUpdate() {
repaint();
}
}

View File

@@ -21,7 +21,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TransitionSystemViewer extends JPanel {
public class TransitionSystemViewer extends JPanel implements RSObserver {
//---------------------------------------------------------------
// Visual configuration settings
@@ -266,6 +266,10 @@ public class TransitionSystemViewer extends JPanel {
}
}
public void onRSUpdate() {
clear();
}
}
@@ -353,7 +357,6 @@ class TSState {
String.join("<br/>", blocks) +
"</html>";
}
}