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 COPYING file, which can be found  *
009 * at the root of the source code distribution tree,                         *
010 * or in https://www.hdfgroup.org/licenses.                                  *
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.lang.reflect.Array;
018import java.lang.reflect.Constructor;
019import java.math.BigInteger;
020import java.util.ArrayList;
021import java.util.HashMap;
022import java.util.Iterator;
023import java.util.LinkedHashSet;
024import java.util.List;
025import java.util.Set;
026import java.util.StringTokenizer;
027
028import hdf.object.Attribute;
029import hdf.object.DataFormat;
030import hdf.object.Dataset;
031import hdf.object.Datatype;
032import hdf.object.FileFormat;
033import hdf.object.HObject;
034import hdf.object.ScalarDS;
035import hdf.object.Utils;
036import hdf.object.h5.H5Datatype;
037import hdf.object.h5.H5File;
038import hdf.object.h5.H5ReferenceType;
039import hdf.object.h5.H5ReferenceType.H5ReferenceData;
040import hdf.object.h5.H5ScalarAttr;
041import hdf.view.DataView.DataViewManager;
042import hdf.view.HDFView;
043import hdf.view.Tools;
044import hdf.view.ViewProperties;
045import hdf.view.dialog.InputDialog;
046
047import hdf.hdf5lib.HDF5Constants;
048
049import org.slf4j.Logger;
050import org.slf4j.LoggerFactory;
051
052import org.eclipse.nebula.widgets.nattable.NatTable;
053import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommand;
054import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
055import org.eclipse.nebula.widgets.nattable.config.EditableRule;
056import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
057import org.eclipse.nebula.widgets.nattable.coordinate.PositionCoordinate;
058import org.eclipse.nebula.widgets.nattable.coordinate.Range;
059import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
060import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
061import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
062import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
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.ILayerListener;
068import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
069import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
070import org.eclipse.nebula.widgets.nattable.selection.command.SelectCellCommand;
071import org.eclipse.nebula.widgets.nattable.selection.event.CellSelectionEvent;
072import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
073import org.eclipse.swt.SWT;
074import org.eclipse.swt.custom.ScrolledComposite;
075import org.eclipse.swt.events.DisposeEvent;
076import org.eclipse.swt.events.DisposeListener;
077import org.eclipse.swt.events.SelectionAdapter;
078import org.eclipse.swt.events.SelectionEvent;
079import org.eclipse.swt.widgets.Composite;
080import org.eclipse.swt.widgets.Menu;
081import org.eclipse.swt.widgets.MenuItem;
082import org.eclipse.swt.widgets.Shell;
083
084/**
085 * A class to construct a ScalarDS TableView.
086 */
087public class DefaultScalarDSTableView extends DefaultBaseTableView implements TableView {
088    private static final Logger log = LoggerFactory.getLogger(DefaultScalarDSTableView.class);
089
090    /**
091     * Constructs a ScalarDS TableView with no additional data properties.
092     *
093     * @param theView
094     *            the main HDFView.
095     */
096    public DefaultScalarDSTableView(DataViewManager theView) { this(theView, null); }
097
098    /**
099     * Constructs a ScalarDS TableView with the specified data properties.
100     *
101     * @param theView
102     *            the main HDFView.
103     *
104     * @param dataPropertiesMap
105     *            the properties on how to show the data. The map is used to allow
106     *            applications to pass properties on how to display the data, such
107     *            as: transposing data, showing data as characters, applying a
108     *            bitmask, and etc. Predefined keys are listed at
109     *            ViewProperties.DATA_VIEW_KEY.
110     */
111    @SuppressWarnings("rawtypes")
112    public DefaultScalarDSTableView(DataViewManager theView, HashMap dataPropertiesMap)
113    {
114        super(theView, dataPropertiesMap);
115
116        if (!shell.isDisposed()) {
117            if (System.getProperty("os.name").toLowerCase().contains("mac")) {
118                shell.setImages(ViewProperties.getHdfIcons());
119            }
120            else {
121                shell.setImage(dataObject.getDatatype().isText() ? ViewProperties.getTextIcon()
122                                                                 : ViewProperties.getDatasetIcon());
123            }
124
125            shell.addDisposeListener(new DisposeListener() {
126                @Override
127                public void widgetDisposed(DisposeEvent e)
128                {
129                    if (dataObject instanceof ScalarDS) {
130                        ScalarDS ds = (ScalarDS)dataObject;
131
132                        /*
133                         * Reload the data when it is displayed next time because the display type
134                         * (table or image) may be different.
135                         */
136                        if (ds.isImage())
137                            ds.clearData();
138                    }
139                }
140            });
141
142            viewer.addDataView(this);
143
144            shell.open();
145        }
146    }
147
148    @Override
149    protected void loadData(DataFormat dataObject) throws Exception
150    {
151        super.loadData(dataObject);
152
153        try {
154            if (Tools.applyBitmask(dataValue, bitmask, bitmaskOP)) {
155                isReadOnly    = true;
156                String opName = "Bits ";
157
158                if (bitmaskOP == ViewProperties.BITMASK_OP.AND)
159                    opName = "Bitwise AND ";
160
161                String title = indexBaseGroup.getText();
162                title += ", " + opName + bitmask;
163                indexBaseGroup.setText(title);
164            }
165
166            dataObject.convertFromUnsignedC();
167
168            dataValue = dataObject.getData();
169        }
170        catch (Exception ex) {
171            log.debug("loadData(): ", ex);
172            dataValue = null;
173            throw ex;
174        }
175
176        if (dataValue == null) {
177            log.debug("loadData(): data value is null");
178            throw new RuntimeException("data value is null");
179        }
180
181        fillValue = dataObject.getFillValue();
182        log.trace("loadData(): fillValue={}", fillValue);
183
184        char runtimeTypeClass = Utils.getJavaObjectRuntimeClass(dataValue);
185        log.trace("loadData(): cName={} runtimeTypeClass={}", dataValue.getClass().getName(),
186                  runtimeTypeClass);
187
188        /*
189         * Convert numerical data into character data; only possible cases are byte[]
190         * and short[] (converted from unsigned byte)
191         */
192        if (isDisplayTypeChar && ((runtimeTypeClass == 'B') || (runtimeTypeClass == 'S'))) {
193            int n           = Array.getLength(dataValue);
194            char[] charData = new char[n];
195            for (int i = 0; i < n; i++) {
196                if (runtimeTypeClass == 'B')
197                    charData[i] = (char)Array.getByte(dataValue, i);
198                else if (runtimeTypeClass == 'S')
199                    charData[i] = (char)Array.getShort(dataValue, i);
200            }
201
202            dataValue = charData;
203        }
204        else if ((runtimeTypeClass == 'B') && dataObject.getDatatype().isArray()) {
205            Datatype baseType = dataObject.getDatatype().getDatatypeBase();
206            if (baseType.isString())
207                dataValue = Dataset.byteToString((byte[])dataValue, (int)baseType.getDatatypeSize());
208        }
209    }
210
211    /**
212     * Creates the menubar for the Shell.
213     */
214    @Override
215    protected Menu createMenuBar(final Shell theShell)
216    {
217        Menu baseMenu            = super.createMenuBar(theShell);
218        MenuItem[] baseMenuItems = baseMenu.getItems();
219        MenuItem item            = null;
220
221        /*****************************************************************************
222         *                                                                           *
223         * Add in a few MenuItems for importing/exporting data from/to binary files. *
224         *                                                                           *
225         *****************************************************************************/
226
227        MenuItem importExportMenuItem = null;
228        for (int i = 0; i < baseMenuItems.length; i++)
229            if (baseMenuItems[i].getText().equals("&Import/Export Data"))
230                importExportMenuItem = baseMenuItems[i];
231
232        if (importExportMenuItem != null) {
233            Menu importExportMenu            = importExportMenuItem.getMenu();
234            MenuItem[] importExportMenuItems = importExportMenu.getItems();
235
236            for (int i = 0; i < importExportMenuItems.length; i++)
237                if (importExportMenuItems[i].getText().equals("Export Data to"))
238                    item = importExportMenuItems[i];
239
240            if (item != null) {
241                Menu exportMenu = item.getMenu();
242
243                MenuItem exportAsBinaryMenuItem = new MenuItem(exportMenu, SWT.CASCADE);
244                exportAsBinaryMenuItem.setText("Binary File");
245
246                Menu exportAsBinaryMenu = new Menu(exportAsBinaryMenuItem);
247                exportAsBinaryMenuItem.setMenu(exportAsBinaryMenu);
248
249                item = new MenuItem(exportAsBinaryMenu, SWT.PUSH);
250                item.setText("Native Order");
251                item.addSelectionListener(new SelectionAdapter() {
252                    @Override
253                    public void widgetSelected(SelectionEvent e)
254                    {
255                        binaryOrder = 1;
256
257                        try {
258                            saveAsBinary();
259                        }
260                        catch (Exception ex) {
261                            theShell.getDisplay().beep();
262                            Tools.showError(theShell, "Export", ex.getMessage());
263                        }
264                    }
265                });
266
267                item = new MenuItem(exportAsBinaryMenu, SWT.PUSH);
268                item.setText("Little Endian");
269                item.addSelectionListener(new SelectionAdapter() {
270                    @Override
271                    public void widgetSelected(SelectionEvent e)
272                    {
273                        binaryOrder = 2;
274
275                        try {
276                            saveAsBinary();
277                        }
278                        catch (Exception ex) {
279                            theShell.getDisplay().beep();
280                            Tools.showError(theShell, "Export", ex.getMessage());
281                        }
282                    }
283                });
284
285                item = new MenuItem(exportAsBinaryMenu, SWT.PUSH);
286                item.setText("Big Endian");
287                item.addSelectionListener(new SelectionAdapter() {
288                    @Override
289                    public void widgetSelected(SelectionEvent e)
290                    {
291                        binaryOrder = 3;
292
293                        try {
294                            saveAsBinary();
295                        }
296                        catch (Exception ex) {
297                            theShell.getDisplay().beep();
298                            Tools.showError(theShell, "Export", ex.getMessage());
299                        }
300                    }
301                });
302            }
303
304            item = null;
305            for (int i = 0; i < importExportMenuItems.length; i++)
306                if (importExportMenuItems[i].getText().equals("Import Data from"))
307                    item = importExportMenuItems[i];
308
309            if (item != null) {
310                Menu importMenu = item.getMenu();
311
312                MenuItem importAsBinaryMenuItem = new MenuItem(importMenu, SWT.CASCADE);
313                importAsBinaryMenuItem.setText("Binary File");
314
315                Menu importAsBinaryMenu = new Menu(importAsBinaryMenuItem);
316                importAsBinaryMenuItem.setMenu(importAsBinaryMenu);
317
318                item = new MenuItem(importAsBinaryMenu, SWT.PUSH);
319                item.setText("Native Order");
320                item.setEnabled(!isReadOnly);
321                item.addSelectionListener(new SelectionAdapter() {
322                    @Override
323                    public void widgetSelected(SelectionEvent e)
324                    {
325                        binaryOrder = 1;
326
327                        try {
328                            importBinaryData();
329                        }
330                        catch (Exception ex) {
331                            Tools.showError(theShell, "Import", ex.getMessage());
332                        }
333                    }
334                });
335
336                item = new MenuItem(importAsBinaryMenu, SWT.PUSH);
337                item.setText("Little Endian");
338                item.setEnabled(!isReadOnly);
339                item.addSelectionListener(new SelectionAdapter() {
340                    @Override
341                    public void widgetSelected(SelectionEvent e)
342                    {
343                        binaryOrder = 2;
344
345                        try {
346                            importBinaryData();
347                        }
348                        catch (Exception ex) {
349                            Tools.showError(theShell, "Import", ex.getMessage());
350                        }
351                    }
352                });
353
354                item = new MenuItem(importAsBinaryMenu, SWT.PUSH);
355                item.setText("Big Endian");
356                item.setEnabled(!isReadOnly);
357                item.addSelectionListener(new SelectionAdapter() {
358                    @Override
359                    public void widgetSelected(SelectionEvent e)
360                    {
361                        binaryOrder = 3;
362
363                        try {
364                            importBinaryData();
365                        }
366                        catch (Exception ex) {
367                            Tools.showError(theShell, "Import", ex.getMessage());
368                        }
369                    }
370                });
371            }
372
373            new MenuItem(importExportMenu, SWT.SEPARATOR);
374
375            checkFixedDataLength = new MenuItem(importExportMenu, SWT.CHECK);
376            checkFixedDataLength.setText("Fixed Data Length");
377            checkFixedDataLength.addSelectionListener(new SelectionAdapter() {
378                @Override
379                public void widgetSelected(SelectionEvent e)
380                {
381                    if (!checkFixedDataLength.getSelection()) {
382                        fixedDataLength = -1;
383                        return;
384                    }
385
386                    String str =
387                        new InputDialog(theShell, "",
388                                        "Enter fixed data length when importing text data\n\n"
389                                            + "For example, for a text string of \"12345678\"\n\t\tenter 2,"
390                                            + "the data will be 12, 34, 56, 78\n\t\tenter 4, the data will be"
391                                            + "1234, 5678\n")
392                            .open();
393
394                    if ((str == null) || (str.length() < 1)) {
395                        checkFixedDataLength.setSelection(false);
396                        return;
397                    }
398
399                    try {
400                        fixedDataLength = Integer.parseInt(str);
401                    }
402                    catch (Exception ex) {
403                        fixedDataLength = -1;
404                    }
405
406                    if (fixedDataLength < 1) {
407                        checkFixedDataLength.setSelection(false);
408                    }
409                }
410            });
411        }
412
413        /*****************************************************************************************
414         *                                                                                       *
415         * Add a section for changing the way that data is displayed, e.g. as hexadecimal values *
416         *                                                                                       *
417         *****************************************************************************************/
418
419        MenuItem dataDisplayMenuItem = new MenuItem(baseMenu, SWT.CASCADE);
420        dataDisplayMenuItem.setText("Data Display");
421
422        Menu dataDisplayMenu = new Menu(theShell, SWT.DROP_DOWN);
423        dataDisplayMenuItem.setMenu(dataDisplayMenu);
424
425        checkScientificNotation = new MenuItem(dataDisplayMenu, SWT.CHECK);
426        checkScientificNotation.setText("Show Scientific Notation");
427        checkScientificNotation.addSelectionListener(new SelectionAdapter() {
428            @Override
429            public void widgetSelected(SelectionEvent e)
430            {
431                if (checkScientificNotation.getSelection()) {
432                    if (checkCustomNotation != null)
433                        checkCustomNotation.setSelection(false);
434                    if (checkEnum != null)
435                        checkEnum.setSelection(false);
436                    if (checkHex != null)
437                        checkHex.setSelection(false);
438                    if (checkBin != null)
439                        checkBin.setSelection(false);
440
441                    numberFormat = scientificFormat;
442                    showAsHex    = false;
443                    showAsBin    = false;
444                }
445                else {
446                    numberFormat = normalFormat;
447                }
448
449                updateDataConversionSettings();
450
451                dataTable.doCommand(new VisualRefreshCommand());
452
453                PositionCoordinate lastSelectedCell = getSelectionLayer().getLastSelectedCellPosition();
454                if (lastSelectedCell != null) {
455                    /*
456                     * Send down a cell selection event for the current cell to update the cell
457                     * value labels
458                     */
459                    dataTable.doCommand(new SelectCellCommand(getSelectionLayer(),
460                                                              lastSelectedCell.columnPosition,
461                                                              lastSelectedCell.rowPosition, false, false));
462                }
463            }
464        });
465
466        checkCustomNotation = new MenuItem(dataDisplayMenu, SWT.CHECK);
467        checkCustomNotation.setText("Show Custom Notation");
468        checkCustomNotation.addSelectionListener(new SelectionAdapter() {
469            @Override
470            public void widgetSelected(SelectionEvent e)
471            {
472                if (checkCustomNotation.getSelection()) {
473                    if (checkScientificNotation != null)
474                        checkScientificNotation.setSelection(false);
475                    if (checkEnum != null)
476                        checkEnum.setSelection(false);
477                    if (checkHex != null)
478                        checkHex.setSelection(false);
479                    if (checkBin != null)
480                        checkBin.setSelection(false);
481
482                    numberFormat = customFormat;
483                    showAsHex    = false;
484                    showAsBin    = false;
485                }
486                else {
487                    numberFormat = normalFormat;
488                }
489
490                updateDataConversionSettings();
491
492                dataTable.doCommand(new VisualRefreshCommand());
493
494                PositionCoordinate lastSelectedCell = getSelectionLayer().getLastSelectedCellPosition();
495                if (lastSelectedCell != null) {
496                    /*
497                     * Send down a cell selection event for the current cell to update the cell
498                     * value labels
499                     */
500                    dataTable.doCommand(new SelectCellCommand(getSelectionLayer(),
501                                                              lastSelectedCell.columnPosition,
502                                                              lastSelectedCell.rowPosition, false, false));
503                }
504            }
505        });
506
507        item = new MenuItem(dataDisplayMenu, SWT.PUSH);
508        item.setText("Create custom notation");
509        item.addSelectionListener(new SelectionAdapter() {
510            @Override
511            public void widgetSelected(SelectionEvent e)
512            {
513                String msg =
514                    "Create number format by pattern \nINTEGER . FRACTION E EXPONENT\nusing # for optional digits and 0 for required digits"
515                    + "\nwhere, INTEGER: the pattern for the integer part"
516                    + "\n       FRACTION: the pattern for the fractional part"
517                    + "\n       EXPONENT: the pattern for the exponent part"
518                    + "\n\nFor example, "
519                    + "\n\t the normalized scientific notation format is \"#.0###E0##\""
520                    + "\n\t to make the digits required \"0.00000E000\"\n\n";
521
522                String str = (new InputDialog(theShell, "Create a custom number format", msg,
523                                              customFormat.toPattern()))
524                                 .open();
525
526                if ((str == null) || (str.length() < 1))
527                    return;
528
529                try {
530                    customFormat.applyPattern(str);
531                }
532                catch (Exception ex) {
533                    log.debug("Invalid custom number notation format: {}:", str, ex);
534                    Tools.showError(shell, "Create", "Invalid custom notation format " + str);
535                }
536            }
537        });
538
539        char runtimeTypeClass = Utils.getJavaObjectRuntimeClass(dataValue);
540        boolean isInt = (runtimeTypeClass == 'B' || runtimeTypeClass == 'S' || runtimeTypeClass == 'I' ||
541                         runtimeTypeClass == 'J');
542
543        if (isInt || dataObject.getDatatype().isBitField() || dataObject.getDatatype().isOpaque()) {
544            checkHex = new MenuItem(dataDisplayMenu, SWT.CHECK);
545            checkHex.setText("Show Hexadecimal");
546            checkHex.addSelectionListener(new SelectionAdapter() {
547                @Override
548                public void widgetSelected(SelectionEvent e)
549                {
550                    showAsHex = checkHex.getSelection();
551                    if (showAsHex) {
552                        if (checkScientificNotation != null)
553                            checkScientificNotation.setSelection(false);
554                        if (checkCustomNotation != null)
555                            checkCustomNotation.setSelection(false);
556                        if (checkEnum != null)
557                            checkEnum.setSelection(false);
558                        if (checkBin != null)
559                            checkBin.setSelection(false);
560
561                        showAsBin    = false;
562                        numberFormat = normalFormat;
563                    }
564
565                    updateDataConversionSettings();
566
567                    dataTable.doCommand(new VisualRefreshCommand());
568
569                    PositionCoordinate lastSelectedCell = getSelectionLayer().getLastSelectedCellPosition();
570                    if (lastSelectedCell != null) {
571                        /*
572                         * Send down a cell selection event for the current cell to update the cell
573                         * value labels
574                         */
575                        dataTable.doCommand(
576                            new SelectCellCommand(getSelectionLayer(), lastSelectedCell.columnPosition,
577                                                  lastSelectedCell.rowPosition, false, false));
578                    }
579                }
580            });
581
582            checkBin = new MenuItem(dataDisplayMenu, SWT.CHECK);
583            checkBin.setText("Show Binary");
584            checkBin.addSelectionListener(new SelectionAdapter() {
585                @Override
586                public void widgetSelected(SelectionEvent e)
587                {
588                    showAsBin = checkBin.getSelection();
589                    if (showAsBin) {
590                        if (checkScientificNotation != null)
591                            checkScientificNotation.setSelection(false);
592                        if (checkCustomNotation != null)
593                            checkCustomNotation.setSelection(false);
594                        if (checkEnum != null)
595                            checkEnum.setSelection(false);
596                        if (checkHex != null)
597                            checkHex.setSelection(false);
598
599                        showAsHex    = false;
600                        numberFormat = normalFormat;
601                    }
602
603                    updateDataConversionSettings();
604
605                    dataTable.doCommand(new VisualRefreshCommand());
606
607                    PositionCoordinate lastSelectedCell = getSelectionLayer().getLastSelectedCellPosition();
608                    if (lastSelectedCell != null) {
609                        /*
610                         * Send down a cell selection event for the current cell to update the cell
611                         * value labels
612                         */
613                        dataTable.doCommand(
614                            new SelectCellCommand(getSelectionLayer(), lastSelectedCell.columnPosition,
615                                                  lastSelectedCell.rowPosition, false, false));
616                    }
617                }
618            });
619
620            checkEnum = new MenuItem(dataDisplayMenu, SWT.CHECK);
621            checkEnum.setText("Show Enum Values");
622            checkEnum.addSelectionListener(new SelectionAdapter() {
623                @Override
624                public void widgetSelected(SelectionEvent e)
625                {
626                    isEnumConverted = checkEnum.getSelection();
627                    if (isEnumConverted) {
628                        if (checkScientificNotation != null)
629                            checkScientificNotation.setSelection(false);
630                        if (checkCustomNotation != null)
631                            checkCustomNotation.setSelection(false);
632                        if (checkHex != null)
633                            checkHex.setSelection(false);
634                        if (checkBin != null)
635                            checkBin.setSelection(false);
636
637                        showAsBin    = false;
638                        showAsHex    = false;
639                        numberFormat = normalFormat;
640                    }
641
642                    updateDataConversionSettings();
643
644                    dataTable.doCommand(new VisualRefreshCommand());
645
646                    PositionCoordinate lastSelectedCell = getSelectionLayer().getLastSelectedCellPosition();
647                    if (lastSelectedCell != null) {
648                        /*
649                         * Send down a cell selection event for the current cell to update the cell
650                         * value labels
651                         */
652                        dataTable.doCommand(
653                            new SelectCellCommand(getSelectionLayer(), lastSelectedCell.columnPosition,
654                                                  lastSelectedCell.rowPosition, false, false));
655                    }
656                }
657            });
658        }
659
660        return baseMenu;
661    }
662
663    /**
664     * Creates a NatTable for a Scalar dataset.
665     *
666     * @param parent
667     *            The parent for the NatTable
668     * @param dataObject
669     *            The Scalar dataset for the NatTable to display
670     *
671     * @return The newly created NatTable
672     */
673    @Override
674    protected NatTable createTable(Composite parent, DataFormat dataObject)
675    {
676        // Create body layer
677        try {
678            dataProvider = DataProviderFactory.getDataProvider(dataObject, dataValue, isDataTransposed);
679
680            log.trace("createTable(): rows={} : cols={}", dataProvider.getRowCount(),
681                      dataProvider.getColumnCount());
682
683            dataLayer = new DataLayer(dataProvider);
684        }
685        catch (Exception ex) {
686            log.debug("createTable(): failed to retrieve DataProvider for table: ", ex);
687            return null;
688        }
689
690        selectionLayer                    = new SelectionLayer(dataLayer);
691        final ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
692
693        dataLayer.setDefaultColumnWidth(80);
694
695        // Create the Column Header layer
696        columnHeaderDataProvider = new ScalarDSColumnHeaderDataProvider(dataObject);
697        ColumnHeaderLayer columnHeaderLayer =
698            new ColumnHeader(new DataLayer(columnHeaderDataProvider), viewportLayer, selectionLayer);
699
700        // Create the Row Header layer
701        rowHeaderDataProvider = new RowHeaderDataProvider(dataObject);
702
703        // Try to adapt row height to current font
704        int defaultRowHeight = curFont == null ? 20 : (2 * curFont.getFontData()[0].getHeight());
705
706        DataLayer baseLayer           = new DataLayer(rowHeaderDataProvider, 40, defaultRowHeight);
707        RowHeaderLayer rowHeaderLayer = new RowHeader(baseLayer, viewportLayer, selectionLayer);
708
709        // Create the Corner Layer
710        ILayer cornerLayer = new CornerLayer(
711            new DataLayer(new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider)),
712            rowHeaderLayer, columnHeaderLayer);
713
714        // Create the Grid Layer
715        GridLayer gridLayer =
716            new EditingGridLayer(viewportLayer, columnHeaderLayer, rowHeaderLayer, cornerLayer);
717
718        final NatTable natTable = new NatTable(parent, gridLayer, false);
719        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
720        natTable.addLayerListener(new ScalarDSCellSelectionListener());
721
722        // Create popup menu for region or object ref.
723        if (isStdRef || isRegRef || isObjRef)
724            natTable.addConfiguration(new RefContextMenu(natTable));
725
726        natTable.configure();
727
728        return natTable;
729    }
730
731    /**
732     * Returns the selected data values of the ScalarDS
733     */
734    @Override
735    public Object getSelectedData()
736    {
737        Object selectedData = null;
738
739        // Since NatTable returns the selected row positions as a Set<Range>, convert
740        // this to an Integer[]
741        Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
742        log.trace("getSelectedData() rowPositions: {}", rowPositions);
743        Set<Integer> selectedRowPos = new LinkedHashSet<>();
744        Iterator<Range> i1          = rowPositions.iterator();
745        while (i1.hasNext())
746            selectedRowPos.addAll(i1.next().getMembers());
747
748        Integer[] selectedRows = selectedRowPos.toArray(new Integer[0]);
749        int[] selectedCols     = selectionLayer.getSelectedColumnPositions();
750
751        if (selectedRows == null || selectedRows.length <= 0 || selectedCols == null ||
752            selectedCols.length <= 0) {
753            shell.getDisplay().beep();
754            Tools.showError(shell, "Select", "No data is selected.");
755            return null;
756        }
757
758        int size = selectedCols.length * selectedRows.length;
759        log.trace("getSelectedData() data size: {}", size);
760
761        // the whole table is selected
762        if ((dataTable.getPreferredColumnCount() - 1 == selectedCols.length) &&
763            (dataTable.getPreferredRowCount() - 1 == selectedRows.length))
764            return dataValue;
765
766        if (dataObject.getDatatype().isRef()) {
767            // ref data are stored in bytes
768            selectedData = new byte[size * (int)dataObject.getDatatype().getDatatypeSize()];
769        }
770        else {
771            switch (Utils.getJavaObjectRuntimeClass(dataValue)) {
772            case 'B':
773                selectedData = new byte[size];
774                break;
775            case 'S':
776                selectedData = new short[size];
777                break;
778            case 'I':
779                selectedData = new int[size];
780                break;
781            case 'J':
782                selectedData = new long[size];
783                break;
784            case 'F':
785                selectedData = new float[size];
786                break;
787            case 'D':
788                selectedData = new double[size];
789                break;
790            default:
791                selectedData = null;
792                break;
793            }
794        }
795
796        if (selectedData == null) {
797            shell.getDisplay().beep();
798            Tools.showError(shell, "Select", "Unsupported data type.");
799            return null;
800        }
801
802        log.trace("getSelectedData(): selectedData is type {}", Utils.getJavaObjectRuntimeClass(dataValue));
803
804        int w = dataTable.getPreferredColumnCount() - 1;
805        log.trace("getSelectedData(): getColumnCount={}", w);
806        int idxSrc = 0;
807        int idxDst = 0;
808        log.trace("getSelectedData(): Rows.length={} Cols.length={}", selectedRows.length,
809                  selectedCols.length);
810        for (int i = 0; i < selectedRows.length; i++) {
811            for (int j = 0; j < selectedCols.length; j++) {
812                idxSrc                = selectedRows[i] * w + selectedCols[j];
813                Object dataArrayValue = null;
814                if (dataValue instanceof ArrayList) {
815                    dataArrayValue = ((ArrayList)dataValue).get(idxSrc);
816                    System.arraycopy(dataArrayValue, 0, selectedData, idxDst,
817                                     (int)dataObject.getDatatype().getDatatypeSize());
818                }
819                else {
820                    dataArrayValue = Array.get(dataValue, idxSrc);
821                    Array.set(selectedData, idxDst, dataArrayValue);
822                }
823                log.trace("getSelectedData()[{},{}]: dataValue[{}]={} from r{} and c{}", i, j, idxSrc,
824                          dataArrayValue, selectedRows[i], selectedCols[j]);
825                idxDst++;
826            }
827        }
828
829        return selectedData;
830    }
831
832    /**
833     * Returns an IEditableRule that determines whether cells can be edited.
834     *
835     * Cells can be edited as long as the dataset is not opened in read-only mode
836     * and the data is not currently displayed in hexadecimal, binary, or character
837     * mode.
838     *
839     * @param dataObject
840     *            The dataset for editing
841     *
842     * @return a new IEditableRule for the dataset
843     */
844    @Override
845    protected IEditableRule getDataEditingRule(final DataFormat dataObject)
846    {
847        if (dataObject == null)
848            return null;
849
850        // Only Allow editing if not in read-only mode
851        return new EditableRule() {
852            @Override
853            public boolean isEditable(int columnIndex, int rowIndex)
854            {
855                /*
856                 * TODO: Should be able to edit character-displayed types and datasets when
857                 * displayed as hex/binary.
858                 */
859                return !(isReadOnly || isDisplayTypeChar || showAsBin || showAsHex);
860            }
861        };
862    }
863
864    /**
865     * Display data pointed to by object references. Data of each object is shown in
866     * a separate spreadsheet.
867     *
868     * @param refarr
869     *            the array of bytes that contain the object reference information.
870     *
871     */
872    @Override
873    @SuppressWarnings({"rawtypes", "unchecked"})
874    protected void showObjRefData(byte[] refarr)
875    {
876        log.trace("showObjRefData(): start: refarr={}", refarr);
877
878        if (refarr == null || (refarr.length <= 0) || H5Datatype.zeroArrayCheck(refarr)) {
879            Tools.showError(shell, "Select", "Could not show object reference data: invalid or null data");
880            log.debug("showObjRefData(): refarr is null or invalid");
881            return;
882        }
883
884        String objref =
885            H5Datatype.descReferenceObject(((HObject)dataObject).getFileFormat().getFID(), refarr);
886        log.trace("showObjRefData(): start: objref={}", objref);
887
888        // find the object location
889        String oidStr = objref.substring(objref.indexOf('/'), objref.indexOf("H5O_TYPE_OBJ_REF") - 1);
890        HObject obj   = FileFormat.findObject(((HObject)dataObject).getFileFormat(), oidStr);
891        if (obj == null || !(obj instanceof ScalarDS)) {
892            Tools.showError(shell, "Select", "Could not show object reference data: invalid or null data");
893            log.debug("showObjRefData(): obj is null or not a Scalar Dataset");
894            return;
895        }
896
897        ScalarDS dset     = (ScalarDS)obj;
898        ScalarDS dsetCopy = null;
899
900        // create an instance of the dataset constructor
901        Constructor<? extends ScalarDS> constructor = null;
902        Object[] paramObj                           = null;
903        Object data                                 = null;
904
905        try {
906            Class[] paramClass = {FileFormat.class, String.class, String.class};
907            constructor        = dset.getClass().getConstructor(paramClass);
908            paramObj           = new Object[] {dset.getFileFormat(), dset.getName(), dset.getPath()};
909            dsetCopy           = constructor.newInstance(paramObj);
910            data               = dsetCopy.getData();
911        }
912        catch (Exception ex) {
913            log.debug("showObjRefData(): couldn't show data: ", ex);
914            Tools.showError(shell, "Select", "Object Reference: " + ex.getMessage());
915            data = null;
916        }
917
918        if (data == null)
919            return;
920
921        Class<?> theClass = null;
922        String viewName   = null;
923
924        switch (viewType) {
925        case IMAGE:
926            viewName = HDFView.getListOfImageViews().get(0);
927            break;
928        case TABLE:
929            viewName = (String)HDFView.getListOfTableViews().get(0);
930            break;
931        default:
932            viewName = null;
933        }
934
935        try {
936            theClass = Class.forName(viewName);
937        }
938        catch (Exception ex) {
939            try {
940                theClass = ViewProperties.loadExtClass().loadClass(viewName);
941            }
942            catch (Exception ex2) {
943                theClass = null;
944            }
945        }
946
947        // Use default dataview
948        if (theClass == null) {
949            switch (viewType) {
950            case IMAGE:
951                viewName = ViewProperties.DEFAULT_IMAGEVIEW_NAME;
952                break;
953            case TABLE:
954                viewName = ViewProperties.DEFAULT_SCALAR_DATASET_TABLEVIEW_NAME;
955                break;
956            default:
957                viewName = null;
958            }
959
960            try {
961                theClass = Class.forName(viewName);
962            }
963            catch (Exception ex) {
964                log.debug("showObjRefData(): no suitable display class found");
965                Tools.showError(shell, "Select",
966                                "Could not show reference data: no suitable display class found");
967                return;
968            }
969        }
970
971        HashMap map = new HashMap(1);
972        map.put(ViewProperties.DATA_VIEW_KEY.OBJECT, dsetCopy);
973        Object[] args = {viewer, map};
974
975        try {
976            Tools.newInstance(theClass, args);
977        }
978        catch (Exception ex) {
979            log.debug("showObjRefData(): Could not show reference data: ", ex);
980            Tools.showError(shell, "Select", "Could not show reference data: " + ex.toString());
981        }
982    }
983
984    /**
985     * Display data pointed to by region references. Data of each region is shown in
986     * a separate spreadsheet.
987     *
988     * @param refarr
989     *            the array of bytes that contain the reg. ref information.
990     *
991     */
992    @Override
993    @SuppressWarnings({"rawtypes", "unchecked"})
994    protected void showRegRefData(byte[] refarr)
995    {
996        if (refarr == null || (refarr.length <= 0) || H5Datatype.zeroArrayCheck(refarr)) {
997            Tools.showError(shell, "Select", "Could not show region reference data: invalid or null data");
998            log.debug("showRegRefData(): refarr is null or invalid");
999            return;
1000        }
1001
1002        String reg = null;
1003        if (refarr.length == HDF5Constants.H5R_DSET_REG_REF_BUF_SIZE)
1004            reg = H5Datatype.descRegionDataset(((HObject)dataObject).getFileFormat().getFID(), refarr);
1005        else
1006            reg = ((H5ReferenceType)dataObject.getDatatype()).getReferenceRegion(refarr, false);
1007
1008        boolean isPointSelection = (reg.indexOf('-') <= 0);
1009
1010        // find the object location
1011        String oidStr = reg.substring(reg.indexOf('/'), reg.indexOf("REGION_TYPE") - 1);
1012
1013        // decode the region selection
1014        String regStr = reg.substring(reg.indexOf('{') + 1, reg.indexOf('}'));
1015        if (regStr == null || regStr.length() <= 0) {
1016            Tools.showError(shell, "Select",
1017                            "Could not show region reference data: no region selection made.");
1018            log.debug("showRegRefData(): no region selection made");
1019            return; // no selection
1020        }
1021
1022        // TODO: do we need to do something with what's past the closing bracket
1023        // regStr = reg.substring(reg.indexOf('}') + 1);
1024
1025        StringTokenizer st = new StringTokenizer(regStr);
1026        int nSelections    = st.countTokens();
1027        if (nSelections <= 0) {
1028            Tools.showError(shell, "Select",
1029                            "Could not show region reference data: no region selection made.");
1030            log.debug("showRegRefData(): no region selection made");
1031            return; // no selection
1032        }
1033
1034        HObject obj = FileFormat.findObject(((HObject)dataObject).getFileFormat(), oidStr);
1035        if (obj == null || !(obj instanceof ScalarDS)) {
1036            Tools.showError(shell, "Select", "Could not show object reference data: invalid or null data");
1037            log.debug("showRegRefData(): obj is null or not a Scalar Dataset");
1038            return;
1039        }
1040
1041        ScalarDS dset     = (ScalarDS)obj;
1042        ScalarDS dsetCopy = null;
1043
1044        // create an instance of the dataset constructor
1045        Constructor<? extends ScalarDS> constructor = null;
1046        Object[] paramObj                           = null;
1047        try {
1048            Class[] paramClass = {FileFormat.class, String.class, String.class};
1049            constructor        = dset.getClass().getConstructor(paramClass);
1050            paramObj           = new Object[] {dset.getFileFormat(), dset.getName(), dset.getPath()};
1051        }
1052        catch (Exception ex) {
1053            log.debug("showRegRefData(): constructor failure: ", ex);
1054            constructor = null;
1055        }
1056
1057        // load each selection into a separate dataset and display it in
1058        // a separate spreadsheet
1059
1060        while (st.hasMoreTokens()) {
1061            try {
1062                dsetCopy = constructor.newInstance(paramObj);
1063            }
1064            catch (Exception ex) {
1065                log.debug("showRegRefData(): constructor newInstance failure: ", ex);
1066                continue;
1067            }
1068
1069            if (dsetCopy == null) {
1070                log.debug("showRegRefData(): continue after null dataset copy");
1071                continue;
1072            }
1073
1074            try {
1075                dsetCopy.init();
1076            }
1077            catch (Exception ex) {
1078                log.debug("showRegRefData(): continue after copied dataset init failure: ", ex);
1079                continue;
1080            }
1081
1082            dsetCopy.getRank();
1083            long[] start = dsetCopy.getStartDims();
1084            long[] count = dsetCopy.getSelectedDims();
1085
1086            // set the selected dimension sizes based on the region selection
1087            // info.
1088            int idx        = 0;
1089            String sizeStr = null;
1090            String token   = st.nextToken();
1091
1092            token = token.replace('(', ' ');
1093            token = token.replace(')', ' ');
1094            if (isPointSelection) {
1095                // point selection
1096                StringTokenizer tmp = new StringTokenizer(token, ",");
1097                while (tmp.hasMoreTokens()) {
1098                    count[idx] = 1;
1099                    sizeStr    = tmp.nextToken().trim();
1100                    start[idx] = Long.valueOf(sizeStr);
1101                    idx++;
1102                }
1103            }
1104            else {
1105                // rectangle selection
1106                String startStr     = token.substring(0, token.indexOf('-'));
1107                String endStr       = token.substring(token.indexOf('-') + 1);
1108                StringTokenizer tmp = new StringTokenizer(startStr, ",");
1109                while (tmp.hasMoreTokens()) {
1110                    sizeStr    = tmp.nextToken().trim();
1111                    start[idx] = Long.valueOf(sizeStr);
1112                    idx++;
1113                }
1114
1115                idx = 0;
1116                tmp = new StringTokenizer(endStr, ",");
1117                while (tmp.hasMoreTokens()) {
1118                    sizeStr    = tmp.nextToken().trim();
1119                    count[idx] = Long.valueOf(sizeStr) - start[idx] + 1;
1120                    idx++;
1121                }
1122            }
1123
1124            try {
1125                dsetCopy.getData();
1126            }
1127            catch (Exception ex) {
1128                log.debug("showRegRefData(): getData failure: ", ex);
1129                Tools.showError(shell, "Select", "Region Reference: " + ex.getMessage());
1130            }
1131
1132            Class<?> theClass = null;
1133            String viewName   = null;
1134
1135            switch (viewType) {
1136            case IMAGE:
1137                viewName = HDFView.getListOfImageViews().get(0);
1138                break;
1139            case TABLE:
1140                viewName = (String)HDFView.getListOfTableViews().get(0);
1141                break;
1142            default:
1143                viewName = null;
1144            }
1145
1146            try {
1147                theClass = Class.forName(viewName);
1148            }
1149            catch (Exception ex) {
1150                try {
1151                    theClass = ViewProperties.loadExtClass().loadClass(viewName);
1152                }
1153                catch (Exception ex2) {
1154                    theClass = null;
1155                }
1156            }
1157
1158            // Use default dataview
1159            if (theClass == null) {
1160                switch (viewType) {
1161                case IMAGE:
1162                    viewName = ViewProperties.DEFAULT_IMAGEVIEW_NAME;
1163                    break;
1164                case TABLE:
1165                    viewName = ViewProperties.DEFAULT_SCALAR_DATASET_TABLEVIEW_NAME;
1166                    break;
1167                default:
1168                    viewName = null;
1169                }
1170
1171                try {
1172                    theClass = Class.forName(viewName);
1173                }
1174                catch (Exception ex) {
1175                    log.debug("showRegRefData(): no suitable display class found");
1176                    Tools.showError(shell, "Select",
1177                                    "Could not show reference data: no suitable display class found");
1178                    return;
1179                }
1180            }
1181
1182            HashMap map = new HashMap(1);
1183            map.put(ViewProperties.DATA_VIEW_KEY.OBJECT, dsetCopy);
1184            Object[] args = {viewer, map};
1185
1186            try {
1187                Tools.newInstance(theClass, args);
1188            }
1189            catch (Exception ex) {
1190                log.debug("showRegRefData(): Could not show reference data: ", ex);
1191                Tools.showError(shell, "Select", "Could not show reference data: " + ex.toString());
1192            }
1193        } // (st.hasMoreTokens())
1194    }     // end of showRegRefData()
1195
1196    /**
1197     * Display data pointed to by references. Data of each reference is shown in
1198     * a separate spreadsheet. The std. ref. information is stored in bytes
1199     *
1200     * @param refarr
1201     *            the array of bytes that contain the std. ref information.
1202     *
1203     */
1204    @Override
1205    @SuppressWarnings({"rawtypes", "unchecked"})
1206    protected void showStdRefData(byte[] refarr)
1207    {
1208        if (refarr == null || H5ReferenceType.zeroArrayCheck(refarr)) {
1209            Tools.showError(shell, "Select", "Could not show region reference data: invalid or null data");
1210            log.debug("showStdRefData(): ref is null or invalid");
1211            return;
1212        }
1213
1214        H5ReferenceType refType = ((H5ReferenceType)dataObject.getDatatype());
1215        H5ReferenceData refdata = refType.getReferenceData(refarr);
1216        /* get the filename associated with the reference */
1217        String reffile = refdata.file_name;
1218        if ((refdata.ref_type == HDF5Constants.H5R_DATASET_REGION1) ||
1219            (refdata.ref_type == HDF5Constants.H5R_DATASET_REGION2)) {
1220            String ref_ptr = refType.getReferenceRegion(refarr, false);
1221            if ("REGION_TYPE UNKNOWN".equals(refdata.region_type)) {
1222                String msg = "Reference to " + ref_ptr + " cannot be displayed in a table";
1223                Tools.showInformation(shell, "Reference", msg);
1224            }
1225            else {
1226                showRegRefData(refarr);
1227            }
1228        }
1229        if (((refdata.ref_type == HDF5Constants.H5R_OBJECT1) &&
1230             (refdata.obj_type == HDF5Constants.H5O_TYPE_DATASET)) ||
1231            ((refdata.ref_type == HDF5Constants.H5R_OBJECT2) &&
1232             (refdata.obj_type == HDF5Constants.H5O_TYPE_DATASET))) {
1233            String ref_obj = refdata.obj_name;
1234            showObjStdRefData(ref_obj);
1235        }
1236        else if (refdata.ref_type == HDF5Constants.H5R_ATTR) {
1237            String ref_attr_name = refdata.attr_name;
1238            String ref_obj_name  = refdata.obj_name;
1239            showAttrStdRefData(ref_obj_name, ref_attr_name);
1240        }
1241        else if ("H5O_TYPE_OBJ_REF".equals(refdata.region_type)) {
1242            String msg = "Reference to " + refdata.obj_name + " cannot be displayed in a table";
1243            // String ref_ptr = refType.getObjectReferenceName(refarr);
1244            Tools.showInformation(shell, "Reference", msg);
1245        }
1246        else {
1247            // Other types
1248        }
1249    } // end of showStdRefData(byte[] refarr)
1250
1251    /**
1252     * Display data pointed to by object references. Data of each object is shown in
1253     * a separate spreadsheet.
1254     *
1255     * @param ref
1256     *            the string that contain the object reference information.
1257     *
1258     */
1259    @SuppressWarnings({"rawtypes", "unchecked"})
1260    protected void showObjStdRefData(String ref)
1261    {
1262        if (ref == null || (ref.length() <= 0) || (ref.compareTo("NULL") == 0)) {
1263            Tools.showError(shell, "Select", "Could not show object reference data: invalid or null data");
1264            log.debug("showObjStdRefData(): ref is null or invalid");
1265            return;
1266        }
1267
1268        HObject obj = FileFormat.findObject(((HObject)dataObject).getFileFormat(), ref);
1269        if (obj == null || !(obj instanceof ScalarDS)) {
1270            Tools.showError(shell, "Select", "Could not show object reference data: invalid or null data");
1271            log.debug("showObjStdRefData(): obj is null or not a Scalar Dataset");
1272            return;
1273        }
1274
1275        ScalarDS dset     = (ScalarDS)obj;
1276        ScalarDS dsetCopy = null;
1277
1278        // create an instance of the dataset constructor
1279        Constructor<? extends ScalarDS> constructor = null;
1280        Object[] paramObj                           = null;
1281        Object data                                 = null;
1282
1283        try {
1284            Class[] paramClass = {FileFormat.class, String.class, String.class};
1285            constructor        = dset.getClass().getConstructor(paramClass);
1286            paramObj           = new Object[] {dset.getFileFormat(), dset.getName(), dset.getPath()};
1287            dsetCopy           = constructor.newInstance(paramObj);
1288            data               = dsetCopy.getData();
1289        }
1290        catch (Exception ex) {
1291            log.debug("showObjStdRefData(): couldn't show data: ", ex);
1292            Tools.showError(shell, "Select", "Object Reference: " + ex.getMessage());
1293            data = null;
1294        }
1295
1296        if (data == null)
1297            return;
1298
1299        Class<?> theClass = null;
1300        String viewName   = null;
1301
1302        switch (viewType) {
1303        case IMAGE:
1304            viewName = HDFView.getListOfImageViews().get(0);
1305            break;
1306        case TABLE:
1307            viewName = (String)HDFView.getListOfTableViews().get(0);
1308            break;
1309        default:
1310            viewName = null;
1311        }
1312
1313        try {
1314            theClass = Class.forName(viewName);
1315        }
1316        catch (Exception ex) {
1317            try {
1318                theClass = ViewProperties.loadExtClass().loadClass(viewName);
1319            }
1320            catch (Exception ex2) {
1321                theClass = null;
1322            }
1323        }
1324
1325        // Use default dataview
1326        if (theClass == null) {
1327            switch (viewType) {
1328            case IMAGE:
1329                viewName = ViewProperties.DEFAULT_IMAGEVIEW_NAME;
1330                break;
1331            case TABLE:
1332                viewName = ViewProperties.DEFAULT_SCALAR_DATASET_TABLEVIEW_NAME;
1333                break;
1334            default:
1335                viewName = null;
1336            }
1337
1338            try {
1339                theClass = Class.forName(viewName);
1340            }
1341            catch (Exception ex) {
1342                log.debug("showObjStdRefData(): no suitable display class found");
1343                Tools.showError(shell, "Select",
1344                                "Could not show reference data: no suitable display class found");
1345                return;
1346            }
1347        }
1348
1349        HashMap map = new HashMap(1);
1350        map.put(ViewProperties.DATA_VIEW_KEY.OBJECT, dsetCopy);
1351        Object[] args = {viewer, map};
1352
1353        try {
1354            Tools.newInstance(theClass, args);
1355        }
1356        catch (Exception ex) {
1357            log.debug("showObjStdRefData(): Could not show reference data: ", ex);
1358            Tools.showError(shell, "Select", "Could not show reference data: " + ex.toString());
1359        }
1360    }
1361
1362    /**
1363     * Display data pointed to by attribute references. Data of each object is shown in
1364     * a separate spreadsheet.
1365     *
1366     * @param ref_obj_name
1367     *            the string that contain the attribute reference information.
1368     * @param ref_attr_name
1369     *            the string that contain the attribute reference information.
1370     *
1371     */
1372    @SuppressWarnings({"rawtypes", "unchecked"})
1373    protected void showAttrStdRefData(String ref_obj_name, String ref_attr_name)
1374    {
1375        if (ref_obj_name == null || (ref_obj_name.length() <= 0) || (ref_obj_name.compareTo("NULL") == 0)) {
1376            log.debug("showAttrStdRefData(): ref_obj_name is null or invalid");
1377            Tools.showError(shell, "Select",
1378                            "Could not show attribute reference data: invalid or null object name");
1379            return;
1380        }
1381
1382        if (ref_attr_name == null || (ref_attr_name.length() <= 0) ||
1383            (ref_attr_name.compareTo("NULL") == 0)) {
1384            log.debug("showAttrStdRefData(): ref_attr_name is null or invalid");
1385            Tools.showError(shell, "Select",
1386                            "Could not show attribute reference data: invalid or null attribute name");
1387            return;
1388        }
1389
1390        // find the parent object first
1391        HObject obj = FileFormat.findObject(((HObject)dataObject).getFileFormat(), ref_obj_name);
1392        if (obj == null) {
1393            log.debug("showAttrStdRefData(): obj is null");
1394            Tools.showError(shell, "Select", "Could not show attribute reference data: invalid or null data");
1395            return;
1396        }
1397        List<Attribute> attrs = H5File.getAttribute(obj);
1398        if ((attrs == null) || (attrs.size() < 1)) {
1399            log.debug("showAttrStdRefData(): attrs is null");
1400            Tools.showError(shell, "Select", "Could not show attribute reference data: no attributes found");
1401            return;
1402        }
1403        H5ScalarAttr attr     = null;
1404        H5ScalarAttr attrCopy = null;
1405        int n                 = attrs.size();
1406        for (int i = 0; i < n; i++) {
1407            attr = (H5ScalarAttr)attrs.get(i);
1408            if (attr.getAttributeName().equals(ref_attr_name))
1409                break;
1410            else
1411                attr = null;
1412        }
1413
1414        // create an instance of the Attribute constructor
1415        Constructor<? extends H5ScalarAttr> constructor = null;
1416        Object[] paramObj                               = null;
1417        Object data                                     = null;
1418
1419        try {
1420            Class[] paramClass = {HObject.class, String.class, Datatype.class, long[].class};
1421            constructor        = attr.getClass().getConstructor(paramClass);
1422            paramObj           = new Object[] {obj, attr.getName(), attr.getDatatype(), null};
1423            attrCopy           = constructor.newInstance(paramObj);
1424            data               = attrCopy.getData();
1425        }
1426        catch (Exception ex) {
1427            log.debug("showAttrStdRefData(): couldn't show data: ", ex);
1428            Tools.showError(shell, "Select", "Attribute Reference: " + ex.getMessage());
1429            data = null;
1430        }
1431
1432        if (data == null)
1433            return;
1434
1435        Class<?> theClass = null;
1436        String viewName   = null;
1437
1438        switch (viewType) {
1439        case IMAGE:
1440            viewName = HDFView.getListOfImageViews().get(0);
1441            break;
1442        case TABLE:
1443            viewName = (String)HDFView.getListOfTableViews().get(0);
1444            break;
1445        default:
1446            viewName = null;
1447        }
1448
1449        try {
1450            theClass = Class.forName(viewName);
1451        }
1452        catch (Exception ex) {
1453            try {
1454                theClass = ViewProperties.loadExtClass().loadClass(viewName);
1455            }
1456            catch (Exception ex2) {
1457                theClass = null;
1458            }
1459        }
1460
1461        // Use default dataview
1462        if (theClass == null) {
1463            switch (viewType) {
1464            case IMAGE:
1465                viewName = ViewProperties.DEFAULT_IMAGEVIEW_NAME;
1466                break;
1467            case TABLE:
1468                viewName = ViewProperties.DEFAULT_SCALAR_DATASET_TABLEVIEW_NAME;
1469                break;
1470            default:
1471                viewName = null;
1472            }
1473
1474            try {
1475                theClass = Class.forName(viewName);
1476            }
1477            catch (Exception ex) {
1478                log.debug("showAttrStdRefData(): no suitable display class found");
1479                Tools.showError(shell, "Select",
1480                                "Could not show reference data: no suitable display class found");
1481                return;
1482            }
1483        }
1484
1485        HashMap map = new HashMap(1);
1486        map.put(ViewProperties.DATA_VIEW_KEY.OBJECT, attrCopy);
1487        Object[] args = {viewer, map};
1488
1489        try {
1490            Tools.newInstance(theClass, args);
1491        }
1492        catch (Exception ex) {
1493            log.debug("showAttrStdRefData(): Could not show reference data: ", ex);
1494            Tools.showError(shell, "Select", "Could not show reference data: " + ex.toString());
1495        }
1496    }
1497
1498    /**
1499     * Update cell value label and cell value field when a cell is selected
1500     */
1501    private class ScalarDSCellSelectionListener implements ILayerListener {
1502        @Override
1503        public void handleLayerEvent(ILayerEvent e)
1504        {
1505            if (e instanceof CellSelectionEvent) {
1506                CellSelectionEvent event = (CellSelectionEvent)e;
1507                Object val =
1508                    dataTable.getDataValueByPosition(event.getColumnPosition(), event.getRowPosition());
1509                String strVal = null;
1510
1511                String[] columnNames =
1512                    ((ScalarDSColumnHeaderDataProvider)columnHeaderDataProvider).columnNames;
1513                int rowStart  = ((RowHeaderDataProvider)rowHeaderDataProvider).start;
1514                int rowStride = ((RowHeaderDataProvider)rowHeaderDataProvider).stride;
1515
1516                cellLabel.setText(
1517                    String.valueOf(rowStart + indexBase +
1518                                   dataTable.getRowIndexByPosition(event.getRowPosition()) * rowStride) +
1519                    ", " + columnNames[dataTable.getColumnIndexByPosition(event.getColumnPosition())] +
1520                    "  =  ");
1521
1522                if (val == null) {
1523                    cellValueField.setText("Null");
1524                    ((ScrolledComposite)cellValueField.getParent())
1525                        .setMinSize(cellValueField.computeSize(SWT.DEFAULT, SWT.DEFAULT));
1526                    return;
1527                }
1528
1529                if (isStdRef) {
1530                    boolean displayValues = ViewProperties.showRegRefValues();
1531
1532                    if (displayValues && val != null &&
1533                        !(val instanceof String)) { //((String) val).compareTo("NULL") != 0) {
1534                        strVal =
1535                            ((H5ReferenceType)dataObject.getDatatype()).getReferenceRegion((byte[])val, true);
1536                    }
1537                }
1538                else if (isRegRef) {
1539                    boolean displayValues = ViewProperties.showRegRefValues();
1540
1541                    if (displayValues && val != null && ((String)val).compareTo("NULL") != 0) {
1542                        String reg               = (String)val;
1543                        boolean isPointSelection = (reg.indexOf('-') <= 0);
1544
1545                        // find the object location
1546                        String oidStr = reg.substring(reg.indexOf('/'), reg.indexOf(' '));
1547
1548                        // decode the region selection
1549                        String regStr = reg.substring(reg.indexOf('{') + 1, reg.indexOf('}'));
1550
1551                        // no selection
1552                        if (regStr == null || regStr.length() <= 0) {
1553                            log.debug("ScalarDSCellSelectionListener:RegRef CellSelected: no selection made");
1554                            strVal = null;
1555                        }
1556                        else {
1557                            // TODO: do we need to do something with what's past the closing bracket
1558                            // regStr = reg.substring(reg.indexOf('}') + 1);
1559
1560                            StringTokenizer st = new StringTokenizer(regStr);
1561                            int nSelections    = st.countTokens();
1562                            if (nSelections <= 0) { // no selection
1563                                strVal = null;
1564                            }
1565                            else {
1566                                HObject obj =
1567                                    FileFormat.findObject(((HObject)dataObject).getFileFormat(), oidStr);
1568                                if (obj == null || !(obj instanceof ScalarDS)) { // no selection
1569                                    strVal = null;
1570                                }
1571                                else {
1572                                    ScalarDS dset = (ScalarDS)obj;
1573                                    try {
1574                                        dset.init();
1575                                    }
1576                                    catch (Exception ex) {
1577                                        log.debug(
1578                                            "ScalarDSCellSelectionListener:RegRef CellSelected: reference dset did not init()",
1579                                            ex);
1580                                    }
1581                                    StringBuilder strvalSB = new StringBuilder();
1582
1583                                    int idx = 0;
1584                                    while (st.hasMoreTokens()) {
1585                                        int space_type = dset.getSpaceType();
1586                                        int rank       = dset.getRank();
1587                                        long[] start   = dset.getStartDims();
1588                                        long[] count   = dset.getSelectedDims();
1589                                        // long count[] = new long[rank];
1590
1591                                        // set the selected dimension sizes
1592                                        // based on the region selection
1593                                        // info.
1594                                        String sizeStr = null;
1595                                        String token   = st.nextToken();
1596
1597                                        token = token.replace('(', ' ');
1598                                        token = token.replace(')', ' ');
1599                                        if (isPointSelection) {
1600                                            // point selection
1601                                            String[] tmp = token.split(",");
1602                                            for (int x = 0; x < tmp.length; x++) {
1603                                                count[x] = 1;
1604                                                sizeStr  = tmp[x].trim();
1605                                                start[x] = Long.valueOf(sizeStr);
1606                                                log.trace(
1607                                                    "ScalarDSCellSelectionListener:RegRef CellSelected: point sel={}",
1608                                                    tmp[x]);
1609                                            }
1610                                        }
1611                                        else {
1612                                            // rectangle selection
1613                                            String startStr = token.substring(0, token.indexOf('-'));
1614                                            String endStr   = token.substring(token.indexOf('-') + 1);
1615                                            log.trace(
1616                                                "ScalarDSCellSelectionListener:RegRef CellSelected: rect sel with startStr={} endStr={}",
1617                                                startStr, endStr);
1618                                            String[] tmp = startStr.split(",");
1619                                            log.trace(
1620                                                "ScalarDSCellSelectionListener:RegRef CellSelected: tmp with length={} rank={}",
1621                                                tmp.length, rank);
1622                                            for (int x = 0; x < tmp.length; x++) {
1623                                                sizeStr  = tmp[x].trim();
1624                                                start[x] = Long.valueOf(sizeStr);
1625                                                log.trace(
1626                                                    "ScalarDSCellSelectionListener:RegRef CellSelected: rect start={}",
1627                                                    tmp[x]);
1628                                            }
1629                                            tmp = endStr.split(",");
1630                                            for (int x = 0; x < tmp.length; x++) {
1631                                                sizeStr  = tmp[x].trim();
1632                                                count[x] = Long.valueOf(sizeStr) - start[x] + 1;
1633                                                log.trace(
1634                                                    "ScalarDSCellSelectionListener:RegRef CellSelected: rect end={} count={}",
1635                                                    tmp[x], count[x]);
1636                                            }
1637                                        }
1638
1639                                        Object dbuf = null;
1640                                        try {
1641                                            dbuf = dset.getData();
1642                                        }
1643                                        catch (Exception ex) {
1644                                            Tools.showError(shell, "Select",
1645                                                            "Region Reference:" + ex.getMessage());
1646                                        }
1647
1648                                        /* Convert dbuf to a displayable string */
1649                                        char runtimeTypeClass = Utils.getJavaObjectRuntimeClass(dbuf);
1650                                        log.trace(
1651                                            "ScalarDSCellSelectionListener:RegRef CellSelected: cName={} runtimeTypeClass={}",
1652                                            dbuf.getClass().getName(), runtimeTypeClass);
1653
1654                                        if (idx > 0)
1655                                            strvalSB.append(',');
1656
1657                                        // convert numerical data into char
1658                                        // only possible cases are byte[]
1659                                        // and short[] (converted from
1660                                        // unsigned byte)
1661                                        Datatype dtype    = dset.getDatatype();
1662                                        Datatype baseType = dtype.getDatatypeBase();
1663                                        log.trace(
1664                                            "ScalarDSCellSelectionListener:RegRef CellSelected: dtype={} baseType={}",
1665                                            dtype.getDescription(), baseType);
1666                                        if (baseType == null)
1667                                            baseType = dtype;
1668                                        if ((dtype.isArray() && baseType.isChar()) &&
1669                                            ((runtimeTypeClass == 'B') || (runtimeTypeClass == 'S'))) {
1670                                            int n = Array.getLength(dbuf);
1671                                            log.trace(
1672                                                "ScalarDSCellSelectionListener:RegRef CellSelected charData length = {}",
1673                                                n);
1674                                            char[] charData = new char[n];
1675                                            for (int i = 0; i < n; i++) {
1676                                                if (runtimeTypeClass == 'B') {
1677                                                    charData[i] = (char)Array.getByte(dbuf, i);
1678                                                }
1679                                                else if (runtimeTypeClass == 'S') {
1680                                                    charData[i] = (char)Array.getShort(dbuf, i);
1681                                                }
1682                                            }
1683
1684                                            strvalSB.append(charData);
1685                                        }
1686                                        else {
1687                                            // numerical values
1688                                            boolean isUnsigned = dtype.isUnsigned();
1689                                            if (dtype.isArray())
1690                                                isUnsigned = baseType.isUnsigned();
1691                                            int n = Array.getLength(dbuf);
1692                                            if (isUnsigned) {
1693                                                switch (runtimeTypeClass) {
1694                                                case 'B':
1695                                                    byte[] barray = (byte[])dbuf;
1696                                                    short sValue  = barray[0];
1697                                                    if (sValue < 0) {
1698                                                        sValue += 256;
1699                                                    }
1700                                                    strvalSB.append(sValue);
1701                                                    for (int i = 1; i < n; i++) {
1702                                                        strvalSB.append(',');
1703                                                        sValue = barray[i];
1704                                                        if (sValue < 0) {
1705                                                            sValue += 256;
1706                                                        }
1707                                                        strvalSB.append(sValue);
1708                                                    }
1709                                                    break;
1710                                                case 'S':
1711                                                    short[] sarray = (short[])dbuf;
1712                                                    int iValue     = sarray[0];
1713                                                    if (iValue < 0) {
1714                                                        iValue += 65536;
1715                                                    }
1716                                                    strvalSB.append(iValue);
1717                                                    for (int i = 1; i < n; i++) {
1718                                                        strvalSB.append(',');
1719                                                        iValue = sarray[i];
1720                                                        if (iValue < 0) {
1721                                                            iValue += 65536;
1722                                                        }
1723                                                        strvalSB.append(iValue);
1724                                                    }
1725                                                    break;
1726                                                case 'I':
1727                                                    int[] iarray = (int[])dbuf;
1728                                                    long lValue  = iarray[0];
1729                                                    if (lValue < 0) {
1730                                                        lValue += 4294967296L;
1731                                                    }
1732                                                    strvalSB.append(lValue);
1733                                                    for (int i = 1; i < n; i++) {
1734                                                        strvalSB.append(',');
1735                                                        lValue = iarray[i];
1736                                                        if (lValue < 0) {
1737                                                            lValue += 4294967296L;
1738                                                        }
1739                                                        strvalSB.append(lValue);
1740                                                    }
1741                                                    break;
1742                                                case 'J':
1743                                                    long[] larray   = (long[])dbuf;
1744                                                    Long l          = larray[0];
1745                                                    String theValue = Long.toString(l);
1746                                                    if (l < 0) {
1747                                                        l = (l << 1) >>> 1;
1748                                                        BigInteger big1 =
1749                                                            new BigInteger("9223372036854775808"); // 2^65
1750                                                        BigInteger big2 = new BigInteger(l.toString());
1751                                                        BigInteger big  = big1.add(big2);
1752                                                        theValue        = big.toString();
1753                                                    }
1754                                                    strvalSB.append(theValue);
1755                                                    for (int i = 1; i < n; i++) {
1756                                                        strvalSB.append(',');
1757                                                        l        = larray[i];
1758                                                        theValue = Long.toString(l);
1759                                                        if (l < 0) {
1760                                                            l = (l << 1) >>> 1;
1761                                                            BigInteger big1 =
1762                                                                new BigInteger("9223372036854775808"); // 2^65
1763                                                            BigInteger big2 = new BigInteger(l.toString());
1764                                                            BigInteger big  = big1.add(big2);
1765                                                            theValue        = big.toString();
1766                                                        }
1767                                                        strvalSB.append(theValue);
1768                                                    }
1769                                                    break;
1770                                                default:
1771                                                    strvalSB.append(Array.get(dbuf, 0));
1772                                                    for (int i = 1; i < n; i++) {
1773                                                        strvalSB.append(',');
1774                                                        strvalSB.append(Array.get(dbuf, i));
1775                                                    }
1776                                                    break;
1777                                                }
1778                                            }
1779                                            else {
1780                                                for (int x = 0; x < n; x++) {
1781                                                    Object theValue = Array.get(dbuf, x);
1782                                                    if (x > 0)
1783                                                        strvalSB.append(',');
1784                                                    strvalSB.append(theValue);
1785                                                }
1786                                            }
1787                                        }
1788                                        idx++;
1789                                        dset.clearData();
1790                                    } // (st.hasMoreTokens())
1791                                    strVal = strvalSB.toString();
1792                                }
1793                            }
1794                        }
1795                    }
1796                    else {
1797                        strVal = null;
1798                    }
1799                }
1800                else if (isObjRef) {
1801                    if (val != null && ((String)val).compareTo("NULL") != 0) {
1802                        strVal = (String)val;
1803                    }
1804                    else {
1805                        strVal = null;
1806                    }
1807                }
1808
1809                if (strVal == null && val != null)
1810                    strVal = dataDisplayConverter.canonicalToDisplayValue(val).toString();
1811
1812                cellValueField.setText(strVal);
1813                ((ScrolledComposite)cellValueField.getParent())
1814                    .setMinSize(cellValueField.computeSize(SWT.DEFAULT, SWT.DEFAULT));
1815            }
1816        }
1817    }
1818
1819    /**
1820     * Custom Column Header data provider to set column indices based on Index Base
1821     * for Scalar Datasets.
1822     */
1823    private class ScalarDSColumnHeaderDataProvider implements IDataProvider {
1824        private final String columnNames[];
1825
1826        private final int space_type;
1827        private final int rank;
1828
1829        private final long[] startArray;
1830        private final long[] strideArray;
1831        private final int[] selectedIndex;
1832
1833        private final int ncols;
1834
1835        public ScalarDSColumnHeaderDataProvider(DataFormat theDataObject)
1836        {
1837            space_type = theDataObject.getSpaceType();
1838            rank       = theDataObject.getRank();
1839
1840            startArray    = theDataObject.getStartDims();
1841            strideArray   = theDataObject.getStride();
1842            selectedIndex = theDataObject.getSelectedIndex();
1843
1844            if (rank > 1) {
1845                ncols = (int)theDataObject.getWidth();
1846
1847                int start  = (int)startArray[selectedIndex[1]];
1848                int stride = (int)strideArray[selectedIndex[1]];
1849
1850                columnNames = new String[ncols];
1851
1852                for (int i = 0; i < ncols; i++)
1853                    columnNames[i] = String.valueOf(start + indexBase + i * stride);
1854            }
1855            else {
1856                ncols = 1;
1857
1858                columnNames = new String[] {"  "};
1859            }
1860        }
1861
1862        @Override
1863        public int getColumnCount()
1864        {
1865            return ncols;
1866        }
1867
1868        @Override
1869        public int getRowCount()
1870        {
1871            return 1;
1872        }
1873
1874        @Override
1875        public Object getDataValue(int columnIndex, int rowIndex)
1876        {
1877            return columnNames[columnIndex];
1878        }
1879
1880        @Override
1881        public void setDataValue(int columnIndex, int rowIndex, Object newValue)
1882        {
1883            // intentional
1884        }
1885    }
1886}