package kernel.ui; import rescuecore2.score.ScoreFunction; import rescuecore2.score.CompositeScoreFunction; import rescuecore2.score.DelegatingScoreFunction; import rescuecore2.worldmodel.Entity; import rescuecore2.worldmodel.WorldModel; import rescuecore2.config.Config; import rescuecore2.Timestep; import rescuecore2.GUIComponent; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.JList; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.AbstractListModel; import javax.swing.UIManager; import javax.swing.ListCellRenderer; import javax.swing.table.AbstractTableModel; import javax.swing.table.JTableHeader; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import java.util.Set; /** A ScoreFunction that also provides a JTable for viewing the components of the score. */ public class ScoreTable extends DelegatingScoreFunction implements GUIComponent { private ScoreModel model; /** Construct a ScoreTable that wraps a child score function. @param child The child score function. */ public ScoreTable(ScoreFunction child) { super("Score Table", child); } @Override public void initialise(WorldModel<? extends Entity> world, Config config) { super.initialise(world, config); model = new ScoreModel(child); } @Override public double score(WorldModel<? extends Entity> world, Timestep timestep) { model.update(world, timestep); return super.score(world, timestep); } @Override public JComponent getGUIComponent() { JTable table = new JTable(model.table); table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); JScrollPane scroll = new JScrollPane(table); JList rowHeader = new JList(model.list); rowHeader.setFixedCellHeight(table.getRowHeight()); rowHeader.setCellRenderer(new RowHeaderRenderer(table)); rowHeader.setBackground(table.getBackground()); rowHeader.setOpaque(true); scroll.setRowHeaderView(rowHeader); return scroll; } @Override public String getGUIComponentName() { return "Score"; } private static class RowHeaderRenderer extends JLabel implements ListCellRenderer { RowHeaderRenderer(JTable table) { JTableHeader header = table.getTableHeader(); setOpaque(true); setBorder(UIManager.getBorder("TableHeader.cellBorder")); setHorizontalAlignment(LEFT); setForeground(header.getForeground()); setBackground(header.getBackground()); setFont(header.getFont()); } @Override public JLabel getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { setText((value == null) ? "" : value.toString()); return this; } } private static class ScoreModel { ScoreTableModel table; ScoreListModel list; private int steps; private List<ScoreFunctionEntry> entries; ScoreModel(ScoreFunction root) { steps = 0; entries = new ArrayList<ScoreFunctionEntry>(); populateEntries(root, ""); table = new ScoreTableModel(); list = new ScoreListModel(); } private void populateEntries(ScoreFunction root, String prefix) { String suffix = ""; if (!(root instanceof ScoreTable || root instanceof ScoreGraph)) { entries.add(new ScoreFunctionEntry(root, prefix)); suffix = "--"; } if (root instanceof DelegatingScoreFunction) { populateEntries(((DelegatingScoreFunction)root).getChildFunction(), prefix + suffix); } if (root instanceof CompositeScoreFunction) { Set<ScoreFunction> children = ((CompositeScoreFunction)root).getChildFunctions(); for (ScoreFunction next : children) { populateEntries(next, prefix + suffix); } } } void update(WorldModel<? extends Entity> world, Timestep timestep) { for (ScoreFunctionEntry next : entries) { next.update(world, timestep); } steps = timestep.getTime(); table.fireTableStructureChanged(); } private class ScoreTableModel extends AbstractTableModel { @Override public String getColumnName(int col) { return String.valueOf(col + 1); } @Override public int getRowCount() { return entries.size(); } @Override public int getColumnCount() { return steps; } @Override public Object getValueAt(int row, int column) { return entries.get(row).getScore(column + 1); } } private class ScoreListModel extends AbstractListModel { @Override public int getSize() { return entries.size(); } @Override public Object getElementAt(int row) { return entries.get(row).getScoreFunctionName(); } } private static class ScoreFunctionEntry { private ScoreFunction function; private String prefix; private Map<Integer, Double> scores; public ScoreFunctionEntry(ScoreFunction f, String prefix) { function = f; this.prefix = prefix; scores = new HashMap<Integer, Double>(); } public String getScoreFunctionName() { return prefix + function.getName(); } public double getScore(int step) { return scores.containsKey(step) ? scores.get(step) : Double.NaN; } void update(WorldModel<? extends Entity> world, Timestep timestep) { double d = function.score(world, timestep); scores.put(timestep.getTime(), d); } } } }