    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}