code: //==============================================================================
  //	
  //	Copyright (c) 2002-
  //	Authors:
  //	* Andrew Hinton <ug60axh@cs.bham.ac.uk> (University of Birmingham)
  //	* Mark Kattenbelt <mark.kattenbelt@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
  //	* Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
  //	
  //------------------------------------------------------------------------------
  //	
  //	This file is part of PRISM.
  //	
  //	PRISM is free software; you can redistribute it and/or modify
  //	it under the terms of the GNU General Public License as published by
  //	the Free Software Foundation; either version 2 of the License, or
  //	(at your option) any later version.
  //	
  //	PRISM is distributed in the hope that it will be useful,
  //	but WITHOUT ANY WARRANTY; without even the implied warranty of
  //	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  //	GNU General Public License for more details.
  //	
  //	You should have received a copy of the GNU General Public License
  //	along with PRISM; if not, write to the Free Software Foundation,
  //	Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  //	
  //==============================================================================
  
  package userinterface.simulator;
  
  import java.util.*;
  import javax.swing.table.AbstractTableModel;
  
  import simulator.PathFullInfo;
  import userinterface.simulator.SimulationView.*;
  import userinterface.util.GUIGroupedTableModel;
  import parser.ast.*;
  
  public class GUISimulatorPathTableModel extends AbstractTableModel implements GUIGroupedTableModel, Observer
  {
  	private static final long serialVersionUID = 1L;
  
  	private GUISimulator simulator;
  	private SimulationView view;
  
  	private boolean pathActive;
  	private ModulesFile parsedModel;
  	private PathFullInfo path; 
  
  	private RewardStructureValue rewardStructureValue;
  	private VariableValue variableValue;
  	private TimeValue timeValue;
  	private ActionValue actionValue;
  
  	public GUISimulatorPathTableModel(GUISimulator simulator, SimulationView view)
  	{
  		this.simulator = simulator;
  		this.view = view;
  		this.view.addObserver(this);
  
  		rewardStructureValue = view.new RewardStructureValue(null, null);
  		variableValue = view.new VariableValue(null, null);
  	}
  
  	public void setPathActive(boolean pathActive)
  	{
  		this.pathActive = pathActive;
  	}
  
  	public void setPath(PathFullInfo path)
  	{
  		this.path = path;
  	}
  
  	public void setParsedModel(ModulesFile parsedModel)
  	{
  		this.parsedModel = parsedModel;
  	}
  
  	public boolean canShowTime()
  	{
  		return parsedModel.getModelType().continuousTime();
  	}
  
  	public int getGroupCount()
  	{
  		if (!pathActive) {
  			return 0;
  		} else {
  			int groupCount = 0;
  
  			if (view.showActions() || view.showSteps()) {
  				groupCount++;
  			}
  
  			if (canShowTime() && (view.showTime() || view.showCumulativeTime())) {
  				groupCount++;
  			}
  
  			ArrayList<Variable> vars = view.getVisibleVariables();
  			Set<String> varNames = new HashSet<String>();
  
  			for (Variable variable : vars) {
  				varNames.add(variable.getName());
  			}
  
  			for (int g = 0; g < parsedModel.getNumGlobals(); g++) {
  				if (varNames.contains(parsedModel.getGlobal(g).getName())) {
  					groupCount++;
  					break;
  				}
  			}
  
  			for (int m = 0; m < parsedModel.getNumModules(); m++) {
  				parser.ast.Module module = parsedModel.getModule(m);
  				for (int v = 0; v < module.getNumDeclarations(); v++) {
  					if (varNames.contains(module.getDeclaration(v).getName())) {
  						groupCount++;
  						break;
  					}
  				}
  			}
  
  			if (view.getVisibleRewardColumns().size() > 0) {
  				groupCount++;
  			}
  
  			return groupCount;
  		}
  	}
  
  	public void update(Observable o, Object arg)
  	{
  		if (o == view) {
  			fireTableStructureChanged();
  
  			//Sort out the minimum widths for each column
  			simulator.sortOutColumnSizes();
  		}
  	}
  
  	public String getGroupName(int groupIndex)
  	{
  		if (!pathActive) {
  			return "";
  		} else {
  			int groupCount = 0;
  
  			if (view.showActions() || view.showSteps()) {
  				if (groupCount == groupIndex) {
  					return "Step";
  				}
  
  				groupCount++;
  			}
  
  			if (canShowTime() && (view.showTime() || view.showCumulativeTime())) {
  				if (groupCount == groupIndex) {
  					return "Time";
  				}
  
  				groupCount++;
  			}
  
  			if (view.getVisibleVariables().size() > 0) {
  				ArrayList<Variable> vars = view.getVisibleVariables();
  				Set<String> varNames = new HashSet<String>();
  
  				for (Variable variable : vars) {
  					varNames.add(variable.getName());
  				}
  
  				for (int g = 0; g < parsedModel.getNumGlobals(); g++) {
  					if (varNames.contains(parsedModel.getGlobal(g).getName())) {
  						if (groupCount == groupIndex) {
  							return "Globals";
  						}
  
  						groupCount++;
  						break;
  					}
  				}
  
  				for (int m = 0; m < parsedModel.getNumModules(); m++) {
  					parser.ast.Module module = parsedModel.getModule(m);
  					for (int v = 0; v < module.getNumDeclarations(); v++) {
  						if (varNames.contains(module.getDeclaration(v).getName())) {
  							if (groupCount == groupIndex) {
  								return "" + parsedModel.getModuleName(m) + "";
  							}
  
  							groupCount++;
  							break;
  						}
  					}
  				}
  			}
  
  			// Add state and transitions rewards for each reward structure.
  			if (view.getVisibleRewardColumns().size() > 0) {
  				if (groupCount == groupIndex) {
  					return "Rewards";
  				}
  
  				groupCount++;
  			}
  
  			return "Undefined Group";
  		}
  	}
  
  	public String getGroupToolTip(int groupIndex)
  	{
  		ArrayList<Variable> vars = view.getVisibleVariables();
  		Set<String> varNames = new HashSet<String>();
  
  		for (Variable variable : vars) {
  			varNames.add(variable.getName());
  		}
  
  		int groupCount = 0;
  
  		if (view.showActions() || view.showSteps()) {
  			if (groupCount == groupIndex) {
  				return null;
  			}
  
  			groupCount++;
  		}
  
  		if (canShowTime() && (view.showTime() || view.showCumulativeTime())) {
  			if (groupCount == groupIndex) {
  				return null;
  			}
  
  			groupCount++;
  		}
  
  		for (int g = 0; g < parsedModel.getNumGlobals(); g++) {
  			if (varNames.contains(parsedModel.getGlobal(g).getName())) {
  				if (groupCount == groupIndex) {
  					return "Global variables";
  				}
  
  				groupCount++;
  				break;
  			}
  		}
  
  		for (int m = 0; m < parsedModel.getNumModules(); m++) {
  			parser.ast.Module module = parsedModel.getModule(m);
  			for (int v = 0; v < module.getNumDeclarations(); v++) {
  				if (varNames.contains(module.getDeclaration(v).getName())) {
  					if (groupCount == groupIndex) {
  						return "Variables of module \"" + parsedModel.getModuleName(m) + "\"";
  					}
  
  					groupCount++;
  					break;
  				}
  			}
  		}
  
  		// Add state and transitions rewards for each reward structure.
  		if (view.getVisibleRewardColumns().size() > 0) {
  			if (groupCount == groupIndex) {
  				return "State, transition and cumulative rewards";
  			}
  
  			groupCount++;
  		}
  
  		return null;
  	}
  
  	public int getLastColumnOfGroup(int groupIndex)
  	{
  		int stepStart = 0;
  		int timeStart = stepStart + (view.showActions() ? 1 : 0) + (view.showSteps() ? 1 : 0);
  		int varStart = timeStart + (canShowTime() && view.showCumulativeTime() ? 1 : 0) + (canShowTime() && view.showTime() ? 1 : 0);
  		int rewardStart = varStart + view.getVisibleVariables().size();
  
  		int groupCount = 0;
  
  		if (view.showActions() || view.showSteps()) {
  			if (groupCount == groupIndex) {
  				if (view.showActions() && view.showSteps())
  					return stepStart + 1;
  				else
  					return stepStart;
  			}
  
  			groupCount++;
  		}
  
  		if (canShowTime() && (view.showCumulativeTime() || view.showTime())) {
  			if (groupCount == groupIndex) {
  				if (view.showCumulativeTime() && view.showTime())
  					return timeStart + 1;
  				else
  					return timeStart;
  			}
  
  			groupCount++;
  		}
  
  		if (view.getVisibleVariables().size() > 0) {
  			int visVarCount = 0;
  
  			ArrayList<Variable> vars = view.getVisibleVariables();
  			Set<String> varNames = new HashSet<String>();
  
  			for (Variable variable : vars) {
  				varNames.add(variable.getName());
  			}
  
  			boolean atLeastOneGlobal = false;
  
  			for (int g = 0; g < parsedModel.getNumGlobals(); g++) {
  				boolean contained = varNames.contains(parsedModel.getGlobal(g).getName());
  
  				if (!atLeastOneGlobal && contained) {
  					atLeastOneGlobal = true;
  				}
  
  				if (contained)
  					visVarCount++;
  			}
  
  			if (atLeastOneGlobal && groupCount == groupIndex) {
  				return varStart + visVarCount - 1;
  			}
  
  			if (atLeastOneGlobal) {
  				groupCount++;
  			}
  
  			for (int m = 0; m < parsedModel.getNumModules(); m++) {
  				parser.ast.Module module = parsedModel.getModule(m);
  				boolean atLeastOne = false;
  
  				for (int v = 0; v < module.getNumDeclarations(); v++) {
  					boolean contained = varNames.contains(module.getDeclaration(v).getName());
  					if (!atLeastOne && contained) {
  						atLeastOne = true;
  					}
  
  					if (contained)
  						visVarCount++;
  				}
  
  				if (atLeastOne && groupCount == groupIndex) {
  					return varStart + visVarCount - 1;
  				}
  
  				if (atLeastOne) {
  					groupCount++;
  				}
  			}
  		}
  
  		// Add state and transitions rewards for each reward structure.
  		if (view.getVisibleRewardColumns().size() > 0) {
  			if (groupCount == groupIndex) {
  				return rewardStart + view.getVisibleRewardColumns().size() - 1;
  			}
  
  			groupCount++;
  		}
  
  		return 0;
  	}
  
  	/**
  	 * Returns the number of columns.
  	 * @see javax.swing.table.TableModel#getColumnCount()
  	 */
  	public int getColumnCount()
  	{
  		if (!pathActive) {
  			return 0;
  		} else {
  			int colCount = 0;
  
  			colCount += (view.showActions() ? 1 : 0);
  			colCount += (view.showSteps() ? 1 : 0);
  			colCount += (canShowTime() && view.showCumulativeTime() ? 1 : 0) + (canShowTime() && view.showTime() ? 1 : 0);
  			colCount += view.getVisibleVariables().size();
  			colCount += view.getVisibleRewardColumns().size();
  
  			return colCount;
  		}
  	}
  
  	/**
  	 * Returns the number of rows.
  	 * @see javax.swing.table.TableModel#getRowCount()
  	 */
  	public int getRowCount()
  	{
  		// Return current path size if there is an active path.
  		return (pathActive ? (int) path.size() + 1 : 0);
  	}
  
  	public boolean shouldColourRow(int row)
  	{
  		int selection = simulator.getStateLabelList().getSelectedIndex();
  		if (selection != -1) {
  			GUISimLabelList.SimLabel label = (GUISimLabelList.SimLabel) simulator.getStateLabelList().getModel().getElementAt(selection);
  			if (row == getRowCount() - 1) {
  				if (label.getResult() == 1)
  					return true;
  			} else {
  				if (label.getResult(row) == 1)
  					return true;
  			}
  		}
  
  		return false;
  	}
  
  	public String getColumnName(int columnIndex)
  	{
  		if (pathActive) {
  			int actionStart = 0;
  			int stepStart = actionStart + (view.showActions() ? 1 : 0);
  			int cumulativeTimeStart = stepStart + (view.showSteps() ? 1 : 0);
  			int timeStart = cumulativeTimeStart + (canShowTime() && view.showCumulativeTime() ? 1 : 0);
  			int varStart = timeStart + (canShowTime() && view.showTime() ? 1 : 0);
  			int rewardStart = varStart + view.getVisibleVariables().size();
  
  			// The step column
  			if (actionStart <= columnIndex && columnIndex < stepStart) {
  				return "Action";
  			} else if (stepStart <= columnIndex && columnIndex < cumulativeTimeStart) {
  				return "#";
  			} else if (cumulativeTimeStart <= columnIndex && columnIndex < timeStart) {
  				return "Time (+)";
  			} else if (timeStart <= columnIndex && columnIndex < varStart) {
  				return "Time";
  			}
  			// A variable column
  			else if (varStart <= columnIndex && columnIndex < rewardStart) {
  				return ((Variable) view.getVisibleVariables().get(columnIndex - varStart)).toString();
  			}
  
  			else if (rewardStart <= columnIndex) {
  				return ((RewardStructureColumn) view.getVisibleRewardColumns().get(columnIndex - rewardStart)).getColumnName();
  			}
  		}
  		return "Undefined Column";
  	}
  
  	public String getColumnToolTip(int columnIndex)
  	{
  		if (pathActive) {
  			int actionStart = 0;
  			int stepStart = actionStart + (view.showActions() ? 1 : 0);
  			int cumulativeTimeStart = stepStart + (view.showSteps() ? 1 : 0);
  			int timeStart = cumulativeTimeStart + (canShowTime() && view.showCumulativeTime() ? 1 : 0);
  			int varStart = timeStart + (canShowTime() && view.showTime() ? 1 : 0);
  			int rewardStart = varStart + view.getVisibleVariables().size();
  
  			// The step column
  			if (actionStart <= columnIndex && columnIndex < stepStart) {
  				return "Module name or [action] label";
  			} else if (stepStart <= columnIndex && columnIndex < cumulativeTimeStart) {
  				return "Index of state in path";
  			} else if (cumulativeTimeStart <= columnIndex && columnIndex < timeStart) {
  				return "Cumulative time";
  			} else if (timeStart <= columnIndex && columnIndex < varStart) {
  				return "Time spent in state";
  			}
  			// A variable column
  			else if (varStart <= columnIndex && columnIndex < rewardStart) {
  				return "Values of variable \"" + ((Variable) view.getVisibleVariables().get(columnIndex - varStart)).toString() + "\"";
  			}
  
  			else if (rewardStart <= columnIndex) {
  				RewardStructureColumn column = ((RewardStructureColumn) view.getVisibleRewardColumns().get(columnIndex - rewardStart));
  				String rewardName = column.getRewardStructure().getColumnName();
  
  				if (column.isStateReward())
  					return "State reward of reward structure " + rewardName;
  				if (column.isTransitionReward())
  					return "Transition reward of reward structure " + rewardName;
  				if (column.isCumulativeReward())
  					return "Cumulative reward of reward structure " + rewardName;
  			}
  		}
  		return "Undefined Column";
  	}
  
  	public Object getValueAt(int rowIndex, int columnIndex)
  	{
  		if (pathActive) {
  			int actionStart = 0;
  			int stepStart = actionStart + (view.showActions() ? 1 : 0);
  			int cumulativeTimeStart = stepStart + (view.showSteps() ? 1 : 0);
  			int timeStart = cumulativeTimeStart + (canShowTime() && view.showCumulativeTime() ? 1 : 0);
  			int varStart = timeStart + (canShowTime() && view.showTime() ? 1 : 0);
  			int rewardStart = varStart + view.getVisibleVariables().size();
  
  			// The action column
  			if (actionStart <= columnIndex && columnIndex < stepStart) {
  				actionValue = view.new ActionValue(rowIndex == 0 ? "" : path.getModuleOrAction(rowIndex - 1));
  				actionValue.setActionValueUnknown(false);
  				return actionValue;
  			}
  			// The step column
  			else if (stepStart <= columnIndex && columnIndex < cumulativeTimeStart) {
  				return "" + rowIndex;
  			}
  			// Cumulative time column
  			else if (cumulativeTimeStart <= columnIndex && columnIndex < timeStart) {
  				timeValue = view.new TimeValue(path.getCumulativeTime(rowIndex), true);
  				timeValue.setTimeValueUnknown(rowIndex > path.size()); // Never unknown
  				return timeValue;
  			}
  			// Time column
  			else if (timeStart <= columnIndex && columnIndex < varStart) {
  				timeValue = view.new TimeValue(path.getTime(rowIndex), false);
  				timeValue.setTimeValueUnknown(rowIndex >= path.size());
  				return timeValue;
  			}
  			// A variable column
  			else if (varStart <= columnIndex && columnIndex < rewardStart) {
  				Variable var = view.getVisibleVariables().get(columnIndex - varStart);
  				Object result = path.getState(rowIndex).varValues[var.getIndex()];
  				variableValue.setVariable(var);
  				variableValue.setValue(result);
  				variableValue.setChanged(rowIndex == 0 || !path.getState(rowIndex - 1).varValues[var.getIndex()].equals(result));
  				return variableValue;
  			}
  			// A reward column
  			else if (rewardStart <= columnIndex) {
  				RewardStructureColumn rewardColumn = (RewardStructureColumn) view.getVisibleRewardColumns().get(columnIndex - rewardStart);
  				rewardStructureValue.setRewardStructureColumn(rewardColumn);
  				rewardStructureValue.setRewardValueUnknown(false);
  				// A state reward column
  				if (rewardColumn.isStateReward()) {
  					double value = path.getStateReward(rowIndex, rewardColumn.getRewardStructure().getIndex());
  					rewardStructureValue.setChanged(rowIndex == 0
  							|| value != path.getStateReward(rowIndex - 1, rewardColumn.getRewardStructure().getIndex()));
  					rewardStructureValue.setRewardValue(new Double(value));
  					rewardStructureValue.setRewardValueUnknown(rowIndex > path.size()); // Never unknown
  				}
  				// A transition reward column
  				else if (rewardColumn.isTransitionReward()) {
  					double value = path.getTransitionReward(rowIndex, rewardColumn.getRewardStructure().getIndex());
  					rewardStructureValue.setChanged(rowIndex == 0
  							|| value != path.getTransitionReward(rowIndex - 1, rewardColumn.getRewardStructure().getIndex()));
  					rewardStructureValue.setRewardValue(new Double(value));
  					rewardStructureValue.setRewardValueUnknown(rowIndex >= path.size());
  				}
  				// A cumulative reward column
  				else {
  					double value = path.getCumulativeReward(rowIndex, rewardColumn.getRewardStructure().getIndex());
  					rewardStructureValue.setChanged(rowIndex == 0
  							|| value != (path.getCumulativeReward(rowIndex - 1, rewardColumn.getRewardStructure().getIndex())));
  					rewardStructureValue.setRewardValue(new Double(value));
  					rewardStructureValue.setRewardValueUnknown(rowIndex > path.size()); // Never unknown
  				}
  				return rewardStructureValue;
  			}
  		}
  
  		return "Undefined value";
  	}
  
  	/** 
  	 * Method is called when a new path is created.
  	 * The structure of the path may be for a different model etc.
  	 */
  	public void restartPathTable()
  	{
  		view.refreshToDefaultView(pathActive, parsedModel);
  	}
  
  	/** 
  	 * Method is called whenever a path is modified.
  	 */
  	public void updatePathTable()
  	{
  		fireTableDataChanged();
  	}
  
  	public boolean isPathLooping()
  	{
  		return path.isLooping();
  	}
  
  	public int getLoopStart()
  	{
  		return (int) path.loopStart();
  	}
  
  	public int getLoopEnd()
  	{
  		return (int) path.loopEnd();
  	}
  
  	public SimulationView getView()
  	{
  		return view;
  	}
  
  	public void setView(SimulationView view)
  	{
  		this.view.deleteObserver(this);
  		this.view = view;
  		this.view.addObserver(this);
  	}
  }
  