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    }