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