/*
* JFLAP - Formal Languages and Automata Package
*
*
* Susan H. Rodger
* Computer Science Department
* Duke University
* August 27, 2009
* Copyright (c) 2002-2009
* All rights reserved.
* JFLAP is open source software. Please see the LICENSE for terms.
*
*/
package gui.sim;
import gui.viewer.SelectionDrawer;
import java.awt.Component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Stack;
import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.JSplitPane;
import automata.Automaton;
import automata.AutomatonSimulator;
import automata.Configuration; import automata.State;
import automata.turing.TMSimulator;
import automata.turing.TMConfiguration;
import automata.turing.TMState;
import automata.turing.TuringMachine;
/**
* This is an intermediary object between the simulator GUI and the automaton
* simulators.
*
* @author Thomas Finley
*/
public class ConfigurationController implements ConfigurationSelectionListener {
/**
* Instantiates a new configuration controller.
*
* @param pane
* the pane from which we retrieve configurations
* @param simulator
* the automaton simulator
* @param drawer
* the drawer of the automaton
* @param component
* the component in which the automaton is displayed
*/
public ConfigurationController(ConfigurationPane pane,
AutomatonSimulator simulator, SelectionDrawer drawer,
Component component) {
this.configurations = pane;
this.simulator = simulator;
this.drawer = drawer;
this.component = component;
changeSelection();
this.configurations.addSelectionListener(this);
this.originalConfigurations = (Configuration[]) configurations
.getConfigurations();
// for(int k = 0; k < originalConfigurations.length; k++){
// Configuration current = originalConfigurations[k];
// if(current instanceof TMConfiguration){
// TMConfiguration currentTM = (TMConfiguration)current;
// originalConfigurations[k] = (Configuration)currentTM.clone();
// }
// }
}
/**
* This sets the configuration pane to have the initial configuration for
* this input.
*/
public void reset() {
configurations.clear();
if (simulator instanceof TMSimulator) {
TMSimulator tmSim = (TMSimulator) simulator;
Configuration[] configs = tmSim.getInitialConfigurations(tmSim
.getInputStrings());
for (int i = 0; i < configs.length; i++) {
configurations.add(configs[i]);
}
} else {
for (int i = 0; i < originalConfigurations.length; i++) {
originalConfigurations[i].reset();
configurations.add(originalConfigurations[i]);
}
}
// What the devil do I have to do to get it to repaint?
// configurations.invalidate();
configurations.validate();
configurations.repaint();
// Change them darned selections.
changeSelection();
}
/**
* This method should be called when the simulator pane that this
* configuration controller belongs to is removed from the environment. This
* will remove all of the open configuration trace windows.
*/
public void cleanup() {
Collection windows = configurationToTraceWindow.values();
Iterator it = windows.iterator();
while (it.hasNext())
((TraceWindow) it.next()).dispose();
configurationToTraceWindow.clear();
}
/**
* The step method takes all configurations from the configuration pane, and
* replaces them with "successor" transitions.
*
* @param blockStep
*/
public void step(boolean blockStep) {
Configuration[] configs = configurations.getValidConfigurations();
ArrayList list = new ArrayList();
HashSet reject = new HashSet();
// Clear out old states.
configurations.clearThawed();
if (!blockStep){ //for ordinary automaton
for (int i = 0; i < configs.length; i++) {
//System.out.println("HERE!");
ArrayList next = simulator.stepConfiguration(configs[i]);
//MERLIN MERLIN MERLIN MERLIN MERLIN//
if (next.size() == 0) { //crucial check for rejection
//System.out.println("Rejected");
reject.add(configs[i]);
list.add(configs[i]);
} else
list.addAll(next);
}
}
else{
do{
assert configs.length == 1;
assert configs[0] instanceof TMConfiguration;
assert simulator instanceof TMSimulator;
if (configs.length == 0) break; //bit of a hack, but not much time to debug right now.
List next = ((TMSimulator) simulator).stepBlock((TMConfiguration)configs[0]);
//MERLIN MERLIN MERLIN MERLIN MERLIN//
if (next.size() == 0) { //crucial check for rejection
//System.out.println("Rejected");
reject.add(configs[0]);
list.add(configs[0]);
} else
list.addAll(next);
}while(false);
}
// Replace them with the successors.
Iterator it = list.iterator();
while (it.hasNext()) {
Configuration config = (Configuration) it.next();
configurations.add(config);
if (reject.contains(config))
configurations.setReject(config);
}
// What the devil do I have to do to get it to repaint?
configurations.validate();
configurations.repaint();
// Change them darned selections.
changeSelection();
// Ready for the ugliest code in the whole world, ever?
try {
// I take this action without the knowledge or sanction of
// my government...
JSplitPane split = (JSplitPane) configurations.getParent()
.getParent().getParent().getParent();
int loc = split.getDividerLocation();
split.setDividerLocation(loc - 1);
split.setDividerLocation(loc);
// Yes! GridLayout doesn't display properly in a scroll
// pane, but if the user "wiggled" the size a little it
// displays correctly -- now the size is wiggled in code!
} catch (Throwable e) {
}
// State current = null;
// Iterator iter = list.iterator();
// int count = 0;
//MERLIN MERLIN MERLIN MERLIN MERLIN// //forgetting BlockStep for now
//// if (blockStep) { //should ONLY apply to Turing Machines // while (iter.hasNext()) {
//// Configuration configure = (Configuration) iter.next();
//// current = configure.getCurrentState();
//// if (configure.getBlockStack().size() > 0) {
//// if(((Automaton)configure.getAutoStack().peek()).getInitialState() != current || configure.getBlockStack().size()>1){
//// if(!configure.isAccept()){
//// count++;
//// if(count > 10000){
//// int result = JOptionPane.showConfirmDialog(null, "JFLAP has generated 10000 configurations. Continue?");
//// switch (result) {
//// case JOptionPane.CANCEL_OPTION:
//// case JOptionPane.NO_OPTION:
//// return;
//// default:
//// }
//// }
// step(blockStep);
//// }
//// break;
//// }
//// }
//// }
// }
}
/**
* Freezes selected configurations.
*/
public void freeze() {
Configuration[] configs = configurations.getSelected();
if (configs.length == 0) {
JOptionPane.showMessageDialog(configurations,
NO_CONFIGURATION_ERROR, NO_CONFIGURATION_ERROR_TITLE,
JOptionPane.ERROR_MESSAGE);
return;
}
for (int i = 0; i < configs.length; i++) {
configurations.setFrozen(configs[i]);
}
configurations.deselectAll();
configurations.repaint();
}
/**
* Removes the selected configurations.
*/
public void remove() {
Configuration[] configs = configurations.getSelected();
if (configs.length == 0) {
JOptionPane.showMessageDialog(configurations,
NO_CONFIGURATION_ERROR, NO_CONFIGURATION_ERROR_TITLE,
JOptionPane.ERROR_MESSAGE);
return;
}
for (int i = 0; i < configs.length; i++) {
configurations.remove(configs[i]);
}
configurations.validate();
configurations.repaint();
}
/**
* Zooms in on selected configuration
*/
public void focus() {
Configuration[] configs = configurations.getSelected();
if (configs.length == 0) {
JOptionPane.showMessageDialog(configurations,
NO_CONFIGURATION_ERROR, NO_CONFIGURATION_ERROR_TITLE,
JOptionPane.ERROR_MESSAGE);
return;
} else if (configs.length > 1) {
JOptionPane.showMessageDialog(configurations,
FOCUS_CONFIGURATION_ERROR, FOCUS_CONFIGURATION_ERROR_TITLE,
JOptionPane.ERROR_MESSAGE);
return;
}
Configuration toFocus = configs[0];
configurations.setFocused(toFocus);
toFocus.setFocused(true);
// State block = toFocus.getCurrentState().getParentBlock();
// State parent = block;
// Automaton checkTop = null;
// Stack heirarchy = new Stack();
// if (parent != null) {
// checkTop = findAutomaton(checkTop, heirarchy, parent, false);
// //System.out.println("Call from focus");
// if (checkTop != null)
// drawer.setAutomaton(checkTop);
// }
// drawer.invalidate();
component.repaint();
}
/*public Automaton findAutomaton(Automaton checkTop, Stack heirarchy,
State parent, boolean select) {
Configuration[] configs;
configs = configurations.getConfigurations();
for (int k = 0; k < configs.length; k++) {
if (configs[k].getFocused()) {
Automaton temp = (Automaton) configs[k].getAutoStack().peek();
return temp;
}
}
while (parent != null) {
if (select)
drawer.addSelected(parent);
checkTop = (Automaton) simulator.getAutomaton().getBlockMap().get(
parent.getInternalName());
if (checkTop != null) {
while (!heirarchy.isEmpty()) {
State popped = (State) heirarchy.pop();
checkTop = (Automaton) checkTop.getBlockMap().get(
popped.getInternalName());
}
break;
} else
heirarchy.push(parent);
parent = parent.getParentBlock();
}
return checkTop;
}
*/
/**
* Sets the drawer to draw the selected configurations' states as selected,
* or to draw all configurations' states as selected in the event that there
* are no selected configurations. In this case, the selection refers to the
* selection of states within the automata, though
*/
public void changeSelection() {
drawer.clearSelected();
Configuration[] configs;
configs = configurations.getConfigurations();
boolean foundFocused = false;
for (int i = 0; i < configs.length; i++) {
Configuration current = configs[i];
foundFocused = setFocusIfNeeded(current, foundFocused);
if (current instanceof TMConfiguration){
//then blocks become relevant
TMState cur = (TMState) current.getCurrentState();
while (((TuringMachine) cur.getAutomaton()).getParent() != null) cur = ((TuringMachine) cur.getAutomaton()).getParent();
drawer.addSelected(cur);
}
// Stack blocks = (Stack) configs[i].getBlockStack().clone();
// if (!blocks.empty()) {
// State parent = (State) configs[i].getBlockStack().peek();
// int start = blocks.lastIndexOf(parent);
// while (start >= 0) {
// parent = (State) blocks.get(start);
// drawer.addSelected(parent);
// start--;
// }
// }
drawer.addSelected(configs[i].getCurrentState());
}
component.repaint();
}
private boolean setFocusIfNeeded(Configuration current, boolean foundFocused) {
Configuration parentConfig = current.getParent();
if (parentConfig == null)
return foundFocused;
if (parentConfig.getFocused()) {
current.setFocused(true);
if (!foundFocused) {
configurations.setFocused(current);
current.setFocused(true);
//MERLIN MERLIN MERLIN MERLIN MERLIN// //not sure what might have been broken here
// Automaton setWith = (Automaton) current.getAutoStack().peek();
//System.out.println("Stack size "
// + current.getAutoStack().size());
// drawer.setAutomaton(setWith);
foundFocused = true;
}
}
return foundFocused;
}
public void defocus() {
Configuration[] configs = configurations.getConfigurations();
for (int i = 0; i < configs.length; i++) {
if (configs[i].getFocused()) {
configurations.defocus(configs[i]);
}
}
drawer.setAutomaton(simulator.getAutomaton());
drawer.invalidate();
component.repaint();
}
/**
* Thaws the selected configurations.
*/
public void thaw() {
Configuration[] configs = configurations.getSelected();
if (configs.length == 0) {
JOptionPane.showMessageDialog(configurations,
NO_CONFIGURATION_ERROR, NO_CONFIGURATION_ERROR_TITLE,
JOptionPane.ERROR_MESSAGE);
return;
}
for (int i = 0; i < configs.length; i++) {
configurations.setNormal(configs[i]);
}
configurations.deselectAll();
configurations.repaint();
}
/**
* Given the selected configurations, shows their "trace."
*/
public void trace() {
Configuration[] configs = configurations.getSelected();
if (configs.length == 0) {
JOptionPane.showMessageDialog(configurations,
NO_CONFIGURATION_ERROR, NO_CONFIGURATION_ERROR_TITLE,
JOptionPane.ERROR_MESSAGE);
return;
}
for (int i = 0; i < configs.length; i++) {
TraceWindow window = (TraceWindow) configurationToTraceWindow
.get(configs[i]);
if (window == null) {
configurationToTraceWindow.put(configs[i], new TraceWindow(
configs[i]));
} else {
window.setVisible(true);
window.toFront();
}
}
}
/**
* This method is used to find out if we need the Focus and
* Defocus buttons in the simulator.
*
* @return true
if the automaton is a turing machine,
* false
otherwise
* @author Jinghui Lim
*/
public boolean isTuringMachine()
{
/*
* Sorry about this pretty cheap method.
*/
return simulator instanceof TMSimulator;
}
/**
* Listens for configuration selection events.
*
* @param event
* the selection event
*/
public void configurationSelectionChange(ConfigurationSelectionEvent event) {
// changeSelection();
}
/** This is the pane holding the configurations. */
private ConfigurationPane configurations;
/** This is the simulator that we step through configurations with. */
private AutomatonSimulator simulator;
/** This is the selection drawer that draws the automaton. */
private SelectionDrawer drawer;
/** This is the pane in which the automaton is displayed. */
private Component component;
/**
* The mapping of a particular configuration to a trace window. If there is
* no trace window for that configuration, then that trace window no longer
* exists.
*/
private HashMap configurationToTraceWindow = new HashMap();
/**
* This is the set of original configurations when the configuration pane
* started.
*/
private Configuration[] originalConfigurations = new Configuration[0];
/** The error message displayed when there is no config selected. */
private static final String NO_CONFIGURATION_ERROR = "Select at least one configuration!";
/** The error message displayed when there is no config selected. */
private static final String NO_CONFIGURATION_ERROR_TITLE = "No Configuration Selected";
/** The error message displayed when there is no config selected. */
private static final String FOCUS_CONFIGURATION_ERROR = "JFLAP can only focus on one configuration at a time!";
/** The error message displayed when there is no config selected. */
private static final String FOCUS_CONFIGURATION_ERROR_TITLE = "Too many configurations selected";
}