001 package jigcell.compare.cellcycle;
002
003 import java.awt.Color;
004 import java.awt.Component;
005 import java.util.ArrayList;
006 import java.util.Collections;
007 import java.util.Iterator;
008 import java.util.List;
009 import javax.swing.JComboBox;
010 import javax.swing.JFileChooser;
011 import javax.swing.JTable;
012 import javax.swing.table.TableColumn;
013 import jigcell.compare.IConfigEditor;
014 import jigcell.compare.IDataElement;
015 import jigcell.compare.IDataGenerator;
016 import jigcell.compare.IEditableDataGenerator;
017 import jigcell.compare.IStructuredTypeChecker;
018 import jigcell.compare.ITypeChecker;
019 import jigcell.compare.data.DataGenerator;
020 import jigcell.compare.data.DataGeneratorList;
021 import jigcell.compare.data.EditableDataGenerator;
022 import jigcell.compare.data.IDataGeneratorList;
023 import jigcell.compare.data.SparseTreeDataElement;
024 import jigcell.compare.data.type.TypeChecker;
025 import jigcell.compare.impl.Compare;
026 import jigcell.compare.impl.Config;
027 import jigcell.compare.impl.SuffixFileFilter;
028 import jigcell.compare.ui.BasicTable;
029 import jigcell.compare.ui.ConfigEditor;
030 import jigcell.compare.ui.InterfaceBuilder;
031 import jigcell.compare.ui.ListComboBoxModel;
032 import jigcell.compare.views.EditableSeriesView;
033 import jigcell.runsupport.Run;
034 import jigcell.runsupport.RunFile;
035
036 /**
037 * An editable display for working with experimental data.
038 *
039 * <p>
040 * This code is licensed under the DARPA BioCOMP Open Source License. See LICENSE for more details.
041 * </p>
042 *
043 * @author Nicholas Allen
044 */
045
046 public class CellCycleExperimentView extends EditableSeriesView {
047
048 /**
049 * Default background color for cells with type errors
050 */
051
052 public final static Color DEFAULT_BACKGROUNDTYPEERROR = new Color (255, 96, 255);
053
054 /**
055 * Property key for the type error background
056 */
057
058 public final static String CONFIG_BACKGROUNDTYPEERROR = "CellCycleExperimentView.backgroundTypeError";
059
060 /**
061 * Property key for generator types
062 */
063
064 public final static String CONFIG_TYPES = "CellCycleExperimentView.types";
065
066 /**
067 * Selection group for the cellcycle components
068 */
069
070 public final static String SELECTIONGROUP_CELLCYCLE = "jigcell_cellcycle";
071
072 /**
073 * Error message when a type checker fails to load
074 */
075
076 protected final static String MESSAGE_TYPEERROR = "Error loading type checker: ";
077
078 /**
079 * List of known types
080 */
081
082 protected List types;
083
084 /**
085 * Display for list of known types
086 */
087
088 protected ListComboBoxModel typeModel;
089
090 /**
091 * Table model for the data in an ExperimentDataSeriesView.
092 */
093
094 protected class ExperimentModel extends EditableSeriesModel {
095
096 /**
097 * Type column
098 */
099
100 public final Marker COLUMN_TYPE = new Marker ("ExperimentModel.type", "Value Type");
101
102 /**
103 * Creates a new table model.
104 */
105
106 public ExperimentModel () {
107 super ();
108 COLUMN_VALUE.setName ("Experiment Value");
109 setColumnMarkers (new Marker [] {COLUMN_NAME, COLUMN_VALUE, COLUMN_TYPE, COLUMN_COMMENT});
110 setColumnWeights (new double [] {1.75, 2.0, 0.75, 2.0});
111 }
112
113 /**
114 * A value in the table.
115 *
116 * @param row Row
117 * @param column Column
118 */
119
120 public Object getValueAt (int row, int column) {
121 if (column == findColumn (COLUMN_TYPE))
122 return ((IDataGenerator) generators.get (row)).getAttribute (ITypeChecker.ATTRIBUTE_TYPE);
123 return super.getValueAt (row, column);
124 }
125
126 /**
127 * Sets a value in the table.
128 *
129 * @param value Value
130 * @param row Row
131 * @param column Column
132 */
133
134 public void setValueAt (Object value, int row, int column) {
135 if (!isCellEditable (row, column))
136 return;
137 if (column == findColumn (COLUMN_TYPE)) {
138 if (value == null)
139 return;
140 String item = value instanceof String ? ((String) value).trim () : value.toString ();
141 if (item != null && item.length () > 0) {
142 int pos = Collections.binarySearch (types, item, TypeChecker.COMPARATOR_NAME);
143 if (pos < 0) {
144 typeModel.insertElementAt (item, -pos - 1);
145 types.add (-pos - 1, item);
146 } else
147 value = types.get (pos);
148 }
149 IEditableDataGenerator generator = (IEditableDataGenerator) generators.get (row);
150 generator.setAttribute (ITypeChecker.ATTRIBUTE_TYPE, item);
151 if (value instanceof ITypeChecker) {
152 generator.addOption (ITypeChecker.OPTION_TYPECHECKER, IDataGenerator.Option.COPYONLY);
153 generator.setOption (ITypeChecker.OPTION_TYPECHECKER, value);
154 if (value instanceof IStructuredTypeChecker && generator.getElement ().getType () == IDataElement.Type.NONE) {
155 IDataElement prototype = ((IStructuredTypeChecker) value).getPrototype ();
156 if (prototype != null)
157 generator.setElement (SparseTreeDataElement.createElement (prototype));
158 }
159 } else
160 generator.setOption (ITypeChecker.OPTION_TYPECHECKER, null);
161 fireTableRowsUpdated (row, row);
162 compare.firePropertyChange (IDataGenerator.PROPERTY_GENERATOR_EDIT);
163 return;
164 }
165 super.setValueAt (value, row, column);
166 }
167 }
168
169 /**
170 * Colors cells when a type error is detected.
171 */
172
173 protected class ExperimentViewRenderer extends BasicTable.BasicRenderer {
174
175 /**
176 * Creates a new view renderer.
177 */
178
179 public ExperimentViewRenderer () {
180 super (CellCycleExperimentView.this);
181 }
182
183 public Component getTableCellRendererComponent (JTable table, Object value, boolean selected, boolean focus, int row, int column) {
184 Component cell = super.getTableCellRendererComponent (table, value, selected, focus, row, column);
185 IDataGenerator generator = (IDataGenerator) generators.get (row);
186 if (!TypeChecker.validate (TypeChecker.getTypeCheckerForGenerator (generator), generator))
187 cell.setBackground (getColor ("CellCycleExperimentView.backgroundTypeError"));
188 return cell;
189 }
190
191 /**
192 * {@inheritDoc}
193 */
194
195 public void readConfiguration (String state) {
196 super.readConfiguration (state);
197 putColor ("CellCycleExperimentView.backgroundTypeError", getColorFromConfig (CONFIG_BACKGROUNDTYPEERROR,
198 DEFAULT_BACKGROUNDTYPEERROR));
199 }
200 }
201
202 /**
203 * Creates a new table view with the ability to display experiment data.
204 *
205 * @param compare Comparator backend to interface with
206 * @param configMarker Marker for retrieving configuration information from Comparator backend
207 */
208
209 public CellCycleExperimentView (Compare compare, String configMarker) {
210 super (compare, configMarker);
211 setSelectionGroup (SELECTIONGROUP_CELLCYCLE);
212 }
213
214 /**
215 * {@inheritDoc}
216 */
217
218 public IConfigEditor createConfigEditor () {
219 IConfigEditor editor = new ConfigEditor (compare, this, configMarkers, getConfigForView ());
220 editor.addOption (CONFIG_TABNAME, "Name", String.class);
221 editor.addOption (CONFIG_RECENTSOURCECOUNT, "File History Size", Integer.class);
222 editor.addOption (CONFIG_PLOTTER, "Plotter", Class.class);
223 editor.addOption (CONFIG_TEXTCUTOFF, "Text Display Cutoff", Integer.class);
224 editor.addOption (BasicTable.BasicRenderer.CONFIG_BACKGROUNDEDITABLE, "Editable Cell Background Color", Color.class);
225 editor.addOption (BasicTable.BasicRenderer.CONFIG_BACKGROUNDUNEDITABLE, "Uneditable Cell Background Color", Color.class);
226 editor.addOption (BasicTable.BasicEditor.CONFIG_FOREGROUNDMODIFIED, "Modified Cell Foreground Color", Color.class);
227 editor.addOption (CONFIG_TYPES, "Type Checkers", Class [].class);
228 editor.addOption (CONFIG_BACKGROUNDTYPEERROR, "Type Error Background Color", Color.class);
229 return editor;
230 }
231
232 /**
233 * Prompts the user for a run file on their disk and then creates new experiments for each run in the run file.
234 */
235
236 public void importNamesFromRunFile () {
237 JFileChooser chooser = InterfaceBuilder.createFileChooser (new SuffixFileFilter (".run", "Run File"));
238 if (chooser.showOpenDialog (this) != JFileChooser.APPROVE_OPTION)
239 return;
240 try {
241 importNamesFromRunFile (RunFile.readFile (chooser.getSelectedFile ().getAbsolutePath ()));
242 } catch (Exception e) {
243 compare.shellHandleException (Compare.MESSAGE_WARNING, "Unable to import from run file: " + e.getMessage (), e);
244 }
245 }
246
247 /**
248 * Creates new experiments for each run in the run file. The names of the experiments come from the names of the runs.
249 *
250 * @param runFile Run file
251 */
252
253 public void importNamesFromRunFile (RunFile runFile) {
254 if (runFile == null)
255 throw new IllegalArgumentException ();
256 IDataGeneratorList runs = new DataGeneratorList ();
257 for (Iterator iterator = runFile.getRuns ().iterator (); iterator.hasNext (); ) {
258 Run run = (Run) iterator.next ();
259 if (run == null)
260 continue;
261 String name = run.getName ();
262 DataGenerator generator = new EditableDataGenerator ();
263 try {
264 generator.setName (name);
265 runs.add (generator);
266 } catch (RuntimeException e) {
267 compare.shellHandleException (Compare.MESSAGE_WARNING, "Unable to create a new experiment named " + name, e);
268 }
269 }
270 generators.addAll (runs);
271 model.fireTableDataChanged ();
272 compare.firePropertyChange (IDataGenerator.RESOURCE_GENERATORS);
273 }
274
275 /**
276 * {@inheritDoc}
277 */
278
279 public void readConfiguration (String state) {
280 super.readConfiguration (state);
281 if (state != STATE_INITIALIZE)
282 return;
283 types = new ArrayList ();
284 Class parameter [] = new Class [0];
285 Object argument [] = new Object [0];
286 for (Iterator iterator = Config.convertToList (compare.getConfig ().findValue (configMarkers, CONFIG_TYPES)).iterator ();
287 iterator.hasNext (); ) {
288 try {
289 types.add (Class.forName ((String) iterator.next ()).getMethod (ITypeChecker.METHOD_GENERATE, parameter).invoke (null, argument));
290 } catch (Exception e) {
291 compare.shellHandleException (Compare.MESSAGE_WARNING, MESSAGE_TYPEERROR + e.getMessage (), e);
292 }
293 }
294 Collections.sort (types, TypeChecker.COMPARATOR_NAME);
295 }
296
297 /**
298 * {@inheritDoc}
299 */
300
301 protected void createUI () {
302 createUI (new ExperimentModel ());
303 }
304
305 /**
306 * @see jigcell.compare.views.BasicTableView#createUI(jigcell.compare.ui.BasicTable.BasicTableModel)
307 */
308
309 protected void createUI (ExperimentModel model) {
310 super.createUI (model);
311 typeModel = new ListComboBoxModel (types);
312 JComboBox box = new JComboBox (typeModel);
313 box.setEditable (true);
314 TableColumn columnType = table.getColumnModel ().getColumn (table.findColumn (model.COLUMN_TYPE));
315 columnType.setCellEditor (new BasicTable.BasicEditor (this, box));
316 columnType.setCellRenderer (new ExperimentViewRenderer ());
317 }
318
319 /**
320 * {@inheritDoc}
321 */
322
323 protected void readInternal (Object readResult) throws Exception {
324 super.readInternal (readResult);
325 int pos;
326 Object checker;
327 for (Iterator iterator = generators.iterator (); iterator.hasNext (); ) {
328 IDataGenerator generator = (IEditableDataGenerator) iterator.next ();
329 String type = generator.getAttribute (ITypeChecker.ATTRIBUTE_TYPE);
330 if (type == null)
331 continue;
332 pos = Collections.binarySearch (types, type, TypeChecker.COMPARATOR_NAME);
333 if (pos < 0) {
334 typeModel.insertElementAt (type, -pos - 1);
335 types.add (-pos - 1, type);
336 continue;
337 }
338 checker = types.get (pos);
339 if (checker instanceof ITypeChecker) {
340 generator.addOption (ITypeChecker.OPTION_TYPECHECKER, IDataGenerator.Option.COPYONLY);
341 generator.setOption (ITypeChecker.OPTION_TYPECHECKER, checker);
342 }
343 }
344 }
345 }