001 package jigcell.compare.views;
002
003 import EDU.oswego.cs.dl.util.concurrent.Sync;
004 import java.awt.Color;
005 import java.awt.Component;
006 import java.beans.DefaultPersistenceDelegate;
007 import java.beans.Encoder;
008 import java.beans.Expression;
009 import java.beans.PropertyChangeEvent;
010 import java.text.DecimalFormat;
011 import java.util.ArrayList;
012 import java.util.Arrays;
013 import java.util.HashSet;
014 import java.util.Iterator;
015 import java.util.List;
016 import java.util.Map;
017 import javax.swing.JComboBox;
018 import javax.swing.JFileChooser;
019 import javax.swing.JLabel;
020 import javax.swing.JTable;
021 import javax.swing.ListCellRenderer;
022 import javax.swing.SwingConstants;
023 import javax.swing.filechooser.FileFilter;
024 import javax.swing.table.TableColumn;
025 import javax.swing.table.TableColumnModel;
026 import jigcell.compare.IConfigEditor;
027 import jigcell.compare.IDataGenerator;
028 import jigcell.compare.IEditableDataGenerator;
029 import jigcell.compare.IMultipleDataSource;
030 import jigcell.compare.IWriteableDataSource;
031 import jigcell.compare.data.DataElement;
032 import jigcell.compare.data.DataGenerator;
033 import jigcell.compare.data.DataGeneratorList;
034 import jigcell.compare.data.DataGeneratorMap;
035 import jigcell.compare.data.IDataGeneratorCollection;
036 import jigcell.compare.data.IDataGeneratorList;
037 import jigcell.compare.data.IDataGeneratorMap;
038 import jigcell.compare.data.IResultSetMap;
039 import jigcell.compare.data.ResultSetMap;
040 import jigcell.compare.data.SparseTreeDataElement;
041 import jigcell.compare.impl.Compare;
042 import jigcell.compare.impl.DataGeneratorEvaluator;
043 import jigcell.compare.impl.FileDataSource;
044 import jigcell.compare.impl.SuffixFileFilter;
045 import jigcell.compare.objective.IObjective;
046 import jigcell.compare.objective.Objective;
047 import jigcell.compare.ui.BasicTable;
048 import jigcell.compare.ui.ConfigEditor;
049 import jigcell.compare.ui.IResultTab;
050 import jigcell.compare.ui.InterfaceBuilder;
051 import jigcell.compare.ui.ListComboBoxModel;
052 import jigcell.compare.ui.ProgressMonitor;
053
054 /**
055 * An editable display for working with objective data.
056 *
057 * <p>
058 * This code is licensed under the DARPA BioCOMP Open Source License. See LICENSE for more details.
059 * </p>
060 *
061 * @author Nicholas Allen
062 */
063
064 public class ObjectiveSeriesView extends EditableSeriesView implements IResultTab, DataGeneratorEvaluator.IProcessor {
065
066 /**
067 * Default background color for cells over the threshold value
068 */
069
070 public final static Color DEFAULT_BACKGROUNDOVERTHRESHOLD = new Color (255, 96, 96);
071
072 /**
073 * Default background color for cells that cannot be compared to the threshold value
074 */
075
076 public final static Color DEFAULT_BACKGROUNDUNKNOWNTHRESHOLD = new Color (255, 192, 0);
077
078 /**
079 * Attribute key for the comparison type
080 */
081
082 public final static String ATTRIBUTE_COMPARISONTYPE = "ObjectiveSeriesView.comparisonType";
083
084 /**
085 * Attribute key for the objective threshold
086 */
087
088 public final static String ATTRIBUTE_THRESHOLD = "ObjectiveSeriesView.threshold";
089
090 /**
091 * Property key for the threshold exceeded background
092 */
093
094 public final static String CONFIG_BACKGROUNDOVERTHRESHOLD = "ObjectiveSeriesView.backgroundOverThreshold";
095
096 /**
097 * Property key for the threshold not comparable background
098 */
099
100 public final static String CONFIG_BACKGROUNDUNKNOWNTHRESHOLD = "ObjectiveSeriesView.backgroundUnknownThreshold";
101
102 /**
103 * File filter for objective result set files
104 */
105
106 public final static FileFilter FILTER_OBJECTIVERESULT = new SuffixFileFilter (".res", "Objective Result Set (*.res)");
107
108 /**
109 * File filter for objective files
110 */
111
112 public final static FileFilter FILTER_OBJECTIVESET = new SuffixFileFilter (".obs", "Objective Set (*.obs)");
113
114 /**
115 * Display value for an unevaluated value
116 */
117
118 protected final static String VALUE_NOTEVALUATED = "Not Evaluated";
119
120 /**
121 * Display value for a non-existent value
122 */
123
124 protected final static String VALUE_NOVALUE = "No Value";
125
126 /**
127 * List of known objectives
128 */
129
130 protected IDataGeneratorList objectives;
131
132 /**
133 * Display for list of known objectives
134 */
135
136 protected ListComboBoxModel objectiveModel;
137
138 /**
139 * Data model of the generators
140 */
141
142 protected ObjectiveSeriesModel model;
143
144 /**
145 * Comparison type for threshold flagging.
146 */
147
148 public final static class Comparison {
149
150 /**
151 * Accept equivalent values only
152 */
153
154 public final static Comparison EQUIVALENT = new Comparison ("=");
155
156 /**
157 * Accept values greater than the threshold
158 */
159
160 public final static Comparison GREATERTHAN = new Comparison (">");
161
162 /**
163 * Accept values greater than or equal to the threshold
164 */
165
166 public final static Comparison GREATERTHANEQUAL = new Comparison (">=");
167
168 /**
169 * Ignore the value of this comparison
170 */
171
172 public final static Comparison IGNORE = new Comparison ("Ignore");
173
174 /**
175 * Accept values less than the threshold
176 */
177
178 public final static Comparison LESSTHAN = new Comparison ("<");
179
180 /**
181 * Accept values less than or equal to the threshold
182 */
183
184 public final static Comparison LESSTHANEQUAL = new Comparison ("<=");
185
186 /**
187 * List of available types
188 */
189
190 private final static List types = new ArrayList ();
191
192 /**
193 * Name of this type
194 */
195
196 private final String name;
197
198 /**
199 * Looks up the singleton value instead of creating a new instance.
200 */
201
202 protected static class ComparisonDelegate extends DefaultPersistenceDelegate {
203 protected Expression instantiate (Object o, Encoder encoder) {
204 return new Expression (o, Comparison.class, "findInstance", new Object [] {((Comparison) o).toString ()});
205 }
206 }
207
208 static {
209 types.add (EQUIVALENT);
210 types.add (LESSTHAN);
211 types.add (LESSTHANEQUAL);
212 types.add (GREATERTHAN);
213 types.add (GREATERTHANEQUAL);
214 types.add (IGNORE);
215 Compare.addDelegate (Comparison.class, new ComparisonDelegate ());
216 }
217
218 /**
219 * Finds an instance of this class based on a given name.
220 *
221 * @param name Name
222 */
223
224 public static Comparison findInstance (String name) {
225 if (name == null || IGNORE.name.equals (name))
226 return IGNORE;
227 if (GREATERTHAN.name.equals (name))
228 return GREATERTHAN;
229 if (GREATERTHANEQUAL.name.equals (name))
230 return GREATERTHANEQUAL;
231 if (LESSTHAN.name.equals (name))
232 return LESSTHAN;
233 if (LESSTHANEQUAL.name.equals (name))
234 return LESSTHANEQUAL;
235 return EQUIVALENT;
236 }
237
238 /**
239 * List of possible types.
240 */
241
242 public static List getTypes () {
243 return types;
244 }
245
246 /**
247 * Tests whether the value is acceptable for this comparison.
248 *
249 * @param value Value
250 * @param threshold Threshold
251 */
252
253 public boolean testAcceptable (double value, double threshold) {
254 if (this == IGNORE)
255 return true;
256 if (this == LESSTHAN)
257 return value < threshold;
258 if (this == LESSTHANEQUAL)
259 return value <= threshold;
260 if (this == GREATERTHAN)
261 return value > threshold;
262 if (this == GREATERTHANEQUAL)
263 return value >= threshold;
264 return value == 0.0;
265 }
266
267 /**
268 * Whether the second value is better than the first value for this comparison.
269 *
270 * @param value1 First value
271 * @param value2 Second value
272 */
273
274 public boolean testImproving (double value1, double value2) {
275 if (this == IGNORE)
276 return false;
277 if (this == GREATERTHAN || this == GREATERTHANEQUAL)
278 return value2 > value1;
279 return value2 < value1;
280 }
281
282 public String toString () {
283 return name;
284 }
285
286 /**
287 * Creates a new type of objective comparison.
288 *
289 * @param name Display name
290 */
291
292 private Comparison (String name) {
293 this.name = name;
294 }
295 }
296
297 /**
298 * Table model for an ObjectiveSummaryView.
299 */
300
301 protected class ObjectiveSeriesModel extends EditableSeriesModel {
302
303 /**
304 * Prompt to choose an objective
305 */
306
307 protected final static String MESSAGE_CHOOSERCOMPARISON = "Choose a Comparison Type";
308
309 /**
310 * Prompt to choose an objective
311 */
312
313 protected final static String MESSAGE_CHOOSEROBJECTIVE = "Choose an Objective";
314
315 /**
316 * Threshold display for equivalent values
317 */
318
319 protected final static String THRESHOLD_EQUIVALENT = "Equivalent";
320
321 /**
322 * Comparison type column
323 */
324
325 public final Marker COLUMN_COMPARISONTYPE = new Marker ("ObjectiveSeriesModel.comparisonType", "Criterion");
326
327 /**
328 * Objective column
329 */
330
331 public final Marker COLUMN_OBJECTIVE = new Marker ("ObjectiveSeriesModel.objective", "Objective");
332
333 /**
334 * Objective value column
335 */
336
337 public final Marker COLUMN_OBJECTIVEVALUE = new Marker ("ObjectiveSeriesModel.objectiveValue", "Objective Result");
338
339 /**
340 * Threshold column
341 */
342
343 public final Marker COLUMN_THRESHOLD = new Marker ("ObjectiveSeriesModel.threshold", "Acceptable");
344
345 /**
346 * Formatter for objective result
347 */
348
349 protected DecimalFormat objectiveResultFormat;
350
351 /**
352 * Creates a new table model.
353 */
354
355 public ObjectiveSeriesModel () {
356 super ();
357 setColumnMarkers (
358 new Marker [] {COLUMN_NAME, COLUMN_OBJECTIVE, COLUMN_OBJECTIVEVALUE, COLUMN_COMPARISONTYPE, COLUMN_THRESHOLD, COLUMN_COMMENT});
359 setColumnWeights (new double [] {3.0, 2.0, 0.0, 0.0, 0.0, 3.0});
360 objectiveResultFormat = new DecimalFormat ("##0.0####E0");
361 objectiveResultFormat.setDecimalSeparatorAlwaysShown (true);
362 }
363
364 /**
365 * Starts editing a cell in the table.
366 *
367 * @param row Row
368 * @param column Column
369 */
370
371 public void editCellAt (int row, int column) {
372 if (!isCellEditable (row, column))
373 return;
374 if (column == findColumn (COLUMN_OBJECTIVE)) {
375 setValueAt (manager.showInputDialog (MESSAGE_CHOOSEROBJECTIVE, objectives.toArray (), getValueAt (row, column), "Set"), row,
376 column);
377 return;
378 }
379 if (column == findColumn (COLUMN_COMPARISONTYPE)) {
380 setValueAt (manager.showInputDialog (MESSAGE_CHOOSERCOMPARISON, Comparison.getTypes ().toArray (new Comparison [0]),
381 getValueAt (row, column), "Set"), row, column);
382 return;
383 }
384 super.editCellAt (row, column);
385 }
386
387 /**
388 * Fills along a column.
389 *
390 * @param row Row
391 * @param column Column
392 * @param startRow First row
393 * @param endRow Last row
394 */
395
396 public void fillRangeAt (int row, int column, int startRow, int endRow) {
397 if (column == findColumn (COLUMN_OBJECTIVE)) {
398 fillRange (column, startRow, endRow, getObjectiveForGenerator (row));
399 return;
400 }
401 super.fillRangeAt (row, column, startRow, endRow);
402 }
403
404 /**
405 * A value in the table.
406 *
407 * @param row Row
408 * @param column Column
409 */
410
411 public Object getValueAt (int row, int column) {
412 if (column == findColumn (COLUMN_OBJECTIVEVALUE)) {
413 IObjective objective = getObjectiveForGenerator (row);
414 return objective == null ? VALUE_NOVALUE :
415 objective.isCached () ? objectiveResultFormat.format (objective.getElement ().getRealValue ()) : VALUE_NOTEVALUATED;
416 }
417 if (column == findColumn (COLUMN_OBJECTIVE)) {
418 IObjective objective = getObjectiveForGenerator (row);
419 return objective == null ? (Object) VALUE_NOVALUE : objective;
420 }
421 if (column == findColumn (COLUMN_THRESHOLD)) {
422 IObjective objective = getObjectiveForGenerator (row);
423 if (objective == null)
424 return null;
425 Comparison type = Comparison.findInstance (objective.getAttribute (ATTRIBUTE_COMPARISONTYPE));
426 if (type == Comparison.IGNORE)
427 return null;
428 if (type == Comparison.EQUIVALENT)
429 return THRESHOLD_EQUIVALENT;
430 String value = objective.getAttribute (ATTRIBUTE_THRESHOLD);
431 return value == null ? VALUE_NOVALUE : value;
432 }
433 if (column == findColumn (COLUMN_COMPARISONTYPE)) {
434 IObjective objective = getObjectiveForGenerator (row);
435 if (objective == null)
436 return null;
437 return Comparison.findInstance (objective.getAttribute (ATTRIBUTE_COMPARISONTYPE));
438 }
439 return super.getValueAt (row, column);
440 }
441
442 /**
443 * Whether a value in the table is editable.
444 *
445 * @param row Row
446 * @param column Column
447 */
448
449 public boolean isCellEditable (int row, int column) {
450 if (row < 0 || row >= getRowCount ())
451 return false;
452 IDataGenerator generator = (IDataGenerator) generators.get (row);
453 if (!(generator instanceof IEditableDataGenerator))
454 return false;
455 if (column == findColumn (COLUMN_OBJECTIVE) || column == findColumn (COLUMN_COMMENT))
456 return true;
457 IObjective objective = Objective.getObjectiveForGenerator (generator);
458 if (objective == null)
459 return false;
460 if (column == findColumn (COLUMN_COMPARISONTYPE))
461 return true;
462 if (column == findColumn (COLUMN_THRESHOLD)) {
463 Comparison type = Comparison.findInstance (objective.getAttribute (ATTRIBUTE_COMPARISONTYPE));
464 return type != Comparison.IGNORE && type != Comparison.EQUIVALENT;
465 }
466 return false;
467 }
468
469 /**
470 * Sets a value in the table.
471 *
472 * @param value Value
473 * @param row Row
474 * @param column Column
475 */
476
477 public void setValueAt (Object value, int row, int column) {
478 if (!isCellEditable (row, column))
479 return;
480 if (column == findColumn (COLUMN_OBJECTIVE)) {
481 if (!(value instanceof IObjective) || value == null)
482 return;
483 IEditableDataGenerator generator = (IEditableDataGenerator) generators.get (row);
484 IObjective objective = (IObjective) ((IObjective) value).copy (true);
485 objective.setCopyName (true);
486 generator.addOption (IObjective.OPTION_OBJECTIVE, IDataGenerator.Option.COPYONLY);
487 generator.setOption (IObjective.OPTION_OBJECTIVE, objective);
488 model.fireTableDataChanged ();
489 return;
490 }
491 if (column == findColumn (COLUMN_THRESHOLD)) {
492 if (!(value instanceof String) || value == null)
493 return;
494 IObjective objective = getObjectiveForGenerator (row);
495 if (objective == null)
496 return;
497 String item = ((String) value).trim ();
498 if (item.length () == 0 || DataElement.isSpecialNonNumeric (item))
499 objective.setAttribute (ATTRIBUTE_THRESHOLD, null);
500 else
501 try {
502 objective.setAttribute (ATTRIBUTE_THRESHOLD, String.valueOf (Double.parseDouble (item)));
503 } catch (Exception e) {
504 objective.setAttribute (ATTRIBUTE_THRESHOLD, null);
505 Compare.assertion ("Unable to parse threshold value.", e);
506 }
507 model.fireTableDataChanged ();
508 return;
509 }
510 if (column == findColumn (COLUMN_COMPARISONTYPE)) {
511 if (!(value instanceof Comparison) || value == null)
512 return;
513 IObjective objective = getObjectiveForGenerator (row);
514 if (objective == null)
515 return;
516 objective.setAttribute (ATTRIBUTE_COMPARISONTYPE, value.toString ());
517 model.fireTableDataChanged ();
518 return;
519 }
520 super.setValueAt (value, row, column);
521 }
522 }
523
524 /**
525 * Cell renderer for a ObjectiveSummaryView.
526 */
527
528 protected class ObjectiveSeriesRenderer extends BasicTable.BasicRenderer {
529
530 /**
531 * Whether to center a text label
532 */
533
534 protected boolean centerTextLabels;
535
536 /**
537 * Creates a new view renderer.
538 *
539 * @param centerTextLabels Whether to center text labels
540 */
541
542 public ObjectiveSeriesRenderer (boolean centerTextLabels) {
543 super (ObjectiveSeriesView.this);
544 this.centerTextLabels = centerTextLabels;
545 }
546
547 public Component getTableCellRendererComponent (JTable table, Object value, boolean selected, boolean focus, int row, int column) {
548 Component cell = super.getTableCellRendererComponent (table, value, selected, focus, row, column);
549 if (centerTextLabels) {
550 JLabel label = null;
551 if (cell instanceof JLabel)
552 label = (JLabel) cell;
553 else if (cell instanceof JComboBox) {
554 ListCellRenderer renderer = ((JComboBox) cell).getRenderer ();
555 if (renderer instanceof JLabel)
556 label = (JLabel) renderer;
557 }
558 if (label != null)
559 label.setHorizontalAlignment (SwingConstants.CENTER);
560 }
561 if (selected || model.getValueAt (row, model.findColumn (model.COLUMN_OBJECTIVE)) == VALUE_NOVALUE)
562 return cell;
563 int valueColumn = model.findColumn (model.COLUMN_OBJECTIVEVALUE);
564 int viewValueColumn = table.convertColumnIndexToView (valueColumn);
565 try {
566 String entry = (String) model.getValueAt (row, valueColumn);
567 if (entry == VALUE_NOVALUE || entry == VALUE_NOTEVALUATED) {
568 if (column == viewValueColumn)
569 cell.setBackground (getColor ("ObjectiveSeriesView.backgroundUnknownThreshold"));
570 return cell;
571 }
572 Comparison type = (Comparison) model.getValueAt (row, model.findColumn (model.COLUMN_COMPARISONTYPE));
573 if (type == Comparison.IGNORE)
574 return cell;
575 double objective = Double.parseDouble (entry);
576 if (Double.isNaN (objective) || Double.isInfinite (objective)) {
577 cell.setBackground (getColor ("ObjectiveSeriesView.backgroundOverThreshold"));
578 return cell;
579 }
580 if (!type.testAcceptable (objective, type == Comparison.EQUIVALENT ? 0.0 :
581 SparseTreeDataElement.createElementSafe ((String) model.getValueAt (row,
582 model.findColumn (model.COLUMN_THRESHOLD))).getRealValue ()))
583 cell.setBackground (getColor ("ObjectiveSeriesView.backgroundOverThreshold"));
584 return cell;
585 } catch (Exception e) {
586 if (column == viewValueColumn)
587 cell.setBackground (getColor ("ObjectiveSeriesView.backgroundUnknownThreshold"));
588 Compare.assertion ("Unable to compute cell color.", e);
589 }
590 return cell;
591 }
592
593 /**
594 * {@inheritDoc}
595 */
596
597 public void readConfiguration (String state) {
598 super.readConfiguration (state);
599 putColor ("ObjectiveSeriesView.backgroundOverThreshold", getColorFromConfig (CONFIG_BACKGROUNDOVERTHRESHOLD,
600 DEFAULT_BACKGROUNDOVERTHRESHOLD));
601 putColor ("ObjectiveSeriesView.backgroundUnknownThreshold", getColorFromConfig (CONFIG_BACKGROUNDUNKNOWNTHRESHOLD,
602 DEFAULT_BACKGROUNDUNKNOWNTHRESHOLD));
603 }
604 }
605
606 /**
607 * Clears an objective.
608 *
609 * @param objective Objective
610 */
611
612 protected static void clearObjective (IObjective objective) {
613 if (objective == null)
614 return;
615 Sync sync = objective.getEvaluationLock ();
616 if (!Compare.attemptSync (sync))
617 return;
618 try {
619 objective.clear ();
620 } finally {
621 sync.release ();
622 }
623 }
624
625 /**
626 * Creates a new table view with the ability to display experimental and objective data.
627 *
628 * @param compare Comparator backend to interface with
629 * @param configMarker Marker for retrieving configuration information from Comparator backend
630 */
631
632 public ObjectiveSeriesView (Compare compare, String configMarker) {
633 super (compare, configMarker);
634 }
635
636 /**
637 * {@inheritDoc}
638 */
639
640 public void addRow () {
641 throw new UnsupportedOperationException ();
642 }
643
644 /**
645 * {@inheritDoc}
646 */
647
648 public void addRows (int count) {
649 throw new UnsupportedOperationException ();
650 }
651
652 /**
653 * {@inheritDoc}
654 */
655
656 public void clear () {
657 for (Iterator iterator = generators.iterator (); iterator.hasNext (); )
658 ((IEditableDataGenerator) iterator.next ()).setOption (IObjective.OPTION_OBJECTIVE, null);
659 List list = (List) compare.getResourceMap (Objective.RESOURCE_OBJECTIVES).get (this);
660 if (list != null)
661 list.clear ();
662 model.fireTableDataChanged ();
663 compare.firePropertyChange (IDataGenerator.PROPERTY_GENERATOR_EDIT);
664 compare.firePropertyChange (Objective.RESOURCE_OBJECTIVES);
665 }
666
667 /**
668 * {@inheritDoc}
669 */
670
671 public void clipboardCut () {
672 throw new UnsupportedOperationException ();
673 }
674
675 /**
676 * {@inheritDoc}
677 */
678
679 public void clipboardPaste () {
680 throw new UnsupportedOperationException ();
681 }
682
683 /**
684 * {@inheritDoc}
685 */
686
687 public IConfigEditor createConfigEditor () {
688 IConfigEditor editor = new ConfigEditor (compare, this, configMarkers, getConfigForView ());
689 editor.addOption (CONFIG_TABNAME, "Name", String.class);
690 editor.addOption (CONFIG_RECENTSOURCECOUNT, "File History Size", Integer.class);
691 editor.addOption (CONFIG_PLOTTER, "Plotter", Class.class);
692 editor.addOption (CONFIG_TEXTCUTOFF, "Text Display Cutoff", Integer.class);
693 editor.addOption (BasicTable.BasicRenderer.CONFIG_BACKGROUNDEDITABLE, "Editable Cell Background Color", Color.class);
694 editor.addOption (BasicTable.BasicRenderer.CONFIG_BACKGROUNDUNEDITABLE, "Uneditable Cell Background Color", Color.class);
695 editor.addOption (BasicTable.BasicEditor.CONFIG_FOREGROUNDMODIFIED, "Modified Cell Foreground Color", Color.class);
696 editor.addOption (CONFIG_BACKGROUNDUNKNOWNTHRESHOLD, "Not Evaluated Background Color", Color.class);
697 editor.addOption (CONFIG_BACKGROUNDOVERTHRESHOLD, "Flagged Background Color", Color.class);
698 return editor;
699 }
700
701 /**
702 * {@inheritDoc}
703 */
704
705 public void deleteRow (int row) {
706 ((IEditableDataGenerator) generators.get (row)).setOption (IObjective.OPTION_OBJECTIVE, null);
707 model.fireTableDataChanged ();
708 compare.firePropertyChange (IDataGenerator.PROPERTY_GENERATOR_EDIT);
709 }
710
711 /**
712 * {@inheritDoc}
713 */
714
715 public void deleteRows (int rows []) {
716 if (rows.length == 0)
717 return;
718 Arrays.sort (rows);
719 for (int i = rows.length - 1; i >= 0; i--)
720 ((IEditableDataGenerator) generators.get (rows [i])).setOption (IObjective.OPTION_OBJECTIVE, null);
721 model.fireTableDataChanged ();
722 compare.firePropertyChange (IDataGenerator.PROPERTY_GENERATOR_EDIT);
723 }
724
725 /**
726 * {@inheritDoc}
727 */
728
729 public void evaluate () {
730 evaluate (generators.toArray ());
731 }
732
733 /**
734 * {@inheritDoc}
735 */
736
737 public void evaluate (int rows []) {
738 int l = rows.length;
739 IDataGenerator tasks [] = new IDataGenerator [l];
740 for (int i = 0; i < l; i++)
741 tasks [i] = (IDataGenerator) generators.get (rows [i]);
742 evaluate (tasks);
743 }
744
745 /**
746 * {@inheritDoc}
747 */
748
749 public void insertRow (int row) {
750 throw new UnsupportedOperationException ();
751 }
752
753 /**
754 * {@inheritDoc}
755 */
756
757 public void insertRows (int row, int count) {
758 throw new UnsupportedOperationException ();
759 }
760
761 /**
762 * {@inheritDoc}
763 */
764
765 public void moveRowsDown (int rows []) {
766 throw new UnsupportedOperationException ();
767 }
768
769 /**
770 * {@inheritDoc}
771 */
772
773 public void moveRowsUp (int rows []) {
774 throw new UnsupportedOperationException ();
775 }
776
777 /**
778 * {@inheritDoc}
779 */
780
781 public IDataGenerator processGenerator (IDataGenerator generator) {
782 IObjective objective = Objective.getObjectiveForGenerator (generator);
783 if (objective == null)
784 return null;
785 Sync sync = objective.getEvaluationLock ();
786 if (!Compare.attemptSync (sync))
787 return null;
788 try {
789 objective.setFunction (generator.getAttribute (IDataGenerator.ATTRIBUTE_GUID));
790 objective.setData (generators);
791 objective.addOption (DataGeneratorEvaluator.OPTION_EVALUATIONNAME, IDataGenerator.Option.COPYONLY);
792 objective.setOption (DataGeneratorEvaluator.OPTION_EVALUATIONNAME, generator.getName ());
793 objective.addOption (DataGeneratorEvaluator.OPTION_EVALUATIONTARGET, IDataGenerator.Option.COPYONLY);
794 objective.setOption (DataGeneratorEvaluator.OPTION_EVALUATIONTARGET, generator);
795 } finally {
796 sync.release ();
797 }
798 return objective;
799 }
800
801 public void propertyChange (PropertyChangeEvent e) {
802 String name = e.getPropertyName ();
803 if (IDataGenerator.RESOURCE_GENERATORS.equals (name)) {
804 generators.clear ();
805 generators.addAll (createGenerators ());
806 model.fireTableDataChanged ();
807 } else if (IDataGenerator.PROPERTY_GENERATOR_EDIT.equals (name))
808 model.fireTableDataChanged ();
809 else if (Objective.RESOURCE_OBJECTIVES.equals (name) || IObjective.PROPERTY_OBJECTIVE_EDIT.equals (name)) {
810 objectives.clear ();
811 objectives.addAll (createObjectives ());
812 objectives.sort (DataGenerator.COMPARATOR_NAME);
813 objectiveModel.replaceAll (objectives.asList ());
814 model.fireTableDataChanged ();
815 } else if (DataGeneratorEvaluator.PROPERTY_EVALUATIONUPDATE.equals (name))
816 model.fireTableDataChanged ();
817 else if (DataGeneratorEvaluator.PROPERTY_EVALUATIONFINISHED.equals (name))
818 ((DataGeneratorEvaluator) e.getSource ()).removePropertyChangeListener (this);
819 else
820 super.propertyChange (e);
821 }
822
823 /**
824 * {@inheritDoc}
825 */
826
827 public void reset () {
828 for (Iterator iterator = generators.iterator (); iterator.hasNext (); )
829 clearObjective (Objective.getObjectiveForGenerator ((IDataGenerator) iterator.next ()));
830 model.fireTableDataChanged ();
831 }
832
833 /**
834 * {@inheritDoc}
835 */
836
837 public void reset (int rows []) {
838 int l = rows.length;
839 if (l == 0)
840 return;
841 for (int i = 0; i < l; i++)
842 clearObjective (getObjectiveForGenerator (rows [i]));
843 model.fireTableDataChanged ();
844 }
845
846 /**
847 * {@inheritDoc}
848 */
849
850 public void saveResults (IWriteableDataSource source) {
851 if (source instanceof FileDataSource) {
852 FileDataSource fileSource = (FileDataSource) source;
853 fileSource.setExceptionListenerOption (exception);
854 fileSource.setFileChooserOption (createFileChooserResults ());
855 fileSource.setFileChooserSelectOption (OPTION_CHOOSERSAVE);
856 }
857 try {
858 if (!source.configure ())
859 return;
860 saveResultsDirect (source);
861 } catch (Exception e) {
862 compare.shellHandleException (Compare.MESSAGE_ERROR, MESSAGE_SAVEERROR + e.getMessage (), e);
863 }
864 }
865
866 /**
867 * {@inheritDoc}
868 */
869
870 public void saveResultsDirect (IWriteableDataSource source) {
871 try {
872 writeResults (source);
873 } catch (Exception e) {
874 compare.shellHandleException (Compare.MESSAGE_ERROR, MESSAGE_SAVEERROR + e.getMessage (), e);
875 } finally {
876 if (source instanceof IMultipleDataSource)
877 ((IMultipleDataSource) source).finish ();
878 }
879 }
880
881 /**
882 * {@inheritDoc}
883 */
884
885 protected JFileChooser createFileChooser () {
886 return InterfaceBuilder.createFileChooser (FILTER_OBJECTIVESET);
887 }
888
889 /**
890 * A file chooser specialized for the view results.
891 */
892
893 protected JFileChooser createFileChooserResults () {
894 return InterfaceBuilder.createFileChooser (FILTER_OBJECTIVERESULT);
895 }
896
897 /**
898 * {@inheritDoc}
899 */
900
901 protected IDataGeneratorList createGenerators () {
902 return DataGeneratorList.createList (compare.scanResourceCatalog (IDataGenerator.RESOURCE_GENERATORS));
903 }
904
905 /**
906 * A list of objectives for the view.
907 */
908
909 protected List createObjectives () {
910 return compare.scanResourceCatalog (Objective.RESOURCE_OBJECTIVES);
911 }
912
913 /**
914 * {@inheritDoc}
915 */
916
917 protected void createUI () {
918 createUI (new ObjectiveSeriesModel ());
919 }
920
921 /**
922 * @see jigcell.compare.views.BasicTableView#createUI(jigcell.compare.ui.BasicTable.BasicTableModel)
923 */
924
925 protected void createUI (ObjectiveSeriesModel model) {
926 super.createUI (model);
927 this.model = model;
928 table.setDefaultRenderer (Object.class, new ObjectiveSeriesRenderer (false));
929 objectiveModel = new ListComboBoxModel (objectives.asList ());
930 TableColumnModel columnModel = table.getColumnModel ();
931 columnModel.getColumn (table.findColumn (model.COLUMN_OBJECTIVE)).setCellEditor (new BasicTable.BasicEditor (this,
932 new JComboBox (objectiveModel)));
933 TableColumn comparisonTypeColumn = columnModel.getColumn (table.findColumn (model.COLUMN_COMPARISONTYPE));
934 comparisonTypeColumn.setCellEditor (new BasicTable.BasicEditor (this, new JComboBox (new ListComboBoxModel (
935 Comparison.getTypes ()))));
936 comparisonTypeColumn.setCellRenderer (new ObjectiveSeriesRenderer (true));
937 }
938
939 /**
940 * Evaluates a collection of data generators on a separate thread.
941 *
942 * @param generators Data generators to evaluate
943 */
944
945 protected void evaluate (IDataGenerator generators []) {
946 ProgressMonitor progress = new ProgressMonitor (compare.getDisplayFrame (), "Not started", 0, generators.length);
947 DataGeneratorEvaluator evaluator = new DataGeneratorEvaluator (generators, this);
948 evaluator.addPropertyChangeListener (this);
949 evaluator.addPropertyChangeListener (progress);
950 progress.addPropertyChangeListener (evaluator);
951 evaluator.execute ();
952 }
953
954 /**
955 * The objective of a generator.
956 *
957 * @param index Index of generator in generator list
958 */
959
960 protected IObjective getObjectiveForGenerator (int index) {
961 return Objective.getObjectiveForGenerator ((IDataGenerator) generators.get (index));
962 }
963
964 /**
965 * {@inheritDoc}
966 */
967
968 protected void initialize () {
969 super.initialize ();
970 }
971
972 /**
973 * {@inheritDoc}
974 */
975
976 protected void initializeData () {
977 generators = createGenerators ();
978 objectives = new DataGeneratorList ();
979 }
980
981 /**
982 * {@inheritDoc}
983 */
984
985 protected void readInternal (Object readResult) throws Exception {
986 Iterator iterator = DataGeneratorMap.createMap (readResult).iterator ();
987 objectives.clear ();
988 HashSet used = new HashSet ();
989 generators.createIndex ();
990 while (iterator.hasNext ())
991 try {
992 Map.Entry entry = (Map.Entry) iterator.next ();
993 IObjective generator = (IObjective) entry.getValue ();
994 IObjective original = generator.isCopy () ? (IObjective) generator.getCopiedFrom () : generator;
995 if (used.add (original))
996 objectives.add (original);
997 IDataGeneratorCollection.Key key = (IDataGeneratorCollection.Key) entry.getKey ();
998 IDataGenerator target = generators.get (key);
999 if (target == null) {
1000 compare.shellHandleException (Compare.MESSAGE_WARNING, Compare.formatString ("ObjectiveSeriesView.objectiveTargetWarning",
1001 key));
1002 continue;
1003 }
1004 target.addOption (IObjective.OPTION_OBJECTIVE, IDataGenerator.Option.COPYONLY);
1005 generator = (IObjective) generator.copy (true);
1006 target.setOption (IObjective.OPTION_OBJECTIVE, generator);
1007 generator.setCopyName (true);
1008 } catch (Exception e) {
1009 compare.shellHandleException (Compare.MESSAGE_WARNING, Compare.getString ("ObjectiveSeriesView.objectiveAttachWarning"), e);
1010 }
1011 compare.updateResourceMap (Objective.RESOURCE_OBJECTIVES, this, ((IDataGeneratorList) objectives.clone ()).asList ());
1012 compare.firePropertyChange (Objective.RESOURCE_OBJECTIVES);
1013 }
1014
1015 /**
1016 * {@inheritDoc}
1017 */
1018
1019 protected void readNotify () {
1020 model.fireTableDataChanged ();
1021 compare.firePropertyChange (IDataGenerator.PROPERTY_GENERATOR_EDIT);
1022 }
1023
1024 /**
1025 * {@inheritDoc}
1026 */
1027
1028 protected void write (IWriteableDataSource source) throws Exception {
1029 IDataGeneratorMap map = new DataGeneratorMap (1 + (objectives.size () << 2), 1);
1030 for (Iterator iterator = generators.iterator (); iterator.hasNext (); ) {
1031 IDataGenerator generator = (IDataGenerator) iterator.next ();
1032 IObjective objective = Objective.getObjectiveForGenerator (generator);
1033 if (objective != null)
1034 map.put (generator.getName (), generator.getAttribute (IDataGenerator.ATTRIBUTE_GUID), objective);
1035 }
1036 source.write (map);
1037 }
1038
1039 /**
1040 * Writes results data to some external source.
1041 *
1042 * @param source Data source to write to
1043 */
1044
1045 protected void writeResults (IWriteableDataSource source) throws Exception {
1046 IResultSetMap map = new ResultSetMap (1 + (objectives.size () << 2), 1);
1047 for (Iterator iterator = generators.iterator (); iterator.hasNext (); ) {
1048 IDataGenerator generator = (IDataGenerator) iterator.next ();
1049 IObjective objective = Objective.getObjectiveForGenerator (generator);
1050 if (objective != null && objective.isCached ())
1051 map.put (generator.getName (), generator.getAttribute (IDataGenerator.ATTRIBUTE_GUID), objective.getElement ());
1052 }
1053 source.write (map);
1054 }
1055 }