001 package jigcell.compare.cellcycle;
002
003 import EDU.oswego.cs.dl.util.concurrent.Sync;
004 import java.awt.Color;
005 import java.awt.Component;
006 import java.beans.PropertyChangeEvent;
007 import java.util.Iterator;
008 import javax.swing.JTable;
009 import jigcell.compare.IConfigEditor;
010 import jigcell.compare.IDataElement;
011 import jigcell.compare.IDataGenerator;
012 import jigcell.compare.ITypeChecker;
013 import jigcell.compare.data.DataElement;
014 import jigcell.compare.data.EditableDataGenerator;
015 import jigcell.compare.data.FlippedDataElement;
016 import jigcell.compare.data.IDataGeneratorList;
017 import jigcell.compare.data.SparseTreeDataElement;
018 import jigcell.compare.data.type.TimeSeriesTypeChecker;
019 import jigcell.compare.data.type.TypeChecker;
020 import jigcell.compare.impl.Compare;
021 import jigcell.compare.impl.DataGeneratorEvaluator;
022 import jigcell.compare.objective.ChainedDataGeneratorList;
023 import jigcell.compare.objective.IObjective;
024 import jigcell.compare.objective.Objective;
025 import jigcell.compare.plotter.IPlotter;
026 import jigcell.compare.plotter.PlotException;
027 import jigcell.compare.plotter.Plotter;
028 import jigcell.compare.transform.ITransform;
029 import jigcell.compare.transform.ITransformContext;
030 import jigcell.compare.transform.Transform;
031 import jigcell.compare.transform.TransformContext;
032 import jigcell.compare.ui.BasicTable;
033 import jigcell.compare.ui.ConfigEditor;
034 import jigcell.compare.ui.ViewerDialog;
035 import jigcell.compare.views.EvaluationErrorViewer;
036 import jigcell.compare.views.ObjectiveSeriesView;
037
038 /**
039 * An editable display for working with objective data. References model and experiment data in the system.
040 *
041 * <p>
042 * This code is licensed under the DARPA BioCOMP Open Source License. See LICENSE for more details.
043 * </p>
044 *
045 * @author Nicholas Allen
046 */
047
048 public class CellCycleObjectiveView extends ObjectiveSeriesView {
049
050 /**
051 * Error message when generating a report
052 */
053
054 protected final static String MESSAGE_REPORTERROR = "Unable to create report: ";
055
056 /**
057 * Displays errors encountered during evaluation
058 */
059
060 protected EvaluationErrorViewer errorViewer;
061
062 /**
063 * Context for performing transform evaluations
064 */
065
066 protected ITransformContext context;
067
068 /**
069 * Table model for the data in an MEOSummaryView.
070 */
071
072 protected class ObjectiveModel extends ObjectiveSeriesModel {
073
074 /**
075 * Prefix for experimental data
076 */
077
078 protected final static String PREFIX_EXPERIMENT = "Experiment- ";
079
080 /**
081 * Prefix for transform data
082 */
083
084 protected final static String PREFIX_TRANSFORM = "Transform- ";
085
086 /**
087 * Transform value column name
088 */
089
090 public final Marker COLUMN_MODELVALUE = new Marker ("ObjectiveModel.transformValue", "Transform Value");
091
092 /**
093 * Type column name
094 */
095
096 public final Marker COLUMN_TYPE = new Marker ("ObjectiveModel.type", "Value Type");
097
098 /**
099 * Creates a new table model.
100 */
101
102 public ObjectiveModel () {
103 super ();
104 COLUMN_VALUE.setName ("Experiment Value");
105 setColumnMarkers (
106 new Marker [] {
107 COLUMN_NAME, COLUMN_VALUE, COLUMN_MODELVALUE, COLUMN_OBJECTIVE, COLUMN_OBJECTIVEVALUE, COLUMN_COMPARISONTYPE, COLUMN_THRESHOLD,
108 COLUMN_TYPE, COLUMN_COMMENT
109 });
110 setColumnWeights (new double [] {3.0, 2.0, 2.0, 2.0, 0.0, 0.0, 0.0, 1.0, 3.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_MODELVALUE)) {
122 ITransform transform = Transform.getTransformForGenerator ((IDataGenerator) generators.get (row));
123 return transform == null ? VALUE_NOVALUE :
124 transform.isCached () ? DataElement.toString (transform.getElement (), true, textCutoff) : VALUE_NOTEVALUATED;
125 }
126 if (column == findColumn (COLUMN_TYPE))
127 return ((IDataGenerator) generators.get (row)).getAttribute (ITypeChecker.ATTRIBUTE_TYPE);
128 return super.getValueAt (row, column);
129 }
130
131 /**
132 * Starts viewing a cell in the table but does not allow editing.
133 *
134 * @param row Row
135 * @param column Column
136 */
137
138 public void viewCellAt (int row, int column) {
139 if (column == findColumn (COLUMN_VALUE) || column == findColumn (COLUMN_MODELVALUE)) {
140 IDataGenerator generator = (IDataGenerator) generators.get (row);
141 if (generator.getElement ().isScalar ())
142 new ViewerDialog (compare, generator.getName (), row, column, model).setVisible (true);
143 else
144 new CellCycleDataDialog (compare, generator, row, column, model).setVisible (true);
145 return;
146 }
147 super.viewCellAt (row, column);
148 }
149
150 /**
151 * Plots the data contained in a table cell.
152 *
153 * @param row Row
154 * @param column Column
155 */
156
157 protected void plotCellAt (int row, int column) throws PlotException {
158 IDataGenerator experiment = (IDataGenerator) generators.get (row);
159 ITransform transform = Transform.getTransformForGenerator (experiment);
160 if (transform == null || !transform.isCached ())
161 throw new PlotException (MESSAGE_PLOTNODATAERROR);
162 IDataElement experimentElement = FlippedDataElement.flip (experiment.getElement ());
163 long experimentCount = experimentElement.getLength () - 1;
164 if (experimentCount == -1)
165 throw new PlotException (MESSAGE_PLOTNODATAERROR);
166 experimentElement = Plotter.splitMultipleSeries (experimentElement);
167 IDataElement transformElement = FlippedDataElement.flip (transform.getElement ());
168 long transformCount = transformElement.getLength () - 1;
169 if (transformCount == -1)
170 throw new PlotException (MESSAGE_PLOTNODATAERROR);
171 transformElement = Plotter.splitMultipleSeries (transformElement);
172 TimeSeriesTypeChecker type = computeTypeForPlot (experiment);
173 plotter.setData (new SparseTreeDataElement ());
174 plotter.setSeriesName (1, type.getName (1));
175 for (int experimentIndex = 1; experimentIndex <= experimentCount; experimentIndex++) {
176 plotter.setSeries (experimentIndex, experimentElement.getListValue (experimentIndex));
177 plotter.setSeriesName (experimentIndex + 1, PREFIX_EXPERIMENT + type.getName (experimentIndex + 1));
178 }
179 for (int transformIndex = 1; transformIndex <= transformCount; transformIndex++) {
180 plotter.setSeries (experimentCount + transformIndex, transformElement.getListValue (transformIndex));
181 plotter.setSeriesName (experimentCount + transformIndex + 1, PREFIX_TRANSFORM + type.getName (transformIndex + 1));
182 }
183 plotter.setSeriesCombine (IPlotter.Combine.NONE);
184 plotter.setTitle (experiment.getName ());
185 plotter.plot ();
186 }
187 }
188
189 /**
190 * Colors cells when a type error is detected.
191 */
192
193 protected class ObjectiveViewRenderer extends ObjectiveSeriesRenderer {
194
195 /**
196 * Creates a new view renderer.
197 *
198 * @param centerTextLabels Whether to center text labels
199 */
200
201 public ObjectiveViewRenderer (boolean centerTextLabels) {
202 super (centerTextLabels);
203 }
204
205 public Component getTableCellRendererComponent (JTable table, Object value, boolean selected, boolean focus, int row, int column) {
206 Component cell = super.getTableCellRendererComponent (table, value, selected, focus, row, column);
207 IDataGenerator generator = (IDataGenerator) generators.get (row);
208 ITransform transform = Transform.getTransformForGenerator (generator);
209 IObjective objective = Objective.getObjectiveForGenerator (generator);
210 ITypeChecker checker = TypeChecker.getTypeCheckerForGenerator (generator);
211 ITypeChecker transformChecker = transform == null ? null : transform.getOutputTypeChecker ();
212 ITypeChecker objectiveChecker = objective == null ? null : objective.getInputTypeChecker ();
213 if (!TypeChecker.validate (checker, generator) || !TypeChecker.validate (transformChecker, generator) ||
214 !TypeChecker.validate (objectiveChecker, generator) || !TypeChecker.isCompatible (objectiveChecker, checker) ||
215 !TypeChecker.isCompatible (objectiveChecker, transformChecker))
216 cell.setBackground (getColor ("CellCycleObjectiveView.backgroundTypeError"));
217 return cell;
218 }
219
220 /**
221 * {@inheritDoc}
222 */
223
224 public void readConfiguration (String state) {
225 super.readConfiguration (state);
226 putColor ("CellCycleObjectiveView.backgroundTypeError", getColorFromConfig (CellCycleExperimentView.CONFIG_BACKGROUNDTYPEERROR,
227 CellCycleExperimentView.DEFAULT_BACKGROUNDTYPEERROR));
228 }
229 }
230
231 /**
232 * Creates a new table view with the ability to display objective, model, and experiment data.
233 *
234 * @param compare Comparator backend to interface with
235 * @param configMarker Marker for retrieving configuration information from Comparator backend
236 */
237
238 public CellCycleObjectiveView (Compare compare, String configMarker) {
239 super (compare, configMarker);
240 setSelectionGroup (CellCycleExperimentView.SELECTIONGROUP_CELLCYCLE);
241 context = new TransformContext ();
242 if (!compare.addResource (CellCycleTransformView.TRANSFORMCONTEXT_CELLCYCLE, context))
243 context = (ITransformContext) compare.getResource (CellCycleTransformView.TRANSFORMCONTEXT_CELLCYCLE);
244 errorViewer = new EvaluationErrorViewer (compare, this);
245 errorViewer.add (context);
246 }
247
248 /**
249 * {@inheritDoc}
250 */
251
252 public IConfigEditor createConfigEditor () {
253 IConfigEditor editor = new ConfigEditor (compare, this, configMarkers, getConfigForView ());
254 editor.addOption (CONFIG_TABNAME, "Name", String.class);
255 editor.addOption (CONFIG_RECENTSOURCECOUNT, "File History Size", Integer.class);
256 editor.addOption (CONFIG_PLOTTER, "Plotter", Class.class);
257 editor.addOption (CONFIG_TEXTCUTOFF, "Text Display Cutoff", Integer.class);
258 editor.addOption (BasicTable.BasicRenderer.CONFIG_BACKGROUNDEDITABLE, "Editable Cell Background Color", Color.class);
259 editor.addOption (BasicTable.BasicRenderer.CONFIG_BACKGROUNDUNEDITABLE, "Uneditable Cell Background Color", Color.class);
260 editor.addOption (BasicTable.BasicEditor.CONFIG_FOREGROUNDMODIFIED, "Modified Cell Foreground Color", Color.class);
261 editor.addOption (CONFIG_BACKGROUNDUNKNOWNTHRESHOLD, "Not Evaluated Background Color", Color.class);
262 editor.addOption (CONFIG_BACKGROUNDOVERTHRESHOLD, "Flagged Background Color", Color.class);
263 editor.addOption (CellCycleExperimentView.CONFIG_BACKGROUNDTYPEERROR, "Type Error Background Color", Color.class);
264 return editor;
265 }
266
267 /**
268 * Produces a report containing the results of the comparison.
269 */
270
271 public void generateReport () {
272 CellCycleReportGenerator report = new CellCycleReportGenerator (compare, generators);
273 try {
274 if (!report.configure ())
275 return;
276 report.write (generators);
277 } catch (Exception e) {
278 compare.shellHandleException (Compare.MESSAGE_ERROR, MESSAGE_REPORTERROR + e.getMessage (), e);
279 }
280 }
281
282 /**
283 * {@inheritDoc}
284 */
285
286 public IDataGenerator processGenerator (IDataGenerator generator) {
287 IObjective objective = Objective.getObjectiveForGenerator (generator);
288 if (objective == null)
289 return null;
290 Sync sync = objective.getEvaluationLock ();
291 if (!Compare.attemptSync (sync))
292 return null;
293 try {
294 objective.addOption (DataGeneratorEvaluator.OPTION_EVALUATIONNAME, IDataGenerator.Option.COPYONLY);
295 objective.setOption (DataGeneratorEvaluator.OPTION_EVALUATIONNAME, generator.getName ());
296 objective.addOption (DataGeneratorEvaluator.OPTION_EVALUATIONTARGET, IDataGenerator.Option.COPYONLY);
297 objective.setOption (DataGeneratorEvaluator.OPTION_EVALUATIONTARGET, generator);
298 IDataGeneratorList data = new ChainedDataGeneratorList (generators);
299 ITransform transform = Transform.getTransformForGenerator (generator);
300 Sync _sync = transform == null ? null : transform.getEvaluationLock ();
301 if (_sync == null || !Compare.attemptSync (_sync)) {
302 objective.setFunction (generator.getAttribute (IDataGenerator.ATTRIBUTE_GUID));
303 objective.setData (data);
304 return objective;
305 }
306 try {
307 transform.setEvaluationContext (context);
308 objective.setFunction (generator.getAttribute (IDataGenerator.ATTRIBUTE_GUID) + EditableDataGenerator.SEPARATOR_PAIR +
309 transform.getAttribute (IDataGenerator.ATTRIBUTE_GUID));
310 data.add (transform);
311 objective.setData (data);
312 } finally {
313 _sync.release ();
314 }
315 objective.removeEvaluationListener (errorViewer);
316 objective.addEvaluationListener (errorViewer);
317 } finally {
318 sync.release ();
319 }
320 return objective;
321 }
322
323 public void propertyChange (PropertyChangeEvent e) {
324 String name = e.getPropertyName ();
325 if (IDataGenerator.PROPERTY_GENERATOR_EDIT.equals (name)) {
326 boolean clear = false;
327 for (Iterator iterator = generators.iterator (); iterator.hasNext (); ) {
328 IDataGenerator generator = (IDataGenerator) iterator.next ();
329 IObjective objective = Objective.getObjectiveForGenerator (generator);
330 if (objective == null || !objective.isCached ())
331 continue;
332 ITransform transform = Transform.getTransformForGenerator (generator);
333 if (transform == null || !transform.isCached ()) {
334 Sync sync = objective.getEvaluationLock ();
335 try {
336 if (!Compare.attemptSync (sync))
337 continue;
338 objective.clear ();
339 clear = true;
340 } finally {
341 sync.release ();
342 }
343 }
344 }
345 model.fireTableDataChanged ();
346 if (clear)
347 compare.firePropertyChange (IDataGenerator.PROPERTY_GENERATOR_EDIT);
348 return;
349 }
350 super.propertyChange (e);
351 }
352
353 /**
354 * {@inheritDoc}
355 */
356
357 protected void createUI () {
358 createUI (new ObjectiveModel ());
359 }
360
361 /**
362 * @see jigcell.compare.views.BasicTableView#createUI(jigcell.compare.ui.BasicTable.BasicTableModel)
363 */
364
365 protected void createUI (ObjectiveModel model) {
366 super.createUI (model);
367 table.getColumnModel ().getColumn (table.findColumn (model.COLUMN_TYPE)).setCellRenderer (new ObjectiveViewRenderer (false));
368 }
369 }