/*
* 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.multiple;
import grammar.Grammar;
import gui.GrowableTableModel;
import automata.Automaton;
import automata.Configuration;
import automata.turing.Tape;
import automata.turing.TMConfiguration;
import javax.swing.event.TableModelListener;
import javax.swing.event.TableModelEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import automata.turing.TuringMachine;
/**
* The InputTableModel
is a table specifically used for the input
* of multiple inputs for simulation in an automaton. It has columns for each of
* the inputs, each of the outputs (when run on a Turing machine and the user
* wants to treat it as a transducer), and the final result.
*
* @author Thomas Finley
*/
public class InputTableModel extends GrowableTableModel {
/**
* This instantiates an InputTableModel
.
*
* @param automaton
* the automaton that we're inputting stuff for
*/
public InputTableModel(Automaton automaton, int add) {
super(2 * inputsForMachine(automaton)+1+add);
}
public InputTableModel(Grammar gram, int add) {
super(2 * 1 + 1+add);
}
/**
* This instantiates a copy of the InputTableModel
.
*
* @param model
* the model to copy
*/
public InputTableModel(InputTableModel model) {
super(model);
}
/**
* This instantiates an InputTableModel
.
*
* @param columns
* the number of columns for this input table model
*/
protected InputTableModel(int columns) {
super(columns);
}
/**
* Initializes the contents of a new array to be all blank strings.
*/
protected Object[] initializeRow(int row) {
Object[] nr = super.initializeRow(row);
Arrays.fill(nr, "");
return nr;
}
/**
* This returns the name of the columns. In the input table model, each of
* the first {@link #getColumnCount} columns is an input column and is
* titled "Input #", where # is replaced with the number of the column (e.g.
* "Input 1", then "Input 2" for a two tape machine), unless there is only
* one input column, in which case the single input column is just called
* "Input". There are as many output columns as input columns, and are
* numbered in a similar fashion except with the prefix "Output" instead of
* "Input". The last column is always reserved for the result.
*
* @param column
* the number of the column to get the name for
* @return the name of the column
*/
public String getColumnName(int column) {
int count = getColumnCount();
if (column == count - 1)
return "Result";
int offset = 0;
if(isMultiple){
offset = 1;
if(column == 0) return "File";
}
String word = "";
if(column <= (getInputCount()-1+offset) && column >= (offset)){
word = "Input";
}
else if(column > (getInputCount()-1+offset)){
word = "Output";
column -= getInputCount();
}
if (getInputCount()==1)
return word;
return word + " " + (column+1-offset);
}
/**
* This returns an array of the inputs for the table. The input is organized
* by arrays of arrays of strings. The first index of the array is the row
* of the input. The second index of the array is the particular input,
* which will be a single element array for most machines but an n
* element array for an n-tape Turing machine.
*
* @return an array of inputs, the first index corresponds directly to the
* row, the second to the column
*/
public String[][] getInputs() {
String[][] inputs = new String[getRowCount() - 1][getInputCount()];
for (int r = 0; r < inputs.length; r++){
int begin = 0;
if(isMultiple) begin= 1;
for (int c=0; c < inputs[r].length; c++)
inputs[r][c] = (String) getValueAt(r, c+begin);
}
return inputs;
}
/**
* This returns if a cell is editable. In this model, a cell is editable if
* it's anything other than the last column, which is where the results are
* reported.
*
* @param row
* the row to check for editableness
* @param column
* the column to check for editableness
* @return by default this returns true
if this is any column
* other than the last column; in that instance this returns false
*/
public boolean isCellEditable(int row, int column) {
if(isMultiple) return (column < getInputCount() && column > 0);
return column < getInputCount();
}
/**
* Returns the number of inputs needed for this type of automaton.
*
* @param automaton
* the automaton to pass in
* @return the number of input strings needed for this automaton; e.g., n
* for an n-tape turing machine, 1 for most anything else
*/
public static int inputsForMachine(Automaton automaton) {
return automaton instanceof TuringMachine ? ((TuringMachine) automaton)
.tapes() : 1;
}
/**
* This returns the number of inputs this table model supports.
*
* @return the number of inputs for this table model
*/
public int getInputCount() {
int columnCount = getColumnCount();
if(isMultiple) columnCount-=1;
return columnCount / 2;
}
/**
* This returns the cached table model for an automaton of this type. It is
* desirable that automatons, upon asking to run input, should be presented
* with the same data in the same table since multiple inputs tables are oft
* used to test the same sets of input on different automaton's again and
* again. In the event that there are multiple models active, this method
* returns the last table model that was modified. If there have been no
* applicable table models cached yet, then a blank table model is created.
*
* @param automaton
* the automaton to get a model for
* @return a copy of the model that was last edited with the correct number
* of inputs for this automaton
*/
public static InputTableModel getModel(Automaton automaton, boolean multipleFile) {
InputTableModel model = (InputTableModel) INPUTS_TO_MODELS
.get(new Integer(inputsForMachine(automaton)));
if (model != null && (model.isMultiple == multipleFile)) {
model = new InputTableModel(model);
// Clear out the results column.
for (int i = 0; i < (model.getRowCount() - 1); i++)
model.setResult(i, "", null, null, 0);
}
else {
int add = 0;
if(multipleFile) add = 1;
model = new InputTableModel(automaton, add);
}
model.addTableModelListener(LISTENER);
if(multipleFile){
model.isMultiple = true;
}
return model;
}
public static InputTableModel getModel(Grammar gram, boolean multipleFile) {
InputTableModel model = (InputTableModel) INPUTS_TO_MODELS
.get(new Integer(1));
if (model != null) {
model = new InputTableModel(model);
// Clear out the results column.
for (int i = 0; i < (model.getRowCount() - 1); i++)
model.setResult(i, "", null, null, 0);
} else {
int add = 0;
if(multipleFile) add = 1;
model = new InputTableModel(gram, add);
}
model.addTableModelListener(LISTENER);
if(multipleFile){
model.isMultiple = true;
}
return model;
}
/**
* Sets the result string for a particular row. If you wanted to clear a row
* of all results, you would call this function setResult(row, "",
* null)
.
*
* @param row
* the row to set the result of
* @param result
* the result to put in the result column
* @param config
* the associated configuration, or null
if you
* wish to not have a configuration associated with a row
*/
public void setResult(int row, String result, Configuration config, ArrayList comparison, int index) {
int halfway = getInputCount();
if(isMultiple) halfway++;
int outNum = 1;
// Set the output columns.
if(config instanceof TMConfiguration && config != null){
TMConfiguration c = (TMConfiguration) config;
Tape[] tapes = c.getTapes();
outNum = tapes.length;
if (config.isAccept()) {
for (int i = 0; i < tapes.length; i++) {
String put = tapes[i].getOutput();
if(comparison!=null){
String expected = ((String)comparison.get(index+i));
if(!expected.equals("~") && !expected.equals(put)) put = put+"("+expected+")";
if(((String)comparison.get(index+outNum)).toLowerCase().startsWith("r")){
if(!result.endsWith(")")) result = result+"(Reject)";
if(!put.endsWith(")")) put = put + "("+expected+")";
}
}
setValueAt(put, row, halfway + i);
}
}
else{
for (int i = 0; i < halfway; i++){
String put = "";
if(comparison!=null){
String expected = ((String)comparison.get(index+i));
if(!((String)comparison.get(index+outNum)).toLowerCase().startsWith("r")){
if(!result.endsWith(")")) result = result+"(Accept)";
put = put + "("+expected+")";
}
}
setValueAt(put, row, halfway + i);
}
}
} else{
for (int i = 0; (halfway+i) < this.columns; i++)
setValueAt("", row, halfway + i);
boolean accept = false;
if(config==null){
if(result.equals("Reject")) accept = false;
}
else accept = config.isAccept();
if(comparison!=null && (index+outNum)null
* if there is no associated accepting configuration
*/
public Configuration getAssociatedConfigurationForRow(int row) {
return (Configuration) rowToAssociatedConfiguration
.get(new Integer(row));
}
/** The static table model listener for caching inputs. */
protected final static TableModelListener LISTENER = new TableModelListener() {
public void tableChanged(TableModelEvent event) {
InputTableModel model = (InputTableModel) event.getSource();
if (event.getColumn() != TableModelEvent.ALL_COLUMNS
&& event.getColumn() >= model.getInputCount())
return; // If the inputs weren't changed, don't bother.
Integer inputs = new Integer(model.getInputCount());
INPUTS_TO_MODELS.put(inputs, model);
}
};
public boolean isMultiple = false;
/**
* The map of number of inputs (stored as integers) to input table models.
*/
protected final static Map INPUTS_TO_MODELS = new HashMap();
/**
* The map of row to the associated configuration. If this row does not have
* an associated configuration, this map should not hold an entry.
*/
private final Map rowToAssociatedConfiguration = new HashMap();
}