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