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    }