001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see https://support.hdfgroup.org/products/licenses.html               *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view.TableView;
016
017import java.awt.Toolkit;
018import java.awt.datatransfer.Clipboard;
019import java.awt.datatransfer.DataFlavor;
020import java.awt.datatransfer.StringSelection;
021import java.io.BufferedReader;
022import java.io.BufferedWriter;
023import java.io.DataOutputStream;
024import java.io.File;
025import java.io.FileNotFoundException;
026import java.io.FileOutputStream;
027import java.io.FileReader;
028import java.io.FileWriter;
029import java.io.IOException;
030import java.io.PrintWriter;
031import java.lang.reflect.Array;
032import java.lang.reflect.Constructor;
033import java.nio.ByteOrder;
034import java.text.DecimalFormat;
035import java.text.NumberFormat;
036import java.util.ArrayList;
037import java.util.BitSet;
038import java.util.HashMap;
039import java.util.Iterator;
040import java.util.LinkedHashSet;
041import java.util.List;
042import java.util.Set;
043import java.util.StringTokenizer;
044
045import org.eclipse.nebula.widgets.nattable.NatTable;
046import org.eclipse.nebula.widgets.nattable.command.StructuralRefreshCommand;
047import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommand;
048import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
049import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;
050import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
051import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
052import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
053import org.eclipse.nebula.widgets.nattable.coordinate.Range;
054import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
055import org.eclipse.nebula.widgets.nattable.data.validate.DataValidator;
056import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
057import org.eclipse.nebula.widgets.nattable.edit.action.KeyEditAction;
058import org.eclipse.nebula.widgets.nattable.edit.action.MouseEditAction;
059import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditConfiguration;
060import org.eclipse.nebula.widgets.nattable.edit.config.DialogErrorHandling;
061import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
062import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
063import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
064import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
065import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
066import org.eclipse.nebula.widgets.nattable.layer.ILayer;
067import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
068import org.eclipse.nebula.widgets.nattable.layer.config.DefaultColumnHeaderLayerConfiguration;
069import org.eclipse.nebula.widgets.nattable.layer.config.DefaultColumnHeaderStyleConfiguration;
070import org.eclipse.nebula.widgets.nattable.layer.config.DefaultRowHeaderLayerConfiguration;
071import org.eclipse.nebula.widgets.nattable.layer.config.DefaultRowHeaderStyleConfiguration;
072import org.eclipse.nebula.widgets.nattable.painter.cell.TextPainter;
073import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.BeveledBorderDecorator;
074import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.LineBorderDecorator;
075import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
076import org.eclipse.nebula.widgets.nattable.selection.command.SelectAllCommand;
077import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
078import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
079import org.eclipse.nebula.widgets.nattable.style.HorizontalAlignmentEnum;
080import org.eclipse.nebula.widgets.nattable.style.Style;
081import org.eclipse.nebula.widgets.nattable.ui.action.IMouseAction;
082import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
083import org.eclipse.nebula.widgets.nattable.ui.matcher.CellEditorMouseEventMatcher;
084import org.eclipse.nebula.widgets.nattable.ui.matcher.LetterOrDigitKeyEventMatcher;
085import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
086import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction;
087import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
088import org.eclipse.swt.SWT;
089import org.eclipse.swt.custom.SashForm;
090import org.eclipse.swt.custom.ScrolledComposite;
091import org.eclipse.swt.events.DisposeEvent;
092import org.eclipse.swt.events.DisposeListener;
093import org.eclipse.swt.events.MouseEvent;
094import org.eclipse.swt.events.SelectionAdapter;
095import org.eclipse.swt.events.SelectionEvent;
096import org.eclipse.swt.events.TraverseEvent;
097import org.eclipse.swt.events.TraverseListener;
098import org.eclipse.swt.graphics.Color;
099import org.eclipse.swt.graphics.Font;
100import org.eclipse.swt.graphics.Point;
101import org.eclipse.swt.graphics.Rectangle;
102import org.eclipse.swt.layout.FillLayout;
103import org.eclipse.swt.layout.GridData;
104import org.eclipse.swt.layout.GridLayout;
105import org.eclipse.swt.widgets.Button;
106import org.eclipse.swt.widgets.Combo;
107import org.eclipse.swt.widgets.Composite;
108import org.eclipse.swt.widgets.Dialog;
109import org.eclipse.swt.widgets.Display;
110import org.eclipse.swt.widgets.FileDialog;
111import org.eclipse.swt.widgets.Label;
112import org.eclipse.swt.widgets.Menu;
113import org.eclipse.swt.widgets.MenuItem;
114import org.eclipse.swt.widgets.Shell;
115import org.eclipse.swt.widgets.Text;
116import org.eclipse.swt.widgets.ToolBar;
117import org.eclipse.swt.widgets.ToolItem;
118
119import hdf.object.CompoundDS;
120import hdf.object.DataFormat;
121import hdf.object.Dataset;
122import hdf.object.Datatype;
123import hdf.object.FileFormat;
124import hdf.object.Group;
125import hdf.object.HObject;
126import hdf.object.ScalarDS;
127import hdf.view.Chart;
128import hdf.view.DefaultFileFilter;
129import hdf.view.HDFView;
130import hdf.view.Tools;
131import hdf.view.ViewProperties;
132import hdf.view.ViewProperties.BITMASK_OP;
133import hdf.view.DataView.DataViewManager;
134import hdf.view.TableView.DataDisplayConverterFactory.HDFDisplayConverter;
135import hdf.view.TableView.DataProviderFactory.HDFDataProvider;
136import hdf.view.TreeView.TreeView;
137import hdf.view.dialog.InputDialog;
138import hdf.view.dialog.MathConversionDialog;
139import hdf.view.dialog.NewDatasetDialog;
140
141/**
142 * DefaultBaseTableView serves as the base class for a DataView that displays
143 * HDF data in a tabular format. This class is used for internal bookkeeping and
144 * as a place to store higher-level data manipulation functions, whereas its
145 * subclasses are responsible for setting up the actual GUI components.
146 *
147 * @author jhenderson
148 * @version 1.0 4/13/2018
149 */
150public abstract class DefaultBaseTableView implements TableView {
151
152    private static final org.slf4j.Logger   log = org.slf4j.LoggerFactory.getLogger(DefaultBaseTableView.class);
153
154    private final Display                   display = Display.getDefault();
155    protected final Shell                   shell;
156    protected Font                          curFont;
157
158    // The main HDFView
159    protected final DataViewManager         viewer;
160
161    protected NatTable                      dataTable;
162
163    // The data object to be displayed in the Table
164    protected final DataFormat              dataObject;
165
166    // The data value of the data object
167    protected Object                        dataValue;
168
169    protected Object                        fillValue;
170
171    protected enum ViewType { TABLE, IMAGE };
172    protected      ViewType                 viewType = ViewType.TABLE;
173
174    // Changed to use normalized scientific notation (1 <= coefficient < 10).
175    // private final DecimalFormat scientificFormat = new DecimalFormat("###.#####E0#");
176    protected final DecimalFormat           scientificFormat = new DecimalFormat("0.0###E0###");
177    protected DecimalFormat                 customFormat     = new DecimalFormat("###.#####");
178    protected final NumberFormat            normalFormat     = null;
179    protected NumberFormat                  numberFormat     = normalFormat;
180
181    // Used for bitmask operations on data
182    protected BitSet                        bitmask = null;
183    protected BITMASK_OP                    bitmaskOP = BITMASK_OP.EXTRACT;
184
185    // Fields to keep track of which 'frame' of 3 dimensional data is being displayed
186    private Text                            frameField;
187    private long                            curDataFrame = 0;
188    private long                            maxDataFrame = 1;
189
190    // The index base used for display row and column numbers of data
191    protected int                           indexBase = 0;
192
193    protected int                           fixedDataLength = -1;
194
195    protected int                           binaryOrder;
196
197    protected boolean                       isReadOnly = false;
198
199    protected boolean                       isEnumConverted = false;
200
201    protected boolean                       isDisplayTypeChar, isDataTransposed;
202
203    protected boolean                       isRegRef = false, isObjRef = false;
204    protected boolean                       showAsHex = false, showAsBin = false;
205
206    // Keep references to the selection and data layers for ease of access
207    protected SelectionLayer                selectionLayer;
208    protected DataLayer                     dataLayer;
209
210    protected IDataProvider                 rowHeaderDataProvider;
211    protected IDataProvider                 columnHeaderDataProvider;
212
213    protected HDFDataProvider               dataProvider;
214    protected HDFDisplayConverter           dataDisplayConverter;
215
216    /**
217     * Global variables for GUI components
218     */
219    protected MenuItem                      checkFixedDataLength = null;
220    protected MenuItem                      checkCustomNotation = null;
221    protected MenuItem                      checkScientificNotation = null;
222    protected MenuItem                      checkHex = null;
223    protected MenuItem                      checkBin = null;
224    protected MenuItem                      checkEnum = null;
225
226    // Labeled Group to display the index base
227    protected org.eclipse.swt.widgets.Group indexBaseGroup;
228
229    // Text field to display the value of the currently selected table cell
230    protected Text                          cellValueField;
231
232    // Label to indicate the current cell location
233    protected Label                         cellLabel;
234
235
236    /**
237     * Constructs a base TableView with no additional data properties.
238     *
239     * @param theView
240     *            the main HDFView.
241     */
242    public DefaultBaseTableView(DataViewManager theView) {
243        this(theView, null);
244    }
245
246    /**
247     * Constructs a base TableView with the specified data properties.
248     *
249     * @param theView
250     *            the main HDFView.
251     *
252     * @param dataPropertiesMap
253     *            the properties on how to show the data. The map is used to allow
254     *            applications to pass properties on how to display the data, such
255     *            as: transposing data, showing data as characters, applying a
256     *            bitmask, and etc. Predefined keys are listed at
257     *            ViewProperties.DATA_VIEW_KEY.
258     */
259    @SuppressWarnings("rawtypes")
260    public DefaultBaseTableView(DataViewManager theView, HashMap dataPropertiesMap) {
261        shell = new Shell(display, SWT.SHELL_TRIM);
262
263        shell.setData(this);
264
265        shell.setLayout(new GridLayout(1, true));
266
267        /*
268         * When the table is closed, make sure to prompt the user about saving their
269         * changes, then do any pending cleanup work.
270         */
271        shell.addDisposeListener(new DisposeListener() {
272            @Override
273            public void widgetDisposed(DisposeEvent e) {
274                if (dataProvider != null) {
275                    if (dataProvider.getIsValueChanged() && !isReadOnly) {
276                        if (Tools.showConfirm(shell, "Changes Detected", "\"" + ((HObject) dataObject).getName()
277                                + "\" has changed.\nDo you want to save the changes?"))
278                            updateValueInFile();
279                        else
280                            dataObject.clearData();
281                    }
282                }
283
284                dataValue = null;
285                dataTable = null;
286
287                if (curFont != null)
288                    curFont.dispose();
289
290                viewer.removeDataView(DefaultBaseTableView.this);
291            }
292        });
293
294        /* Grab the current font to be used for all GUI components */
295        try {
296            curFont = new Font(display, ViewProperties.getFontType(), ViewProperties.getFontSize(), SWT.NORMAL);
297        }
298        catch (Exception ex) {
299            curFont = null;
300        }
301
302        viewer = theView;
303
304        /* Retrieve any display properties passed in via the HashMap parameter */
305        HObject hObject = null;
306
307        if (ViewProperties.isIndexBase1()) indexBase = 1;
308
309        if (dataPropertiesMap != null) {
310            hObject = (HObject) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.OBJECT);
311
312            bitmask = (BitSet) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.BITMASK);
313            bitmaskOP = (BITMASK_OP) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.BITMASKOP);
314
315            Boolean b = (Boolean) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.CHAR);
316            if (b != null) isDisplayTypeChar = b.booleanValue();
317
318            b = (Boolean) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.TRANSPOSED);
319            if (b != null) isDataTransposed = b.booleanValue();
320
321            b = (Boolean) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.INDEXBASE1);
322            if (b != null) {
323                if (b.booleanValue())
324                    indexBase = 1;
325                else
326                    indexBase = 0;
327            }
328        }
329
330        log.trace("Index base = {} - Is data transposed = {} - Is display type char = {}",
331                indexBase, isDataTransposed, isDisplayTypeChar);
332
333        if (hObject == null) hObject = viewer.getTreeView().getCurrentObject();
334
335        /* Only edit objects which actually contain editable data */
336        if ((hObject == null) || !(hObject instanceof DataFormat)) {
337            log.debug("data object is null or not an instanceof DataFormat");
338            dataObject = null;
339            shell.dispose();
340            return;
341        }
342
343        dataObject = (DataFormat) hObject;
344        if (((HObject) dataObject).getFileFormat() == null) {
345            log.debug("DataFormat object cannot access FileFormat");
346            shell.dispose();
347            return;
348        }
349
350        isReadOnly = ((HObject) dataObject).getFileFormat().isReadOnly();
351
352        if (((HObject) dataObject).getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4))
353                && (dataObject instanceof CompoundDS)) {
354            /* Cannot edit HDF4 VData */
355            isReadOnly = true;
356        }
357
358        /* Disable edit feature for SZIP compression when encode is not enabled */
359        if (!isReadOnly) {
360            String compression = dataObject.getCompression();
361            if ((compression != null) && compression.startsWith("SZIP")) {
362                if (!compression.endsWith("ENCODE_ENABLED")) {
363                    isReadOnly = true;
364                }
365            }
366        }
367
368        log.trace("dataObject({}) isReadOnly={}", dataObject, isReadOnly);
369
370        long[] dims = dataObject.getDims();
371        long tsize = 1;
372
373        if (dims == null) {
374            log.debug("data object has null dimensions");
375            viewer.showError("Error: Data object '" + ((HObject) dataObject).getName() + "' has null dimensions.");
376            shell.dispose();
377            Tools.showError(display.getActiveShell(), "Error", "Could not open data object '" + ((HObject) dataObject).getName()
378                    + "'. Data object has null dimensions.");
379            return;
380        }
381
382        for (int i = 0; i < dims.length; i++)
383            tsize *= dims[i];
384
385        log.trace("Data object Size={} Height={} Width={}", tsize, dataObject.getHeight(), dataObject.getWidth());
386
387        if (dataObject.getHeight() <= 0 || dataObject.getWidth() <= 0 || tsize <= 0) {
388            log.debug("data object has dimension of size 0");
389            viewer.showError("Error: Data object '" + ((HObject) dataObject).getName() + "' has dimension of size 0.");
390            shell.dispose();
391            Tools.showError(display.getActiveShell(), "Error", "Could not open data object '" + ((HObject) dataObject).getName()
392                    + "'. Data object has dimension of size 0.");
393            return;
394        }
395
396        /*
397         * Determine whether the data is to be displayed as characters and whether or
398         * not enum data is to be converted.
399         */
400        Datatype dtype = dataObject.getDatatype();
401
402        log.trace("Data object getDatatypeClass()={}", dtype.getDatatypeClass());
403        isDisplayTypeChar = (isDisplayTypeChar
404                && (dtype.getDatatypeSize() == 1 || (dtype.isArray() && dtype.getDatatypeBase().isChar())));
405
406        isEnumConverted = ViewProperties.isConvertEnum();
407
408        log.trace("Data object isDisplayTypeChar={} isEnumConverted={}", isDisplayTypeChar, isEnumConverted);
409
410        if (dtype.isRef()) {
411            if (dtype.getDatatypeSize() > 8) {
412                isReadOnly = true;
413                isRegRef = true;
414            }
415            else
416                isObjRef = true;
417        }
418
419        log.trace("Data object isRegRef={} isObjRef={} showAsHex={}", isRegRef, isObjRef, showAsHex);
420
421        // Setup subset information
422        int rank = dataObject.getRank();
423        int[] selectedIndex = dataObject.getSelectedIndex();
424        long[] count = dataObject.getSelectedDims();
425        long[] stride = dataObject.getStride();
426        long[] start = dataObject.getStartDims();
427        int n = Math.min(3, rank);
428
429        if (rank > 2) {
430            curDataFrame = start[selectedIndex[2]] + indexBase;
431            maxDataFrame = (indexBase == 1) ? dims[selectedIndex[2]] : dims[selectedIndex[2]] - 1;
432        }
433
434        /* Create the toolbar area that contains useful shortcuts */
435        ToolBar toolBar = createToolbar(shell);
436        toolBar.setSize(shell.getSize().x, 30);
437        toolBar.setLocation(0, 0);
438
439        /*
440         * Create the group that contains the text fields for displaying the value and
441         * location of the current cell, as well as the index base.
442         */
443        indexBaseGroup = new org.eclipse.swt.widgets.Group(shell, SWT.SHADOW_ETCHED_OUT);
444        indexBaseGroup.setFont(curFont);
445        indexBaseGroup.setText(indexBase + "-based");
446        indexBaseGroup.setLayout(new GridLayout(1, true));
447        indexBaseGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
448
449        SashForm content = new SashForm(indexBaseGroup, SWT.VERTICAL);
450        content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
451        content.setSashWidth(10);
452
453        SashForm cellValueComposite = new SashForm(content, SWT.HORIZONTAL);
454        cellValueComposite.setSashWidth(8);
455
456        cellLabel = new Label(cellValueComposite, SWT.RIGHT | SWT.BORDER);
457        cellLabel.setAlignment(SWT.CENTER);
458        cellLabel.setFont(curFont);
459
460        final ScrolledComposite cellValueFieldScroller = new ScrolledComposite(cellValueComposite, SWT.V_SCROLL | SWT.H_SCROLL);
461        cellValueFieldScroller.setLayout(new FillLayout());
462
463        cellValueField = new Text(cellValueFieldScroller, SWT.MULTI | SWT.BORDER | SWT.WRAP);
464        cellValueField.setEditable(false);
465        cellValueField.setBackground(new Color(display, 255, 255, 240));
466        cellValueField.setEnabled(false);
467        cellValueField.setFont(curFont);
468
469        cellValueFieldScroller.setContent(cellValueField);
470        cellValueFieldScroller.setExpandHorizontal(true);
471        cellValueFieldScroller.setExpandVertical(true);
472        cellValueFieldScroller.setMinSize(cellValueField.computeSize(SWT.DEFAULT, SWT.DEFAULT));
473
474        cellValueComposite.setWeights(new int[] { 1, 5 });
475
476        /* Make sure that the Dataset's data value is accessible for conditionally adding GUI components */
477        try {
478            loadData(dataObject);
479        }
480        catch (Exception ex) {
481            log.debug("loadData(): data not loaded: ", ex);
482            viewer.showError("Error: unable to load table data");
483            shell.dispose();
484            Tools.showError(display.getActiveShell(), "Open", "An error occurred while loading data for the table:\n\n" + ex.getMessage());
485            return;
486        }
487
488        /* Create the Shell's MenuBar */
489        shell.setMenuBar(createMenuBar(shell));
490
491        /*
492         * Set the default selection on the "Show Hexadecimal/Show Binary", etc. MenuItems.
493         * This step must be done after the menu bar has actually been created.
494         */
495        if (dataObject.getDatatype().isBitField() || dataObject.getDatatype().isOpaque()) {
496            showAsHex = true;
497            checkHex.setSelection(true);
498            checkScientificNotation.setSelection(false);
499            checkCustomNotation.setSelection(false);
500            checkBin.setSelection(false);
501            showAsBin = false;
502            numberFormat = normalFormat;
503        }
504
505        /*
506         * Set the default selection on the "Show Enum", etc. MenuItems.
507         * This step must be done after the menu bar has actually been created.
508         */
509        if (dataObject.getDatatype().isEnum()) {
510            checkEnum.setSelection(isEnumConverted);
511            checkScientificNotation.setSelection(false);
512            checkCustomNotation.setSelection(false);
513            checkBin.setSelection(false);
514            checkHex.setSelection(false);
515            showAsBin = false;
516            showAsHex = false;
517            numberFormat = normalFormat;
518        }
519
520        /* Create the actual NatTable */
521        log.debug("table creation {}", ((HObject) dataObject).getName());
522        try {
523            dataTable = createTable(content, dataObject);
524            if (dataTable == null) {
525                log.debug("table creation for object {} failed", ((HObject) dataObject).getName());
526                viewer.showError("Creating table for object '" + ((HObject) dataObject).getName() + "' failed.");
527                shell.dispose();
528                Tools.showError(display.getActiveShell(), "Open", "Failed to create Table object");
529                return;
530            }
531        }
532        catch (UnsupportedOperationException ex) {
533            log.debug("Subclass does not implement createTable()");
534            shell.dispose();
535            return;
536        }
537
538        /*
539         * Set the default data display conversion settings.
540         */
541        updateDataConversionSettings();
542
543        dataTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
544
545        /*
546         * Set the Shell's title using the object path and name
547         */
548        StringBuilder sb = new StringBuilder(hObject.getName());
549
550        if (((HObject) dataObject).getFileFormat() != null) {
551            sb.append("  at  ")
552              .append(hObject.getPath())
553              .append("  [")
554              .append(((HObject) dataObject).getFileFormat().getName())
555              .append("  in  ")
556              .append(((HObject) dataObject).getFileFormat().getParent())
557              .append("]");
558        }
559
560        shell.setText(sb.toString());
561
562        /*
563         * Append subsetting information and show this as a status message in the
564         * HDFView main window
565         */
566        sb.append(" [ dims");
567        sb.append(selectedIndex[0]);
568        for (int i = 1; i < n; i++) {
569            sb.append("x");
570            sb.append(selectedIndex[i]);
571        }
572        sb.append(", start");
573        sb.append(start[selectedIndex[0]]);
574        for (int i = 1; i < n; i++) {
575            sb.append("x");
576            sb.append(start[selectedIndex[i]]);
577        }
578        sb.append(", count");
579        sb.append(count[selectedIndex[0]]);
580        for (int i = 1; i < n; i++) {
581            sb.append("x");
582            sb.append(count[selectedIndex[i]]);
583        }
584        sb.append(", stride");
585        sb.append(stride[selectedIndex[0]]);
586        for (int i = 1; i < n; i++) {
587            sb.append("x");
588            sb.append(stride[selectedIndex[i]]);
589        }
590        sb.append(" ] ");
591
592        if (log.isTraceEnabled())
593            log.trace("subset={}", sb);
594
595        viewer.showStatus(sb.toString());
596
597        indexBaseGroup.pack();
598
599        content.setWeights(new int[] { 1, 12 });
600
601        shell.pack();
602
603        int width = 700 + (ViewProperties.getFontSize() - 12) * 15;
604        int height = 500 + (ViewProperties.getFontSize() - 12) * 10;
605        shell.setSize(width, height);
606    }
607
608    /**
609     * Creates the toolbar for the Shell.
610     */
611    private ToolBar createToolbar(final Shell shell) {
612        ToolBar toolbar = new ToolBar(shell, SWT.HORIZONTAL | SWT.RIGHT | SWT.BORDER);
613        toolbar.setFont(curFont);
614        toolbar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
615
616        // Chart button
617        ToolItem item = new ToolItem(toolbar, SWT.PUSH);
618        item.setImage(ViewProperties.getChartIcon());
619        item.setToolTipText("Line Plot");
620        item.addSelectionListener(new SelectionAdapter() {
621            @Override
622            public void widgetSelected(SelectionEvent e) {
623                showLineplot();
624            }
625        });
626
627        if (dataObject.getRank() > 2) {
628            new ToolItem(toolbar, SWT.SEPARATOR).setWidth(20);
629
630            // First frame button
631            item = new ToolItem(toolbar, SWT.PUSH);
632            item.setImage(ViewProperties.getFirstIcon());
633            item.setToolTipText("First Frame");
634            item.addSelectionListener(new SelectionAdapter() {
635                @Override
636                public void widgetSelected(SelectionEvent e) {
637                    firstFrame();
638                }
639            });
640
641            // Previous frame button
642            item = new ToolItem(toolbar, SWT.PUSH);
643            item.setImage(ViewProperties.getPreviousIcon());
644            item.setToolTipText("Previous Frame");
645            item.addSelectionListener(new SelectionAdapter() {
646                @Override
647                public void widgetSelected(SelectionEvent e) {
648                    previousFrame();
649                }
650            });
651
652            ToolItem separator = new ToolItem(toolbar, SWT.SEPARATOR);
653
654            frameField = new Text(toolbar, SWT.SINGLE | SWT.BORDER | SWT.CENTER);
655            frameField.setFont(curFont);
656            frameField.setText(String.valueOf(curDataFrame));
657            frameField.addTraverseListener(new TraverseListener() {
658                @Override
659                public void keyTraversed(TraverseEvent e) {
660                    if (e.detail == SWT.TRAVERSE_RETURN) {
661                        try {
662                            int frame = 0;
663
664                            try {
665                                frame = Integer.parseInt(frameField.getText().trim()) - indexBase;
666                            }
667                            catch (Exception ex) {
668                                frame = -1;
669                            }
670
671                            gotoFrame(frame);
672                        }
673                        catch (Exception ex) {
674                            log.debug("Frame change failure: ", ex);
675                        }
676                    }
677                }
678            });
679
680            frameField.pack();
681
682            separator.setWidth(frameField.getSize().x + 30);
683            separator.setControl(frameField);
684
685            separator = new ToolItem(toolbar, SWT.SEPARATOR);
686
687            Text maxFrameText = new Text(toolbar, SWT.SINGLE | SWT.BORDER | SWT.CENTER);
688            maxFrameText.setFont(curFont);
689            maxFrameText.setText(String.valueOf(maxDataFrame));
690            maxFrameText.setEditable(false);
691            maxFrameText.setEnabled(false);
692
693            maxFrameText.pack();
694
695            separator.setWidth(maxFrameText.getSize().x + 30);
696            separator.setControl(maxFrameText);
697
698            new ToolItem(toolbar, SWT.SEPARATOR).setWidth(10);
699
700            // Next frame button
701            item = new ToolItem(toolbar, SWT.PUSH);
702            item.setImage(ViewProperties.getNextIcon());
703            item.setToolTipText("Next Frame");
704            item.addSelectionListener(new SelectionAdapter() {
705                @Override
706                public void widgetSelected(SelectionEvent e) {
707                    nextFrame();
708                }
709            });
710
711            // Last frame button
712            item = new ToolItem(toolbar, SWT.PUSH);
713            item.setImage(ViewProperties.getLastIcon());
714            item.setToolTipText("Last Frame");
715            item.addSelectionListener(new SelectionAdapter() {
716                @Override
717                public void widgetSelected(SelectionEvent e) {
718                    lastFrame();
719                }
720            });
721        }
722
723        return toolbar;
724    }
725
726    /*
727     * Creates the menubar for the Shell.
728     */
729    protected Menu createMenuBar(final Shell theShell) {
730        Menu menuBar = new Menu(theShell, SWT.BAR);
731        boolean isEditable = !isReadOnly;
732
733        MenuItem tableMenuItem = new MenuItem(menuBar, SWT.CASCADE);
734        tableMenuItem.setText("&Table");
735
736        Menu tableMenu = new Menu(theShell, SWT.DROP_DOWN);
737        tableMenuItem.setMenu(tableMenu);
738
739        MenuItem item = new MenuItem(tableMenu, SWT.PUSH);
740        item.setText("Select All");
741        item.setAccelerator(SWT.CTRL | 'A');
742        item.addSelectionListener(new SelectionAdapter() {
743            @Override
744            public void widgetSelected(SelectionEvent e) {
745                try {
746                    dataTable.doCommand(new SelectAllCommand());
747                }
748                catch (Exception ex) {
749                    theShell.getDisplay().beep();
750                    Tools.showError(theShell, "Select", ex.getMessage());
751                }
752            }
753        });
754
755        item = new MenuItem(tableMenu, SWT.PUSH);
756        item.setText("Copy");
757        item.setAccelerator(SWT.CTRL | 'C');
758        item.addSelectionListener(new SelectionAdapter() {
759            @Override
760            public void widgetSelected(SelectionEvent e) {
761                copyData();
762            }
763        });
764
765        item = new MenuItem(tableMenu, SWT.PUSH);
766        item.setText("Paste");
767        item.setAccelerator(SWT.CTRL | 'V');
768        item.setEnabled(isEditable);
769        item.addSelectionListener(new SelectionAdapter() {
770            @Override
771            public void widgetSelected(SelectionEvent e) {
772                pasteData();
773            }
774        });
775
776        new MenuItem(tableMenu, SWT.SEPARATOR);
777
778        item = new MenuItem(tableMenu, SWT.PUSH);
779        item.setText("Copy to New Dataset");
780        item.setEnabled(isEditable && (dataObject instanceof ScalarDS));
781        item.addSelectionListener(new SelectionAdapter() {
782            @Override
783            public void widgetSelected(SelectionEvent e) {
784                if ((selectionLayer.getSelectedColumnPositions().length <= 0)
785                        || (selectionLayer.getSelectedRowCount() <= 0)) {
786                    Tools.showInformation(theShell, "Copy", "Select table cells to write.");
787                    return;
788                }
789
790                TreeView treeView = viewer.getTreeView();
791                Group pGroup = (Group) (treeView.findTreeItem((HObject) dataObject).getParentItem().getData());
792                HObject root = ((HObject) dataObject).getFileFormat().getRootObject();
793
794                if (root == null) return;
795
796                ArrayList<HObject> list = new ArrayList<>(((HObject) dataObject).getFileFormat().getNumberOfMembers() + 5);
797                Iterator<HObject> it = ((Group) root).depthFirstMemberList().iterator();
798
799                while (it.hasNext())
800                    list.add(it.next());
801                list.add(root);
802
803                NewDatasetDialog dialog = new NewDatasetDialog(theShell, pGroup, list, DefaultBaseTableView.this);
804                dialog.open();
805
806                HObject obj = dialog.getObject();
807                if (obj != null) {
808                    Group pgroup = dialog.getParentGroup();
809                    try {
810                        treeView.addObject(obj, pgroup);
811                    }
812                    catch (Exception ex) {
813                        log.debug("Write selection to dataset:", ex);
814                    }
815                }
816
817                list.clear();
818            }
819        });
820
821        item = new MenuItem(tableMenu, SWT.PUSH);
822        item.setText("Save Changes to File");
823        item.setAccelerator(SWT.CTRL | 'U');
824        item.setEnabled(isEditable);
825        item.addSelectionListener(new SelectionAdapter() {
826            @Override
827            public void widgetSelected(SelectionEvent e) {
828                try {
829                    updateValueInFile();
830                }
831                catch (Exception ex) {
832                    theShell.getDisplay().beep();
833                    Tools.showError(theShell, "Save", ex.getMessage());
834                }
835            }
836        });
837
838        new MenuItem(tableMenu, SWT.SEPARATOR);
839
840        item = new MenuItem(tableMenu, SWT.PUSH);
841        item.setText("Show Lineplot");
842        item.addSelectionListener(new SelectionAdapter() {
843            @Override
844            public void widgetSelected(SelectionEvent e) {
845                showLineplot();
846            }
847        });
848
849        item = new MenuItem(tableMenu, SWT.PUSH);
850        item.setText("Show Statistics");
851        item.addSelectionListener(new SelectionAdapter() {
852            @Override
853            public void widgetSelected(SelectionEvent e) {
854                try {
855                    Object theData = getSelectedData();
856
857                    if (dataObject instanceof CompoundDS) {
858                        int cols = selectionLayer.getFullySelectedColumnPositions().length;
859                        if (cols != 1) {
860                            Tools.showError(theShell, "Statistics", "Please select one column at a time for compound dataset.");
861                            return;
862                        }
863                    }
864                    else if (theData == null) {
865                        theData = dataValue;
866                    }
867
868                    double[] minmax = new double[2];
869                    double[] stat = new double[2];
870
871                    Tools.findMinMax(theData, minmax, fillValue);
872                    if (Tools.computeStatistics(theData, stat, fillValue) > 0) {
873                        String stats = "Min                      = " + minmax[0] + "\nMax                      = "
874                                + minmax[1] + "\nMean                     = " + stat[0] + "\nStandard deviation = "
875                                + stat[1];
876                        Tools.showInformation(theShell, "Statistics", stats);
877                    }
878
879                    System.gc();
880                }
881                catch (Exception ex) {
882                    theShell.getDisplay().beep();
883                    Tools.showError(shell, "Statistics", ex.getMessage());
884                }
885            }
886        });
887
888        new MenuItem(tableMenu, SWT.SEPARATOR);
889
890        item = new MenuItem(tableMenu, SWT.PUSH);
891        item.setText("Math Conversion");
892        item.setEnabled(isEditable);
893        item.addSelectionListener(new SelectionAdapter() {
894            @Override
895            public void widgetSelected(SelectionEvent e) {
896                try {
897                    mathConversion();
898                }
899                catch (Exception ex) {
900                    shell.getDisplay().beep();
901                    Tools.showError(theShell, "Convert", ex.getMessage());
902                }
903            }
904        });
905
906        new MenuItem(tableMenu, SWT.SEPARATOR);
907
908        item = new MenuItem(tableMenu, SWT.PUSH);
909        item.setText("Close");
910        item.addSelectionListener(new SelectionAdapter() {
911            @Override
912            public void widgetSelected(SelectionEvent e) {
913                theShell.dispose();
914            }
915        });
916
917
918        /********************************************************************
919         *                                                                  *
920         * Set up MenuItems for Importing/Exporting Data from the TableView *
921         *                                                                  *
922         ********************************************************************/
923
924
925        MenuItem importExportMenuItem = new MenuItem(menuBar, SWT.CASCADE);
926        importExportMenuItem.setText("&Import/Export Data");
927
928        Menu importExportMenu = new Menu(theShell, SWT.DROP_DOWN);
929        importExportMenuItem.setMenu(importExportMenu);
930
931        item = new MenuItem(importExportMenu, SWT.CASCADE);
932        item.setText("Export Data to");
933
934        Menu exportMenu = new Menu(item);
935        item.setMenu(exportMenu);
936
937        item = new MenuItem(exportMenu, SWT.PUSH);
938        item.setText("Text File");
939        item.addSelectionListener(new SelectionAdapter() {
940            @Override
941            public void widgetSelected(SelectionEvent e) {
942                try {
943                    saveAsText();
944                }
945                catch (Exception ex) {
946                    theShell.getDisplay().beep();
947                    Tools.showError(theShell, "Save", ex.getMessage());
948                }
949            }
950        });
951
952        item = new MenuItem(importExportMenu, SWT.CASCADE);
953        item.setText("Import Data from");
954
955        Menu importMenu = new Menu(item);
956        item.setMenu(importMenu);
957
958        item = new MenuItem(importMenu, SWT.PUSH);
959        item.setText("Text File");
960        item.setEnabled(!isReadOnly);
961        item.addSelectionListener(new SelectionAdapter() {
962            @Override
963            public void widgetSelected(SelectionEvent e) {
964                String currentDir = ((HObject) dataObject).getFileFormat().getParent();
965
966                String filename = null;
967                if (((HDFView) viewer).getTestState()) {
968                    filename = currentDir + File.separator + new InputDialog(theShell, "Enter a file name", "").open();
969                }
970                else {
971                    FileDialog fChooser = new FileDialog(theShell, SWT.OPEN);
972                    fChooser.setFilterPath(currentDir);
973
974                    DefaultFileFilter filter = DefaultFileFilter.getFileFilterText();
975                    fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() });
976                    fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() });
977                    fChooser.setFilterIndex(1);
978
979                    filename = fChooser.open();
980                }
981
982                if (filename == null) return;
983
984                File chosenFile = new File(filename);
985                if (!chosenFile.exists()) {
986                    Tools.showError(theShell, "Import Data From Text File", "Data import error: " + filename + " does not exist.");
987                    return;
988                }
989
990                if (!Tools.showConfirm(theShell, "Import Data From Text File", "Do you want to paste selected data?"))
991                    return;
992
993                importTextData(chosenFile.getAbsolutePath());
994            }
995        });
996
997        return menuBar;
998    }
999
1000    protected void loadData(DataFormat dataObject) throws Exception {
1001        if (!dataObject.isInited()) {
1002            try {
1003                dataObject.init();
1004            }
1005            catch (Exception ex) {
1006                dataValue = null;
1007                log.debug("loadData(): ", ex);
1008                throw ex;
1009            }
1010        }
1011
1012        // use lazy convert for large number of strings
1013        if (dataObject.getHeight() > 10000 && dataObject instanceof CompoundDS) {
1014            ((CompoundDS) dataObject).setConvertByteToString(false);
1015        }
1016
1017        // Make sure entire dataset is not loaded when looking at 3D
1018        // datasets using the default display mode (double clicking the
1019        // data object)
1020        if (dataObject.getRank() > 2) {
1021            dataObject.getSelectedDims()[dataObject.getSelectedIndex()[2]] = 1;
1022        }
1023
1024        dataValue = null;
1025        try {
1026            dataValue = dataObject.getData();
1027        }
1028        catch (Exception ex) {
1029            dataValue = null;
1030            log.debug("loadData(): ", ex);
1031            throw ex;
1032        }
1033    }
1034
1035    protected abstract NatTable createTable(Composite parent, DataFormat dataObject);
1036
1037    protected abstract void showObjRefData(long ref);
1038
1039    protected abstract void showRegRefData(String reg);
1040
1041    /**
1042     * Display data pointed to by object references. Data of each object is shown in
1043     * a separate spreadsheet.
1044     *
1045     * @param ref
1046     *            the array of strings that contain the object reference
1047     *            information.
1048     *
1049     */
1050    @SuppressWarnings({ "rawtypes", "unchecked" })
1051    protected void NewshowObjRefData(long ref) {
1052        long[] oid = { ref };
1053        log.trace("showObjRefData(): start: ref={}", ref);
1054
1055        HObject obj = FileFormat.findObject(((HObject) dataObject).getFileFormat(), oid);
1056        if (obj == null || !(obj instanceof ScalarDS)) {
1057            Tools.showError(shell, "Select", "Could not show object reference data: invalid or null data");
1058            log.debug("showObjRefData(): obj is null or not a Dataset");
1059            return;
1060        }
1061
1062        ScalarDS dset = (ScalarDS) obj;
1063        ScalarDS dsetCopy = null;
1064
1065        // create an instance of the dataset constructor
1066        Constructor<? extends ScalarDS> constructor = null;
1067        Object[] paramObj = null;
1068        Object data = null;
1069
1070        try {
1071            Class[] paramClass = { FileFormat.class, String.class, String.class };
1072            constructor = dset.getClass().getConstructor(paramClass);
1073            paramObj = new Object[] { dset.getFileFormat(), dset.getName(), dset.getPath() };
1074            dsetCopy = constructor.newInstance(paramObj);
1075            data = dsetCopy.getData();
1076        }
1077        catch (Exception ex) {
1078            log.debug("showObjRefData(): couldn't show data: ", ex);
1079            Tools.showError(shell, "Select", "Object Reference: " + ex.getMessage());
1080            data = null;
1081        }
1082
1083        if (data == null) {
1084            return;
1085        }
1086
1087        Class<?> theClass = null;
1088        String viewName = null;
1089
1090        switch (viewType) {
1091            case IMAGE:
1092                viewName = HDFView.getListOfImageViews().get(0);
1093                break;
1094            case TABLE:
1095                viewName = (String) HDFView.getListOfTableViews().get(0);
1096                break;
1097            default:
1098                viewName = null;
1099        }
1100
1101        try {
1102            theClass = Class.forName(viewName);
1103        }
1104        catch (Exception ex) {
1105            try {
1106                theClass = ViewProperties.loadExtClass().loadClass(viewName);
1107            }
1108            catch (Exception ex2) {
1109                theClass = null;
1110            }
1111        }
1112
1113        // Use default dataview
1114        if (theClass == null) {
1115            switch (viewType) {
1116                case IMAGE:
1117                    viewName = ViewProperties.DEFAULT_IMAGEVIEW_NAME;
1118                    break;
1119                case TABLE:
1120                    viewName = ViewProperties.DEFAULT_SCALAR_DATASET_TABLEVIEW_NAME;
1121                    break;
1122                default:
1123                    viewName = null;
1124            }
1125
1126            try {
1127                theClass = Class.forName(viewName);
1128            }
1129            catch (Exception ex) {
1130                log.debug("showObjRefData(): no suitable display class found");
1131                Tools.showError(shell, "Select", "Could not show reference data: no suitable display class found");
1132                return;
1133            }
1134        }
1135
1136        HashMap map = new HashMap(1);
1137        map.put(ViewProperties.DATA_VIEW_KEY.OBJECT, dsetCopy);
1138        Object[] args = { viewer, map };
1139
1140        try {
1141            Tools.newInstance(theClass, args);
1142        }
1143        catch (Exception ex) {
1144            log.debug("showObjRefData(): Could not show reference data: ", ex);
1145            Tools.showError(shell, "Select", "Could not show reference data: " + ex.toString());
1146        }
1147    }
1148
1149    /**
1150     * Display data pointed to by region references. Data of each region is shown in
1151     * a separate spreadsheet. The reg. ref. information is stored in strings of the
1152     * format below:
1153     * <ul>
1154     * <li>For point selections: "<code>file_id:obj_id { [point1] [point2] ...) }</code>", where
1155     * <code>[point1]</code> is in the form of (location_of_dim0, location_of_dim1, ...). For
1156     * example, <code>0:800 { (0,1) (2,11) (1,0) (2,4) }</code></li>
1157     * <li>For rectangle selections: "<code>file_id:obj_id { [corner coordinates1] [corner coordinates2] ... }</code>",
1158     * where [corner coordinates1] is in the form of
1159     * (start_corner)-(oposite_corner).
1160     * For example, <code>0:800 { (0,0)-(0,2) (0,11)-(0,13) (2,0)-(2,2) (2,11)-(2,13) }</code></li>
1161     * </ul>
1162     *
1163     * @param reg
1164     *            the array of strings that contain the reg. ref information.
1165     *
1166     */
1167    @SuppressWarnings({ "rawtypes", "unchecked" })
1168    protected void NewshowRegRefData(String reg) {
1169        log.trace("showRegRefData(): start: reg={}", reg);
1170
1171        if (reg == null || (reg.length() <= 0) || (reg.compareTo("NULL") == 0)) {
1172            Tools.showError(shell, "Select", "Could not show region reference data: invalid or null data");
1173            log.debug("showRegRefData(): ref is null or invalid");
1174            return;
1175        }
1176
1177        boolean isPointSelection = (reg.indexOf('-') <= 0);
1178
1179        // find the object location
1180        String oidStr = reg.substring(reg.indexOf('/'), reg.indexOf("REGION_TYPE")-1);
1181        log.trace("showRegRefData(): isPointSelection={} oidStr={}", isPointSelection,
1182                oidStr);
1183
1184        // decode the region selection
1185        String regStr = reg.substring(reg.indexOf('{') + 1, reg.indexOf('}'));
1186        if (regStr == null || regStr.length() <= 0) {
1187            Tools.showError(shell, "Select", "Could not show region reference data: no region selection made.");
1188            log.debug("showRegRefData(): no region selection made");
1189            return; // no selection
1190        }
1191
1192        // TODO: do we need to do something with what's past the closing bracket
1193        // regStr = reg.substring(reg.indexOf('}') + 1);
1194
1195        StringTokenizer st = new StringTokenizer(regStr);
1196        int nSelections = st.countTokens();
1197        if (nSelections <= 0) {
1198            Tools.showError(shell, "Select", "Could not show region reference data: no region selection made.");
1199            log.debug("showRegRefData(): no region selection made");
1200            return; // no selection
1201        }
1202        log.trace("showRegRefData(): nSelections={}", nSelections);
1203
1204        HObject obj = FileFormat.findObject(((HObject) dataObject).getFileFormat(), oidStr);
1205        if (obj == null || !(obj instanceof ScalarDS)) {
1206            Tools.showError(shell, "Select", "Could not show object reference data: invalid or null data");
1207            log.debug("showRegRefData(): obj is null or not a Dataset");
1208            return;
1209        }
1210
1211        ScalarDS dset = (ScalarDS) obj;
1212        ScalarDS dsetCopy = null;
1213
1214        // create an instance of the dataset constructor
1215        Constructor<? extends ScalarDS> constructor = null;
1216        Object[] paramObj = null;
1217        try {
1218            Class[] paramClass = { FileFormat.class, String.class, String.class };
1219            constructor = dset.getClass().getConstructor(paramClass);
1220            paramObj = new Object[] { dset.getFileFormat(), dset.getName(), dset.getPath() };
1221        }
1222        catch (Exception ex) {
1223            log.debug("showRegRefData(): constructor failure: ", ex);
1224            constructor = null;
1225        }
1226
1227        // load each selection into a separate dataset and display it in
1228        // a separate spreadsheet
1229
1230        while (st.hasMoreTokens()) {
1231            try {
1232                dsetCopy = constructor.newInstance(paramObj);
1233            }
1234            catch (Exception ex) {
1235                log.debug("showRegRefData(): constructor newInstance failure: ", ex);
1236                continue;
1237            }
1238
1239            if (dsetCopy == null) {
1240                log.debug("showRegRefData(): continue after null dataset copy");
1241                continue;
1242            }
1243
1244            try {
1245                dsetCopy.init();
1246            }
1247            catch (Exception ex) {
1248                log.debug("showRegRefData(): continue after copied dataset init failure: ",
1249                        ex);
1250                continue;
1251            }
1252
1253            dsetCopy.getRank();
1254            long[] start = dsetCopy.getStartDims();
1255            long[] count = dsetCopy.getSelectedDims();
1256
1257            // set the selected dimension sizes based on the region selection
1258            // info.
1259            int idx = 0;
1260            String sizeStr = null;
1261            String token = st.nextToken();
1262
1263            token = token.replace('(', ' ');
1264            token = token.replace(')', ' ');
1265            if (isPointSelection) {
1266                // point selection
1267                StringTokenizer tmp = new StringTokenizer(token, ",");
1268                while (tmp.hasMoreTokens()) {
1269                    count[idx] = 1;
1270                    sizeStr = tmp.nextToken().trim();
1271                    start[idx] = Long.valueOf(sizeStr);
1272                    idx++;
1273                }
1274            }
1275            else {
1276                // rectangle selection
1277                String startStr = token.substring(0, token.indexOf('-'));
1278                String endStr = token.substring(token.indexOf('-') + 1);
1279                StringTokenizer tmp = new StringTokenizer(startStr, ",");
1280                while (tmp.hasMoreTokens()) {
1281                    sizeStr = tmp.nextToken().trim();
1282                    start[idx] = Long.valueOf(sizeStr);
1283                    idx++;
1284                }
1285
1286                idx = 0;
1287                tmp = new StringTokenizer(endStr, ",");
1288                while (tmp.hasMoreTokens()) {
1289                    sizeStr = tmp.nextToken().trim();
1290                    count[idx] = Long.valueOf(sizeStr) - start[idx] + 1;
1291                    idx++;
1292                }
1293            }
1294
1295            try {
1296                dsetCopy.getData();
1297            }
1298            catch (Exception ex) {
1299                log.debug("showRegRefData(): getData failure: ", ex);
1300                Tools.showError(shell, "Select", "Region Reference: " + ex.getMessage());
1301            }
1302
1303            Class<?> theClass = null;
1304            String viewName = null;
1305
1306            switch (viewType) {
1307                case IMAGE:
1308                    viewName = HDFView.getListOfImageViews().get(0);
1309                    break;
1310                case TABLE:
1311                    viewName = (String) HDFView.getListOfTableViews().get(0);
1312                    break;
1313                default:
1314                    viewName = null;
1315            }
1316
1317            try {
1318                theClass = Class.forName(viewName);
1319            }
1320            catch (Exception ex) {
1321                try {
1322                    theClass = ViewProperties.loadExtClass().loadClass(viewName);
1323                }
1324                catch (Exception ex2) {
1325                    theClass = null;
1326                }
1327            }
1328
1329            // Use default dataview
1330            if (theClass == null) {
1331                switch (viewType) {
1332                    case IMAGE:
1333                        viewName = ViewProperties.DEFAULT_IMAGEVIEW_NAME;
1334                        break;
1335                    case TABLE:
1336                        viewName = ViewProperties.DEFAULT_SCALAR_DATASET_TABLEVIEW_NAME;
1337                        break;
1338                    default:
1339                        viewName = null;
1340                }
1341
1342                try {
1343                    theClass = Class.forName(viewName);
1344                }
1345                catch (Exception ex) {
1346                    log.debug("showRegRefData(): no suitable display class found");
1347                    Tools.showError(shell, "Select", "Could not show reference data: no suitable display class found");
1348                    return;
1349                }
1350            }
1351
1352            HashMap map = new HashMap(1);
1353            map.put(ViewProperties.DATA_VIEW_KEY.OBJECT, dsetCopy);
1354            Object[] args = { viewer, map };
1355
1356            try {
1357                Tools.newInstance(theClass, args);
1358            }
1359            catch (Exception ex) {
1360                log.debug("showRegRefData(): Could not show reference data: ", ex);
1361                Tools.showError(shell, "Select", "Could not show reference data: " + ex.toString());
1362            }
1363        } // (st.hasMoreTokens())
1364    } // end of showRegRefData(String reg)
1365
1366    protected abstract IEditableRule getDataEditingRule(DataFormat dataObject);
1367
1368    protected void updateDataConversionSettings() {
1369        if (dataDisplayConverter != null) {
1370            dataDisplayConverter.setShowAsHex(showAsHex);
1371            dataDisplayConverter.setShowAsBin(showAsBin);
1372            dataDisplayConverter.setNumberFormat(numberFormat);
1373            dataDisplayConverter.setConvertEnum(isEnumConverted);
1374        }
1375    }
1376
1377    /**
1378     * Update dataset's value in file. The changes will go to the file.
1379     */
1380    @Override
1381    public void updateValueInFile() {
1382
1383        if (isReadOnly || !dataProvider.getIsValueChanged() || showAsBin || showAsHex) {
1384            log.debug("updateValueInFile(): file not updated; read-only or unchanged data or displayed as hex or binary");
1385            return;
1386        }
1387
1388        try {
1389            dataObject.write();
1390        }
1391        catch (Exception ex) {
1392            shell.getDisplay().beep();
1393            Tools.showError(shell, "Update", ex.getMessage());
1394            log.debug("updateValueInFile(): ", ex);
1395            return;
1396        }
1397
1398        dataProvider.setIsValueChanged(false);
1399    }
1400
1401    @Override
1402    public HObject getDataObject() {
1403        return (HObject) dataObject;
1404    }
1405
1406    @Override
1407    public Object getTable() {
1408        return dataTable;
1409    }
1410
1411    @Override
1412    public int getSelectedRowCount() {
1413        return selectionLayer.getSelectedRowCount();
1414    }
1415
1416    @Override
1417    public int getSelectedColumnCount() {
1418        return selectionLayer.getSelectedColumnPositions().length;
1419    }
1420
1421    public SelectionLayer getSelectionLayer() {
1422        return selectionLayer;
1423    }
1424
1425    public DataLayer getDataLayer() {
1426        return dataLayer;
1427    }
1428
1429    // Flip to previous 'frame' of Table data
1430    private void previousFrame() {
1431        // Only valid operation if data object has 3 or more dimensions
1432        if (dataObject.getRank() < 3) return;
1433
1434        long[] start = dataObject.getStartDims();
1435        int[] selectedIndex = dataObject.getSelectedIndex();
1436        long curFrame = start[selectedIndex[2]];
1437
1438        if (curFrame == 0) return; // Current frame is the first frame
1439
1440        gotoFrame(curFrame - 1);
1441    }
1442
1443    // Flip to next 'frame' of Table data
1444    private void nextFrame() {
1445        // Only valid operation if data object has 3 or more dimensions
1446        if (dataObject.getRank() < 3) return;
1447
1448        long[] start = dataObject.getStartDims();
1449        int[] selectedIndex = dataObject.getSelectedIndex();
1450        long[] dims = dataObject.getDims();
1451        long curFrame = start[selectedIndex[2]];
1452
1453        if (curFrame == dims[selectedIndex[2]] - 1) return; // Current frame is the last frame
1454
1455        gotoFrame(curFrame + 1);
1456    }
1457
1458    // Flip to the first 'frame' of Table data
1459    private void firstFrame() {
1460        // Only valid operation if data object has 3 or more dimensions
1461        if (dataObject.getRank() < 3) return;
1462
1463        long[] start = dataObject.getStartDims();
1464        int[] selectedIndex = dataObject.getSelectedIndex();
1465        long curFrame = start[selectedIndex[2]];
1466
1467        if (curFrame == 0) return; // Current frame is the first frame
1468
1469        gotoFrame(0);
1470    }
1471
1472    // Flip to the last 'frame' of Table data
1473    private void lastFrame() {
1474        // Only valid operation if data object has 3 or more dimensions
1475        if (dataObject.getRank() < 3) return;
1476
1477        long[] start = dataObject.getStartDims();
1478        int[] selectedIndex = dataObject.getSelectedIndex();
1479        long[] dims = dataObject.getDims();
1480        long curFrame = start[selectedIndex[2]];
1481
1482        if (curFrame == dims[selectedIndex[2]] - 1) return; // Current page is the last page
1483
1484        gotoFrame(dims[selectedIndex[2]] - 1);
1485    }
1486
1487    // Flip to the specified 'frame' of Table data
1488    private void gotoFrame(long idx) {
1489        // Only valid operation if data object has 3 or more dimensions
1490        if (dataObject.getRank() < 3 || idx == (curDataFrame - indexBase)) {
1491            return;
1492        }
1493
1494        // Make sure to save any changes to this frame of data before changing frames
1495        if (dataProvider.getIsValueChanged()) {
1496            updateValueInFile();
1497        }
1498
1499        long[] start = dataObject.getStartDims();
1500        int[] selectedIndex = dataObject.getSelectedIndex();
1501        long[] dims = dataObject.getDims();
1502
1503        // Do a bit of frame index validation
1504        if ((idx < 0) || (idx >= dims[selectedIndex[2]])) {
1505            shell.getDisplay().beep();
1506            Tools.showError(shell, "Select",
1507                    "Frame number must be between " + indexBase + " and " + (dims[selectedIndex[2]] - 1 + indexBase));
1508            return;
1509        }
1510
1511        start[selectedIndex[2]] = idx;
1512        curDataFrame = idx + indexBase;
1513        frameField.setText(String.valueOf(curDataFrame));
1514
1515        dataObject.clearData();
1516
1517        shell.setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
1518
1519        try {
1520            dataValue = dataObject.getData();
1521
1522            /*
1523             * TODO: Converting data from unsigned C integers to Java integers
1524             *       is currently unsupported for Compound Datasets.
1525             */
1526            if (!(dataObject instanceof CompoundDS))
1527                dataObject.convertFromUnsignedC();
1528
1529            dataValue = dataObject.getData();
1530        }
1531        catch (Exception ex) {
1532            shell.getDisplay().beep();
1533            Tools.showError(shell, "Error loading data", "Dataset getData: " + ex.getMessage());
1534            log.debug("gotoFrame(): ", ex);
1535            dataValue = null;
1536        }
1537        finally {
1538            shell.setCursor(null);
1539        }
1540
1541        dataProvider.updateDataBuffer(dataValue);
1542
1543        dataTable.doCommand(new VisualRefreshCommand());
1544    }
1545
1546    /**
1547     * Copy data from the spreadsheet to the system clipboard.
1548     */
1549    private void copyData() {
1550        StringBuilder sb = new StringBuilder();
1551
1552        Rectangle selection = selectionLayer.getLastSelectedRegion();
1553        if (selection == null) {
1554            Tools.showError(shell, "Copy", "Select data to copy.");
1555            return;
1556        }
1557
1558        int r0 = selectionLayer.getLastSelectedRegion().y; // starting row
1559        int c0 = selectionLayer.getLastSelectedRegion().x; // starting column
1560
1561        if ((r0 < 0) || (c0 < 0)) {
1562            return;
1563        }
1564
1565        int nr = selectionLayer.getSelectedRowCount();
1566        int nc = selectionLayer.getSelectedColumnPositions().length;
1567        int r1 = r0 + nr; // finish row
1568        int c1 = c0 + nc; // finishing column
1569
1570        try {
1571            for (int i = r0; i < r1; i++) {
1572                sb.append(selectionLayer.getDataValueByPosition(c0, i).toString());
1573                for (int j = c0 + 1; j < c1; j++) {
1574                    sb.append("\t").append(selectionLayer.getDataValueByPosition(j, i).toString());
1575                }
1576                sb.append("\n");
1577            }
1578        }
1579        catch (java.lang.OutOfMemoryError err) {
1580            shell.getDisplay().beep();
1581            Tools.showError(shell, "Copy",
1582                    "Copying data to system clipboard failed. \nUse \"export/import data\" for copying/pasting large data.");
1583            return;
1584        }
1585
1586        Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1587        StringSelection contents = new StringSelection(sb.toString());
1588        cb.setContents(contents, null);
1589    }
1590
1591    /**
1592     * Paste data from the system clipboard to the spreadsheet.
1593     */
1594    private void pasteData() {
1595        if (!Tools.showConfirm(shell, "Clipboard Data", "Do you want to paste selected data?")) return;
1596
1597        int cols = selectionLayer.getPreferredColumnCount();
1598        int rows = selectionLayer.getPreferredRowCount();
1599        int r0 = 0;
1600        int c0 = 0;
1601
1602        Rectangle selection = selectionLayer.getLastSelectedRegion();
1603        if (selection != null) {
1604            r0 = selection.y;
1605            c0 = selection.x;
1606        }
1607
1608        if (c0 < 0) {
1609            c0 = 0;
1610        }
1611        if (r0 < 0) {
1612            r0 = 0;
1613        }
1614        int r = r0;
1615        int c = c0;
1616
1617        Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1618        String line = "";
1619        try {
1620            String s = (String) cb.getData(DataFlavor.stringFlavor);
1621
1622            StringTokenizer st = new StringTokenizer(s, "\n");
1623            // read line by line
1624            while (st.hasMoreTokens() && (r < rows)) {
1625                line = st.nextToken();
1626
1627                if (fixedDataLength < 1) {
1628                    // separate by delimiter
1629                    StringTokenizer lt = new StringTokenizer(line, "\t");
1630                    while (lt.hasMoreTokens() && (c < cols)) {
1631                        try {
1632                            dataProvider.setDataValue(c, r, lt.nextToken());
1633                        }
1634                        catch (Exception ex) {
1635                            continue;
1636                        }
1637                        c++;
1638                    }
1639                    r = r + 1;
1640                    c = c0;
1641                }
1642                else {
1643                    // the data has fixed length
1644                    int n = line.length();
1645                    String theVal;
1646                    for (int i = 0; i < n; i = i + fixedDataLength) {
1647                        try {
1648                            theVal = line.substring(i, i + fixedDataLength);
1649                            dataProvider.setDataValue(c, r, theVal);
1650                        }
1651                        catch (Exception ex) {
1652                            continue;
1653                        }
1654                        c++;
1655                    }
1656                }
1657            }
1658        }
1659        catch (Exception ex) {
1660            shell.getDisplay().beep();
1661            Tools.showError(shell, "Paste", ex.getMessage());
1662        }
1663    }
1664
1665    /**
1666     * Save data as text.
1667     *
1668     * @throws Exception
1669     *             if a failure occurred
1670     */
1671    protected void saveAsText() throws Exception {
1672        String currentDir = ((HObject) dataObject).getFileFormat().getParent();
1673
1674        String filename = null;
1675        if (((HDFView) viewer).getTestState()) {
1676            filename = currentDir + File.separator + new InputDialog(shell, "Enter a file name", "").open();
1677        }
1678        else {
1679            FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
1680            fChooser.setFilterPath(currentDir);
1681
1682            DefaultFileFilter filter = DefaultFileFilter.getFileFilterText();
1683            fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() });
1684            fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() });
1685            fChooser.setFilterIndex(1);
1686            fChooser.setText("Save Current Data To Text File --- " + ((HObject) dataObject).getName());
1687
1688            filename = fChooser.open();
1689        }
1690        if (filename == null) return;
1691
1692        File chosenFile = new File(filename);
1693        String fname = chosenFile.getAbsolutePath();
1694
1695        log.trace("saveAsText: file={}", fname);
1696
1697        // Check if the file is in use and prompt for overwrite
1698        if (chosenFile.exists()) {
1699            List<?> fileList = viewer.getTreeView().getCurrentFiles();
1700            if (fileList != null) {
1701                FileFormat theFile = null;
1702                Iterator<?> iterator = fileList.iterator();
1703                while (iterator.hasNext()) {
1704                    theFile = (FileFormat) iterator.next();
1705                    if (theFile.getFilePath().equals(fname)) {
1706                        shell.getDisplay().beep();
1707                        Tools.showError(shell, "Save",
1708                                "Unable to save data to file \"" + fname + "\". \nThe file is being used.");
1709                        return;
1710                    }
1711                }
1712            }
1713
1714            if (!Tools.showConfirm(shell, "Save", "File exists. Do you want to replace it?")) return;
1715        }
1716
1717        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(chosenFile)));
1718
1719        String delName = ViewProperties.getDataDelimiter();
1720        String delimiter = "";
1721
1722        // delimiter must include a tab to be consistent with copy/paste for
1723        // compound fields
1724        if (dataObject instanceof CompoundDS) delimiter = "\t";
1725
1726        if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_TAB)) {
1727            delimiter = "\t";
1728        }
1729        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SPACE)) {
1730            delimiter = " " + delimiter;
1731        }
1732        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COMMA)) {
1733            delimiter = "," + delimiter;
1734        }
1735        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COLON)) {
1736            delimiter = ":" + delimiter;
1737        }
1738        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SEMI_COLON)) {
1739            delimiter = ";" + delimiter;
1740        }
1741
1742        int cols = selectionLayer.getPreferredColumnCount();
1743        int rows = selectionLayer.getPreferredRowCount();
1744
1745        for (int i = 0; i < rows; i++) {
1746            out.print(selectionLayer.getDataValueByPosition(0, i));
1747            for (int j = 1; j < cols; j++) {
1748                out.print(delimiter);
1749                out.print(selectionLayer.getDataValueByPosition(j, i));
1750            }
1751            out.println();
1752        }
1753
1754        out.flush();
1755        out.close();
1756
1757        viewer.showStatus("Data saved to: " + fname);
1758    }
1759
1760    /** Save data as text (from TextView). */
1761    // private void saveAsTextTextView() throws Exception {
1762    // FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
1763    // fChooser.setText("Save Current Data To Text File --- " + dataset.getName());
1764    // fChooser.setFilterPath(dataset.getFileFormat().getParent());
1765    //
1766    // DefaultFileFilter filter = DefaultFileFilter.getFileFilterText();
1767    // fChooser.setFilterExtensions(new String[] {"*", filter.getExtensions()});
1768    // fChooser.setFilterNames(new String[] {"All Files", filter.getDescription()});
1769    // fChooser.setFilterIndex(1);
1770    //
1771    // // fchooser.changeToParentDirectory();
1772    // fChooser.setFileName(dataset.getName() + ".txt");
1773    // fChooser.setOverwrite(true);
1774    //
1775    // String filename = fChooser.open();
1776    //
1777    //  (filename == null) return;
1778    //
1779    // File chosenFile = new File(filename);
1780    //
1781    // // check if the file is in use
1782    // String fname = chosenFile.getAbsolutePath();
1783    // List<FileFormat> fileList = viewer.getTreeView().getCurrentFiles();
1784    //  (fileList != null) {
1785    // FileFormat theFile = null;
1786    // Iterator<FileFormat> iterator = fileList.iterator();
1787    // while (iterator.hasNext()) {
1788    // theFile = iterator.next();
1789    //  (theFile.getFilePath().equals(fname)) {
1790    // Tools.showError(shell, "Save", "Unable to save data to file \"" + fname
1791    // + "\". \nThe file is being used.");
1792    // return;
1793    // }
1794    // }
1795    // }
1796    //
1797    // PrintWriter out = new PrintWriter(new BufferedWriter(new
1798    // FileWriter(chosenFile)));
1799    //
1800    // int rows = text.length;
1801    //  (int i = 0; i < rows; i++) {
1802    // out.print(text[i].trim());
1803    // out.println();
1804    // out.println();
1805    // }
1806    //
1807    // out.flush();
1808    // out.close();
1809    //
1810    // viewer.showStatus("Data saved to: " + fname);
1811    //
1812    // try {
1813    // RandomAccessFile rf = new RandomAccessFile(chosenFile, "r");
1814    // long size = rf.length();
1815    // rf.close();
1816    // viewer.showStatus("File size (bytes): " + size);
1817    // }
1818    // catch (Exception ex) {
1819    // log.debug("raf file size:", ex);
1820    // }
1821    // }
1822
1823    // print the table (from TextView)
1824    // private void print() {
1825    // // StreamPrintServiceFactory[] spsf = StreamPrintServiceFactory
1826    // // .lookupStreamPrintServiceFactories(null, null);
1827    // //  (int i = 0; i < spsf.length; i++) {
1828    // // System.out.println(spsf[i]);
1829    // // }
1830    // // DocFlavor[] docFlavors = spsf[0].getSupportedDocFlavors();
1831    // //  (int i = 0; i < docFlavors.length; i++) {
1832    // // System.out.println(docFlavors[i]);
1833    // // }
1834    //
1835    // // TODO: windows url
1836    // // Get a text DocFlavor
1837    // InputStream is = null;
1838    // try {
1839    // is = new BufferedInputStream(new java.io.FileInputStream(
1840    // "e:\\temp\\t.html"));
1841    // }
1842    // catch (Exception ex) {
1843    // log.debug("Get a text DocFlavor:", ex);
1844    // }
1845    // DocFlavor flavor = DocFlavor.STRING.TEXT_HTML;
1846    //
1847    // // Get all available print services
1848    // PrintService[] services = PrintServiceLookup.lookupPrintServices(null,
1849    // null);
1850    //
1851    // // Print it
1852    // try {
1853    // // Print this job on the first print server
1854    // DocPrintJob job = services[0].createPrintJob();
1855    // Doc doc = new SimpleDoc(is, flavor, null);
1856    //
1857    // job.print(doc, null);
1858    // }
1859    // catch (Exception ex) {
1860    // log.debug("print(): failure: ", ex);
1861    // }
1862    // }
1863
1864    /**
1865     * Save data as binary.
1866     *
1867     * @throws Exception
1868     *             if a failure occurred
1869     */
1870    protected void saveAsBinary() throws Exception {
1871        String currentDir = ((HObject) dataObject).getFileFormat().getParent();
1872
1873        String filename = null;
1874        if (((HDFView) viewer).getTestState()) {
1875            filename = currentDir + File.separator + new InputDialog(shell, "Enter a file name", "").open();
1876        }
1877        else {
1878            FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
1879            fChooser.setFilterPath(currentDir);
1880
1881            DefaultFileFilter filter = DefaultFileFilter.getFileFilterBinary();
1882            fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() });
1883            fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() });
1884            fChooser.setFilterIndex(1);
1885            fChooser.setText("Save Current Data To Binary File --- " + ((HObject) dataObject).getName());
1886
1887            filename = fChooser.open();
1888        }
1889        if (filename == null) return;
1890
1891        File chosenFile = new File(filename);
1892        String fname = chosenFile.getAbsolutePath();
1893
1894        log.trace("saveAsBinary: file={}", fname);
1895
1896        // Check if the file is in use and prompt for overwrite
1897        if (chosenFile.exists()) {
1898            List<?> fileList = viewer.getTreeView().getCurrentFiles();
1899            if (fileList != null) {
1900                FileFormat theFile = null;
1901                Iterator<?> iterator = fileList.iterator();
1902                while (iterator.hasNext()) {
1903                    theFile = (FileFormat) iterator.next();
1904                    if (theFile.getFilePath().equals(fname)) {
1905                        shell.getDisplay().beep();
1906                        Tools.showError(shell, "Save",
1907                                "Unable to save data to file \"" + fname + "\". \nThe file is being used.");
1908                        return;
1909                    }
1910                }
1911            }
1912
1913            if (!Tools.showConfirm(shell, "Save", "File exists. Do you want to replace it?")) return;
1914        }
1915
1916        try (DataOutputStream out = new DataOutputStream(new FileOutputStream(chosenFile))) {
1917            if (dataObject instanceof ScalarDS) {
1918                ((ScalarDS) dataObject).convertToUnsignedC();
1919                Object data = dataObject.getData();
1920                ByteOrder bo = ByteOrder.nativeOrder();
1921
1922                if (binaryOrder == 1)
1923                    bo = ByteOrder.nativeOrder();
1924                else if (binaryOrder == 2)
1925                    bo = ByteOrder.LITTLE_ENDIAN;
1926                else if (binaryOrder == 3)
1927                    bo = ByteOrder.BIG_ENDIAN;
1928
1929                Tools.saveAsBinary(out, data, bo);
1930
1931                viewer.showStatus("Data saved to: " + fname);
1932            }
1933            else
1934                viewer.showError("Data not saved - not a ScalarDS");
1935        }
1936    }
1937
1938    /**
1939     * Import data values from text file.
1940     *
1941     * @param fname
1942     *            the file to import text from
1943     */
1944    protected void importTextData(String fname) {
1945        int cols = selectionLayer.getPreferredColumnCount();
1946        int rows = selectionLayer.getPreferredRowCount();
1947        int r0;
1948        int c0;
1949
1950        Rectangle lastSelection = selectionLayer.getLastSelectedRegion();
1951        if (lastSelection != null) {
1952            r0 = lastSelection.y;
1953            c0 = lastSelection.x;
1954
1955            if (c0 < 0) {
1956                c0 = 0;
1957            }
1958            if (r0 < 0) {
1959                r0 = 0;
1960            }
1961        }
1962        else {
1963            r0 = 0;
1964            c0 = 0;
1965        }
1966
1967        // Start at the first column for compound datasets
1968        if (dataObject instanceof CompoundDS) c0 = 0;
1969
1970        String importLine = null;
1971        StringTokenizer tokenizer1 = null;
1972        try (BufferedReader in = new BufferedReader(new FileReader(fname))) {
1973            try {
1974                importLine = in.readLine();
1975            }
1976            catch (FileNotFoundException ex) {
1977                log.debug("import data values from text file {}:", fname, ex);
1978                return;
1979            }
1980            catch (IOException ex) {
1981                log.debug("read text file {}:", fname, ex);
1982                return;
1983            }
1984
1985            String delName = ViewProperties.getDataDelimiter();
1986            String delimiter = "";
1987
1988            if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_TAB)) {
1989                delimiter = "\t";
1990            }
1991            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SPACE)) {
1992                delimiter = " " + delimiter;
1993            }
1994            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COMMA)) {
1995                delimiter = ",";
1996            }
1997            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COLON)) {
1998                delimiter = ":";
1999            }
2000            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SEMI_COLON)) {
2001                delimiter = ";";
2002            }
2003            String token = null;
2004            int r = r0;
2005            int c = c0;
2006            while ((importLine != null) && (r < rows)) {
2007                if (fixedDataLength > 0) {
2008                    // the data has fixed length
2009                    int n = importLine.length();
2010                    String theVal;
2011                    for (int i = 0; i < n; i = i + fixedDataLength) {
2012                        try {
2013                            theVal = importLine.substring(i, i + fixedDataLength);
2014                            dataProvider.setDataValue(c, r, theVal);
2015                        }
2016                        catch (Exception ex) {
2017                            continue;
2018                        }
2019                        c++;
2020                    }
2021                }
2022                else {
2023                    try {
2024                        tokenizer1 = new StringTokenizer(importLine, delimiter);
2025                        while (tokenizer1.hasMoreTokens() && (c < cols)) {
2026                            token = tokenizer1.nextToken();
2027                            StringTokenizer tokenizer2 = new StringTokenizer(token);
2028                            if (tokenizer2.hasMoreTokens()) {
2029                                while (tokenizer2.hasMoreTokens() && (c < cols)) {
2030                                    dataProvider.setDataValue(c, r, tokenizer2.nextToken());
2031                                    c++;
2032                                }
2033                            }
2034                            else
2035                                c++;
2036                        }
2037                    }
2038                    catch (Exception ex) {
2039                        Tools.showError(shell, "Import", ex.getMessage());
2040                        return;
2041                    }
2042                }
2043
2044                try {
2045                    importLine = in.readLine();
2046                }
2047                catch (IOException ex) {
2048                    log.debug("read text file {}:", fname, ex);
2049                    importLine = null;
2050                }
2051
2052                // Start at the first column for compound datasets
2053                if (dataObject instanceof CompoundDS) {
2054                    c = 0;
2055                }
2056                else {
2057                    c = c0;
2058                }
2059
2060                r++;
2061            } // ((line != null) && (r < rows))
2062        }
2063        catch (IOException ex) {
2064            log.debug("import text file {}:", fname, ex);
2065        }
2066    }
2067
2068    /**
2069     * Import data values from binary file.
2070     */
2071    protected void importBinaryData() {
2072        String currentDir = ((HObject) dataObject).getFileFormat().getParent();
2073
2074        String filename = null;
2075        if (((HDFView) viewer).getTestState()) {
2076            filename = currentDir + File.separator + new InputDialog(shell, "Enter a file name", "").open();
2077        }
2078        else {
2079            FileDialog fChooser = new FileDialog(shell, SWT.OPEN);
2080            fChooser.setFilterPath(currentDir);
2081
2082            DefaultFileFilter filter = DefaultFileFilter.getFileFilterBinary();
2083            fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() });
2084            fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() });
2085            fChooser.setFilterIndex(1);
2086
2087            filename = fChooser.open();
2088        }
2089
2090        if (filename == null) return;
2091
2092        File chosenFile = new File(filename);
2093        if (!chosenFile.exists()) {
2094            Tools.showError(shell, "Import Data from Binary File", "Data import error: " + chosenFile.getName() + " does not exist.");
2095            return;
2096        }
2097
2098        if (!Tools.showConfirm(shell, "Import Data from Binary File", "Do you want to paste selected data?"))
2099            return;
2100
2101        ByteOrder bo = ByteOrder.nativeOrder();
2102        if (binaryOrder == 1)
2103            bo = ByteOrder.nativeOrder();
2104        else if (binaryOrder == 2)
2105            bo = ByteOrder.LITTLE_ENDIAN;
2106        else if (binaryOrder == 3)
2107            bo = ByteOrder.BIG_ENDIAN;
2108
2109        try {
2110            if (Tools.getBinaryDataFromFile(dataValue, chosenFile.getAbsolutePath(), bo))
2111                dataProvider.setIsValueChanged(true);
2112
2113            dataTable.doCommand(new StructuralRefreshCommand());
2114        }
2115        catch (Exception ex) {
2116            log.debug("importBinaryData():", ex);
2117        }
2118        catch (OutOfMemoryError e) {
2119            log.debug("importBinaryData(): Out of memory");
2120        }
2121    }
2122
2123    /**
2124     * Convert selected data based on predefined math functions.
2125     */
2126    private void mathConversion() throws Exception {
2127
2128        if (isReadOnly) {
2129            log.debug("mathConversion(): can't convert read-only data");
2130            return;
2131        }
2132
2133        int cols = selectionLayer.getSelectedColumnPositions().length;
2134        if ((dataObject instanceof CompoundDS) && (cols > 1)) {
2135            shell.getDisplay().beep();
2136            Tools.showError(shell, "Convert", "Please select one column at a time for math conversion" + "for compound dataset.");
2137            log.debug("mathConversion(): more than one column selected for CompoundDS");
2138            return;
2139        }
2140
2141        Object theData = getSelectedData();
2142        if (theData == null) {
2143            shell.getDisplay().beep();
2144            Tools.showError(shell, "Convert", "No data is selected.");
2145            log.debug("mathConversion(): no data selected");
2146            return;
2147        }
2148
2149        MathConversionDialog dialog = new MathConversionDialog(shell, theData);
2150        dialog.open();
2151
2152        if (dialog.isConverted()) {
2153            if (dataObject instanceof CompoundDS) {
2154                Object colData = null;
2155                try {
2156                    colData = ((List<?>) dataObject.getData()).get(selectionLayer.getSelectedColumnPositions()[0]);
2157                }
2158                catch (Exception ex) {
2159                    log.debug("mathConversion(): ", ex);
2160                }
2161
2162                if (colData != null) {
2163                    int size = Array.getLength(theData);
2164                    System.arraycopy(theData, 0, colData, 0, size);
2165                }
2166            }
2167            else {
2168                int rows = selectionLayer.getSelectedRowCount();
2169
2170                // Since NatTable returns the selected row positions as a Set<Range>, convert
2171                // this to
2172                // an Integer[]
2173                Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
2174                Set<Integer> selectedRowPos = new LinkedHashSet<>();
2175                Iterator<Range> i1 = rowPositions.iterator();
2176                while (i1.hasNext()) {
2177                    selectedRowPos.addAll(i1.next().getMembers());
2178                }
2179
2180                int r0 = selectedRowPos.toArray(new Integer[0])[0];
2181                int c0 = selectionLayer.getSelectedColumnPositions()[0];
2182
2183                int w = dataTable.getPreferredColumnCount() - 1;
2184                int idxSrc = 0;
2185                int idxDst = 0;
2186
2187                for (int i = 0; i < rows; i++) {
2188                    idxDst = (r0 + i) * w + c0;
2189                    System.arraycopy(theData, idxSrc, dataValue, idxDst, cols);
2190                    idxSrc += cols;
2191                }
2192            }
2193
2194            System.gc();
2195
2196            dataProvider.setIsValueChanged(true);
2197        }
2198    }
2199
2200    private void showLineplot() {
2201        // Since NatTable returns the selected row positions as a Set<Range>, convert
2202        // this to
2203        // an Integer[]
2204        Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
2205        Set<Integer> selectedRowPos = new LinkedHashSet<>();
2206        Iterator<Range> i1 = rowPositions.iterator();
2207        while (i1.hasNext()) {
2208            selectedRowPos.addAll(i1.next().getMembers());
2209        }
2210
2211        Integer[] rows = selectedRowPos.toArray(new Integer[0]);
2212        int[] cols = selectionLayer.getSelectedColumnPositions();
2213
2214        if ((rows == null) || (cols == null) || (rows.length <= 0) || (cols.length <= 0)) {
2215            shell.getDisplay().beep();
2216            Tools.showError(shell, "Select", "Select rows/columns to draw line plot.");
2217            return;
2218        }
2219
2220        int nrow = dataTable.getPreferredRowCount() - 1;
2221        int ncol = dataTable.getPreferredColumnCount() - 1;
2222
2223        log.trace("DefaultTableView showLineplot: {} - {}", nrow, ncol);
2224        LinePlotOption lpo = new LinePlotOption(shell, SWT.NONE, nrow, ncol);
2225        lpo.open();
2226
2227        int plotType = lpo.getPlotBy();
2228        if (plotType == LinePlotOption.NO_PLOT) {
2229            return;
2230        }
2231
2232        boolean isRowPlot = (plotType == LinePlotOption.ROW_PLOT);
2233        int xIndex = lpo.getXindex();
2234
2235        // figure out to plot data by row or by column
2236        // Plot data by rows if all columns are selected and part of
2237        // rows are selected, otherwise plot data by column
2238        double[][] data = null;
2239        int nLines = 0;
2240        String title = "Lineplot - " + ((HObject) dataObject).getPath() + ((HObject) dataObject).getName();
2241        String[] lineLabels = null;
2242        double[] yRange = { Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY };
2243        double[] xData = null;
2244
2245        if (isRowPlot) {
2246            title += " - by row";
2247            nLines = rows.length;
2248            if (nLines > 10) {
2249                shell.getDisplay().beep();
2250                nLines = 10;
2251                Tools.showWarning(shell, "Select",
2252                        "More than 10 rows are selected.\n" + "The first 10 rows will be displayed.");
2253            }
2254            lineLabels = new String[nLines];
2255            data = new double[nLines][cols.length];
2256
2257            double value = 0.0;
2258            for (int i = 0; i < nLines; i++) {
2259                lineLabels[i] = String.valueOf(rows[i] + indexBase);
2260                for (int j = 0; j < cols.length; j++) {
2261                    data[i][j] = 0;
2262                    try {
2263                        value = Double.parseDouble(selectionLayer.getDataValueByPosition(cols[j], rows[i]).toString());
2264                        data[i][j] = value;
2265                        yRange[0] = Math.min(yRange[0], value);
2266                        yRange[1] = Math.max(yRange[1], value);
2267                    }
2268                    catch (NumberFormatException ex) {
2269                        log.debug("rows[{}]:", i, ex);
2270                    }
2271                }
2272            }
2273
2274            if (xIndex >= 0) {
2275                xData = new double[cols.length];
2276                for (int j = 0; j < cols.length; j++) {
2277                    xData[j] = 0;
2278                    try {
2279                        value = Double.parseDouble(selectionLayer.getDataValueByPosition(cols[j], xIndex).toString());
2280                        xData[j] = value;
2281                    }
2282                    catch (NumberFormatException ex) {
2283                        log.debug("xIndex of {}:", xIndex, ex);
2284                    }
2285                }
2286            }
2287        }
2288        else {
2289            title += " - by column";
2290            nLines = cols.length;
2291            if (nLines > 10) {
2292                shell.getDisplay().beep();
2293                nLines = 10;
2294                Tools.showWarning(shell, "Select",
2295                        "More than 10 columns are selected.\n" + "The first 10 columns will be displayed.");
2296            }
2297            lineLabels = new String[nLines];
2298            data = new double[nLines][rows.length];
2299            double value = 0.0;
2300            for (int j = 0; j < nLines; j++) {
2301                lineLabels[j] = columnHeaderDataProvider.getDataValue(cols[j] + indexBase, 0).toString();
2302                for (int i = 0; i < rows.length; i++) {
2303                    data[j][i] = 0;
2304                    try {
2305                        value = Double.parseDouble(selectionLayer.getDataValueByPosition(cols[j], rows[i]).toString());
2306                        data[j][i] = value;
2307                        yRange[0] = Math.min(yRange[0], value);
2308                        yRange[1] = Math.max(yRange[1], value);
2309                    }
2310                    catch (NumberFormatException ex) {
2311                        log.debug("cols[{}]:", j, ex);
2312                    }
2313                }
2314            }
2315
2316            if (xIndex >= 0) {
2317                xData = new double[rows.length];
2318                for (int j = 0; j < rows.length; j++) {
2319                    xData[j] = 0;
2320                    try {
2321                        value = Double.parseDouble(selectionLayer.getDataValueByPosition(xIndex, rows[j]).toString());
2322                        xData[j] = value;
2323                    }
2324                    catch (NumberFormatException ex) {
2325                        log.debug("xIndex of {}:", xIndex, ex);
2326                    }
2327                }
2328            }
2329        }
2330
2331        int n = removeInvalidPlotData(data, xData, yRange);
2332        if (n < data[0].length) {
2333            double[][] dataNew = new double[data.length][n];
2334            for (int i = 0; i < data.length; i++)
2335                System.arraycopy(data[i], 0, dataNew[i], 0, n);
2336
2337            data = dataNew;
2338
2339            if (xData != null) {
2340                double[] xDataNew = new double[n];
2341                System.arraycopy(xData, 0, xDataNew, 0, n);
2342                xData = xDataNew;
2343            }
2344        }
2345
2346        // allow to draw a flat line: all values are the same
2347        if (yRange[0] == yRange[1]) {
2348            yRange[1] += 1;
2349            yRange[0] -= 1;
2350        }
2351        else if (yRange[0] > yRange[1]) {
2352            shell.getDisplay().beep();
2353            Tools.showError(shell, "Select", "Cannot show line plot for the selected data. \n" + "Please check the data range: ("
2354                    + yRange[0] + ", " + yRange[1] + ").");
2355            return;
2356        }
2357        if (xData == null) { // use array index and length for x data range
2358            xData = new double[2];
2359            xData[0] = indexBase; // 1- or zero-based
2360            xData[1] = data[0].length + (double) indexBase - 1; // maximum index
2361        }
2362
2363        Chart cv = new Chart(shell, title, Chart.LINEPLOT, data, xData, yRange);
2364        cv.setLineLabels(lineLabels);
2365
2366        String cname = dataValue.getClass().getName();
2367        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
2368        if ((dname == 'B') || (dname == 'S') || (dname == 'I') || (dname == 'J')) {
2369            cv.setTypeToInteger();
2370        }
2371
2372        cv.open();
2373    }
2374
2375    /**
2376     * Remove values of NaN, INF from the array.
2377     *
2378     * @param data
2379     *            the data array
2380     * @param xData
2381     *            the x-axis data points
2382     * @param yRange
2383     *            the range of data values
2384     *
2385     * @return number of data points in the plot data if successful; otherwise,
2386     *         returns false.
2387     */
2388    private int removeInvalidPlotData(double[][] data, double[] xData, double[] yRange) {
2389        int idx = 0;
2390        boolean hasInvalid = false;
2391
2392        if (data == null || yRange == null) return -1;
2393
2394        yRange[0] = Double.POSITIVE_INFINITY;
2395        yRange[1] = Double.NEGATIVE_INFINITY;
2396
2397        for (int i = 0; i < data[0].length; i++) {
2398            hasInvalid = false;
2399
2400            for (int j = 0; j < data.length; j++) {
2401                hasInvalid = Tools.isNaNINF(data[j][i]);
2402                if (xData != null) hasInvalid = hasInvalid || Tools.isNaNINF(xData[i]);
2403
2404                if (hasInvalid)
2405                    break;
2406                else {
2407                    data[j][idx] = data[j][i];
2408                    if (xData != null) xData[idx] = xData[i];
2409                    yRange[0] = Math.min(yRange[0], data[j][idx]);
2410                    yRange[1] = Math.max(yRange[1], data[j][idx]);
2411                }
2412            }
2413
2414            if (!hasInvalid) idx++;
2415        }
2416
2417        return idx;
2418    }
2419
2420    /**
2421     * An implementation of a GridLayer with support for column grouping and with
2422     * editing triggered by a double click instead of a single click.
2423     */
2424    protected class EditingGridLayer extends GridLayer {
2425        public EditingGridLayer(ILayer bodyLayer, ILayer columnHeaderLayer, ILayer rowHeaderLayer, ILayer cornerLayer) {
2426            super(bodyLayer, columnHeaderLayer, rowHeaderLayer, cornerLayer, false);
2427
2428            // Left-align cells, change font for rendering cell text
2429            // and add cell data display converter for displaying as
2430            // Hexadecimal, Binary, etc.
2431            this.addConfiguration(new AbstractRegistryConfiguration() {
2432                @Override
2433                public void configureRegistry(IConfigRegistry configRegistry) {
2434                    Style cellStyle = new Style();
2435
2436                    cellStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, HorizontalAlignmentEnum.LEFT);
2437                    cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,
2438                            Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
2439
2440                    if (curFont != null) {
2441                        cellStyle.setAttributeValue(CellStyleAttributes.FONT, curFont);
2442                    }
2443                    else {
2444                        cellStyle.setAttributeValue(CellStyleAttributes.FONT, Display.getDefault().getSystemFont());
2445                    }
2446
2447                    configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle,
2448                            DisplayMode.NORMAL, GridRegion.BODY);
2449
2450                    configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle,
2451                            DisplayMode.SELECT, GridRegion.BODY);
2452
2453                    // Add data display conversion capability
2454                    try {
2455                        dataDisplayConverter = DataDisplayConverterFactory.getDataDisplayConverter(dataObject);
2456
2457                        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER,
2458                                dataDisplayConverter, DisplayMode.NORMAL, GridRegion.BODY);
2459                    }
2460                    catch (Exception ex) {
2461                        log.debug("EditingGridLayer: failed to retrieve a DataDisplayConverter: ", ex);
2462                        dataDisplayConverter = null;
2463                    }
2464                }
2465            });
2466
2467            if (isRegRef || isObjRef) {
2468                // Show data pointed to by reference on double click
2469                this.addConfiguration(new AbstractUiBindingConfiguration() {
2470                    @Override
2471                    public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
2472                        uiBindingRegistry.registerDoubleClickBinding(new MouseEventMatcher(), new IMouseAction() {
2473                            @Override
2474                            public void run(NatTable table, MouseEvent event) {
2475                                if (!(isRegRef || isObjRef)) return;
2476
2477                                viewType = ViewType.TABLE;
2478
2479                                Object theData = null;
2480                                try {
2481                                    theData = ((Dataset) getDataObject()).getData();
2482                                }
2483                                catch (Exception ex) {
2484                                    log.debug("show reference data: ", ex);
2485                                    theData = null;
2486                                    Tools.showError(shell, "Select", ex.getMessage());
2487                                }
2488
2489                                if (theData == null) {
2490                                    shell.getDisplay().beep();
2491                                    Tools.showError(shell, "Select", "No data selected.");
2492                                    return;
2493                                }
2494
2495                                // Since NatTable returns the selected row positions as a Set<Range>, convert
2496                                // this to an Integer[]
2497                                Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
2498                                Set<Integer> selectedRowPos = new LinkedHashSet<>();
2499                                Iterator<Range> i1 = rowPositions.iterator();
2500                                while (i1.hasNext()) {
2501                                    selectedRowPos.addAll(i1.next().getMembers());
2502                                }
2503
2504                                Integer[] selectedRows = selectedRowPos.toArray(new Integer[0]);
2505                                if (selectedRows == null || selectedRows.length <= 0) {
2506                                    log.debug("show reference data: no data selected");
2507                                    Tools.showError(shell, "Select", "No data selected.");
2508                                    return;
2509                                }
2510                                int len = Array.getLength(selectedRows);
2511                                for (int i = 0; i < len; i++) {
2512                                    if (isRegRef)
2513                                        showRegRefData((String) Array.get(theData, selectedRows[i]));
2514                                    else if (isObjRef)
2515                                        showObjRefData(Array.getLong(theData, selectedRows[i]));
2516                                }
2517                            }
2518                        });
2519                    }
2520                });
2521            }
2522            else {
2523                // Add default bindings for editing
2524                this.addConfiguration(new DefaultEditConfiguration());
2525
2526                // Register cell editing rules with the table and add
2527                // data validation
2528                this.addConfiguration(new AbstractRegistryConfiguration() {
2529                    @Override
2530                    public void configureRegistry(IConfigRegistry configRegistry) {
2531                        IEditableRule editingRule = getDataEditingRule(dataObject);
2532                        if (editingRule != null) {
2533                            // Register cell editing rules with table
2534                            configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
2535                                    editingRule, DisplayMode.EDIT);
2536                        }
2537
2538                        // Add data validator and validation error handler
2539                        DataValidator validator = null;
2540                        try {
2541                            validator = DataValidatorFactory.getDataValidator(dataObject);
2542                        }
2543                        catch (Exception ex) {
2544                            log.debug("EditingGridLayer: no DataValidator retrieved, data editing will be disabled");
2545                        }
2546
2547                        if (validator != null) {
2548                            configRegistry.registerConfigAttribute(EditConfigAttributes.DATA_VALIDATOR, validator,
2549                                    DisplayMode.EDIT, GridRegion.BODY);
2550                        }
2551
2552                        configRegistry.registerConfigAttribute(EditConfigAttributes.VALIDATION_ERROR_HANDLER,
2553                                new DialogErrorHandling(), DisplayMode.EDIT, GridRegion.BODY);
2554                    }
2555                });
2556
2557                // Change cell editing to be on double click rather than single click
2558                // and allow editing of cells by pressing keys as well
2559                this.addConfiguration(new AbstractUiBindingConfiguration() {
2560                    @Override
2561                    public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
2562                        uiBindingRegistry.registerFirstKeyBinding(new LetterOrDigitKeyEventMatcher(), new KeyEditAction());
2563                        uiBindingRegistry.registerFirstDoubleClickBinding(
2564                                new CellEditorMouseEventMatcher(), new MouseEditAction());
2565                    }
2566                });
2567            }
2568        }
2569    }
2570
2571    /**
2572     * An implementation of the table's Row Header which adapts to the current font.
2573     */
2574    protected class RowHeader extends RowHeaderLayer {
2575        public RowHeader(IUniqueIndexLayer baseLayer, ILayer verticalLayerDependency, SelectionLayer selectionLayer) {
2576            super(baseLayer, verticalLayerDependency, selectionLayer);
2577
2578            this.addConfiguration(new DefaultRowHeaderLayerConfiguration() {
2579                @Override
2580                public void addRowHeaderStyleConfig() {
2581                    this.addConfiguration(new DefaultRowHeaderStyleConfiguration() {
2582                        {
2583                            this.cellPainter = new LineBorderDecorator(new TextPainter(false, true, 2, true));
2584                            this.bgColor = Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
2585                            this.font = (curFont == null) ? Display.getDefault().getSystemFont() : curFont;
2586                        }
2587                    });
2588                }
2589            });
2590        }
2591    }
2592
2593    /**
2594     * Custom Row Header data provider to set row indices based on Index Base for
2595     * both Scalar Datasets and Compound Datasets.
2596     */
2597    protected class RowHeaderDataProvider implements IDataProvider {
2598
2599        private final int    rank;
2600        private final long[] dims;
2601        private final long[] startArray;
2602        private final long[] strideArray;
2603        private final int[]  selectedIndex;
2604
2605        protected final int  start;
2606        protected final int  stride;
2607
2608        private final int    nrows;
2609
2610        public RowHeaderDataProvider(DataFormat theDataObject) {
2611            this.rank = theDataObject.getRank();
2612            this.dims = theDataObject.getSelectedDims();
2613            this.startArray = theDataObject.getStartDims();
2614            this.strideArray = theDataObject.getStride();
2615            this.selectedIndex = theDataObject.getSelectedIndex();
2616
2617            if (rank > 1) {
2618                this.nrows = (int) theDataObject.getHeight();
2619            }
2620            else {
2621                this.nrows = (int) dims[0];
2622            }
2623
2624            start = (int) startArray[selectedIndex[0]];
2625            stride = (int) strideArray[selectedIndex[0]];
2626        }
2627
2628        @Override
2629        public int getColumnCount() {
2630            return 1;
2631        }
2632
2633        @Override
2634        public int getRowCount() {
2635            return nrows;
2636        }
2637
2638        @Override
2639        public Object getDataValue(int columnIndex, int rowIndex) {
2640            return String.valueOf(start + indexBase + (rowIndex * stride));
2641        }
2642
2643        @Override
2644        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
2645            // Intentional
2646        }
2647    }
2648
2649    /**
2650     * An implementation of the table's Column Header which adapts to the current
2651     * font.
2652     */
2653    protected class ColumnHeader extends ColumnHeaderLayer {
2654        public ColumnHeader(IUniqueIndexLayer baseLayer, ILayer horizontalLayerDependency,
2655                SelectionLayer selectionLayer) {
2656            super(baseLayer, horizontalLayerDependency, selectionLayer);
2657
2658            this.addConfiguration(new DefaultColumnHeaderLayerConfiguration() {
2659                @Override
2660                public void addColumnHeaderStyleConfig() {
2661                    this.addConfiguration(new DefaultColumnHeaderStyleConfiguration() {
2662                        {
2663                            this.cellPainter = new BeveledBorderDecorator(new TextPainter(false, true, 2, true));
2664                            this.bgColor = Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
2665                            this.font = (curFont == null) ? Display.getDefault().getSystemFont() : curFont;
2666                        }
2667                    });
2668                }
2669            });
2670        }
2671    }
2672
2673    // Context-menu for dealing with region and object references
2674    protected class RefContextMenu extends AbstractUiBindingConfiguration {
2675        private final Menu contextMenu;
2676
2677        public RefContextMenu(NatTable table) {
2678            this.contextMenu = createMenu(table).build();
2679        }
2680
2681        private PopupMenuBuilder createMenu(NatTable table) {
2682            Menu menu = new Menu(table);
2683
2684            MenuItem item = new MenuItem(menu, SWT.PUSH);
2685            item.setText("Show As &Table");
2686            item.addSelectionListener(new SelectionAdapter() {
2687                @Override
2688                public void widgetSelected(SelectionEvent e) {
2689                    viewType = ViewType.TABLE;
2690
2691                    log.trace("show reference data: Show data as {}", viewType);
2692
2693                    Object theData = getSelectedData();
2694                    if (theData == null) {
2695                        shell.getDisplay().beep();
2696                        Tools.showError(shell, "Select", "No data selected.");
2697                        return;
2698                    }
2699
2700                    // Since NatTable returns the selected row positions as a Set<Range>, convert
2701                    // this to an Integer[]
2702                    Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
2703                    Set<Integer> selectedRowPos = new LinkedHashSet<>();
2704                    Iterator<Range> i1 = rowPositions.iterator();
2705                    while (i1.hasNext()) {
2706                        selectedRowPos.addAll(i1.next().getMembers());
2707                    }
2708
2709                    Integer[] selectedRows = selectedRowPos.toArray(new Integer[0]);
2710                    int[] selectedCols = selectionLayer.getSelectedColumnPositions();
2711                    if (selectedRows == null || selectedRows.length <= 0) {
2712                        shell.getDisplay().beep();
2713                        Tools.showError(shell, "Select", "No data selected.");
2714                        log.trace("show reference data: Show data as {}: selectedRows is empty", viewType);
2715                        return;
2716                    }
2717
2718                    int len = Array.getLength(selectedRows) * Array.getLength(selectedCols);
2719                    log.trace("show reference data: Show data as {}: len={}", viewType, len);
2720
2721                    for (int i = 0; i < len; i++) {
2722                        if (isRegRef) {
2723                            log.trace("show reference data: Show data[{}] as {}: isRegRef={}", i, viewType, isRegRef);
2724                            showRegRefData((String) Array.get(theData, i));
2725                        }
2726                        else if (isObjRef) {
2727                            log.trace("show reference data: Show data[{}] as {}: isObjRef={}", i, viewType, isObjRef);
2728                            showObjRefData(Array.getLong(theData, i));
2729                        }
2730                    }
2731                }
2732            });
2733
2734            item = new MenuItem(menu, SWT.PUSH);
2735            item.setText("Show As &Image");
2736            item.addSelectionListener(new SelectionAdapter() {
2737                @Override
2738                public void widgetSelected(SelectionEvent e) {
2739                    viewType = ViewType.IMAGE;
2740
2741                    log.trace("show reference data: Show data as {}: ", viewType);
2742
2743                    Object theData = getSelectedData();
2744                    if (theData == null) {
2745                        shell.getDisplay().beep();
2746                        Tools.showError(shell, "Select", "No data selected.");
2747                        return;
2748                    }
2749
2750                    // Since NatTable returns the selected row positions as a Set<Range>, convert
2751                    // this to an Integer[]
2752                    Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
2753                    Set<Integer> selectedRowPos = new LinkedHashSet<>();
2754                    Iterator<Range> i1 = rowPositions.iterator();
2755                    while (i1.hasNext()) {
2756                        selectedRowPos.addAll(i1.next().getMembers());
2757                    }
2758
2759                    Integer[] selectedRows = selectedRowPos.toArray(new Integer[0]);
2760                    int[] selectedCols = selectionLayer.getSelectedColumnPositions();
2761                    if (selectedRows == null || selectedRows.length <= 0) {
2762                        shell.getDisplay().beep();
2763                        Tools.showError(shell, "Select", "No data selected.");
2764                        log.trace("show reference data: Show data as {}: selectedRows is empty", viewType);
2765                        return;
2766                    }
2767
2768                    int len = Array.getLength(selectedRows) * Array.getLength(selectedCols);
2769                    log.trace("show reference data: Show data as {}: len={}", viewType, len);
2770
2771                    for (int i = 0; i < len; i++) {
2772                        if (isRegRef) {
2773                            log.trace("show reference data: Show data[{}] as {}: isRegRef={}", i, viewType, isRegRef);
2774                            showRegRefData((String) Array.get(theData, i));
2775                        }
2776                        else if (isObjRef) {
2777                            log.trace("show reference data: Show data[{}] as {}: isObjRef={}", i, viewType, isObjRef);
2778                            showObjRefData(Array.getLong(theData, i));
2779                        }
2780                    }
2781                }
2782            });
2783
2784            // item = new MenuItem(menu, SWT.PUSH);
2785            // item.setText("Show As &Text");
2786            // item.addSelectionListener(new SelectionAdapter() {
2787            //     public void widgetSelected(SelectionEvent e) {
2788            //         viewType = ViewType.IMAGE;
2789            //
2790            //         log.trace("show reference data: Show data as {}: ", viewType);
2791            //
2792            //         Object theData = getSelectedData();
2793            //         if (theData == null) {
2794            //             shell.getDisplay().beep();
2795            //             Tools.showError(shell, "Select", "No data selected.");
2796            //             return;
2797            //         }
2798            //
2799            //         // Since NatTable returns the selected row positions as a Set<Range>,
2800            //         // convert this to an Integer[]
2801            //         Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
2802            //         Set<Integer> selectedRowPos = new LinkedHashSet<Integer>();
2803            //         Iterator<Range> i1 = rowPositions.iterator();
2804            //         while(i1.hasNext()) {
2805            //             selectedRowPos.addAll(i1.next().getMembers());
2806            //         }
2807            //
2808            //         Integer[] selectedRows = selectedRowPos.toArray(new Integer[0]);
2809            //         int[] selectedCols = selectionLayer.getFullySelectedColumnPositions();
2810            //         if (selectedRows == null || selectedRows.length <= 0) {
2811            //             log.trace("show reference data: Show data as {}: selectedRows is empty",
2812            //                       viewType);
2813            //             return;
2814            //         }
2815            //
2816            //         int len = Array.getLength(selectedRows) * Array.getLength(selectedCols);
2817            //         log.trace("show reference data: Show data as {}: len={}", viewType, len);
2818            //
2819            //         if (int i = 0; i < len; i++) {
2820            //             if (isStdRef) {
2821            //                 log.trace("show reference data: Show data[{}] as {}: isStdRef={}", i,
2822            //                           viewType, isStdRef);
2823            //                 showStdRefData((byte[]) Array.get(theData, i));
2824            //             }
2825            //             else if (isRegRef) {
2826            //                 log.trace("show reference data: Show data[{}] as {}: isRegRef={}", i,
2827            //                           viewType, isRegRef);
2828            //                 showRegRefData((String) Array.get(theData, i));
2829            //             }
2830            //             else if (isObjRef) {
2831            //                 log.trace("show reference data: Show data[{}] as {}: isObjRef={}", i,
2832            //                           viewType, isObjRef);
2833            //                 showObjRefData(Array.getLong(theData, i));
2834            //             }
2835            //         }
2836            //     }
2837            // });
2838
2839            return new PopupMenuBuilder(table, menu);
2840        }
2841
2842        @Override
2843        public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
2844            uiBindingRegistry.registerMouseDownBinding(
2845                    new MouseEventMatcher(SWT.NONE, GridRegion.BODY, MouseEventMatcher.RIGHT_BUTTON),
2846                    new PopupMenuAction(this.contextMenu));
2847        }
2848    }
2849
2850    private class LinePlotOption extends Dialog {
2851
2852        private Shell linePlotOptionShell;
2853
2854        private Button rowButton, colButton;
2855
2856        private Combo rowBox, colBox;
2857
2858        public static final int NO_PLOT = -1;
2859        public static final int ROW_PLOT = 0;
2860        public static final int COLUMN_PLOT = 1;
2861
2862        private int nrow, ncol;
2863
2864        private int idx_xaxis = -1;
2865        private int plotType = -1;
2866
2867        public LinePlotOption(Shell parent, int style, int nrow, int ncol) {
2868            super(parent, style);
2869
2870            this.nrow = nrow;
2871            this.ncol = ncol;
2872        }
2873
2874        public void open() {
2875            Shell parent = getParent();
2876            linePlotOptionShell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
2877            linePlotOptionShell.setFont(curFont);
2878            linePlotOptionShell.setText("Line Plot Options -- " + ((HObject) dataObject).getName());
2879            linePlotOptionShell.setImage(ViewProperties.getHdfIcon());
2880            linePlotOptionShell.setLayout(new GridLayout(1, true));
2881
2882            Label label = new Label(linePlotOptionShell, SWT.RIGHT);
2883            label.setFont(curFont);
2884            label.setText("Select Line Plot Options:");
2885
2886            Composite content = new Composite(linePlotOptionShell, SWT.BORDER);
2887            content.setLayout(new GridLayout(3, false));
2888            content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
2889
2890            label = new Label(content, SWT.RIGHT);
2891            label.setFont(curFont);
2892            label.setText(" Series in:");
2893
2894            colButton = new Button(content, SWT.RADIO);
2895            colButton.setFont(curFont);
2896            colButton.setText("Column");
2897            colButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
2898            colButton.addSelectionListener(new SelectionAdapter() {
2899                @Override
2900                public void widgetSelected(SelectionEvent e) {
2901                    colBox.setEnabled(true);
2902                    rowBox.setEnabled(false);
2903                }
2904            });
2905
2906            rowButton = new Button(content, SWT.RADIO);
2907            rowButton.setFont(curFont);
2908            rowButton.setText("Row");
2909            rowButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
2910            rowButton.addSelectionListener(new SelectionAdapter() {
2911                @Override
2912                public void widgetSelected(SelectionEvent e) {
2913                    rowBox.setEnabled(true);
2914                    colBox.setEnabled(false);
2915                }
2916            });
2917
2918            label = new Label(content, SWT.RIGHT);
2919            label.setFont(curFont);
2920            label.setText(" For abscissa use:");
2921
2922            long[] startArray = dataObject.getStartDims();
2923            long[] strideArray = dataObject.getStride();
2924            int[] selectedIndex = dataObject.getSelectedIndex();
2925            int start = (int) startArray[selectedIndex[0]];
2926            int stride = (int) strideArray[selectedIndex[0]];
2927
2928            colBox = new Combo(content, SWT.SINGLE | SWT.READ_ONLY);
2929            colBox.setFont(curFont);
2930            GridData colBoxData = new GridData(SWT.FILL, SWT.FILL, true, false);
2931            colBoxData.minimumWidth = 100;
2932            colBox.setLayoutData(colBoxData);
2933
2934            colBox.add("array index");
2935
2936            for (int i = 0; i < ncol; i++) {
2937                colBox.add("column " + columnHeaderDataProvider.getDataValue(i, 0));
2938            }
2939
2940            rowBox = new Combo(content, SWT.SINGLE | SWT.READ_ONLY);
2941            rowBox.setFont(curFont);
2942            GridData rowBoxData = new GridData(SWT.FILL, SWT.FILL, true, false);
2943            rowBoxData.minimumWidth = 100;
2944            rowBox.setLayoutData(rowBoxData);
2945
2946            rowBox.add("array index");
2947
2948            for (int i = 0; i < nrow; i++) {
2949                rowBox.add("row " + (start + indexBase + i * stride));
2950            }
2951
2952            // Create Ok/Cancel button region
2953            Composite buttonComposite = new Composite(linePlotOptionShell, SWT.NONE);
2954            buttonComposite.setLayout(new GridLayout(2, true));
2955            buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
2956
2957            Button okButton = new Button(buttonComposite, SWT.PUSH);
2958            okButton.setFont(curFont);
2959            okButton.setText("   &OK   ");
2960            okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
2961            okButton.addSelectionListener(new SelectionAdapter() {
2962                @Override
2963                public void widgetSelected(SelectionEvent e) {
2964                    if (colButton.getSelection()) {
2965                        idx_xaxis = colBox.getSelectionIndex() - 1;
2966                        plotType = COLUMN_PLOT;
2967                    }
2968                    else {
2969                        idx_xaxis = rowBox.getSelectionIndex() - 1;
2970                        plotType = ROW_PLOT;
2971                    }
2972
2973                    linePlotOptionShell.dispose();
2974                }
2975            });
2976
2977            Button cancelButton = new Button(buttonComposite, SWT.PUSH);
2978            cancelButton.setFont(curFont);
2979            cancelButton.setText(" &Cancel ");
2980            cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
2981            cancelButton.addSelectionListener(new SelectionAdapter() {
2982                @Override
2983                public void widgetSelected(SelectionEvent e) {
2984                    plotType = NO_PLOT;
2985                    linePlotOptionShell.dispose();
2986                }
2987            });
2988
2989            colButton.setSelection(true);
2990            rowButton.setSelection(false);
2991
2992            colBox.select(0);
2993            rowBox.select(0);
2994
2995            colBox.setEnabled(colButton.getSelection());
2996            rowBox.setEnabled(rowButton.getSelection());
2997
2998            linePlotOptionShell.pack();
2999
3000            linePlotOptionShell.setMinimumSize(linePlotOptionShell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
3001
3002            Rectangle parentBounds = parent.getBounds();
3003            Point shellSize = linePlotOptionShell.getSize();
3004            linePlotOptionShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
3005                    (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
3006
3007            linePlotOptionShell.open();
3008
3009            Display display = parent.getDisplay();
3010            while (!linePlotOptionShell.isDisposed()) {
3011                if (!display.readAndDispatch()) display.sleep();
3012            }
3013        }
3014
3015        int getXindex() {
3016            return idx_xaxis;
3017        }
3018
3019        int getPlotBy() {
3020            return plotType;
3021        }
3022    }
3023}