001 package jigcell.compare.views;
002
003 import java.awt.datatransfer.DataFlavor;
004 import java.awt.datatransfer.Transferable;
005 import java.awt.datatransfer.UnsupportedFlavorException;
006 import java.beans.XMLDecoder;
007 import java.beans.XMLEncoder;
008 import java.io.IOException;
009 import java.io.InputStream;
010 import java.io.PipedInputStream;
011 import java.io.PipedOutputStream;
012 import java.io.Reader;
013 import java.io.StringReader;
014 import java.lang.reflect.InvocationTargetException;
015 import java.util.Arrays;
016 import java.util.ListIterator;
017 import javax.swing.JComponent;
018 import javax.swing.SwingUtilities;
019 import javax.swing.table.TableModel;
020 import jigcell.compare.IDataGenerator;
021 import jigcell.compare.IEditableDataGenerator;
022 import jigcell.compare.ITransferProxy;
023 import jigcell.compare.data.DataGeneratorList;
024 import jigcell.compare.data.IDataGeneratorList;
025 import jigcell.compare.impl.ComparatorDataFlavor;
026 import jigcell.compare.impl.Compare;
027 import jigcell.compare.impl.Config;
028 import jigcell.compare.impl.ExceptionRecorder;
029 import jigcell.compare.impl.Transferer;
030 import jigcell.compare.ui.BasicTable;
031 import jigcell.compare.ui.IClipboardTab;
032 import jigcell.compare.ui.IRowSelectionTab;
033 import jigcell.compare.ui.SeriesDialog;
034 import jigcell.compare.ui.ViewerDialog;
035
036 /**
037 * A static display for presenting a two-dimensional set of data organized by generators.
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 SeriesView extends BasicTableView implements IClipboardTab, IRowSelectionTab {
047
048 /**
049 * Property key for text cutoff value
050 */
051
052 public final static String CONFIG_TEXTCUTOFF = "SeriesView.textLimit";
053
054 /**
055 * Display value for data that is not available
056 */
057
058 protected final static String VALUE_NOTAVAILABLE = "Not available";
059
060 /**
061 * List of displayed generators
062 */
063
064 protected IDataGeneratorList generators;
065
066 /**
067 * Column to cutoff excessively long values at
068 */
069
070 protected int textCutoff;
071
072 /**
073 * Data model of the generators
074 */
075
076 protected SeriesModel model;
077
078 /**
079 * Converts the internal format of the view to and from a Transferable.
080 */
081
082 public static class SeriesViewTransferer extends Transferer implements ITransferProxy {
083
084 /**
085 * Readable name of the exported data flavor
086 */
087
088 protected final static String NAME_EXPORTEDFLAVOR = "Generator List";
089
090 /**
091 * Cached exception recorder for this transferer
092 */
093
094 protected ExceptionRecorder exception;
095
096 /**
097 * List of displayed generators
098 */
099
100 protected IDataGeneratorList generators;
101
102 /**
103 * Model for the data
104 */
105
106 protected SeriesView.SeriesModel model;
107
108 static {
109 addFlavor (SeriesViewTransferer.class,
110 new ComparatorDataFlavor (NAME_EXPORTEDFLAVOR, IDataGeneratorList.class, ComparatorDataFlavor.FLAVOR_LOCALOBJECT));
111 addFlavor (SeriesViewTransferer.class,
112 new ComparatorDataFlavor (NAME_EXPORTEDFLAVOR, InputStream.class, ComparatorDataFlavor.FLAVOR_XMLSERIALIZEDOBJECT));
113 addFlavor (SeriesViewTransferer.class,
114 new ComparatorDataFlavor (NAME_EXPORTEDFLAVOR, IDataGeneratorList.class, ComparatorDataFlavor.FLAVOR_SERIALIZEDOBJECT));
115 addFlavor (SeriesViewTransferer.class,
116 new ComparatorDataFlavor (NAME_EXPORTEDFLAVOR, SeriesView.SeriesModel.class, ComparatorDataFlavor.FLAVOR_LOCALOBJECT));
117 addFlavor (SeriesViewTransferer.class,
118 new ComparatorDataFlavor (NAME_EXPORTEDFLAVOR, Reader.class, ComparatorDataFlavor.FLAVOR_TEXTHTML));
119 addFlavor (SeriesViewTransferer.class,
120 new ComparatorDataFlavor (NAME_EXPORTEDFLAVOR, String.class, ComparatorDataFlavor.FLAVOR_TEXTHTML));
121 addFlavor (SeriesViewTransferer.class,
122 new ComparatorDataFlavor (NAME_EXPORTEDFLAVOR, Reader.class, ComparatorDataFlavor.FLAVOR_TEXTPLAIN));
123 addFlavor (SeriesViewTransferer.class,
124 new ComparatorDataFlavor (NAME_EXPORTEDFLAVOR, String.class, ComparatorDataFlavor.FLAVOR_TEXTPLAIN));
125 }
126
127 /**
128 * Creates a new transferer proxy.
129 */
130
131 public SeriesViewTransferer () {
132 super ();
133 exception = new ExceptionRecorder ("Error encountered during transfer.", true, false, true);
134 }
135
136 /**
137 * Creates a new transferer proxy.
138 *
139 * @param data Data to transfer
140 * @param owner Owner
141 */
142
143 public SeriesViewTransferer (Object data, Object owner) throws InvocationTargetException {
144 SeriesView view = (SeriesView) owner;
145 if (data == null)
146 generators = new DataGeneratorList ();
147 else if (data instanceof InputStream)
148 generators = (IDataGeneratorList) new XMLDecoder ((InputStream) data, this, exception).readObject ();
149 else if (data instanceof IDataGeneratorList)
150 generators = (IDataGeneratorList) data;
151 else
152 throw new ClassCastException (Compare.getString ("SeriesViewTransferer.unwrapError"));
153 try {
154 model = (SeriesView.SeriesModel) view.model.getClass ().getDeclaredConstructor (new Class [] {view.getClass ()}).newInstance (
155 new Object [] {view});
156 model.setGenerators (generators);
157 } catch (Exception e) {
158 throw new InvocationTargetException (e, Compare.getString ("SeriesViewTransferer.unwrapError"));
159 }
160 }
161
162 /**
163 * A transformed version of the transfer data.
164 */
165
166 public Object getData () {
167 return generators;
168 }
169
170 public int getSourceActions (JComponent c) {
171 assert SwingUtilities.getAncestorOfClass (SeriesView.class, c) != null;
172 return COPY;
173 }
174
175 public Object getTransferData (DataFlavor flavor) throws IOException, UnsupportedFlavorException {
176 if (!(flavor instanceof ComparatorDataFlavor))
177 throw new UnsupportedFlavorException (flavor);
178 ComparatorDataFlavor type = (ComparatorDataFlavor) flavor;
179 if (type.isFlavorLocalObjectType () || type.isFlavorSerializedObjectType ()) {
180 Class representation = type.getRepresentationClass ();
181 if (IDataGeneratorList.class.isAssignableFrom (representation))
182 return generators;
183 if (TableModel.class.isAssignableFrom (representation))
184 return model;
185 throw new UnsupportedFlavorException (flavor);
186 }
187 if (type.isFlavorXMLSerializedObjectType ()) {
188 if (!PipedInputStream.class.isAssignableFrom (type.getRepresentationClass ()))
189 throw new UnsupportedFlavorException (flavor);
190 PipedOutputStream out = new PipedOutputStream ();
191 XMLEncoder encoder = new XMLEncoder (out);
192 Compare.updateEncoder (encoder);
193 encoder.setExceptionListener (exception);
194 encoder.writeObject (generators);
195 encoder.close ();
196 if (!exception.hasException ())
197 return new PipedInputStream (out);
198 throw (IOException) new IOException (Compare.getString ("Transferer.transferError")).initCause (exception.getLastException ());
199 }
200 String text;
201 if (type.isFlavorPlainTextType ())
202 text = createCSVTable (model);
203 else if (type.isFlavorHTMLTextType ())
204 text = createHTMLTable (model);
205 else
206 throw new UnsupportedFlavorException (flavor);
207 Class representation = type.getRepresentationClass ();
208 if (representation == String.class)
209 return text;
210 if (Reader.class.isAssignableFrom (representation))
211 return new StringReader (text);
212 throw new UnsupportedFlavorException (flavor);
213 }
214
215 protected Transferable createTransferable (JComponent component) {
216 SeriesView view = (SeriesView) SwingUtilities.getAncestorOfClass (SeriesView.class, component);
217 assert view != null;
218 IDataGeneratorList generators = view.generators;
219 IDataGeneratorList list = new DataGeneratorList ();
220 int rows [] = view.getSelectedRows ();
221 Arrays.sort (rows);
222 for (int i = 0, l = rows.length; i < l; i++)
223 list.add (copyGenerator ((IDataGenerator) generators.get (rows [i])));
224 try {
225 return new SeriesViewTransferer (list, view);
226 } catch (InvocationTargetException _e) {
227 view.compare.shellHandleException (Compare.MESSAGE_ERROR, Compare.getString ("SeriesViewTransferer.dragStartError"), _e);
228 }
229 return null;
230 }
231
232 /**
233 * {@inheritDoc}
234 */
235
236 protected ExceptionRecorder getExceptionRecorder () {
237 return exception;
238 }
239 }
240
241 /**
242 * Table model for a SimpleDataSeriesView.
243 */
244
245 protected class SeriesModel extends BasicTable.BasicTableModel {
246
247 /**
248 * Comment column
249 */
250
251 public final Marker COLUMN_COMMENT = new Marker ("SeriesModel.comment", "Comment");
252
253 /**
254 * Name column
255 */
256
257 public final Marker COLUMN_NAME = new Marker ("SeriesModel.name", "Name");
258
259 /**
260 * Value column
261 */
262
263 public final Marker COLUMN_VALUE = new Marker ("SeriesModel.value", "Value");
264
265 /**
266 * List of displayed generators
267 */
268
269 protected IDataGeneratorList generators;
270
271 /**
272 * Creates a new table model.
273 */
274
275 public SeriesModel () {
276 super ();
277 setColumnMarkers (new Marker [] {COLUMN_NAME, COLUMN_VALUE, COLUMN_COMMENT});
278 setColumnWeights (new double [] {1.0, 2.0, 2.0});
279 generators = SeriesView.this.generators;
280 }
281
282 /**
283 * The generators for this model.
284 */
285
286 public IDataGeneratorList getGenerators () {
287 return generators;
288 }
289
290 /**
291 * The number of generators.
292 */
293
294 public int getRowCount () {
295 return generators.size ();
296 }
297
298 /**
299 * A value in the table.
300 *
301 * @param row Row
302 * @param column Column
303 */
304
305 public Object getValueAt (int row, int column) {
306 if (column == findColumn (COLUMN_VALUE)) {
307 IDataGenerator entry = (IDataGenerator) generators.get (row);
308 return entry.isCached () ? entry.getElement ().toString () : "Not Available";
309 }
310 if (column == findColumn (COLUMN_NAME))
311 return ((IDataGenerator) generators.get (row)).getName ();
312 if (column == findColumn (COLUMN_COMMENT))
313 return ((IDataGenerator) generators.get (row)).getComment ();
314 return super.getValueAt (row, column);
315 }
316
317 /**
318 * Sets the generators for this model.
319 *
320 * @param generators Generators
321 */
322
323 public void setGenerators (IDataGeneratorList generators) {
324 this.generators = generators;
325 }
326
327 /**
328 * Starts viewing a cell in the table but does not allow editing.
329 *
330 * @param row Row
331 * @param column Column
332 */
333
334 public void viewCellAt (int row, int column) {
335 IDataGenerator generator = (IDataGenerator) generators.get (row);
336 if (column != findColumn (COLUMN_VALUE) || generator.getElement ().isScalar ())
337 new ViewerDialog (compare, generator.getName (), row, column, model).setVisible (true);
338 else
339 new SeriesDialog (compare, generator, row, column, model).setVisible (true);
340 }
341 }
342
343 /**
344 * Copies a generator.
345 *
346 * @param generator Generator
347 */
348
349 protected static IDataGenerator copyGenerator (IDataGenerator generator) {
350 if (generator == null)
351 return null;
352 IDataGenerator _generator = (IDataGenerator) generator.clone ();
353 if (generator instanceof IEditableDataGenerator)
354 ((IEditableDataGenerator) _generator).setElement (generator.getElement ());
355 return _generator;
356 }
357
358 /**
359 * Creates a new with an integrated model and data store.
360 *
361 * @param compare Comparator backend to interface with
362 * @param configMarker Marker for retrieving configuration information from Comparator backend
363 */
364
365 public SeriesView (Compare compare, String configMarker) {
366 super (compare, configMarker);
367 clipboard.setTransferClassOption (SeriesViewTransferer.class);
368 clipboard.setOwnerOption (this);
369 clipboard.setFlavorsOption (Transferer.getFlavors (SeriesViewTransferer.class));
370 printer.setTransferClassOption (SeriesViewTransferer.class);
371 printer.setOwnerOption (this);
372 }
373
374 /**
375 * {@inheritDoc}
376 */
377
378 public void clipboardCopy () {
379 IDataGeneratorList list = new DataGeneratorList ();
380 for (int i = 0, l = generators.size (); i < l; i++)
381 list.add (copyGenerator ((IDataGenerator) generators.get (i)));
382 clipboardWrite (list, Compare.getString ("SeriesView.copyError"));
383 }
384
385 /**
386 * {@inheritDoc}
387 */
388
389 public void clipboardCopy (int rows []) {
390 IDataGeneratorList list = new DataGeneratorList ();
391 Arrays.sort (rows);
392 for (int i = 0, l = rows.length; i < l; i++)
393 list.add (copyGenerator ((IDataGenerator) generators.get (rows [i])));
394 clipboardWrite (list, Compare.getString ("SeriesView.copyError"));
395 }
396
397 /**
398 * {@inheritDoc}
399 */
400
401 public void clipboardCut () {
402 throw new UnsupportedOperationException ();
403 }
404
405 /**
406 * {@inheritDoc}
407 */
408
409 public void clipboardCut (int rows []) {
410 throw new UnsupportedOperationException ();
411 }
412
413 /**
414 * {@inheritDoc}
415 */
416
417 public void clipboardPaste () {
418 throw new UnsupportedOperationException ();
419 }
420
421 /**
422 * {@inheritDoc}
423 */
424
425 public int getRowCount () {
426 return generators.size ();
427 }
428
429 /**
430 * {@inheritDoc}
431 */
432
433 public Object getRowData (int row) {
434 try {
435 return generators.get (row);
436 } catch (Exception e) {
437 Compare.assertion ("Unable to retrieve generator for row.", e);
438 }
439 return null;
440 }
441
442 /**
443 * Prints the view.
444 */
445
446 public void print () {
447 try {
448 if (printer.configure ())
449 printer.write (generators);
450 } catch (Exception e) {
451 compare.shellHandleException (Compare.MESSAGE_ERROR, Compare.getString ("BasicTableView.printError"), e);
452 }
453 }
454
455 /**
456 * {@inheritDoc}
457 */
458
459 public void readConfiguration (String state) {
460 super.readConfiguration (state);
461 if (state == STATE_INITIALIZE || state == STATE_RUNNING)
462 textCutoff = (int) Config.convertToInteger (compare.getConfig ().findValue (configMarkers, CONFIG_TEXTCUTOFF), Integer.MAX_VALUE);
463 }
464
465 /**
466 * A list of generators for the view.
467 */
468
469 protected IDataGeneratorList createGenerators () {
470 return new DataGeneratorList ();
471 }
472
473 /**
474 * {@inheritDoc}
475 */
476
477 protected void createUI () {
478 createUI (new SeriesModel ());
479 }
480
481 /**
482 * @see jigcell.compare.views.BasicTableView#createUI(jigcell.compare.ui.BasicTable.BasicTableModel)
483 */
484
485 protected void createUI (SeriesModel model) {
486 super.createUI (model);
487 this.model = model;
488 table.setTransferHandler (new SeriesViewTransferer ());
489 table.setAutoscrolls (true);
490 table.setDragEnabled (true);
491 }
492
493 /**
494 * {@inheritDoc}
495 */
496
497 protected boolean findByName (String name, int row, boolean forward) {
498 if (name == null)
499 return false;
500 lastSearchName = name;
501 int length = generators.size ();
502 int index = -1;
503 if (forward) {
504 if (row < -1 || row >= length)
505 return false;
506 for (ListIterator iterator = generators.iterator (row + 1); iterator.hasNext (); )
507 if (((IDataGenerator) iterator.next ()).getName ().indexOf (name) != -1) {
508 index = iterator.previousIndex ();
509 break;
510 }
511 } else {
512 if (row < 0 || row > length)
513 return false;
514 for (ListIterator iterator = generators.iterator (row); iterator.hasPrevious (); )
515 if (((IDataGenerator) iterator.previous ()).getName ().indexOf (name) != -1) {
516 index = iterator.nextIndex ();
517 break;
518 }
519 }
520 if (index == -1)
521 return false;
522 table.setRowSelectionInterval (index, index);
523 table.scrollRectToVisible (table.getCellRect (index, table.findColumn (model.COLUMN_NAME), true));
524 return true;
525 }
526
527 /**
528 * {@inheritDoc}
529 */
530
531 protected void initialize () {
532 super.initialize ();
533 initializeData ();
534 }
535
536 /**
537 * Initializes data for the view.
538 */
539
540 protected void initializeData () {
541 generators = createGenerators ();
542 compare.updateResourceMap (IDataGenerator.RESOURCE_GENERATORS, this, generators.asList ());
543 compare.firePropertyChange (IDataGenerator.RESOURCE_GENERATORS);
544 }
545 }