package jsetool.gui;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.util.Collection;
import java.util.Iterator;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import jsetool.data.ExplorationState;
import jsetool.data.Transition;
import jsetool.engine.StateExplorer;
/**
* @author Mike Lawson
*
* @since Nov 7, 2004
*
* The JSEToolMainPane class is the primary user interface for the tool. It
* consists of two areas to edit Finite State Machines and a results area.
*
*
*/
public class JSEToolMainPane extends JPanel
{
protected JPanel machine1;
protected FSMEditingPane machine1EditingPane;
protected JPanel machine2;
protected FSMEditingPane machine2EditingPane;
protected JSplitPane mainSplit;
protected JPanel resultsPanel;
protected ReachabilityResults lastResults;
/**
* Constructs a new, empty, pane.
*/
public JSEToolMainPane()
{
buildDisplay();
}
/**
* Sets the results object and updates the display.
*
* @param results
*/
public void setResults(ReachabilityResults results)
{
lastResults = results;
setResultsPane(generateResultsPane());
}
/**
* Generates a results pane with both the exploration state diagram and the
* list of unused states and transitions per machine.
*
* @return Component
*/
protected Component generateResultsPane()
{
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Diagram", lastResults.getResultsPane());
tabbedPane.addTab("Summary", generateSummaryPane());
tabbedPane.addTab("M1 Unused", generateUnusedPane("M1", lastResults.getM1UnusedStates(), lastResults.getM1UnusedTransitions()));
tabbedPane.addTab("M2 Unused", generateUnusedPane("M2", lastResults.getM2UnusedStates(), lastResults.getM2UnusedTransitions()));
return tabbedPane;
}
/**
* Generates a pane that displays summary information about the execution.
*
* @return Component
*/
protected Component generateSummaryPane()
{
JPanel summaryPane = new JPanel();
summaryPane.setLayout(new BorderLayout());
JEditorPane editor = new JEditorPane();
editor.setContentType("text/html");
// Add title
String displayHTML = "Summary of Execution";
int numberOfUnusedStates = lastResults.getM1UnusedStates().size() + lastResults.getM2UnusedStates().size();
int numberOfUnusedTransitions = lastResults.getM1UnusedTransitions().size() + lastResults.getM2UnusedTransitions().size();
// Add list of summary items
displayHTML += "
";
displayHTML += "- Milliseconds to Perform Computation: " + lastResults.getMillisToCompute() + "
";
displayHTML += "- Number of Global States created: " + lastResults.getNumberOfGlobalStates() + "
";
displayHTML += "- Total Number of Unused States: " + numberOfUnusedStates + "
";
displayHTML += "- Total Number of Unused Transitions: " + numberOfUnusedTransitions + "
";
displayHTML += "- Total Number of Deadlocks: " + lastResults.getNumberOfDeadlocks() + "
";
displayHTML += "- Total Number of Unspecified Reception States: " + lastResults.getNumberOfUnspecifiedReceptionStates()
+ "
";
displayHTML += "
";
editor.setText(displayHTML);
editor.setEditable(false);
summaryPane.add(editor, BorderLayout.CENTER);
return summaryPane;
}
/**
* Generates the pane that display what is unused in the given machine.
*
* @param machineName
* @param unusedStates
* @param unusedTransitions
* @return Component
*/
protected Component generateUnusedPane(String machineName, Collection unusedStates, Collection unusedTransitions)
{
JPanel unusedPane = new JPanel();
unusedPane.setLayout(new BorderLayout());
JEditorPane editor = new JEditorPane();
editor.setContentType("text/html");
// Add title of unused states
String displayHTML = "Unused States for Machine: " + machineName + "";
// Add list of states
displayHTML += "";
for (Iterator iter = unusedStates.iterator(); iter.hasNext();)
{
String displayString = iter.next().toString();
displayHTML += "- " + displayString + "
";
}
displayHTML += "
";
// Add title of unused states
displayHTML += "
Unused Transitions for Machine: " + machineName + "";
displayHTML += "
";
for (Iterator iter = unusedTransitions.iterator(); iter.hasNext();)
{
Transition transition = (Transition) iter.next();
displayHTML += "- " + transition.verboseString() + "
";
}
displayHTML += "
";
editor.setText(displayHTML);
editor.setEditable(false);
unusedPane.add(editor, BorderLayout.CENTER);
return unusedPane;
}
/**
* Replaces the current results area with a new one.
*
* @param p
* the ExplorationStateDrawingPane that has the new results.
*/
protected void setResultsPane(Component p)
{
Component results = new JScrollPane(p);
try
{
resultsPanel.remove(1);
}
catch (RuntimeException e)
{
// nada - just means there's nothing yet in the panel.
}
resultsPanel.add(results, BorderLayout.CENTER, 1);
mainSplit.revalidate();
}
/**
* Adds the results area to the display.
*/
protected void addResultsArea()
{
resultsPanel = new JPanel();
resultsPanel.setLayout(new BorderLayout());
resultsPanel.setPreferredSize(new Dimension(350, 350));
resultsPanel.add(createButtonPanelForResultsArea(), BorderLayout.NORTH, 0);
setResultsEmpty();
mainSplit.add(resultsPanel, JSplitPane.BOTTOM);
}
/**
*
*/
private void setResultsEmpty()
{
JPanel empty = new JPanel();
setResultsPane(empty);
}
/**
* Builds the display.
*/
protected void buildDisplay()
{
setLayout(new BorderLayout());
mainSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
mainSplit.setOneTouchExpandable(true);
addFSMEditingPanes();
addResultsArea();
setPreferredSize(new Dimension(850, 550));
add(mainSplit, BorderLayout.CENTER);
}
/**
* Performs the reachability analysis for the data drawn on the screen.
*
* @return ReachabilityResults
*/
protected ReachabilityResults performReachabilityAnalysis()
{
// Show error if one or both machines do not have an initial state.
LabeledState m1Initial = machine1EditingPane.getInitialState();
LabeledState m2Initial = machine2EditingPane.getInitialState();
if ((m1Initial == null) || (m2Initial == null))
{
JOptionPane.showMessageDialog(this, "Cannot perform analysis, both machines must have an initial state.", "JSETool Error", JOptionPane.ERROR_MESSAGE);
return null;
}
DrawingToEngineDataAdapter m1Adapter = new DrawingToEngineDataAdapter();
m1Adapter.setInitial(m1Initial);
m1Adapter.exploreAllStates();
DrawingToEngineDataAdapter m2Adapter = new DrawingToEngineDataAdapter();
m2Adapter.setInitial(m2Initial);
m2Adapter.exploreAllStates();
StateExplorer se = new StateExplorer(m1Adapter.getInitialState(), m2Adapter.getInitialState());
ExplorationState exploredState = se.exploreStates();
ExplorationStateLeveler leveler = new ExplorationStateLeveler(exploredState);
ExplorationStateDrawingPane ldp = new ExplorationStateDrawingPane(leveler);
ReachabilityResults results = new ReachabilityResults();
results.setResultsPane(ldp);
results.setMillsToCompute(se.getMillisToCompute());
results.setM1UnusedStates(m1Adapter.getUnusedStates());
results.setM2UnusedStates(m2Adapter.getUnusedStates());
results.setM1UnusedTransitions(m1Adapter.getUnusedTransitions());
results.setM2UnusedTransitions(m2Adapter.getUnusedTransitions());
return results;
}
/**
*
*/
protected void resetStatesOfExploredObjects()
{
machine1EditingPane.resetObjects();
machine2EditingPane.resetObjects();
}
/**
* Adds the panes that allow the user to edit FSMs
*/
private void addFSMEditingPanes()
{
machine1EditingPane = new FSMEditingPane();
machine2EditingPane = new FSMEditingPane();
machine1 = new JPanel(new BorderLayout());
JPanel m1Title = new JPanel();
m1Title.setLayout(new FlowLayout());
m1Title.add(new JLabel("M1"));
m1Title.setBorder(new BevelBorder(0));
machine1.add(m1Title, BorderLayout.NORTH);
machine1.add(machine1EditingPane, BorderLayout.CENTER);
machine2 = new JPanel(new BorderLayout());
JPanel m2Title = new JPanel();
m2Title.setLayout(new FlowLayout());
m2Title.add(new JLabel("M2"));
m2Title.setBorder(new BevelBorder(0));
machine2.add(m2Title, BorderLayout.NORTH);
machine2.add(machine2EditingPane, BorderLayout.CENTER);
JSplitPane machineSplits = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
machineSplits.setOneTouchExpandable(true);
machineSplits.add(machine1, 0);
machineSplits.add(machine2, 0);
machineSplits.setAutoscrolls(true);
machineSplits.setDividerLocation(0.50D);
JPanel top = new JPanel();
top.setLayout(new BorderLayout());
top.add(machineSplits, BorderLayout.CENTER);
top.add(createButtonPanelForStateMachines(), BorderLayout.SOUTH);
mainSplit.add(top, JSplitPane.TOP);
mainSplit.setDividerLocation(250);
}
/**
* Called when the calculate button is pushed.
*
*/
private void calculateButtonPushed()
{
ReachabilityResults results = performReachabilityAnalysis();
if (results == null) return;
resetStatesOfExploredObjects();
setResults(results);
}
/**
* Creates the button panel for the results area.
*
* @return Component
*
*/
private Component createButtonPanelForResultsArea()
{
JPanel resultsButtonPanel = new JPanel();
resultsButtonPanel.setLayout(new FlowLayout());
JButton saveResultsButton = makeButton("images/save.gif", "Save");
saveResultsButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
saveButtonPushed();
}
});
resultsButtonPanel.add(saveResultsButton);
JButton loadResultsButton = makeButton("images/open.gif", "Load");
loadResultsButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
loadButtonPushed();
}
});
resultsButtonPanel.add(loadResultsButton);
JButton clearButton = makeButton("images/new.gif", "Clear");
clearButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
clearDiagram();
}
});
resultsButtonPanel.add(clearButton);
return resultsButtonPanel;
}
private void saveButtonPushed()
{
try
{
JFileChooser fc = new JFileChooser();
int retval = fc.showSaveDialog(this);
if (retval == JFileChooser.APPROVE_OPTION)
{
saveDrawingPane(fc.getSelectedFile());
}
}
catch (Exception e)
{
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Failed to save drawing", "Error Saving", JOptionPane.ERROR_MESSAGE);
}
}
/**
* Saves the drawing pane to the given file name.
*
* @param file
* @throws Exception
*/
public void saveDrawingPane(File file) throws Exception
{
File saveFile = file;
FileOutputStream fos = new FileOutputStream(saveFile);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(lastResults);
}
private void clearDiagram()
{
setResultsEmpty();
}
private void loadButtonPushed()
{
try
{
JFileChooser fc = new JFileChooser();
int retval = fc.showOpenDialog(this);
if (retval == JFileChooser.APPROVE_OPTION)
{
readDrawingPane(fc.getSelectedFile());
}
}
catch (Exception e)
{
e.printStackTrace();
JOptionPane.showMessageDialog(this, "Failed to load drawing", "Error Reading", JOptionPane.ERROR_MESSAGE);
}
}
/**
* Reads the drawing pane from the given file name.
*
* @param file
* @throws Exception
*/
public void readDrawingPane(File file) throws Exception
{
File saveFile = file;
FileInputStream fis = new FileInputStream(saveFile);
ObjectInputStream ois = new ObjectInputStream(fis);
ReachabilityResults loadedResults = (ReachabilityResults) ois.readObject();
setResults(loadedResults);
}
/**
* Builds the panel of buttons that sits between the editing panes and the
* results pane.
*
* @return Component
*/
private Component createButtonPanelForStateMachines()
{
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout());
JButton calculateButton = new JButton("Perform RA");
calculateButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
calculateButtonPushed();
}
});
buttonPanel.add(calculateButton);
return buttonPanel;
}
/**
* @return
*/
private JButton makeButton(String imagePath, String defaultText)
{
ImageIcon icon = getImageIcon(imagePath);
JButton button = null;
if (icon == null)
{
button = new JButton(defaultText);
}
else
{
Image img = icon.getImage();
icon.setImage(img.getScaledInstance(12, 12, Image.SCALE_SMOOTH));
button = new JButton(icon);
button.setPreferredSize(new Dimension(24, 24));
button.setToolTipText(defaultText);
}
return button;
}
private ImageIcon getImageIcon(String path)
{
int MAX_IMAGE_SIZE = 2400; //Change this to the size of
//your biggest image, in bytes.
int count = 0;
BufferedInputStream imgStream = new BufferedInputStream(
this.getClass().getResourceAsStream(path));
if (imgStream != null) {
byte buf[] = new byte[MAX_IMAGE_SIZE];
try {
count = imgStream.read(buf);
imgStream.close();
} catch (java.io.IOException ioe) {
System.err.println("Couldn't read stream from file: " + path);
ioe.printStackTrace();
return null;
}
if (count <= 0) {
System.err.println("Empty file: " + path);
return null;
}
return new ImageIcon(Toolkit.getDefaultToolkit().createImage(buf));
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
}