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 file COPYING.                     *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * If you do not have access to this file, you may request a copy from       *
011 * help@hdfgroup.org.                                                        *
012 ****************************************************************************/
013
014package hdf.view;
015
016import hdf.hdf5lib.H5;
017import hdf.hdf5lib.exceptions.HDF5Exception;
018import hdf.object.CompoundDS;
019import hdf.object.Dataset;
020import hdf.object.Datatype;
021import hdf.object.FileFormat;
022import hdf.object.Group;
023import hdf.object.HObject;
024import hdf.object.ScalarDS;
025import hdf.object.h5.H5Datatype;
026import hdf.view.ViewProperties.BITMASK_OP;
027
028import java.awt.BorderLayout;
029import java.awt.Color;
030import java.awt.Component;
031import java.awt.Cursor;
032import java.awt.Dimension;
033import java.awt.GridLayout;
034import java.awt.Insets;
035import java.awt.Point;
036import java.awt.Toolkit;
037import java.awt.datatransfer.Clipboard;
038import java.awt.datatransfer.DataFlavor;
039import java.awt.datatransfer.StringSelection;
040import java.awt.event.ActionEvent;
041import java.awt.event.ActionListener;
042import java.awt.event.InputEvent;
043import java.awt.event.ItemEvent;
044import java.awt.event.ItemListener;
045import java.awt.event.KeyEvent;
046import java.awt.event.MouseEvent;
047import java.awt.event.MouseListener;
048import java.io.BufferedInputStream;
049import java.io.BufferedReader;
050import java.io.BufferedWriter;
051import java.io.DataOutputStream;
052import java.io.File;
053import java.io.FileInputStream;
054import java.io.FileNotFoundException;
055import java.io.FileOutputStream;
056import java.io.FileReader;
057import java.io.FileWriter;
058import java.io.IOException;
059import java.io.PrintWriter;
060import java.io.StringReader;
061import java.lang.reflect.Array;
062import java.lang.reflect.Constructor;
063import java.math.BigInteger;
064import java.nio.ByteBuffer;
065import java.nio.ByteOrder;
066import java.nio.DoubleBuffer;
067import java.nio.FloatBuffer;
068import java.nio.IntBuffer;
069import java.nio.LongBuffer;
070import java.nio.ShortBuffer;
071import java.text.DecimalFormat;
072import java.text.NumberFormat;
073import java.util.BitSet;
074import java.util.Enumeration;
075import java.util.HashMap;
076import java.util.Iterator;
077import java.util.List;
078import java.util.StringTokenizer;
079import java.util.Vector;
080
081import javax.swing.BorderFactory;
082import javax.swing.ButtonGroup;
083import javax.swing.CellEditor;
084import javax.swing.JButton;
085import javax.swing.JCheckBoxMenuItem;
086import javax.swing.JComboBox;
087import javax.swing.JComponent;
088import javax.swing.JDialog;
089import javax.swing.JFileChooser;
090import javax.swing.JFrame;
091import javax.swing.JInternalFrame;
092import javax.swing.JLabel;
093import javax.swing.JList;
094import javax.swing.JMenu;
095import javax.swing.JMenuBar;
096import javax.swing.JMenuItem;
097import javax.swing.JOptionPane;
098import javax.swing.JPanel;
099import javax.swing.JPopupMenu;
100import javax.swing.JRadioButton;
101import javax.swing.JScrollPane;
102import javax.swing.JSplitPane;
103import javax.swing.JTable;
104import javax.swing.JTextArea;
105import javax.swing.JTextField;
106import javax.swing.JViewport;
107import javax.swing.KeyStroke;
108import javax.swing.SwingConstants;
109import javax.swing.UIManager;
110import javax.swing.WindowConstants;
111import javax.swing.border.Border;
112import javax.swing.border.CompoundBorder;
113import javax.swing.border.EtchedBorder;
114import javax.swing.border.LineBorder;
115import javax.swing.border.MatteBorder;
116import javax.swing.border.TitledBorder;
117import javax.swing.event.ChangeEvent;
118import javax.swing.event.ListSelectionEvent;
119import javax.swing.table.AbstractTableModel;
120import javax.swing.table.JTableHeader;
121import javax.swing.table.TableCellRenderer;
122import javax.swing.table.TableColumn;
123import javax.swing.tree.DefaultMutableTreeNode;
124import javax.swing.tree.TreeNode;
125
126/**
127 * TableView displays an HDF dataset as a two-dimensional table.
128 */
129public class DefaultTableView extends JInternalFrame implements TableView, ActionListener, MouseListener {
130    private static final long             serialVersionUID = -7452459299532863847L;
131
132    private final static org.slf4j.Logger log              = org.slf4j.LoggerFactory.getLogger(DefaultTableView.class);
133
134    // The main HDFView
135    private final ViewManager               viewer;
136
137    private JTable                          table; // The table used to hold the table data.
138
139    // The Dataset (Scalar or Compound) to be displayed in the Table
140    private Dataset                         dataset;
141
142    /**
143     * The value of the dataset.
144     */
145    private Object                          dataValue;
146
147    private Object                          fillValue               = null;
148
149    private enum ViewType { TABLE, IMAGE, TEXT };
150    private      ViewType                   viewType = ViewType.TABLE;
151
152    /**
153     * Numerical data type. B = byte array, S = short array, I = int array, J = long array, F =
154     * float array, and D = double array.
155     */
156    private char                            NT               = ' ';
157
158    private static final int                FLOAT_BUFFER_SIZE       = 524288;
159    private static final int                INT_BUFFER_SIZE         = 524288;
160    private static final int                SHORT_BUFFER_SIZE       = 1048576;
161    private static final int                LONG_BUFFER_SIZE        = 262144;
162    private static final int                DOUBLE_BUFFER_SIZE      = 262144;
163    private static final int                BYTE_BUFFER_SIZE        = 2097152;
164
165    // Changed to use normalized scientific notation (1 <= coefficient < 10).
166    // private final DecimalFormat scientificFormat = new DecimalFormat("###.#####E0#");
167    private final DecimalFormat             scientificFormat = new DecimalFormat("0.0###E0###");
168    private DecimalFormat                   customFormat     = new DecimalFormat("###.#####");
169    private final NumberFormat              normalFormat     = null; // NumberFormat.getInstance();
170    private NumberFormat                    numberFormat     = normalFormat;
171
172    // Used for bitmask operations on data
173    private BitSet                          bitmask                 = null;
174    private BITMASK_OP                      bitmaskOP               = BITMASK_OP.EXTRACT;
175
176    // Keeps track of which frame of data is being displayed
177    private JTextField                      frameField;
178    private long                            curFrame = 0;
179    private long                            maxFrame = 1;
180
181    private int                             indexBase = 0;
182
183    private int                             fixedDataLength = -1;
184
185    private int                             binaryOrder;
186
187    private boolean                         isReadOnly = false;
188
189    private boolean                         isValueChanged = false;
190
191    private boolean                         isEnumConverted = false;
192
193    private boolean                         isDisplayTypeChar;
194    private boolean                         isDataTransposed;
195
196    private boolean                         isRegRef = false;
197    private boolean                         isObjRef = false;
198
199    private boolean                         showAsHex = false;
200    private boolean                         showAsBin = false;
201
202    private final boolean                   startEditing[]   = { false };
203    private JPopupMenu                      popupMenu;
204
205    /**
206     * Global variables for GUI components
207     */
208
209    private final JCheckBoxMenuItem       checkFixedDataLength;
210    private final JCheckBoxMenuItem       checkCustomNotation;
211    private final JCheckBoxMenuItem       checkScientificNotation;
212    private final JCheckBoxMenuItem       checkHex;
213    private final JCheckBoxMenuItem       checkBin;
214
215    // Text field to display the value of the current cell.
216    private JTextArea                     cellValueField;
217
218    // Label to indicate the current cell location.
219    private JLabel                        cellLabel;
220
221    private final Toolkit                 toolkit;
222
223    /* the value of the current cell value in editing. */
224    private Object           currentEditingCellValue = null;
225
226    /**
227     * Constructs a TableView.
228     *
229     * @param theView
230     *             the main HDFView.
231     */
232    public DefaultTableView(ViewManager theView) {
233        this(theView, null);
234    }
235
236    /**
237     * Constructs a TableView.
238     *
239     * @param theView
240     *             the main HDFView.
241     * @param map
242     *             the properties on how to show the data. The map is used to allow applications to
243     *          pass properties on how to display the data, such as, transposing data, showing
244     *          data as character, applying bitmask, and etc. Predefined keys are listed at
245     *          ViewProperties.DATA_VIEW_KEY.
246     */
247    public DefaultTableView(ViewManager theView, HashMap map) {
248        super();
249        log.trace("DefaultTableView start");
250
251        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
252
253        viewer = theView;
254        toolkit = Toolkit.getDefaultToolkit();
255        isValueChanged = false;
256        isReadOnly = false;
257        isRegRef = false;
258        isObjRef = false;
259        viewType = ViewType.TABLE;
260        fixedDataLength = -1;
261        HObject hObject = null;
262        popupMenu = null;
263        bitmask = null;
264
265        if (ViewProperties.isIndexBase1()) indexBase = 1;
266        log.trace("Index base is {}", indexBase);
267
268        checkFixedDataLength = new JCheckBoxMenuItem("Fixed Data Length", false);
269        checkCustomNotation = new JCheckBoxMenuItem("Show Custom Notation", false);
270        checkScientificNotation = new JCheckBoxMenuItem("Show Scientific Notation", false);
271        checkHex = new JCheckBoxMenuItem("Show Hexadecimal", false);
272        checkBin = new JCheckBoxMenuItem("Show Binary", false);
273
274        if (map != null) {
275            hObject = (HObject) map.get(ViewProperties.DATA_VIEW_KEY.OBJECT);
276
277            bitmask = (BitSet) map.get(ViewProperties.DATA_VIEW_KEY.BITMASK);
278            bitmaskOP = (BITMASK_OP) map.get(ViewProperties.DATA_VIEW_KEY.BITMASKOP);
279
280            Boolean b = (Boolean) map.get(ViewProperties.DATA_VIEW_KEY.CHAR);
281            if (b != null) isDisplayTypeChar = b.booleanValue();
282
283            b = (Boolean) map.get(ViewProperties.DATA_VIEW_KEY.TRANSPOSED);
284            if (b != null) isDataTransposed = b.booleanValue();
285
286            b = (Boolean) map.get(ViewProperties.DATA_VIEW_KEY.INDEXBASE1);
287            if (b != null) {
288                if (b.booleanValue())
289                    indexBase = 1;
290                else
291                    indexBase = 0;
292            }
293        }
294        log.trace("Index base = {} - Is data transposed = {} - Is display type char = {}", indexBase, isDataTransposed, isDisplayTypeChar);
295
296        if (hObject == null) hObject = viewer.getTreeView().getCurrentObject();
297
298        if ((hObject == null) || !(hObject instanceof Dataset)) {
299            return;
300        }
301
302        dataset = (Dataset) hObject;
303        isReadOnly = dataset.getFileFormat().isReadOnly();
304        log.trace("dataset({}) isReadOnly={}", dataset, isReadOnly);
305
306        long[] dims = dataset.getDims();
307        long tsize = 1;
308
309        if (dims == null) {
310            JOptionPane.showMessageDialog(this, "Could not open dataset '" + dataset.getName() + "'. Dataset has null dimensions.");
311            return;
312        }
313
314        for (int i = 0; i < dims.length; i++)
315            tsize *= dims[i];
316
317        log.trace("dataset size={} Height={} Width={}", tsize, dataset.getHeight(), dataset.getWidth());
318        if (dataset.getHeight() <= 0 || dataset.getWidth() <= 0 || tsize <= 0) {
319            JOptionPane.showMessageDialog(this, "Could not open dataset '" + dataset.getName() + "'. Dataset has dimension of size 0.");
320            return;
321        }
322
323        // cannot edit hdf4 vdata
324        if (dataset.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4))
325                && (dataset instanceof CompoundDS)) {
326            isReadOnly = true;
327        }
328
329        // disable edit feature for szip compression when encode is not enabled
330        if (!isReadOnly) {
331            String compression = dataset.getCompression();
332            if ((compression != null) && compression.startsWith("SZIP")) {
333                if (!compression.endsWith("ENCODE_ENABLED")) {
334                    isReadOnly = true;
335                }
336            }
337        }
338
339        Datatype dtype = dataset.getDatatype();
340        log.trace("dataset dtype.getDatatypeClass()={}", dtype.getDatatypeClass());
341        isDisplayTypeChar = (isDisplayTypeChar && (dtype.getDatatypeSize() == 1 || (dtype.getDatatypeClass() == Datatype.CLASS_ARRAY && dtype
342                .getBasetype().getDatatypeClass() == Datatype.CLASS_CHAR)));
343
344        isEnumConverted = ViewProperties.isConvertEnum();
345        log.trace("dataset isDisplayTypeChar={} isEnumConverted={}", isDisplayTypeChar, isEnumConverted);
346        dataset.setEnumConverted(isEnumConverted);
347
348        // Setup subset information
349        log.trace("DefaultTableView: Setup subset information");
350        int rank = dataset.getRank();
351        int[] selectedIndex = dataset.getSelectedIndex();
352        long[] count = dataset.getSelectedDims();
353        long[] stride = dataset.getStride();
354        long[] start = dataset.getStartDims();
355        int n = Math.min(3, rank);
356        if (rank > 2) {
357            curFrame = start[selectedIndex[2]] + indexBase;
358            maxFrame = (indexBase == 1) ? dims[selectedIndex[2]] : dims[selectedIndex[2]] - 1;
359        }
360
361        // set title & border
362        TitledBorder border = BorderFactory.createTitledBorder(
363                BorderFactory.createLineBorder(Color.lightGray, 1),
364                indexBase + "-based",
365                TitledBorder.RIGHT, TitledBorder.TOP, this.getFont(), Color.black);
366        ((JPanel) getContentPane()).setBorder(border);
367
368        // Create the table and its columnHeader
369        if (dataset instanceof CompoundDS) {
370            log.trace("createTable((CompoundDS) dataset): dtype.getDatatypeClass()={}", dtype.getDatatypeClass());
371
372            isDataTransposed = false; // Disable transpose for compound dataset
373            this.setFrameIcon(ViewProperties.getTableIcon());
374            table = createTable((CompoundDS) dataset);
375        }
376        else { /* if (dataset instanceof ScalarDS) */
377            this.setFrameIcon(ViewProperties.getDatasetIcon());
378            table = createTable((ScalarDS) dataset);
379            log.trace("createTable((ScalarDS) dataset) dtype.getDatatypeClass()={}", dtype.getDatatypeClass());
380
381            if (dtype.getDatatypeClass() == Datatype.CLASS_REFERENCE) {
382                table.addMouseListener(this);
383
384                if (dtype.getDatatypeSize() > 8) {
385                    isReadOnly = true;
386                    isRegRef = true;
387                }
388                else
389                    isObjRef = true;
390            }
391
392            if ((dtype.getDatatypeClass() == Datatype.CLASS_BITFIELD) || (dtype.getDatatypeClass() == Datatype.CLASS_OPAQUE)) {
393                showAsHex = true;
394                checkHex.setSelected(true);
395                checkScientificNotation.setSelected(false);
396                checkCustomNotation.setSelected(false);
397                checkBin.setSelected(false);
398                showAsBin = false;
399                numberFormat = normalFormat;
400            }
401            log.trace("createTable((ScalarDS) dataset): isRegRef={} isObjRef={} showAsHex={}", isRegRef, isObjRef, showAsHex);
402        }
403
404        if (table == null) {
405            viewer.showStatus("Creating table failed - " + dataset.getName());
406            dataset = null;
407            super.dispose();
408            return;
409        }
410        table.setName("data");
411
412        log.trace("DefaultTableView create ColumnHeader");
413        ColumnHeader columnHeaders = new ColumnHeader(table);
414        columnHeaders.setName("columnHeaders");
415        table.setTableHeader(columnHeaders);
416        table.setCellSelectionEnabled(true);
417        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
418        table.setGridColor(Color.gray);
419
420        // add the table to a scroller
421        JScrollPane scrollingTable = new JScrollPane(table);
422        scrollingTable.getVerticalScrollBar().setUnitIncrement(100);
423        scrollingTable.getHorizontalScrollBar().setUnitIncrement(100);
424
425        // create row headers and add it to the scroller
426        log.trace("DefaultTableView create RowHeader");
427        RowHeader rowHeaders = new RowHeader(table, dataset);
428        rowHeaders.setName("rowHeaders");
429
430        JViewport viewp = new JViewport();
431        viewp.add(rowHeaders);
432        viewp.setPreferredSize(rowHeaders.getPreferredSize());
433        scrollingTable.setRowHeader(viewp);
434
435        cellLabel = new JLabel("");
436        cellLabel.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
437        Dimension dim = cellLabel.getPreferredSize();
438        dim.width = 75;
439        cellLabel.setPreferredSize(dim);
440        cellLabel.setHorizontalAlignment(SwingConstants.RIGHT);
441
442        cellValueField = new JTextArea();
443        cellValueField.setLineWrap(true);
444        cellValueField.setWrapStyleWord(true);
445        cellValueField.setEditable(false);
446        cellValueField.setBackground(new Color(255, 255, 240));
447
448        JScrollPane scrollingcellValue = new JScrollPane(cellValueField);
449        scrollingcellValue.getVerticalScrollBar().setUnitIncrement(50);
450        scrollingcellValue.getHorizontalScrollBar().setUnitIncrement(50);
451
452        JPanel valuePane = new JPanel();
453        valuePane.setLayout(new BorderLayout());
454        valuePane.add(cellLabel, BorderLayout.WEST);
455        valuePane.add(scrollingcellValue, BorderLayout.CENTER);
456
457        JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, valuePane, scrollingTable);
458        splitPane.setDividerLocation(25);
459        JPanel contentPane = (JPanel) getContentPane();
460        contentPane.add(splitPane);
461
462        StringBuffer sb = new StringBuffer(hObject.getName());
463        sb.append("  at  ");
464        sb.append(hObject.getPath());
465        sb.append("  [");
466        sb.append(dataset.getFileFormat().getName());
467        sb.append("  in  ");
468        sb.append(dataset.getFileFormat().getParent());
469        sb.append("]");
470        setTitle(sb.toString());
471
472        sb.append(" [ dims");
473        sb.append(selectedIndex[0]);
474        for (int i = 1; i < n; i++) {
475            sb.append("x");
476            sb.append(selectedIndex[i]);
477        }
478        sb.append(", start");
479        sb.append(start[selectedIndex[0]]);
480        for (int i = 1; i < n; i++) {
481            sb.append("x");
482            sb.append(start[selectedIndex[i]]);
483        }
484        sb.append(", count");
485        sb.append(count[selectedIndex[0]]);
486        for (int i = 1; i < n; i++) {
487            sb.append("x");
488            sb.append(count[selectedIndex[i]]);
489        }
490        sb.append(", stride");
491        sb.append(stride[selectedIndex[0]]);
492        for (int i = 1; i < n; i++) {
493            sb.append("x");
494            sb.append(stride[selectedIndex[i]]);
495        }
496        sb.append(" ] ");
497        log.trace("DefaultTableView: subset={}", sb.toString());
498
499        setJMenuBar(createMenuBar());
500        viewer.showStatus(sb.toString());
501
502        // set cell height for large fonts
503        int cellRowHeight = table.getFontMetrics(table.getFont()).getHeight();
504        rowHeaders.setRowHeight(cellRowHeight);
505        table.setRowHeight(cellRowHeight);
506
507        // create popup menu for reg. ref.
508        if (isRegRef || isObjRef) popupMenu = createPopupMenu();
509        log.trace("DefaultTableView finish");
510    }
511
512    /**
513     * Initialize a JTable to hold a dataset.
514     *
515     * @param theDataset
516     *          The  dataset for the Table to display
517     */
518    private boolean initTable (Dataset theDataset) {
519        log.trace("initTable(Dataset): start");
520
521        if (theDataset.getRank() <= 0) {
522            theDataset.init();
523        }
524        log.trace("initTable rank={}", theDataset.getRank());
525
526        // Make sure entire dataset is not loaded when looking at 3D
527        // datasets using the default display mode (double clicking the
528        // data object)
529        if (theDataset.getRank() > 2) {
530            theDataset.getSelectedDims()[theDataset.getSelectedIndex()[2]] = 1;
531        }
532
533        dataValue = null;
534        try {
535            dataValue = theDataset.getData();
536            if (dataValue == null) {
537                JOptionPane.showMessageDialog(this, "No data read", "TableView:" + getTitle(),
538                        JOptionPane.WARNING_MESSAGE);
539                log.debug("initTable(): no data read");
540                log.trace("initTable(): finish");
541                return false;
542            }
543        }
544        catch (Throwable ex) {
545            toolkit.beep();
546            JOptionPane.showMessageDialog(this, ex.getMessage(), "TableView" + getTitle(), JOptionPane.ERROR_MESSAGE);
547            log.debug("initTable(): ", ex);
548            dataValue = null;
549        }
550
551        log.trace("initTable(Dataset): finish");
552        return true;
553    }
554
555    /**
556     * Creates a JTable to hold a scalar dataset.
557     *
558     * @param theDataset
559     *          The Scalar dataset for the Table to display
560     *
561     * @return The newly created Table
562     */
563    private JTable createTable (ScalarDS theDataset) {
564        log.trace("createTable(ScalarDS): start");
565        initTable(theDataset);
566
567        if (dataValue == null) {
568            log.debug("createTable(): data value is null");
569            log.trace("createTable(): finish");
570            return null;
571        }
572
573        log.trace("createTable(): dataValue={}", dataValue);
574
575        JTable theTable = null;
576        int rows = 0;
577        int cols = 0;
578
579        int rank = theDataset.getRank();
580
581        long[] dims = theDataset.getSelectedDims();
582        log.trace("createTable dims={}", dims);
583
584        rows = (int) dims[0];
585        cols = 1;
586        if (rank > 1) {
587            rows = theDataset.getHeight();
588            cols = theDataset.getWidth();
589        }
590        log.trace("createTable: rows={} : cols={}", rows, cols);
591
592        if (Tools.applyBitmask(dataValue, bitmask, bitmaskOP)) {
593            isReadOnly = true;
594            String opName = "Bits ";
595
596            if (bitmaskOP == ViewProperties.BITMASK_OP.AND) opName = "Bitwise AND ";
597
598            JPanel contentpane = (JPanel) getContentPane();
599            Border border = contentpane.getBorder();
600
601            String btitle = ((TitledBorder) border).getTitle();
602            btitle += ", " + opName + bitmask;
603            ((TitledBorder) border).setTitle(btitle);
604        }
605
606        theDataset.convertFromUnsignedC();
607        try {
608            dataValue = theDataset.getData();
609        }
610        catch (Throwable ex) {
611            JOptionPane.showMessageDialog(this, ex, "ScalarDS createTable:" + getTitle(), JOptionPane.ERROR_MESSAGE);
612            dataValue = null;
613            return null;
614        }
615
616        if (Array.getLength(dataValue) <= rows) cols = 1;
617
618        fillValue = theDataset.getFillValue();
619        log.trace("createTable(): fillValue={}", fillValue);
620
621        String cName = dataValue.getClass().getName();
622        int cIndex = cName.lastIndexOf("[");
623        if (cIndex >= 0) {
624            NT = cName.charAt(cIndex + 1);
625        }
626        log.trace("createTable(): cName={} NT={}", cName, NT);
627
628        // convert numerical data into char
629        // only possible cases are byte[] and short[] (converted from unsigned
630        // byte)
631        if (isDisplayTypeChar && ((NT == 'B') || (NT == 'S'))) {
632            int n = Array.getLength(dataValue);
633            char[] charData = new char[n];
634            for (int i = 0; i < n; i++) {
635                if (NT == 'B') {
636                    charData[i] = (char) Array.getByte(dataValue, i);
637                }
638                else if (NT == 'S') {
639                    charData[i] = (char) Array.getShort(dataValue, i);
640                }
641            }
642
643            dataValue = charData;
644        }
645        else if ((NT == 'B') && theDataset.getDatatype().getDatatypeClass() == Datatype.CLASS_ARRAY) {
646            Datatype baseType = theDataset.getDatatype().getBasetype();
647            if (baseType.getDatatypeClass() == Datatype.CLASS_STRING) {
648                dataValue = Dataset.byteToString((byte[]) dataValue, baseType.getDatatypeSize());
649            }
650        }
651
652        final String columnNames[] = new String[cols];
653        final int rowCount = rows;
654        final int colCount = cols;
655        final long[] startArray = dataset.getStartDims();
656        final long[] strideArray = dataset.getStride();
657        int[] selectedIndex = dataset.getSelectedIndex();
658        final int rowStart = (int) startArray[selectedIndex[0]];
659        final int rowStride = (int) strideArray[selectedIndex[0]];
660        int start = 0;
661        int stride = 1;
662
663        if (rank > 1) {
664            start = (int) startArray[selectedIndex[1]];
665            stride = (int) strideArray[selectedIndex[1]];
666
667            for (int i = 0; i < cols; i++) {
668                columnNames[i] = String.valueOf(start + indexBase + i * stride);
669            }
670        }
671        else {
672            columnNames[0] = "  ";
673        }
674
675        AbstractTableModel tm = new AbstractTableModel() {
676            private static final long  serialVersionUID = 254175303655079056L;
677            private final StringBuffer stringBuffer     = new StringBuffer();
678            private final Datatype     dtype            = dataset.getDatatype();
679            private final Datatype     btype            = dtype.getBasetype();
680            private final int          typeSize         = dtype.getDatatypeSize();
681            private final boolean      isArray          = (dtype.getDatatypeClass() == Datatype.CLASS_ARRAY);
682            private final boolean      isStr            = (NT == 'L');
683            private final boolean      isInt            = (NT == 'B' || NT == 'S' || NT == 'I' || NT == 'J');
684            private final boolean      isUINT64         = (dtype.isUnsigned() && (NT == 'J'));
685            private Object             theValue;
686
687            boolean                    isNaturalOrder   = (dataset.getRank() == 1 || (dataset.getSelectedIndex()[0] < dataset
688                                                                .getSelectedIndex()[1]));
689
690            @Override
691            public int getColumnCount ( ) {
692                return columnNames.length;
693            }
694
695            @Override
696            public int getRowCount ( ) {
697                return rowCount;
698            }
699
700            @Override
701            public String getColumnName (int col) {
702                return columnNames[col];
703            }
704
705            @Override
706            public Object getValueAt (int row, int column) {
707                if (startEditing[0]) return "";
708                log.trace("ScalarDS:createTable:AbstractTableModel:getValueAt({},{}) start", row, column);
709                log.trace("ScalarDS:createTable:AbstractTableModel:getValueAt isInt={} isArray={} showAsHex={} showAsBin={}",
710                        isInt, isArray, showAsHex, showAsBin);
711
712                if (isArray) {
713                    // ARRAY dataset
714                    log.trace("ScalarDS:createTable:AbstractTableModel:getValueAt({},{}) ARRAY", row, column);
715
716                    stringBuffer.setLength(0); // clear the old string
717
718                    if (dtype instanceof H5Datatype) {
719                        // Since variable-length strings have no datatype size,
720                        // just directly append the data to the string buffer
721                        if (((H5Datatype) dtype).isVLEN() && dtype.getBasetype().getDatatypeClass() == Datatype.CLASS_STRING) {
722                            stringBuffer.append(Array.get(dataValue, 0));
723                            for (int i = 0; i < dtype.getArrayDims()[0]; i++) {
724                                if (i > 0) stringBuffer.append(", ");
725                                stringBuffer.append(Array.get(dataValue, i));
726                            }
727                            return stringBuffer;
728                        }
729                    }
730
731                    int arraySize = dtype.getDatatypeSize() / btype.getDatatypeSize();
732                    log.trace(
733                            "ScalarDS:createTable:AbstractTableModel:getValueAt ARRAY dataset size={} isDisplayTypeChar={} isUINT64={}",
734                            arraySize, isDisplayTypeChar, isUINT64);
735
736                    int i0 = (row * colCount + column) * arraySize;
737                    int i1 = i0 + arraySize;
738
739                    if (isDisplayTypeChar) {
740                        for (int i = i0; i < i1; i++) {
741                            stringBuffer.append(Array.getChar(dataValue, i));
742                            if (stringBuffer.length() > 0 && i < (i1 - 1)) stringBuffer.append(", ");
743                        }
744                    }
745                    else {
746                        if (isUINT64) {
747                            log.trace("ScalarDS:createTable:AbstractTableModel:getValueAt({},{}) ARRAY of unsigned longs", row, column);
748                            // array of unsigned longs
749                            for (int i = i0; i < i1; i++) {
750                                Long l = (Long) Array.get(dataValue, i);
751                                BigInteger big;
752                                if (l < 0) {
753                                    l = (l << 1) >>> 1;
754                                    BigInteger big1 = new BigInteger("9223372036854775808"); // 2^65
755                                    BigInteger big2 = new BigInteger(l.toString());
756                                    big = big1.add(big2);
757                                }
758                                else {
759                                    big = new BigInteger(l.toString());
760                                }
761                                if (showAsHex)
762                                    theValue = Tools.toHexString(big.longValue(), 8);
763                                else if (showAsBin)
764                                    theValue = Tools.toBinaryString(big.longValue(), 8);
765                                else
766                                    theValue = big.toString(10);
767
768                                stringBuffer.append(theValue);
769                                if (stringBuffer.length() > 0 && i < (i1 - 1)) stringBuffer.append(", ");
770                            }
771                        }
772                        else {
773                            log.trace("ScalarDS:createTable:AbstractTableModel:getValueAt({},{}) ARRAY not unsigned longs", row, column);
774                            for (int i = i0; i < i1; i++) {
775                                theValue = Array.get(dataValue, i);
776                                log.trace("ScalarDS:createTable:AbstractTableModel:getValueAt({},{}) ARRAY[{}]={}", row, column, i, theValue);
777                                if (showAsHex)
778                                    theValue = Tools.toHexString(Long.valueOf(theValue.toString()), typeSize / arraySize);
779                                else if (showAsBin)
780                                    theValue = Tools.toBinaryString(Long.valueOf(theValue.toString()), typeSize / arraySize);
781                                else
782                                    theValue = theValue.toString();
783
784                                stringBuffer.append(theValue);
785
786                                if (stringBuffer.length() > 0 && i < (i1 - 1))
787                                    stringBuffer.append(", ");
788                            }
789                        }
790                    }
791                    theValue = stringBuffer;
792                }
793                else {
794                    // not an array
795                    int index = column * rowCount + row;
796
797                    if (dataset.getRank() > 1) {
798                        log.trace(
799                                "ScalarDS:createTable:AbstractTableModel:getValueAt rank={} isDataTransposed={} isNaturalOrder={}",
800                                dataset.getRank(), isDataTransposed, isNaturalOrder);
801                        if (isDataTransposed && isNaturalOrder)
802                            index = column * rowCount + row;
803                        else if (!isDataTransposed && !isNaturalOrder)
804                            // Reshape Data
805                            index = row * colCount + column;
806                        else if (isDataTransposed && !isNaturalOrder)
807                            // Transpose Data
808                            index = column * rowCount + row;
809                        else
810                            index = row * colCount + column;
811                    }
812                    log.trace("ScalarDS:createTable:AbstractTableModel:getValueAt index={} isStr={} isUINT64={}", index, isStr,
813                            isUINT64);
814
815                    if (isStr) {
816                        theValue = Array.get(dataValue, index);
817                        return theValue;
818                    }
819
820                    if (isUINT64) {
821                        theValue = Array.get(dataValue, index);
822                        Long l = (Long) theValue;
823                        BigInteger big;
824                        if (l < 0) {
825                            l = (l << 1) >>> 1;
826                            BigInteger big1 = new BigInteger("9223372036854775808"); // 2^65
827                            BigInteger big2 = new BigInteger(l.toString());
828                            big = big1.add(big2);
829                        }
830                        else {
831                            big = new BigInteger(l.toString());
832                        }
833                        if (showAsHex)
834                            theValue = Tools.toHexString(big.longValue(), 8);// big.toString(16);
835                        else if (showAsBin)
836                            theValue = Tools.toBinaryString(big.longValue(), 8);
837                        else
838                            theValue = big.toString(10);
839                    }
840                    else if (showAsHex && isInt) {
841                        // show in Hexadecimal
842                        theValue = Array.get(dataValue, index);
843                        theValue = Tools.toHexString(Long.valueOf(theValue.toString()), typeSize);
844                    }
845                    else if (showAsBin && isInt) {
846                        theValue = Array.get(dataValue, index);
847                        theValue = Tools.toBinaryString(Long.valueOf(theValue.toString()), typeSize);
848                    }
849                    else if (numberFormat != null) {
850                        // show in scientific format
851                        theValue = Array.get(dataValue, index);
852                        theValue = numberFormat.format(theValue);
853                    }
854                    else {
855                        theValue = Array.get(dataValue, index);
856                    }
857                }
858
859                log.trace("ScalarDS:createTable:AbstractTableModel:getValueAt finish");
860                return theValue;
861            } // getValueAt(int row, int column)
862        };
863
864        theTable = new JTable(tm) {
865            private static final long serialVersionUID = -145476220959400488L;
866            private final Datatype    dtype            = dataset.getDatatype();
867            private final boolean     isArray          = (dtype.getDatatypeClass() == Datatype.CLASS_ARRAY);
868
869            @Override
870            public boolean isCellEditable (int row, int col) {
871                if (isReadOnly || isDisplayTypeChar || isArray || showAsBin || showAsHex) {
872                    return false;
873                }
874                else {
875                    return true;
876                }
877            }
878
879            @Override
880            public boolean editCellAt (int row, int column, java.util.EventObject e) {
881                if (!isCellEditable(row, column)) {
882                    return super.editCellAt(row, column, e);
883                }
884
885                if (e instanceof KeyEvent) {
886                    KeyEvent ke = (KeyEvent) e;
887                    if (ke.getID() == KeyEvent.KEY_PRESSED) {
888                        startEditing[0] = true;
889                    }
890                }
891                else if (e instanceof MouseEvent) {
892                    MouseEvent me = (MouseEvent) e;
893                    int mc = me.getClickCount();
894                    if (mc > 1) {
895                        currentEditingCellValue = getValueAt(row, column);
896                    }
897                }
898
899                return super.editCellAt(row, column, e);
900            }
901
902            @Override
903            public void editingStopped (ChangeEvent e) {
904                int row = getEditingRow();
905                int col = getEditingColumn();
906                super.editingStopped(e);
907                startEditing[0] = false;
908
909                Object source = e.getSource();
910
911                if (source instanceof CellEditor) {
912                    CellEditor editor = (CellEditor) source;
913                    String cellValue = (String) editor.getCellEditorValue();
914
915                    try {
916                        updateValueInMemory(cellValue, row, col);
917                    }
918                    catch (Exception ex) {
919                        toolkit.beep();
920                        JOptionPane.showMessageDialog(this, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
921                    }
922                } // if (source instanceof CellEditor)
923            }
924
925            @Override
926            public boolean isCellSelected (int row, int column) {
927                if ((getSelectedRow() == row) && (getSelectedColumn() == column)) {
928                    cellLabel.setText(String.valueOf(rowStart + indexBase + row * rowStride) + ", " + table.getColumnName(column)
929                            + "  =  ");
930
931                    log.trace("JTable.ScalarDS isCellSelected isRegRef={} isObjRef={}", isRegRef, isObjRef);
932                    Object val = getValueAt(row, column);
933                    String strVal = null;
934
935                    if (isRegRef) {
936                        boolean displayValues = ViewProperties.showRegRefValues();
937                        log.trace("JTable.ScalarDS isCellSelected displayValues={}", displayValues);
938                        if (displayValues && val != null && ((String) val).compareTo("NULL") != 0) {
939                            String reg = (String) val;
940                            boolean isPointSelection = (reg.indexOf('-') <= 0);
941
942                            // find the object location
943                            String oidStr = reg.substring(reg.indexOf('/'), reg.indexOf(' '));
944                            log.trace("JTable.ScalarDS isCellSelected: isPointSelection={} oidStr={}", isPointSelection, oidStr);
945
946                            // decode the region selection
947                            String regStr = reg.substring(reg.indexOf('{') + 1, reg.indexOf('}'));
948                            if (regStr == null || regStr.length() <= 0) { // no
949                                                                          // selection
950                                strVal = null;
951                            }
952                            else {
953                                reg.substring(reg.indexOf('}') + 1);
954
955                                StringTokenizer st = new StringTokenizer(regStr);
956                                int nSelections = st.countTokens();
957                                if (nSelections <= 0) { // no selection
958                                    strVal = null;
959                                }
960                                else {
961                                    log.trace("JTable.ScalarDS isCellSelected: nSelections={}", nSelections);
962
963                                    HObject obj = FileFormat.findObject(dataset.getFileFormat(), oidStr);
964                                    if (obj == null || !(obj instanceof ScalarDS)) { // no
965                                                                                     // selection
966                                        strVal = null;
967                                    }
968                                    else {
969                                        ScalarDS dset = (ScalarDS) obj;
970                                        try {
971                                            dset.init();
972                                        }
973                                        catch (Exception ex) {
974                                            log.debug("reference dset did not init()", ex);
975                                        }
976                                        StringBuffer selectionSB = new StringBuffer();
977                                        StringBuffer strvalSB = new StringBuffer();
978
979                                        int idx = 0;
980                                        while (st.hasMoreTokens()) {
981                                            log.trace("JTable.ScalarDS isCellSelected: st.hasMoreTokens() begin");
982
983                                            int rank = dset.getRank();
984                                            long start[] = dset.getStartDims();
985                                            long count[] = dset.getSelectedDims();
986                                            // long count[] = new long[rank];
987
988                                            // set the selected dimension sizes
989                                            // based on the region selection
990                                            // info.
991                                            String sizeStr = null;
992                                            String token = st.nextToken();
993
994                                            selectionSB.setLength(0);
995                                            selectionSB.append(token);
996                                            log.trace("JTable.ScalarDS isCellSelected: selectionSB={}", selectionSB);
997
998                                            token = token.replace('(', ' ');
999                                            token = token.replace(')', ' ');
1000                                            if (isPointSelection) {
1001                                                // point selection
1002                                                String[] tmp = token.split(",");
1003                                                for (int x = 0; x < tmp.length; x++) {
1004                                                    count[x] = 1;
1005                                                    sizeStr = tmp[x].trim();
1006                                                    start[x] = Long.valueOf(sizeStr);
1007                                                    log.trace("JTable.ScalarDS isCellSelected: point sel={}", tmp[x]);
1008                                                }
1009                                            }
1010                                            else {
1011                                                // rectangle selection
1012                                                String startStr = token.substring(0, token.indexOf('-'));
1013                                                String endStr = token.substring(token.indexOf('-') + 1);
1014                                                log.trace("JTable.ScalarDS isCellSelected: rect sel with startStr={} endStr={}",
1015                                                        startStr, endStr);
1016                                                String[] tmp = startStr.split(",");
1017                                                log.trace("JTable.ScalarDS isCellSelected: tmp with length={} rank={}", tmp.length,
1018                                                        rank);
1019                                                for (int x = 0; x < tmp.length; x++) {
1020                                                    sizeStr = tmp[x].trim();
1021                                                    start[x] = Long.valueOf(sizeStr);
1022                                                    log.trace("JTable.ScalarDS isCellSelected: rect start={}", tmp[x]);
1023                                                }
1024                                                tmp = endStr.split(",");
1025                                                for (int x = 0; x < tmp.length; x++) {
1026                                                    sizeStr = tmp[x].trim();
1027                                                    count[x] = Long.valueOf(sizeStr) - start[x] + 1;
1028                                                    log.trace("JTable.ScalarDS isCellSelected: rect end={} count={}", tmp[x],
1029                                                            count[x]);
1030                                                }
1031                                            }
1032                                            log.trace("JTable.ScalarDS isCellSelected: selection inited");
1033
1034                                            Object dbuf = null;
1035                                            try {
1036                                                dbuf = dset.getData();
1037                                            }
1038                                            catch (Exception ex) {
1039                                                JOptionPane.showMessageDialog(this, ex, "Region Reference:" + getTitle(),
1040                                                        JOptionPane.ERROR_MESSAGE);
1041                                            }
1042
1043                                            // Convert dbuf to a displayable
1044                                            // string
1045                                            String cName = dbuf.getClass().getName();
1046                                            int cIndex = cName.lastIndexOf("[");
1047                                            if (cIndex >= 0) {
1048                                                NT = cName.charAt(cIndex + 1);
1049                                            }
1050                                            log.trace("JTable.ScalarDS isCellSelected: cName={} NT={}", cName, NT);
1051
1052                                            if (idx > 0) strvalSB.append(',');
1053
1054                                            // convert numerical data into char
1055                                            // only possible cases are byte[]
1056                                            // and short[] (converted from
1057                                            // unsigned
1058                                            // byte)
1059                                            Datatype dtype = dset.getDatatype();
1060                                            Datatype baseType = dtype.getBasetype();
1061                                            log.trace("JTable.ScalarDS isCellSelected: dtype={} baseType={}",
1062                                                    dtype.getDatatypeDescription(), baseType);
1063                                            if (baseType == null) baseType = dtype;
1064                                            if ((dtype.getDatatypeClass() == Datatype.CLASS_ARRAY && baseType.getDatatypeClass() == Datatype.CLASS_CHAR)
1065                                                    && ((NT == 'B') || (NT == 'S'))) {
1066                                                int n = Array.getLength(dbuf);
1067                                                log.trace("JTable.ScalarDS isCellSelected charData length = {}", n);
1068                                                char[] charData = new char[n];
1069                                                for (int i = 0; i < n; i++) {
1070                                                    if (NT == 'B') {
1071                                                        charData[i] = (char) Array.getByte(dbuf, i);
1072                                                    }
1073                                                    else if (NT == 'S') {
1074                                                        charData[i] = (char) Array.getShort(dbuf, i);
1075                                                    }
1076                                                }
1077
1078                                                strvalSB.append(charData);
1079                                                log.trace("JTable.ScalarDS isCellSelected charData");// =
1080                                                                                                     // {}",
1081                                                                                                     // strvalSB);
1082                                            }
1083                                            else {
1084                                                // numerical values
1085                                                if (dtype.getDatatypeClass() == Datatype.CLASS_ARRAY) dtype = baseType;
1086                                                boolean is_unsigned = dtype.isUnsigned();
1087                                                int n = Array.getLength(dbuf);
1088                                                if (is_unsigned) {
1089                                                    switch (NT) {
1090                                                        case 'B':
1091                                                            byte[] barray = (byte[]) dbuf;
1092                                                            short sValue = barray[0];
1093                                                            if (sValue < 0) {
1094                                                                sValue += 256;
1095                                                            }
1096                                                            strvalSB.append(sValue);
1097                                                            for (int i = 1; i < n; i++) {
1098                                                                strvalSB.append(',');
1099                                                                sValue = barray[i];
1100                                                                if (sValue < 0) {
1101                                                                    sValue += 256;
1102                                                                }
1103                                                                strvalSB.append(sValue);
1104                                                            }
1105                                                            break;
1106                                                        case 'S':
1107                                                            short[] sarray = (short[]) dbuf;
1108                                                            int iValue = sarray[0];
1109                                                            if (iValue < 0) {
1110                                                                iValue += 65536;
1111                                                            }
1112                                                            strvalSB.append(iValue);
1113                                                            for (int i = 1; i < n; i++) {
1114                                                                strvalSB.append(',');
1115                                                                iValue = sarray[i];
1116                                                                if (iValue < 0) {
1117                                                                    iValue += 65536;
1118                                                                }
1119                                                                strvalSB.append(iValue);
1120                                                            }
1121                                                            break;
1122                                                        case 'I':
1123                                                            int[] iarray = (int[]) dbuf;
1124                                                            long lValue = iarray[0];
1125                                                            if (lValue < 0) {
1126                                                                lValue += 4294967296L;
1127                                                            }
1128                                                            strvalSB.append(lValue);
1129                                                            for (int i = 1; i < n; i++) {
1130                                                                strvalSB.append(',');
1131                                                                lValue = iarray[i];
1132                                                                if (lValue < 0) {
1133                                                                    lValue += 4294967296L;
1134                                                                }
1135                                                                strvalSB.append(lValue);
1136                                                            }
1137                                                            break;
1138                                                        case 'J':
1139                                                            long[] larray = (long[]) dbuf;
1140                                                            Long l = (Long) larray[0];
1141                                                            String theValue = Long.toString(l);
1142                                                            if (l < 0) {
1143                                                                l = (l << 1) >>> 1;
1144                                                                BigInteger big1 = new BigInteger("9223372036854775808"); // 2^65
1145                                                                BigInteger big2 = new BigInteger(l.toString());
1146                                                                BigInteger big = big1.add(big2);
1147                                                                theValue = big.toString();
1148                                                            }
1149                                                            strvalSB.append(theValue);
1150                                                            for (int i = 1; i < n; i++) {
1151                                                                strvalSB.append(',');
1152                                                                l = (Long) larray[i];
1153                                                                theValue = Long.toString(l);
1154                                                                if (l < 0) {
1155                                                                    l = (l << 1) >>> 1;
1156                                                                    BigInteger big1 = new BigInteger("9223372036854775808"); // 2^65
1157                                                                    BigInteger big2 = new BigInteger(l.toString());
1158                                                                    BigInteger big = big1.add(big2);
1159                                                                    theValue = big.toString();
1160                                                                }
1161                                                                strvalSB.append(theValue);
1162                                                            }
1163                                                            break;
1164                                                        default:
1165                                                            strvalSB.append(Array.get(dbuf, 0));
1166                                                            for (int i = 1; i < n; i++) {
1167                                                                strvalSB.append(',');
1168                                                                strvalSB.append(Array.get(dbuf, i));
1169                                                            }
1170                                                            break;
1171                                                    }
1172                                                }
1173                                                else {
1174                                                    for (int x = 0; x < n; x++) {
1175                                                        Object theValue = Array.get(dbuf, x);
1176                                                        if (x > 0) strvalSB.append(',');
1177                                                        strvalSB.append(theValue);
1178                                                    }
1179                                                }
1180                                                log.trace("JTable.ScalarDS isCellSelected byteString");// =
1181                                                                                                       // {}",
1182                                                                                                       // strvalSB);
1183                                            }
1184                                            idx++;
1185                                            dset.clearData();
1186                                            log.trace("JTable.ScalarDS isCellSelected: st.hasMoreTokens() end");// strvalSB
1187                                                                                                                // =
1188                                                                                                                // {}",
1189                                                                                                                // strvalSB);
1190                                        } // while (st.hasMoreTokens())
1191                                        strVal = strvalSB.toString();
1192                                        log.trace("JTable.ScalarDS isCellSelected: st.hasMoreTokens() end");// value
1193                                                                                                            // =
1194                                                                                                            // {}",
1195                                                                                                            // strVal);
1196                                    }
1197                                }
1198                            }
1199                        }
1200                        else {
1201                            strVal = null;
1202                        }
1203                    }
1204                    else if (isObjRef) {
1205                        Long ref = (Long) val;
1206                        long oid[] = { ref.longValue() };
1207
1208                        // decode object ID
1209                        try {
1210                            HObject obj = FileFormat.findObject(dataset.getFileFormat(), oid);
1211                            strVal = obj.getFullName();
1212                        }
1213                        catch (Exception ex) {
1214                            strVal = null;
1215                        }
1216                    }
1217
1218                    if (strVal == null && val != null) strVal = val.toString();
1219
1220                    log.trace("JTable.ScalarDS isCellSelected finish");// value
1221                                                                       // =
1222                                                                       // {}",strVal);
1223                    cellValueField.setText(strVal);
1224                }
1225
1226                return super.isCellSelected(row, column);
1227            }
1228        };
1229        theTable.setName("ScalarDS");
1230
1231        log.trace("createTable: ScalarDS finish");
1232        return theTable;
1233    }
1234
1235    /**
1236     * Creates a JTable to hold a compound dataset.
1237     *
1238     * @param theDataset
1239     *          The Compound dataset for the Table to display
1240     *
1241     * @return The newly created Table
1242     */
1243    private JTable createTable (CompoundDS theDataset) {
1244        log.trace("createTable(CompoundDS): start");
1245        initTable(theDataset);
1246
1247        if ((dataValue == null) || !(dataValue instanceof List)) {
1248            log.debug("createTable(): data value is null or data not a list");
1249            log.trace("createTable(): finish");
1250            return null;
1251        }
1252
1253        JTable theTable = null;
1254
1255        int rank = theDataset.getRank();
1256
1257        long[] startArray = theDataset.getStartDims();
1258        long[] strideArray = theDataset.getStride();
1259        int[] selectedIndex = theDataset.getSelectedIndex();
1260        final int rowStart = (int) startArray[selectedIndex[0]];
1261        final int rowStride = (int) strideArray[selectedIndex[0]];
1262
1263        // use lazy convert for large number of strings
1264        if (theDataset.getHeight() > 10000) {
1265            theDataset.setConvertByteToString(false);
1266        }
1267
1268        final int rows = theDataset.getHeight();
1269        int cols = theDataset.getSelectedMemberCount();
1270        String[] columnNames = new String[cols];
1271
1272        int idx = 0;
1273        String[] columnNamesAll = theDataset.getMemberNames();
1274        for (int i = 0; i < columnNamesAll.length; i++) {
1275            if (theDataset.isMemberSelected(i)) {
1276                columnNames[idx] = columnNamesAll[i];
1277                columnNames[idx] = columnNames[idx].replaceAll(CompoundDS.separator, "->");
1278                idx++;
1279            }
1280        }
1281
1282        String[] subColumnNames = columnNames;
1283        int columns = theDataset.getWidth();
1284        if (columns > 1) {
1285            // multi-dimension compound dataset
1286            subColumnNames = new String[columns * columnNames.length];
1287            int halfIdx = columnNames.length / 2;
1288            for (int i = 0; i < columns; i++) {
1289                for (int j = 0; j < columnNames.length; j++) {
1290                    // display column index only once, in the middle of the
1291                    // compound fields
1292                    if (j == halfIdx) {
1293                        // subColumnNames[i * columnNames.length + j] = (i + 1)
1294                        // + "\n " + columnNames[j];
1295                        subColumnNames[i * columnNames.length + j] = (i + indexBase) + "\n " + columnNames[j];
1296                    }
1297                    else {
1298                        subColumnNames[i * columnNames.length + j] = " \n " + columnNames[j];
1299                    }
1300                }
1301            }
1302        }
1303
1304        final String[] allColumnNames = subColumnNames;
1305        AbstractTableModel tm = new AbstractTableModel() {
1306            private static final long serialVersionUID = -2176296469630678304L;
1307            CompoundDS                compound         = (CompoundDS) dataset;
1308            int                       orders[]         = compound.getSelectedMemberOrders();
1309            Datatype                  types[]          = compound.getSelectedMemberTypes();
1310            StringBuffer              stringBuffer     = new StringBuffer();
1311            int                       nFields          = ((List<?>) dataValue).size();
1312            int                       nRows            = getRowCount();
1313            int                       nSubColumns      = (nFields > 0) ? getColumnCount() / nFields : 0;
1314
1315            @Override
1316            public int getColumnCount ( ) {
1317                return allColumnNames.length;
1318            }
1319
1320            @Override
1321            public int getRowCount ( ) {
1322                return rows;
1323            }
1324
1325            @Override
1326            public String getColumnName (int col) {
1327                return allColumnNames[col];
1328            }
1329
1330            @Override
1331            public Object getValueAt (int row, int col) {
1332                if (startEditing[0]) return "";
1333
1334                int fieldIdx = col;
1335                int rowIdx = row;
1336                char CNT = ' ';
1337                boolean CshowAsHex = false;
1338                boolean CshowAsBin = false;
1339                log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt({},{}) start", row, col);
1340
1341                if (nSubColumns > 1) { // multi-dimension compound dataset
1342                    int colIdx = col / nFields;
1343                    fieldIdx = col - colIdx * nFields;
1344                    // BUG 573: rowIdx = row * orders[fieldIdx] + colIdx * nRows
1345                    // * orders[fieldIdx];
1346                    rowIdx = row * orders[fieldIdx] * nSubColumns + colIdx * orders[fieldIdx];
1347                    log.trace(
1348                            "CompoundDS:createTable:AbstractTableModel:getValueAt() row={} orders[{}]={} nSubColumns={} colIdx={}",
1349                            row, fieldIdx, orders[fieldIdx], nSubColumns, colIdx);
1350                }
1351                else {
1352                    rowIdx = row * orders[fieldIdx];
1353                    log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt() row={} orders[{}]={}", row, fieldIdx,
1354                            orders[fieldIdx]);
1355                }
1356                log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt() fieldIdx={} rowIdx={}", fieldIdx, rowIdx);
1357
1358                Object colValue = ((List<?>) dataValue).get(fieldIdx);
1359                if (colValue == null) {
1360                    return "Null";
1361                }
1362
1363                stringBuffer.setLength(0); // clear the old string
1364                Datatype dtype = types[fieldIdx];
1365                boolean isString = (dtype.getDatatypeClass() == Datatype.CLASS_STRING);
1366                boolean isArray = (dtype.getDatatypeClass() == Datatype.CLASS_ARRAY);
1367                if (isArray) {
1368                    dtype = types[fieldIdx].getBasetype();
1369                    isString = (dtype.getDatatypeClass() == Datatype.CLASS_STRING);
1370                    log.trace("**CompoundDS:createTable:AbstractTableModel:getValueAt(): isArray={} isString={}", isArray, isString);
1371
1372                    if (dtype instanceof H5Datatype) {
1373                        // Since variable-length strings are of type CLASS_STRING, not CLASS_VLEN,
1374                        // the check below cannot determine if the datatype is a variable-length string
1375                        if (((H5Datatype) dtype).isVLEN() && isString) {
1376                            for (int i = 0; i < orders[fieldIdx]; i++) {
1377                                if (i > 0) stringBuffer.append(", ");
1378                                stringBuffer.append(((String[]) colValue)[i]);
1379                            }
1380                            return stringBuffer;
1381                        }
1382                    }
1383                    else if (dtype.getDatatypeClass() == Datatype.CLASS_VLEN) {
1384                        // Only support variable length strings
1385                        if (!(dtype.getBasetype().getDatatypeClass() == Datatype.CLASS_STRING)) {
1386                            int arraylen = (int) types[fieldIdx].getDatatypeSize();
1387                            log.trace("**CompoundDS:createTable:AbstractTableModel:getValueAt(): isArray={} of {} istype={}", isArray, arraylen, dtype);
1388                            String str = new String( "*unsupported*");
1389                            stringBuffer.append(str.trim());
1390                            return stringBuffer;
1391                        }
1392                    }
1393                }
1394                log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt(): isString={} getBasetype()={}", isString, types[fieldIdx].getDatatypeClass());
1395                if (isString && ((colValue instanceof byte[]) || isArray)) {
1396                    // strings
1397                    int strlen = dtype.getDatatypeSize();
1398                    int arraylen = strlen;
1399                    if(isArray) {
1400                        arraylen = types[fieldIdx].getDatatypeSize();
1401                    }
1402                    log.trace("**CompoundDS:createTable:AbstractTableModel:getValueAt(): isArray={} of {} isString={} of {}", isArray, arraylen, isString, strlen);
1403                    int arraycnt = arraylen / strlen;
1404                    for (int loopidx = 0; loopidx < arraycnt; loopidx++) {
1405                        if(isArray && loopidx > 0) {
1406                            stringBuffer.append(", ");
1407                        }
1408                        String str = new String(((byte[]) colValue), rowIdx * strlen + loopidx * strlen, strlen);
1409                        int idx = str.indexOf('\0');
1410                        if (idx > 0) {
1411                            str = str.substring(0, idx);
1412                        }
1413                        stringBuffer.append(str.trim());
1414                    }
1415                }
1416                else if (isArray && dtype.getDatatypeClass() == Datatype.CLASS_COMPOUND) {
1417                    for (int i = 0; i < orders[fieldIdx]; i++) {
1418                        try {
1419                            int tid = dtype.toNative();
1420                            int numberOfMembers = H5.H5Tget_nmembers(tid);
1421                            Object field_data = null;
1422
1423                            try {
1424                                field_data = Array.get(colValue, rowIdx + i);
1425                            }
1426                            catch (Exception ex) {
1427                                log.debug("CompoundDS:createTable:AbstractTableModel:getValueAt(): could not retrieve field_data: ", ex);
1428                            }
1429
1430                            if (i > 0) stringBuffer.append(", ");
1431                            stringBuffer.append("[ ");
1432
1433                            for (int j = 0; j < numberOfMembers; j++) {
1434                                Object theValue = null;
1435
1436                                try {
1437                                    theValue = Array.get(field_data, j);
1438                                    log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt() theValue[{}]={}", j, theValue.toString());
1439                                }
1440                                catch (Exception ex) {
1441                                    log.debug("CompoundDS:createTable:AbstractTableModel:getValueAt() member[{}] is unsupported", j, ex);
1442                                    theValue = "*unsupported*";
1443                                }
1444
1445                                if (j > 0) stringBuffer.append(", ");
1446                                stringBuffer.append(theValue);
1447                            }
1448
1449                            stringBuffer.append(" ]");
1450                        }
1451                        catch (Exception ex) {
1452                            log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt(): ", ex);
1453                        }
1454                    }
1455
1456                    return stringBuffer;
1457                }
1458                else {
1459                    // numerical values
1460
1461                    String cName = colValue.getClass().getName();
1462                    int cIndex = cName.lastIndexOf("[");
1463                    if (cIndex >= 0) {
1464                        CNT = cName.charAt(cIndex + 1);
1465                    }
1466                    log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt(): cName={} CNT={}", cName, CNT);
1467
1468                    boolean isUINT64 = false;
1469                    boolean isInt = (CNT == 'B' || CNT == 'S' || CNT == 'I' || CNT == 'J');
1470                    boolean isEnum = dtype.getDatatypeClass() == Datatype.CLASS_ENUM;
1471                    int typeSize = dtype.getDatatypeSize();
1472
1473                    if ((dtype.getDatatypeClass() == Datatype.CLASS_BITFIELD)
1474                            || (dtype.getDatatypeClass() == Datatype.CLASS_OPAQUE)) {
1475                        CshowAsHex = true;
1476                        log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt() class={} (BITFIELD or OPAQUE)",
1477                                dtype.getDatatypeClass());
1478                    }
1479                    if (dtype.isUnsigned()) {
1480                        if (cIndex >= 0) {
1481                            isUINT64 = (cName.charAt(cIndex + 1) == 'J');
1482                        }
1483                    }
1484                    log.trace(
1485                            "CompoundDS:createTable:AbstractTableModel:getValueAt() isUINT64={} isInt={} CshowAsHex={} typeSize={}",
1486                            isUINT64, isInt, CshowAsHex, typeSize);
1487
1488                    if (isEnum) {
1489                        String[] outValues = new String[getRowCount() * orders[fieldIdx]];
1490
1491                        if (!dataset.isEnumConverted()) {
1492                            try {
1493                                H5Datatype.convertEnumValueToName(dtype.toNative(), colValue, outValues);
1494                            }
1495                            catch (HDF5Exception ex) {
1496                                log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt(): Could not convert enum values to names: ex");
1497                                return stringBuffer;
1498                            }
1499                        }
1500                        else
1501                            outValues = (String[])colValue;
1502                        for (int i = rowIdx; i < (rowIdx + orders[fieldIdx]); i++) {
1503                            if (i > rowIdx) stringBuffer.append(", ");
1504                            stringBuffer.append(outValues[i]);
1505                        }
1506                    }
1507                    else {
1508                        for (int i = 0; i < orders[fieldIdx]; i++) {
1509                            log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt():for[{}]", i);
1510                            if (isUINT64) {
1511                                Object theValue = Array.get(colValue, rowIdx + i);
1512                                log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt() isUINT64 theValue[{}]={}", i,
1513                                        theValue.toString());
1514                                Long l = (Long) theValue;
1515                                BigInteger big;
1516                                if (l < 0) {
1517                                    l = (l << 1) >>> 1;
1518                                    BigInteger big1 = new BigInteger("9223372036854775808"); // 2^65
1519                                    BigInteger big2 = new BigInteger(l.toString());
1520                                    big = big1.add(big2);
1521                                }
1522                                else {
1523                                    big = new BigInteger(l.toString());
1524                                }
1525                                if (showAsHex)
1526                                    theValue = Tools.toHexString(big.longValue(), typeSize);// big.toString(16);
1527                                else if (showAsBin)
1528                                    theValue = Tools.toBinaryString(big.longValue(), typeSize);
1529                                else
1530                                    theValue = big.toString(10);
1531
1532                                if (i > 0) stringBuffer.append(", ");
1533                                stringBuffer.append(theValue);
1534                            }
1535                            else if (CshowAsHex && isInt) {
1536                                char[] hexArray = "0123456789ABCDEF".toCharArray();
1537                                Object theValue = Array.get(colValue, rowIdx * typeSize + typeSize * i);
1538                                log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt() CshowAsHex theValue[{}]={}", i,
1539                                        theValue.toString());
1540                                // show in Hexadecimal
1541                                char[] hexChars = new char[2];
1542                                if (i > 0) stringBuffer.append(", ");
1543                                for (int x = 0; x < typeSize; x++) {
1544                                    if (x > 0)
1545                                        theValue = Array.get(colValue, rowIdx * typeSize + typeSize * i + x);
1546                                    int v = (int) ((Byte) theValue) & 0xFF;
1547                                    hexChars[0] = hexArray[v >>> 4];
1548                                    hexChars[1] = hexArray[v & 0x0F];
1549                                    if (x > 0) stringBuffer.append(":");
1550                                    stringBuffer.append(hexChars);
1551                                    log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt() hexChars[{}]={}", x, hexChars);
1552                                }
1553                            }
1554                            else if (showAsBin && isInt) {
1555                                Object theValue = Array.get(colValue, rowIdx + typeSize * i);
1556                                log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt() showAsBin theValue[{}]={}", i,
1557                                        theValue.toString());
1558                                theValue = Tools.toBinaryString(Long.valueOf(theValue.toString()), typeSize);
1559                                if (i > 0) stringBuffer.append(", ");
1560                                stringBuffer.append(theValue);
1561                            }
1562                            else if (numberFormat != null) {
1563                                // show in scientific format
1564                                Object theValue = Array.get(colValue, rowIdx + i);
1565                                log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt() numberFormat theValue[{}]={}", i,
1566                                        theValue.toString());
1567                                theValue = numberFormat.format(theValue);
1568                                if (i > 0) stringBuffer.append(", ");
1569                                stringBuffer.append(theValue);
1570                            }
1571                            else {
1572                                Object theValue = Array.get(colValue, rowIdx + i);
1573                                log.trace("CompoundDS:createTable:AbstractTableModel:getValueAt() theValue[{}]={}", i,
1574                                        theValue.toString());
1575                                if (i > 0) stringBuffer.append(", ");
1576                                stringBuffer.append(theValue);
1577                            }
1578                        }
1579                    } // end of else {
1580                } // end of else {
1581
1582                return stringBuffer;
1583            }
1584        };
1585
1586        theTable = new JTable(tm) {
1587            private static final long serialVersionUID   = 3221288637329958074L;
1588            int                       lastSelectedRow    = -1;
1589            int                       lastSelectedColumn = -1;
1590
1591            @Override
1592            public boolean isCellEditable (int row, int column) {
1593                return !isReadOnly;
1594            }
1595
1596            @Override
1597            public boolean editCellAt (int row, int column, java.util.EventObject e) {
1598                if (!isCellEditable(row, column)) {
1599                    return super.editCellAt(row, column, e);
1600                }
1601
1602                if (e instanceof KeyEvent) {
1603                    KeyEvent ke = (KeyEvent) e;
1604                    if (ke.getID() == KeyEvent.KEY_PRESSED) startEditing[0] = true;
1605                }
1606                else if (e instanceof MouseEvent) {
1607                    MouseEvent me = (MouseEvent) e;
1608                    int mc = me.getClickCount();
1609                    if (mc > 1) {
1610                        currentEditingCellValue = getValueAt(row, column);
1611                    }
1612                }
1613
1614                return super.editCellAt(row, column, e);
1615            }
1616
1617            @Override
1618            public void editingStopped (ChangeEvent e) {
1619                int row = getEditingRow();
1620                int col = getEditingColumn();
1621                super.editingStopped(e);
1622                startEditing[0] = false;
1623
1624                Object source = e.getSource();
1625
1626                if (source instanceof CellEditor) {
1627                    CellEditor editor = (CellEditor) source;
1628                    String cellValue = (String) editor.getCellEditorValue();
1629
1630                    try {
1631                        updateValueInMemory(cellValue, row, col);
1632                    }
1633                    catch (Exception ex) {
1634                        log.debug("updateValueInMemory: ", ex);
1635                        toolkit.beep();
1636                        JOptionPane.showMessageDialog(this, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
1637                    }
1638                } // if (source instanceof CellEditor)
1639            }
1640
1641            @Override
1642            public boolean isCellSelected (int row, int column) {
1643                if ((lastSelectedRow == row) && (lastSelectedColumn == column)) {
1644                    return super.isCellSelected(row, column);
1645                }
1646                log.trace("JTable.CompoundDS isCellSelected row={} column={}", row, column);
1647
1648                lastSelectedRow = row;
1649                lastSelectedColumn = column;
1650                if ((getSelectedRow() == row) && (getSelectedColumn() == column)) {
1651                    cellLabel.setText(String.valueOf(rowStart + indexBase + row * rowStride) + ", " + table.getColumnName(column)
1652                            + "  =  ");
1653                    cellValueField.setText(getValueAt(row, column).toString());
1654                }
1655
1656                return super.isCellSelected(row, column);
1657            }
1658        };
1659
1660        if (columns > 1) {
1661            // multi-dimension compound dataset
1662            MultiLineHeaderRenderer renderer = new MultiLineHeaderRenderer(columns, columnNames.length);
1663            Enumeration<?> local_enum = theTable.getColumnModel().getColumns();
1664            while (local_enum.hasMoreElements()) {
1665                ((TableColumn) local_enum.nextElement()).setHeaderRenderer(renderer);
1666            }
1667        }
1668        theTable.setName("CompoundDS");
1669
1670        log.trace("createTable(CompoundDS): finish");
1671        return theTable;
1672    } /* createTable */
1673
1674
1675    /**
1676     * Creates the menubar for the Table.
1677     */
1678    private JMenuBar createMenuBar ( ) {
1679        JMenuBar bar = new JMenuBar();
1680        JButton button;
1681        boolean isEditable = !isReadOnly;
1682        boolean is3D = (dataset.getRank() > 2);
1683
1684        JMenu menu = new JMenu("Table", false);
1685        menu.setMnemonic('T');
1686        bar.add(menu);
1687
1688        JMenuItem item = new JMenuItem("Export Data to Text File");
1689        item.addActionListener(this);
1690        item.setActionCommand("Save table as text");
1691        menu.add(item);
1692
1693        JMenu exportAsBinaryMenu = new JMenu("Export Data to Binary File");
1694        if ((dataset instanceof ScalarDS)) {
1695            menu.add(exportAsBinaryMenu);
1696        }
1697        item = new JMenuItem("Native Order");
1698        item.addActionListener(this);
1699        item.setActionCommand("Save table as binary Native Order");
1700        exportAsBinaryMenu.add(item);
1701        item = new JMenuItem("Little Endian");
1702        item.addActionListener(this);
1703        item.setActionCommand("Save table as binary Little Endian");
1704        exportAsBinaryMenu.add(item);
1705        item = new JMenuItem("Big Endian");
1706        item.addActionListener(this);
1707        item.setActionCommand("Save table as binary Big Endian");
1708        exportAsBinaryMenu.add(item);
1709
1710        menu.addSeparator();
1711
1712        item = new JMenuItem("Import Data from Text File");
1713        item.addActionListener(this);
1714        item.setActionCommand("Import data from file");
1715        item.setEnabled(isEditable);
1716        menu.add(item);
1717
1718        item = checkFixedDataLength;
1719        item.addActionListener(this);
1720        item.setActionCommand("Fixed data length");
1721        if (dataset instanceof ScalarDS) {
1722            menu.add(item);
1723        }
1724
1725        JMenu importFromBinaryMenu = new JMenu("Import Data from Binary File");
1726        if ((dataset instanceof ScalarDS)) {
1727            menu.add(importFromBinaryMenu);
1728        }
1729        item = new JMenuItem("Native Order");
1730        item.addActionListener(this);
1731        item.setActionCommand("Order as Native Order");
1732        importFromBinaryMenu.add(item);
1733        item = new JMenuItem("Little Endian");
1734        item.addActionListener(this);
1735        item.setActionCommand("Order as Little Endian");
1736        importFromBinaryMenu.add(item);
1737        item = new JMenuItem("Big Endian");
1738        item.addActionListener(this);
1739        item.setActionCommand("Order as Big Endian");
1740        importFromBinaryMenu.add(item);
1741
1742        menu.addSeparator();
1743
1744        item = new JMenuItem("Copy");
1745        item.addActionListener(this);
1746        item.setActionCommand("Copy data");
1747        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), true));
1748        menu.add(item);
1749
1750        item = new JMenuItem("Paste");
1751        item.addActionListener(this);
1752        item.setActionCommand("Paste data");
1753        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), true));
1754        item.setEnabled(isEditable);
1755        menu.add(item);
1756
1757        menu.addSeparator();
1758
1759        item = new JMenuItem("Copy to New Dataset");
1760        item.addActionListener(this);
1761        item.setActionCommand("Write selection to dataset");
1762        item.setEnabled(isEditable && (dataset instanceof ScalarDS));
1763        menu.add(item);
1764
1765        item = new JMenuItem("Save Changes to File");
1766        item.addActionListener(this);
1767        item.setActionCommand("Save dataset");
1768        item.setEnabled(isEditable);
1769        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), true));
1770        menu.add(item);
1771
1772        menu.addSeparator();
1773
1774        item = new JMenuItem("Select All");
1775        item.addActionListener(this);
1776        item.setActionCommand("Select all data");
1777        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), true));
1778        menu.add(item);
1779
1780        menu.addSeparator();
1781
1782        item = new JMenuItem("Show Lineplot");
1783        item.addActionListener(this);
1784        item.setActionCommand("Show chart");
1785        menu.add(item);
1786
1787        item = new JMenuItem("Show Statistics");
1788        item.addActionListener(this);
1789        item.setActionCommand("Show statistics");
1790        menu.add(item);
1791
1792        menu.addSeparator();
1793
1794        item = new JMenuItem("Math Conversion");
1795        item.addActionListener(this);
1796        item.setActionCommand("Math conversion");
1797        item.setEnabled(isEditable);
1798        menu.add(item);
1799
1800        menu.addSeparator();
1801
1802        item = checkScientificNotation;
1803        item.addActionListener(this);
1804        item.setActionCommand("Show scientific notation");
1805        if (dataset instanceof ScalarDS) {
1806            menu.add(item);
1807        }
1808
1809        item = checkCustomNotation;
1810        item.addActionListener(this);
1811        item.setActionCommand("Show custom notation");
1812        if (dataset instanceof ScalarDS) {
1813            menu.add(item);
1814        }
1815
1816        item = new JMenuItem("Create custom notation");
1817        item.addActionListener(this);
1818        item.setActionCommand("Create custom notation");
1819        menu.add(item);
1820
1821        boolean isInt = (NT == 'B' || NT == 'S' || NT == 'I' || NT == 'J');
1822        // this will allow disabling of hex and binary display menu options
1823        // boolean isUINT64 = (dataset.getDatatype().isUnsigned() && (NT == 'J'));
1824        item = checkHex;
1825        item.addActionListener(this);
1826        item.setActionCommand("Show hexadecimal");
1827        if ((dataset instanceof ScalarDS) && isInt /* && !isUINT64 */) {
1828            menu.add(item);
1829        }
1830
1831        item = checkBin;
1832        item.addActionListener(this);
1833        item.setActionCommand("Show binary");
1834        if ((dataset instanceof ScalarDS) && isInt /* && !isUINT64 */) {
1835            menu.add(item);
1836        }
1837
1838        menu.addSeparator();
1839
1840        item = new JMenuItem("Close");
1841        item.addActionListener(this);
1842        item.setActionCommand("Close");
1843        menu.add(item);
1844
1845        bar.add(new JLabel("     "));
1846
1847        // add icons to the menubar
1848
1849        Insets margin = new Insets(0, 2, 0, 2);
1850
1851        // chart button
1852        button = new JButton(ViewProperties.getChartIcon());
1853        bar.add(button);
1854        button.setToolTipText("Line Plot");
1855        button.setMargin(margin);
1856        button.addActionListener(this);
1857        button.setActionCommand("Show chart");
1858
1859        if (is3D) {
1860            bar.add(new JLabel("     "));
1861
1862            // first button
1863            button = new JButton(ViewProperties.getFirstIcon());
1864            bar.add(button);
1865            button.setToolTipText("First");
1866            button.setMargin(margin);
1867            button.setName("firstbutton");
1868            button.addActionListener(this);
1869            button.setActionCommand("First page");
1870
1871            // previous button
1872            button = new JButton(ViewProperties.getPreviousIcon());
1873            bar.add(button);
1874            button.setToolTipText("Previous");
1875            button.setMargin(margin);
1876            button.setName("prevbutton");
1877            button.addActionListener(this);
1878            button.setActionCommand("Previous page");
1879
1880            frameField = new JTextField(String.valueOf(curFrame));
1881            frameField.setMaximumSize(new Dimension(50, 30));
1882            bar.add(frameField);
1883            frameField.setMargin(margin);
1884            frameField.setName("framenumber");
1885            frameField.addActionListener(this);
1886            frameField.setActionCommand("Go to frame");
1887
1888            JLabel tmpField = new JLabel(String.valueOf(maxFrame), SwingConstants.CENTER);
1889            tmpField.setMaximumSize(new Dimension(50, 30));
1890            bar.add(tmpField);
1891
1892            // next button
1893            button = new JButton(ViewProperties.getNextIcon());
1894            bar.add(button);
1895            button.setToolTipText("Next");
1896            button.setMargin(margin);
1897            button.setName("nextbutton");
1898            button.addActionListener(this);
1899            button.setActionCommand("Next page");
1900
1901            // last button
1902            button = new JButton(ViewProperties.getLastIcon());
1903            bar.add(button);
1904            button.setToolTipText("Last");
1905            button.setMargin(margin);
1906            button.setName("lastbutton");
1907            button.addActionListener(this);
1908            button.setActionCommand("Last page");
1909        }
1910
1911        return bar;
1912    }
1913
1914    @Override
1915    public void actionPerformed (ActionEvent e) {
1916        try {
1917            setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1918
1919            e.getSource();
1920            String cmd = e.getActionCommand();
1921            log.trace("DefaultTableView actionPerformed: {}", cmd);
1922
1923            if (cmd.equals("Close")) {
1924                dispose(); // terminate the application
1925            }
1926            else if (cmd.equals("Save table as text")) {
1927                try {
1928                    saveAsText();
1929                }
1930                catch (Exception ex) {
1931                    toolkit.beep();
1932                    JOptionPane.showMessageDialog((JFrame) viewer, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
1933                }
1934            }
1935            else if (cmd.startsWith("Save table as binary")) {
1936                if (cmd.equals("Save table as binary Native Order")) binaryOrder = 1;
1937                if (cmd.equals("Save table as binary Little Endian")) binaryOrder = 2;
1938                if (cmd.equals("Save table as binary Big Endian")) binaryOrder = 3;
1939                try {
1940                    saveAsBinary();
1941                }
1942                catch (Exception ex) {
1943                    toolkit.beep();
1944                    JOptionPane.showMessageDialog((JFrame) viewer, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
1945                }
1946            }
1947            else if (cmd.equals("Copy data")) {
1948                copyData();
1949            }
1950            else if (cmd.equals("Paste data")) {
1951                pasteData();
1952            }
1953            else if (cmd.equals("Import data from file")) {
1954                String currentDir = dataset.getFileFormat().getParent();
1955                JFileChooser fchooser = new JFileChooser(currentDir);
1956                fchooser.setFileFilter(DefaultFileFilter.getFileFilterText());
1957                int returnVal = fchooser.showOpenDialog(this);
1958
1959                if (returnVal != JFileChooser.APPROVE_OPTION) {
1960                    return;
1961                }
1962
1963                File choosedFile = fchooser.getSelectedFile();
1964                if (choosedFile == null) {
1965                    return;
1966                }
1967
1968                String txtFile = choosedFile.getAbsolutePath();
1969                importTextData(txtFile);
1970            }
1971            else if (cmd.startsWith("Order as")) {
1972                if (cmd.equals("Order as Native Order")) binaryOrder = 1;
1973                if (cmd.equals("Order as Little Endian")) binaryOrder = 2;
1974                if (cmd.equals("Order as Big Endian")) binaryOrder = 3;
1975
1976                importBinaryData();
1977            }
1978            else if (cmd.equals("Write selection to dataset")) {
1979                JTable jtable = getTable();
1980                if ((jtable.getSelectedColumnCount() <= 0) || (jtable.getSelectedRowCount() <= 0)) {
1981                    JOptionPane.showMessageDialog(this, "Select table cells to write.", "HDFView", JOptionPane.INFORMATION_MESSAGE);
1982                    return;
1983                }
1984
1985                TreeView treeView = viewer.getTreeView();
1986                TreeNode node = viewer.getTreeView().findTreeNode(dataset);
1987                Group pGroup = (Group) ((DefaultMutableTreeNode) node.getParent()).getUserObject();
1988                TreeNode root = dataset.getFileFormat().getRootNode();
1989
1990                if (root == null) {
1991                    return;
1992                }
1993
1994                Vector<Object> list = new Vector<Object>(dataset.getFileFormat().getNumberOfMembers() + 5);
1995                DefaultMutableTreeNode theNode = null;
1996                Enumeration<?> local_enum = ((DefaultMutableTreeNode) root).depthFirstEnumeration();
1997                while (local_enum.hasMoreElements()) {
1998                    theNode = (DefaultMutableTreeNode) local_enum.nextElement();
1999                    list.add(theNode.getUserObject());
2000                }
2001
2002                NewDatasetDialog dialog = new NewDatasetDialog((JFrame) viewer, pGroup, list, this);
2003                dialog.setVisible(true);
2004
2005                HObject obj = (HObject) dialog.getObject();
2006                if (obj != null) {
2007                    Group pgroup = dialog.getParentGroup();
2008                    try {
2009                        treeView.addObject(obj, pgroup);
2010                    }
2011                    catch (Exception ex) {
2012                        log.debug("Write selection to dataset:", ex);
2013                    }
2014                }
2015
2016                list.setSize(0);
2017            }
2018            else if (cmd.equals("Save dataset")) {
2019                try {
2020                    updateValueInFile();
2021                }
2022                catch (Exception ex) {
2023                    toolkit.beep();
2024                    JOptionPane.showMessageDialog((JFrame) viewer, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
2025                }
2026            }
2027            else if (cmd.equals("Select all data")) {
2028                try {
2029                    selectAll();
2030                }
2031                catch (Exception ex) {
2032                    toolkit.beep();
2033                    JOptionPane.showMessageDialog((JFrame) viewer, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
2034                }
2035            }
2036            else if (cmd.equals("Show chart")) {
2037                showLineplot();
2038            }
2039            else if (cmd.equals("First page")) {
2040                firstPage();
2041            }
2042            else if (cmd.equals("Previous page")) {
2043                previousPage();
2044            }
2045            else if (cmd.equals("Next page")) {
2046                nextPage();
2047            }
2048            else if (cmd.equals("Last page")) {
2049                lastPage();
2050            }
2051            else if (cmd.equals("Show statistics")) {
2052                try {
2053                    Object theData = null;
2054                    theData = getSelectedData();
2055
2056                    if (dataset instanceof CompoundDS) {
2057                        int cols = table.getSelectedColumnCount();
2058                        if (cols != 1) {
2059                            JOptionPane.showMessageDialog(this, "Please select one colunm a time for compound dataset.",
2060                                    getTitle(), JOptionPane.ERROR_MESSAGE);
2061                            return;
2062                        }
2063                    }
2064                    else if (theData == null) {
2065                        theData = dataValue;
2066                    }
2067
2068                    double[] minmax = new double[2];
2069                    double[] stat = new double[2];
2070                    Tools.findMinMax(theData, minmax, fillValue);
2071                    if (Tools.computeStatistics(theData, stat, fillValue) > 0) {
2072                        String statistics = "Min                      = " + minmax[0] + "\nMax                      = " + minmax[1]
2073                                + "\nMean                     = " + stat[0] + "\nStandard deviation = " + stat[1];
2074                        JOptionPane.showMessageDialog(this, statistics, "Statistics", JOptionPane.INFORMATION_MESSAGE);
2075                    }
2076
2077                    theData = null;
2078                    System.gc();
2079                }
2080                catch (Exception ex) {
2081                    toolkit.beep();
2082                    JOptionPane.showMessageDialog((JFrame) viewer, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
2083                }
2084            }
2085            else if (cmd.equals("Math conversion")) {
2086                try {
2087                    mathConversion();
2088                }
2089                catch (Exception ex) {
2090                    toolkit.beep();
2091                    JOptionPane.showMessageDialog((JFrame) viewer, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
2092                }
2093            }
2094            else if (cmd.startsWith("Go to frame")) {
2095                int page = 0;
2096                try {
2097                    page = Integer.parseInt(frameField.getText().trim()) - indexBase;
2098                }
2099                catch (Exception ex) {
2100                    page = -1;
2101                }
2102
2103                gotoPage(page);
2104            }
2105            else if (cmd.equals("Show scientific notation")) {
2106                if (checkScientificNotation.isSelected()) {
2107                    checkCustomNotation.setSelected(false);
2108                    numberFormat = scientificFormat;
2109                    checkHex.setSelected(false);
2110                    checkBin.setSelected(false);
2111                    showAsHex = false;
2112                    showAsBin = false;
2113                }
2114                else
2115                    numberFormat = normalFormat;
2116                this.updateUI();
2117            }
2118            else if (cmd.equals("Create custom notation")) {
2119                String msg = "Create number format by pattern \nINTEGER . FRACTION E EXPONENT\nusing # for optional digits and 0 for required digits"
2120                        + "\nwhere, INTEGER: the pattern for the integer part"
2121                        + "\n       FRACTION: the pattern for the fractional part"
2122                        + "\n       EXPONENT: the pattern for the exponent part"
2123                        + "\n\nFor example, "
2124                        + "\n\t the normalized scientific notation format is \"#.0###E0##\""
2125                        + "\n\t to make the digits required \"0.00000E000\"\n\n";
2126                String str = (String) JOptionPane.showInputDialog(this, msg, "Create a custom number format",
2127                        JOptionPane.PLAIN_MESSAGE, ViewProperties.getLargeHdfIcon(), null, null);
2128                if ((str == null) || (str.length() < 1)) {
2129                    return;
2130                }
2131
2132                customFormat.applyPattern(str);
2133
2134            }
2135            else if (cmd.equals("Show custom notation")) {
2136                if (checkCustomNotation.isSelected()) {
2137                    numberFormat = customFormat;
2138                    checkScientificNotation.setSelected(false);
2139                    checkHex.setSelected(false);
2140                    checkBin.setSelected(false);
2141                    showAsHex = false;
2142                    showAsBin = false;
2143                }
2144                else
2145                    numberFormat = normalFormat;
2146                this.updateUI();
2147            }
2148            else if (cmd.equals("Show hexadecimal")) {
2149                showAsHex = checkHex.isSelected();
2150                if (showAsHex) {
2151                    checkScientificNotation.setSelected(false);
2152                    checkCustomNotation.setSelected(false);
2153                    checkBin.setSelected(false);
2154                    showAsBin = false;
2155                    numberFormat = normalFormat;
2156                }
2157                this.updateUI();
2158            }
2159            else if (cmd.equals("Show binary")) {
2160                showAsBin = checkBin.isSelected();
2161                if (showAsBin) {
2162                    checkScientificNotation.setSelected(false);
2163                    checkCustomNotation.setSelected(false);
2164                    checkHex.setSelected(false);
2165                    showAsHex = false;
2166                    numberFormat = normalFormat;
2167                }
2168                this.updateUI();
2169            }
2170            else if (cmd.equals("Fixed data length")) {
2171                if (!checkFixedDataLength.isSelected()) {
2172                    fixedDataLength = -1;
2173                    this.updateUI();
2174                    return;
2175                }
2176
2177                String str = JOptionPane
2178                        .showInputDialog(
2179                                this,
2180                                "Enter fixed data length when importing text data\n\n"
2181                                        + "For example, for a text string of \"12345678\"\n\t\tenter 2, the data will be 12, 34, 56, 78\n\t\tenter 4, the data will be 1234, 5678\n",
2182                                "");
2183
2184                if ((str == null) || (str.length() < 1)) {
2185                    checkFixedDataLength.setSelected(false);
2186                    return;
2187                }
2188
2189                try {
2190                    fixedDataLength = Integer.parseInt(str);
2191                }
2192                catch (Exception ex) {
2193                    fixedDataLength = -1;
2194                }
2195
2196                if (fixedDataLength < 1) {
2197                    checkFixedDataLength.setSelected(false);
2198                    return;
2199                }
2200            }
2201            else if (cmd.startsWith("Show data as")) {
2202                log.trace("DefaultTableView actionPerformed: {}", cmd);
2203                // show data pointed by reg. ref.
2204                if (cmd.endsWith("table"))
2205                    viewType = ViewType.TABLE;
2206                else if (cmd.endsWith("image"))
2207                    viewType = ViewType.IMAGE;
2208                else
2209                    viewType = ViewType.TABLE;
2210                log.trace("DefaultTableView actionPerformed: Show data as: {}", viewType);
2211
2212                Object theData = getSelectedData();
2213                if (theData == null) {
2214                    toolkit.beep();
2215                    JOptionPane.showMessageDialog(this, "No data selected.", getTitle(), JOptionPane.ERROR_MESSAGE);
2216                    return;
2217
2218                }
2219
2220                int[] selectedRows = table.getSelectedRows();
2221                int[] selectedCols = table.getSelectedColumns();
2222                if (selectedRows == null || selectedRows.length <= 0) {
2223                    log.trace("DefaultTableView actionPerformed: Show data as: selectedRows is empty");
2224                    return;
2225                }
2226                int len = Array.getLength(selectedRows) * Array.getLength(selectedCols);
2227                log.trace("DefaultTableView actionPerformed: Show data as: len={}", len);
2228                for (int i = 0; i < len; i++) {
2229                    if (isRegRef) {
2230                        log.trace("DefaultTableView actionPerformed: Show data[{}] as: isRegRef={}", i, isRegRef);
2231                        showRegRefData((String) Array.get(theData, i));
2232                    }
2233                    else if (isObjRef) {
2234                        log.trace("DefaultTableView actionPerformed: Show data[{}] as: isObjRef={}", i, isObjRef);
2235                        showObjRefData(Array.getLong(theData, i));
2236                    }
2237                }
2238            }
2239        }
2240        finally {
2241            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
2242        }
2243    }
2244
2245    // Implementing DataView.
2246    @Override
2247    public HObject getDataObject ( ) {
2248        return dataset;
2249    }
2250
2251    @Override
2252    public void dispose ( ) {
2253        if (isValueChanged && !isReadOnly) {
2254            int op = JOptionPane.showConfirmDialog(this, "\"" + dataset.getName() + "\" has changed.\n"
2255                    + "Do you want to save the changes?", getTitle(), JOptionPane.YES_NO_OPTION);
2256
2257            if (op == JOptionPane.YES_OPTION) {
2258                updateValueInFile();
2259            }
2260            else
2261                dataset.clearData(); // reload data
2262
2263        }
2264
2265        if (dataset instanceof ScalarDS) {
2266            ScalarDS sds = (ScalarDS) dataset;
2267            // reload the data when it is displayed next time
2268            // because the display type (table or image) may be
2269            // different.
2270
2271            if (sds.isImage()) {
2272                sds.clearData();
2273            }
2274
2275            dataValue = null;
2276            table = null;
2277        }
2278
2279        viewer.removeDataView(this);
2280
2281        super.dispose();
2282    }
2283
2284    // Implementing DataObserver.
2285    private void previousPage ( ) {
2286        int rank = dataset.getRank();
2287
2288        if (rank < 3) {
2289            return;
2290        }
2291
2292        long[] start = dataset.getStartDims();
2293        dataset.getDims();
2294        int[] selectedIndex = dataset.getSelectedIndex();
2295        long idx = start[selectedIndex[2]];
2296        if (idx == 0) {
2297            return; // current page is the first page
2298        }
2299
2300        gotoPage(start[selectedIndex[2]] - 1);
2301    }
2302
2303    // Implementing DataObserver.
2304    private void nextPage ( ) {
2305        int rank = dataset.getRank();
2306
2307        if (rank < 3) {
2308            return;
2309        }
2310
2311        long[] start = dataset.getStartDims();
2312        int[] selectedIndex = dataset.getSelectedIndex();
2313        long[] dims = dataset.getDims();
2314        long idx = start[selectedIndex[2]];
2315        if (idx == dims[selectedIndex[2]] - 1) {
2316            return; // current page is the last page
2317        }
2318
2319        gotoPage(start[selectedIndex[2]] + 1);
2320    }
2321
2322    // Implementing DataObserver.
2323    private void firstPage ( ) {
2324        int rank = dataset.getRank();
2325
2326        if (rank < 3) {
2327            return;
2328        }
2329
2330        long[] start = dataset.getStartDims();
2331        int[] selectedIndex = dataset.getSelectedIndex();
2332        dataset.getDims();
2333        long idx = start[selectedIndex[2]];
2334        if (idx == 0) {
2335            return; // current page is the first page
2336        }
2337
2338        gotoPage(0);
2339    }
2340
2341    // Implementing DataObserver.
2342    private void lastPage ( ) {
2343        int rank = dataset.getRank();
2344
2345        if (rank < 3) {
2346            return;
2347        }
2348
2349        long[] start = dataset.getStartDims();
2350        int[] selectedIndex = dataset.getSelectedIndex();
2351        long[] dims = dataset.getDims();
2352        long idx = start[selectedIndex[2]];
2353        if (idx == dims[selectedIndex[2]] - 1) {
2354            return; // current page is the last page
2355        }
2356
2357        gotoPage(dims[selectedIndex[2]] - 1);
2358    }
2359
2360    // Implementing TableObserver.
2361    @Override
2362    public JTable getTable ( ) {
2363        return table;
2364    }
2365
2366    // Implementing TableObserver.
2367    private void showLineplot ( ) {
2368        int[] rows = table.getSelectedRows();
2369        int[] cols = table.getSelectedColumns();
2370
2371        if ((rows == null) || (cols == null) || (rows.length <= 0) || (cols.length <= 0)) {
2372            toolkit.beep();
2373            JOptionPane.showMessageDialog(this, "Select rows/columns to draw line plot.", getTitle(), JOptionPane.ERROR_MESSAGE);
2374            return;
2375        }
2376
2377        int nrow = table.getRowCount();
2378        int ncol = table.getColumnCount();
2379
2380        log.trace("DefaultTableView showLineplot: {} - {}", nrow, ncol);
2381        LineplotOption lpo = new LineplotOption((JFrame) viewer, "Line Plot Options -- " + dataset.getName(), nrow, ncol);
2382        lpo.setVisible(true);
2383
2384        int plotType = lpo.getPlotBy();
2385        if (plotType == LineplotOption.NO_PLOT) {
2386            return;
2387        }
2388
2389        boolean isRowPlot = (plotType == LineplotOption.ROW_PLOT);
2390        int xIndex = lpo.getXindex();
2391
2392        // figure out to plot data by row or by column
2393        // Plot data by rows if all columns are selected and part of
2394        // rows are selected, otherwise plot data by column
2395        double[][] data = null;
2396        int nLines = 0;
2397        String title = "Lineplot - " + dataset.getPath() + dataset.getName();
2398        String[] lineLabels = null;
2399        double[] yRange = { Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY };
2400        double xData[] = null;
2401
2402        if (isRowPlot) {
2403            title += " - by row";
2404            nLines = rows.length;
2405            if (nLines > 10) {
2406                toolkit.beep();
2407                nLines = 10;
2408                JOptionPane.showMessageDialog(this, "More than 10 rows are selected.\n" + "The first 10 rows will be displayed.",
2409                        getTitle(), JOptionPane.WARNING_MESSAGE);
2410            }
2411            lineLabels = new String[nLines];
2412            data = new double[nLines][cols.length];
2413
2414            double value = 0.0;
2415            for (int i = 0; i < nLines; i++) {
2416                lineLabels[i] = String.valueOf(rows[i] + indexBase);
2417                for (int j = 0; j < cols.length; j++) {
2418                    data[i][j] = 0;
2419                    try {
2420                        value = Double.parseDouble(table.getValueAt(rows[i], cols[j]).toString());
2421                        data[i][j] = value;
2422                        yRange[0] = Math.min(yRange[0], value);
2423                        yRange[1] = Math.max(yRange[1], value);
2424                    }
2425                    catch (NumberFormatException ex) {
2426                        log.debug("rows[{}]:", i, ex);
2427                    }
2428                } // for (int j = 0; j < ncols; j++)
2429            } // for (int i = 0; i < rows.length; i++)
2430
2431            if (xIndex >= 0) {
2432                xData = new double[cols.length];
2433                for (int j = 0; j < cols.length; j++) {
2434                    xData[j] = 0;
2435                    try {
2436                        value = Double.parseDouble(table.getValueAt(xIndex, cols[j]).toString());
2437                        xData[j] = value;
2438                    }
2439                    catch (NumberFormatException ex) {
2440                        log.debug("xIndex of {}:", xIndex, ex);
2441                    }
2442                }
2443            }
2444        } // if (isRowPlot)
2445        else {
2446            title += " - by column";
2447            nLines = cols.length;
2448            if (nLines > 10) {
2449                toolkit.beep();
2450                nLines = 10;
2451                JOptionPane.showMessageDialog(this, "More than 10 columns are selected.\n"
2452                        + "The first 10 columns will be displayed.", getTitle(), JOptionPane.WARNING_MESSAGE);
2453            }
2454            lineLabels = new String[nLines];
2455            data = new double[nLines][rows.length];
2456            double value = 0.0;
2457            for (int j = 0; j < nLines; j++) {
2458                lineLabels[j] = table.getColumnName(cols[j] /* + indexBase */);
2459                for (int i = 0; i < rows.length; i++) {
2460                    data[j][i] = 0;
2461                    try {
2462                        value = Double.parseDouble(table.getValueAt(rows[i], cols[j]).toString());
2463                        data[j][i] = value;
2464                        yRange[0] = Math.min(yRange[0], value);
2465                        yRange[1] = Math.max(yRange[1], value);
2466                    }
2467                    catch (NumberFormatException ex) {
2468                        log.debug("cols[{}]:", j, ex);
2469                    }
2470                } // for (int j=0; j<ncols; j++)
2471            } // for (int i=0; i<rows.length; i++)
2472
2473            if (xIndex >= 0) {
2474                xData = new double[rows.length];
2475                for (int j = 0; j < rows.length; j++) {
2476                    xData[j] = 0;
2477                    try {
2478                        value = Double.parseDouble(table.getValueAt(rows[j], xIndex).toString());
2479                        xData[j] = value;
2480                    }
2481                    catch (NumberFormatException ex) {
2482                        log.debug("xIndex of {}:", xIndex, ex);
2483                    }
2484                }
2485            }
2486        } // else
2487
2488        int n = removeInvalidPlotData(data, xData, yRange);
2489        if (n < data[0].length) {
2490            double[][] dataNew = new double[data.length][n];
2491            for (int i = 0; i < data.length; i++)
2492                System.arraycopy(data[i], 0, dataNew[i], 0, n);
2493
2494            data = dataNew;
2495
2496            if (xData != null) {
2497                double[] xDataNew = new double[n];
2498                System.arraycopy(xData, 0, xDataNew, 0, n);
2499                xData = xDataNew;
2500            }
2501        }
2502
2503        // allow to draw a flat line: all values are the same
2504        if (yRange[0] == yRange[1]) {
2505            yRange[1] += 1;
2506            yRange[0] -= 1;
2507        }
2508        else if (yRange[0] > yRange[1]) {
2509            toolkit.beep();
2510            JOptionPane.showMessageDialog(this,
2511                    "Cannot show line plot for the selected data. \n" + "Please check the data range: ("
2512                            + yRange[0] + ", " + yRange[1] + ").", getTitle(), JOptionPane.ERROR_MESSAGE);
2513            data = null;
2514            return;
2515        }
2516        if (xData == null) { // use array index and length for x data range
2517            xData = new double[2];
2518            xData[0] = indexBase; // 1- or zero-based
2519            xData[1] = data[0].length + indexBase - 1; // maximum index
2520        }
2521
2522        Chart cv = new Chart((JFrame) viewer, title, Chart.LINEPLOT, data, xData, yRange);
2523        cv.setLineLabels(lineLabels);
2524
2525        String cname = dataValue.getClass().getName();
2526        char dname = cname.charAt(cname.lastIndexOf("[") + 1);
2527        if ((dname == 'B') || (dname == 'S') || (dname == 'I') || (dname == 'J')) {
2528            cv.setTypeToInteger();
2529        }
2530
2531        cv.setVisible(true);
2532    }
2533
2534    /**
2535     * Remove values of NaN, INF from the array.
2536     *
2537     * @param data
2538     *            the data array
2539     * @param xData
2540     *            the x-axis data points
2541     * @param yRange
2542     *            the range of data values
2543     * @return number of data points in the plot data if successful; otherwise, returns false.
2544     */
2545    private int removeInvalidPlotData (double[][] data, double[] xData, double[] yRange) {
2546        int idx = 0;
2547        boolean hasInvalid = false;
2548
2549        if (data == null || yRange == null) return -1;
2550
2551        yRange[0] = Double.POSITIVE_INFINITY;
2552        yRange[1] = Double.NEGATIVE_INFINITY;
2553
2554        for (int i = 0; i < data[0].length; i++) {
2555            hasInvalid = false;
2556
2557            for (int j = 0; j < data.length; j++) {
2558                hasInvalid = Tools.isNaNINF(data[j][i]);
2559                if (xData != null) hasInvalid = hasInvalid || Tools.isNaNINF(xData[i]);
2560
2561                if (hasInvalid)
2562                    break;
2563                else {
2564                    data[j][idx] = data[j][i];
2565                    if (xData != null) xData[idx] = xData[i];
2566                    yRange[0] = Math.min(yRange[0], data[j][idx]);
2567                    yRange[1] = Math.max(yRange[1], data[j][idx]);
2568                }
2569            }
2570
2571            if (!hasInvalid) idx++;
2572        }
2573
2574        return idx;
2575    }
2576
2577    /**
2578     * Returns the selected data values.
2579     */
2580    @Override
2581    public Object getSelectedData ( ) {
2582        if (dataset instanceof CompoundDS) {
2583            return getSelectedCompoundData();
2584        }
2585        else {
2586            return getSelectedScalarData();
2587        }
2588    }
2589
2590    /**
2591     * Returns the selected data values.
2592     */
2593    private Object getSelectedScalarData ( ) {
2594        Object selectedData = null;
2595
2596        int[] selectedRows = table.getSelectedRows();
2597        int[] selectedCols = table.getSelectedColumns();
2598        if (selectedRows == null || selectedRows.length <= 0 || selectedCols == null || selectedCols.length <= 0) {
2599            return null;
2600        }
2601
2602        int size = selectedCols.length * selectedRows.length;
2603        log.trace("DefaultTableView getSelectedScalarData: {}", size);
2604
2605        // the whole table is selected
2606        if ((table.getColumnCount() == selectedCols.length) && (table.getRowCount() == selectedRows.length)) {
2607            return dataValue;
2608        }
2609
2610        selectedData = null;
2611        if (isRegRef) {
2612            // reg. ref data are stored in strings
2613            selectedData = new String[size];
2614        }
2615        else {
2616            switch (NT) {
2617                case 'B':
2618                    selectedData = new byte[size];
2619                    break;
2620                case 'S':
2621                    selectedData = new short[size];
2622                    break;
2623                case 'I':
2624                    selectedData = new int[size];
2625                    break;
2626                case 'J':
2627                    selectedData = new long[size];
2628                    break;
2629                case 'F':
2630                    selectedData = new float[size];
2631                    break;
2632                case 'D':
2633                    selectedData = new double[size];
2634                    break;
2635                default:
2636                    selectedData = null;
2637                    break;
2638            }
2639        }
2640
2641        if (selectedData == null) {
2642            toolkit.beep();
2643            JOptionPane.showMessageDialog(this, "Unsupported data type.", getTitle(), JOptionPane.ERROR_MESSAGE);
2644            return null;
2645        }
2646        log.trace("DefaultTableView getSelectedScalarData: selectedData is type {}", NT);
2647
2648        table.getSelectedRow();
2649        table.getSelectedColumn();
2650        int w = table.getColumnCount();
2651        log.trace("DefaultTableView getSelectedScalarData: getColumnCount={}", w);
2652        int idx_src = 0;
2653        int idx_dst = 0;
2654        log.trace("DefaultTableView getSelectedScalarData: Rows.length={} Cols.length={}", selectedRows.length, selectedCols.length);
2655        for (int i = 0; i < selectedRows.length; i++) {
2656            for (int j = 0; j < selectedCols.length; j++) {
2657                idx_src = selectedRows[i] * w + selectedCols[j];
2658                log.trace("DefaultTableView getSelectedScalarData[{},{}]: dataValue[{}]={} from r{} and c{}", i, j, idx_src,
2659                        Array.get(dataValue, idx_src), selectedRows[i], selectedCols[j]);
2660                Array.set(selectedData, idx_dst, Array.get(dataValue, idx_src));
2661                log.trace("DefaultTableView getSelectedScalarData[{},{}]: selectedData[{}]={}", i, j, idx_dst,
2662                        Array.get(selectedData, idx_dst));
2663                idx_dst++;
2664            }
2665        }
2666
2667        // this only works for continuous cells
2668        // for (int i = 0; i < rows; i++) {
2669        // idx_src = (r0 + i) * w + c0;
2670        // System.arraycopy(dataValue, idx_src, selectedData, idx_dst, cols);
2671        // idx_dst += cols;
2672        // }
2673
2674        return selectedData;
2675    }
2676
2677    /**
2678     * Returns the selected data values.
2679     */
2680    private Object getSelectedCompoundData ( ) {
2681        Object selectedData = null;
2682
2683        int cols = table.getSelectedColumnCount();
2684        int rows = table.getSelectedRowCount();
2685
2686        if ((cols <= 0) || (rows <= 0)) {
2687            toolkit.beep();
2688            JOptionPane.showMessageDialog(this, "No data is selected.", getTitle(), JOptionPane.ERROR_MESSAGE);
2689            return null;
2690        }
2691
2692        Object colData = null;
2693        try {
2694            colData = ((List<?>) dataset.getData()).get(table.getSelectedColumn());
2695        }
2696        catch (Exception ex) {
2697            log.debug("colData:", ex);
2698            return null;
2699        }
2700
2701        int size = Array.getLength(colData);
2702        String cName = colData.getClass().getName();
2703        int cIndex = cName.lastIndexOf("[");
2704        char nt = ' ';
2705        if (cIndex >= 0) {
2706            nt = cName.charAt(cIndex + 1);
2707        }
2708        log.trace("DefaultTableView getSelectedCompoundData: size={} cName={} nt={}", size, cName, nt);
2709
2710        if (nt == 'B') {
2711            selectedData = new byte[size];
2712        }
2713        else if (nt == 'S') {
2714            selectedData = new short[size];
2715        }
2716        else if (nt == 'I') {
2717            selectedData = new int[size];
2718        }
2719        else if (nt == 'J') {
2720            selectedData = new long[size];
2721        }
2722        else if (nt == 'F') {
2723            selectedData = new float[size];
2724        }
2725        else if (nt == 'D') {
2726            selectedData = new double[size];
2727        }
2728        else {
2729            toolkit.beep();
2730            JOptionPane.showMessageDialog(this, "Unsupported data type.", getTitle(), JOptionPane.ERROR_MESSAGE);
2731            return null;
2732        }
2733        log.trace("DefaultTableView getSelectedCompoundData: selectedData={}", selectedData);
2734
2735        System.arraycopy(colData, 0, selectedData, 0, size);
2736
2737        return selectedData;
2738    }
2739
2740    private void gotoPage (long idx) {
2741        if (dataset.getRank() < 3 || idx == (curFrame - indexBase)) {
2742            return;
2743        }
2744
2745        if (isValueChanged) {
2746            updateValueInFile();
2747        }
2748
2749        long[] start = dataset.getStartDims();
2750        int[] selectedIndex = dataset.getSelectedIndex();
2751        long[] dims = dataset.getDims();
2752
2753        if ((idx < 0) || (idx >= dims[selectedIndex[2]])) {
2754            toolkit.beep();
2755            JOptionPane.showMessageDialog(this, "Frame number must be between" + indexBase + " and "
2756                    + (dims[selectedIndex[2]] - 1 + indexBase), getTitle(), JOptionPane.ERROR_MESSAGE);
2757            return;
2758        }
2759
2760        start[selectedIndex[2]] = idx;
2761        curFrame = idx + indexBase;
2762        dataset.clearData();
2763
2764        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
2765
2766        try {
2767            dataValue = dataset.getData();
2768            if (dataset instanceof ScalarDS) {
2769                ((ScalarDS) dataset).convertFromUnsignedC();
2770                dataValue = dataset.getData();
2771            }
2772        }
2773        catch (Exception ex) {
2774            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
2775            dataValue = null;
2776            JOptionPane.showMessageDialog(this, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
2777            return;
2778        }
2779
2780        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
2781
2782        frameField.setText(String.valueOf(curFrame));
2783        updateUI();
2784    }
2785
2786    /** copy data from the spreadsheet to the system clipboard. */
2787    private void copyData ( ) {
2788        StringBuffer sb = new StringBuffer();
2789
2790        int r0 = table.getSelectedRow(); // starting row
2791        int c0 = table.getSelectedColumn(); // starting column
2792
2793        if ((r0 < 0) || (c0 < 0)) {
2794            return;
2795        }
2796
2797        int nr = table.getSelectedRowCount();
2798        int nc = table.getSelectedColumnCount();
2799        int r1 = r0 + nr; // finish row
2800        int c1 = c0 + nc; // finishing column
2801
2802        try {
2803            for (int i = r0; i < r1; i++) {
2804                sb.append(table.getValueAt(i, c0).toString());
2805                for (int j = c0 + 1; j < c1; j++) {
2806                    sb.append("\t");
2807                    sb.append(table.getValueAt(i, j).toString());
2808                }
2809                sb.append("\n");
2810            }
2811        }
2812        catch (java.lang.OutOfMemoryError err) {
2813            toolkit.beep();
2814            JOptionPane.showMessageDialog((JFrame) viewer,
2815                    "Copying data to system clipboard failed. \nUsing \"export/import data\" for copying/pasting large data.",
2816                    getTitle(), JOptionPane.ERROR_MESSAGE);
2817            return;
2818        }
2819
2820        Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
2821        StringSelection contents = new StringSelection(sb.toString());
2822        cb.setContents(contents, null);
2823    }
2824
2825    /** paste data from the system clipboard to the spreadsheet. */
2826    private void pasteData ( ) {
2827        int pasteDataFlag = JOptionPane.showConfirmDialog(this, "Do you want to paste selected data?", this.getTitle(),
2828                JOptionPane.YES_NO_OPTION);
2829        if (pasteDataFlag == JOptionPane.NO_OPTION) {
2830            return;
2831        }
2832
2833        int cols = table.getColumnCount();
2834        int rows = table.getRowCount();
2835        int r0 = table.getSelectedRow();
2836        int c0 = table.getSelectedColumn();
2837
2838        if (c0 < 0) {
2839            c0 = 0;
2840        }
2841        if (r0 < 0) {
2842            r0 = 0;
2843        }
2844        int r = r0;
2845        int c = c0;
2846
2847        Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
2848        // Transferable content = cb.getContents(this);
2849        String line = "";
2850        try {
2851            String s = (String) cb.getData(DataFlavor.stringFlavor);
2852
2853            StringTokenizer st = new StringTokenizer(s, "\n");
2854            // read line by line
2855            while (st.hasMoreTokens() && (r < rows)) {
2856                line = st.nextToken();
2857
2858                if (fixedDataLength < 1) {
2859                    // separate by delimiter
2860                    StringTokenizer lt = new StringTokenizer(line, "\t");
2861                    while (lt.hasMoreTokens() && (c < cols)) {
2862                        try {
2863                            updateValueInMemory(lt.nextToken(), r, c);
2864                        }
2865                        catch (Exception ex) {
2866                            continue;
2867                        }
2868                        c++;
2869                    }
2870                    r = r + 1;
2871                    c = c0;
2872                }
2873                else {
2874                    // the data has fixed length
2875                    int n = line.length();
2876                    String theVal;
2877                    for (int i = 0; i < n; i = i + fixedDataLength) {
2878                        try {
2879                            theVal = line.substring(i, i + fixedDataLength);
2880                            updateValueInMemory(theVal, r, c);
2881                        }
2882                        catch (Exception ex) {
2883                            continue;
2884                        }
2885                        c++;
2886                    }
2887                }
2888            }
2889        }
2890        catch (Throwable ex) {
2891            toolkit.beep();
2892            JOptionPane.showMessageDialog(this, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
2893        }
2894
2895        table.updateUI();
2896    }
2897
2898    /**
2899     * import data values from text file.
2900     */
2901    private void importTextData (String fname) {
2902        int pasteDataFlag = JOptionPane.showConfirmDialog(this, "Do you want to paste selected data?", this.getTitle(),
2903                JOptionPane.YES_NO_OPTION);
2904        if (pasteDataFlag == JOptionPane.NO_OPTION) {
2905            return;
2906        }
2907        int cols = table.getColumnCount();
2908        int rows = table.getRowCount();
2909        int r0 = table.getSelectedRow();
2910        int c0 = table.getSelectedColumn();
2911
2912        if (c0 < 0) {
2913            c0 = 0;
2914        }
2915        if (r0 < 0) {
2916            r0 = 0;
2917        }
2918
2919        // start at the first column for compound datasets
2920        if (dataset instanceof CompoundDS) c0 = 0;
2921
2922        BufferedReader in = null;
2923        try {
2924            in = new BufferedReader(new FileReader(fname));
2925        }
2926        catch (FileNotFoundException ex) {
2927            log.debug("import data values from text file {}:", fname, ex);
2928            return;
2929        }
2930
2931        String line = null;
2932        StringTokenizer tokenizer1 = null;
2933
2934        try {
2935            line = in.readLine();
2936        }
2937        catch (IOException ex) {
2938            try {
2939                in.close();
2940            }
2941            catch (IOException ex2) {
2942                log.debug("close text file {}:", fname, ex2);
2943            }
2944            log.debug("read text file {}:", fname, ex);
2945            return;
2946        }
2947
2948        String delName = ViewProperties.getDataDelimiter();
2949        String delimiter = "";
2950
2951        // delimiter must include a tab to be consistent with copy/paste for
2952        // compound fields
2953        if (dataset instanceof CompoundDS)
2954            delimiter = "\t";
2955        else {
2956            if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_TAB)) {
2957                delimiter = "\t";
2958            }
2959            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SPACE)) {
2960                delimiter = " " + delimiter;
2961            }
2962            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COMMA)) {
2963                delimiter = ",";
2964            }
2965            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COLON)) {
2966                delimiter = ":";
2967            }
2968            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SEMI_COLON)) {
2969                delimiter = ";";
2970            }
2971        }
2972        String token = null;
2973        int r = r0;
2974        int c = c0;
2975        while ((line != null) && (r < rows)) {
2976            if (fixedDataLength > 0) {
2977                // the data has fixed length
2978                int n = line.length();
2979                String theVal;
2980                for (int i = 0; i < n; i = i + fixedDataLength) {
2981                    try {
2982                        theVal = line.substring(i, i + fixedDataLength);
2983                        updateValueInMemory(theVal, r, c);
2984                    }
2985                    catch (Exception ex) {
2986                        continue;
2987                    }
2988                    c++;
2989                }
2990            }
2991            else {
2992                try {
2993                    tokenizer1 = new StringTokenizer(line, delimiter);
2994                    while (tokenizer1.hasMoreTokens() && (c < cols)) {
2995                        token = tokenizer1.nextToken();
2996                        if (dataset instanceof ScalarDS) {
2997                            StringTokenizer tokenizer2 = new StringTokenizer(token);
2998                            while (tokenizer2.hasMoreTokens() && (c < cols)) {
2999                                updateValueInMemory(tokenizer2.nextToken(), r, c);
3000                                c++;
3001                            }
3002                        }
3003                        else {
3004                            updateValueInMemory(token, r, c);
3005                            c++;
3006                        }
3007                    } // while (tokenizer1.hasMoreTokens() && index < size)
3008                }
3009                catch (Exception ex) {
3010                    JOptionPane.showMessageDialog(this, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
3011                    try {
3012                        in.close();
3013                    }
3014                    catch (IOException ex2) {
3015                        log.debug("close text file {}:", fname, ex2);
3016                    }
3017                    return;
3018                }
3019            }
3020
3021            try {
3022                line = in.readLine();
3023            }
3024            catch (IOException ex) {
3025                log.debug("read text file {}:", fname, ex);
3026                line = null;
3027            }
3028            c = 0;
3029            r++;
3030        } // while ((line != null) && (r < rows))
3031
3032        try {
3033            in.close();
3034        }
3035        catch (IOException ex) {
3036            log.debug("close text file {}:", fname, ex);
3037        }
3038
3039        table.updateUI();
3040    }
3041
3042    /**
3043     * import data values from binary file.
3044     */
3045    private void importBinaryData ( ) {
3046        String currentDir = dataset.getFileFormat().getParent();
3047        JFileChooser fchooser = new JFileChooser(currentDir);
3048        fchooser.setFileFilter(DefaultFileFilter.getFileFilterBinary());
3049        int returnVal = fchooser.showOpenDialog(this);
3050
3051        if (returnVal != JFileChooser.APPROVE_OPTION) {
3052            return;
3053        }
3054        File choosedFile = fchooser.getSelectedFile();
3055        if (choosedFile == null) {
3056            return;
3057        }
3058        String fname = choosedFile.getAbsolutePath();
3059
3060        int pasteDataFlag = JOptionPane.showConfirmDialog(this, "Do you want to paste selected data?", this.getTitle(),
3061                JOptionPane.YES_NO_OPTION);
3062        if (pasteDataFlag == JOptionPane.NO_OPTION) {
3063            return;
3064        }
3065
3066        getBinaryDatafromFile(fname);
3067    }
3068
3069    /** Reads data from a binary file into a buffer and updates table. */
3070    private void getBinaryDatafromFile (String fileName) {
3071        String fname = fileName;
3072        FileInputStream inputFile = null;
3073        BufferedInputStream in = null;
3074        ByteBuffer byteBuffer = null;
3075        try {
3076            inputFile = new FileInputStream(fname);
3077            long fileSize = inputFile.getChannel().size();
3078            in = new BufferedInputStream(inputFile);
3079
3080            Object data = dataset.getData();
3081            int datasetSize = Array.getLength(data);
3082            String cname = data.getClass().getName();
3083            char dname = cname.charAt(cname.lastIndexOf("[") + 1);
3084
3085            if (dname == 'B') {
3086                long datasetByteSize = datasetSize;
3087                byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_SIZE);
3088                if (binaryOrder == 1)
3089                    byteBuffer.order(ByteOrder.nativeOrder());
3090                else if (binaryOrder == 2)
3091                    byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
3092                else if (binaryOrder == 3) byteBuffer.order(ByteOrder.BIG_ENDIAN);
3093
3094                int bufferSize = (int) Math.min(fileSize, datasetByteSize);
3095
3096                int remainingSize = bufferSize - (BYTE_BUFFER_SIZE);
3097                int allocValue = 0;
3098                int iterationNumber = 0;
3099                byte[] byteArray = new byte[BYTE_BUFFER_SIZE];
3100                do {
3101                    if (remainingSize <= 0) {
3102                        allocValue = remainingSize + (BYTE_BUFFER_SIZE);
3103                    }
3104                    else {
3105                        allocValue = (BYTE_BUFFER_SIZE);
3106                    }
3107
3108                    in.read(byteBuffer.array(), 0, allocValue);
3109
3110                    byteBuffer.get(byteArray, 0, allocValue);
3111                    System.arraycopy(byteArray, 0, dataValue, (iterationNumber * BYTE_BUFFER_SIZE), allocValue);
3112                    byteBuffer.clear();
3113                    remainingSize = remainingSize - (BYTE_BUFFER_SIZE);
3114                    iterationNumber++;
3115                } while (remainingSize > -(BYTE_BUFFER_SIZE));
3116
3117                isValueChanged = true;
3118            }
3119            else if (dname == 'S') {
3120                long datasetShortSize = datasetSize * 2;
3121                byteBuffer = ByteBuffer.allocate(SHORT_BUFFER_SIZE * 2);
3122                if (binaryOrder == 1)
3123                    byteBuffer.order(ByteOrder.nativeOrder());
3124                else if (binaryOrder == 2)
3125                    byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
3126                else if (binaryOrder == 3) byteBuffer.order(ByteOrder.BIG_ENDIAN);
3127
3128                int bufferSize = (int) Math.min(fileSize, datasetShortSize);
3129                int remainingSize = bufferSize - (SHORT_BUFFER_SIZE * 2);
3130                int allocValue = 0;
3131                int iterationNumber = 0;
3132                ShortBuffer sb = byteBuffer.asShortBuffer();
3133                short[] shortArray = new short[SHORT_BUFFER_SIZE];
3134
3135                do {
3136                    if (remainingSize <= 0) {
3137                        allocValue = remainingSize + (SHORT_BUFFER_SIZE * 2);
3138                    }
3139                    else {
3140                        allocValue = (SHORT_BUFFER_SIZE * 2);
3141                    }
3142                    in.read(byteBuffer.array(), 0, allocValue);
3143                    sb.get(shortArray, 0, allocValue / 2);
3144                    System.arraycopy(shortArray, 0, dataValue, (iterationNumber * SHORT_BUFFER_SIZE), allocValue / 2);
3145                    byteBuffer.clear();
3146                    sb.clear();
3147                    remainingSize = remainingSize - (SHORT_BUFFER_SIZE * 2);
3148                    iterationNumber++;
3149                } while (remainingSize > -(SHORT_BUFFER_SIZE * 2));
3150
3151                isValueChanged = true;
3152            }
3153            else if (dname == 'I') {
3154                long datasetIntSize = datasetSize * 4;
3155                byteBuffer = ByteBuffer.allocate(INT_BUFFER_SIZE * 4);
3156                if (binaryOrder == 1)
3157                    byteBuffer.order(ByteOrder.nativeOrder());
3158                else if (binaryOrder == 2)
3159                    byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
3160                else if (binaryOrder == 3) byteBuffer.order(ByteOrder.BIG_ENDIAN);
3161
3162                int bufferSize = (int) Math.min(fileSize, datasetIntSize);
3163                int remainingSize = bufferSize - (INT_BUFFER_SIZE * 4);
3164                int allocValue = 0;
3165                int iterationNumber = 0;
3166                int[] intArray = new int[INT_BUFFER_SIZE];
3167                byte[] tmpBuf = byteBuffer.array();
3168                IntBuffer ib = byteBuffer.asIntBuffer();
3169
3170                do {
3171                    if (remainingSize <= 0) {
3172                        allocValue = remainingSize + (INT_BUFFER_SIZE * 4);
3173                    }
3174                    else {
3175                        allocValue = (INT_BUFFER_SIZE * 4);
3176                    }
3177                    in.read(tmpBuf, 0, allocValue);
3178                    ib.get(intArray, 0, allocValue / 4);
3179                    System.arraycopy(intArray, 0, dataValue, (iterationNumber * INT_BUFFER_SIZE), allocValue / 4);
3180                    byteBuffer.clear();
3181                    ib.clear();
3182                    remainingSize = remainingSize - (INT_BUFFER_SIZE * 4);
3183                    iterationNumber++;
3184                } while (remainingSize > -(INT_BUFFER_SIZE * 4));
3185
3186                isValueChanged = true;
3187            }
3188            else if (dname == 'J') {
3189                long datasetLongSize = datasetSize * 8;
3190                byteBuffer = ByteBuffer.allocate(LONG_BUFFER_SIZE * 8);
3191
3192                if (binaryOrder == 1)
3193                    byteBuffer.order(ByteOrder.nativeOrder());
3194                else if (binaryOrder == 2)
3195                    byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
3196                else if (binaryOrder == 3) byteBuffer.order(ByteOrder.BIG_ENDIAN);
3197
3198                int bufferSize = (int) Math.min(fileSize, datasetLongSize);
3199                int remainingSize = bufferSize - (LONG_BUFFER_SIZE * 8);
3200                int allocValue = 0;
3201                int iterationNumber = 0;
3202                long[] longArray = new long[LONG_BUFFER_SIZE];
3203                LongBuffer lb = byteBuffer.asLongBuffer();
3204
3205                do {
3206                    if (remainingSize <= 0) {
3207                        allocValue = remainingSize + (LONG_BUFFER_SIZE * 8);
3208                    }
3209                    else {
3210                        allocValue = (LONG_BUFFER_SIZE * 8);
3211                    }
3212
3213                    in.read(byteBuffer.array(), 0, allocValue);
3214                    lb.get(longArray, 0, allocValue / 8);
3215                    System.arraycopy(longArray, 0, dataValue, (iterationNumber * LONG_BUFFER_SIZE), allocValue / 8);
3216                    byteBuffer.clear();
3217                    lb.clear();
3218                    remainingSize = remainingSize - (LONG_BUFFER_SIZE * 8);
3219                    iterationNumber++;
3220                } while (remainingSize > -(LONG_BUFFER_SIZE * 8));
3221
3222                isValueChanged = true;
3223            }
3224            else if (dname == 'F') {
3225                long datasetFloatSize = datasetSize * 4;
3226                byteBuffer = ByteBuffer.allocate(FLOAT_BUFFER_SIZE * 4);
3227                if (binaryOrder == 1)
3228                    byteBuffer.order(ByteOrder.nativeOrder());
3229                else if (binaryOrder == 2)
3230                    byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
3231                else if (binaryOrder == 3) byteBuffer.order(ByteOrder.BIG_ENDIAN);
3232
3233                int bufferSize = (int) Math.min(fileSize, datasetFloatSize);
3234                int remainingSize = bufferSize - (FLOAT_BUFFER_SIZE * 4);
3235                int allocValue = 0;
3236                int iterationNumber = 0;
3237                FloatBuffer fb = byteBuffer.asFloatBuffer();
3238                float[] floatArray = new float[FLOAT_BUFFER_SIZE];
3239                do {
3240                    if (remainingSize <= 0) {
3241                        allocValue = remainingSize + (FLOAT_BUFFER_SIZE * 4);
3242                    }
3243                    else {
3244                        allocValue = (FLOAT_BUFFER_SIZE * 4);
3245                    }
3246
3247                    in.read(byteBuffer.array(), 0, allocValue);
3248                    fb.get(floatArray, 0, allocValue / 4);
3249                    System.arraycopy(floatArray, 0, dataValue, (iterationNumber * FLOAT_BUFFER_SIZE), allocValue / 4);
3250                    byteBuffer.clear();
3251                    fb.clear();
3252                    remainingSize = remainingSize - (FLOAT_BUFFER_SIZE * 4);
3253                    iterationNumber++;
3254                } while (remainingSize > -(FLOAT_BUFFER_SIZE * 4));
3255
3256                isValueChanged = true;
3257            }
3258            else if (dname == 'D') {
3259                long datasetDoubleSize = datasetSize * 8;
3260                byteBuffer = ByteBuffer.allocate(DOUBLE_BUFFER_SIZE * 8);
3261                if (binaryOrder == 1)
3262                    byteBuffer.order(ByteOrder.nativeOrder());
3263                else if (binaryOrder == 2)
3264                    byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
3265                else if (binaryOrder == 3) byteBuffer.order(ByteOrder.BIG_ENDIAN);
3266
3267                int bufferSize = (int) Math.min(fileSize, datasetDoubleSize);
3268                int remainingSize = bufferSize - (DOUBLE_BUFFER_SIZE * 8);
3269                int allocValue = 0;
3270                int iterationNumber = 0;
3271                DoubleBuffer db = byteBuffer.asDoubleBuffer();
3272                double[] doubleArray = new double[DOUBLE_BUFFER_SIZE];
3273
3274                do {
3275                    if (remainingSize <= 0) {
3276                        allocValue = remainingSize + (DOUBLE_BUFFER_SIZE * 8);
3277                    }
3278                    else {
3279                        allocValue = (DOUBLE_BUFFER_SIZE * 8);
3280                    }
3281
3282                    in.read(byteBuffer.array(), 0, allocValue);
3283                    db.get(doubleArray, 0, allocValue / 8);
3284                    System.arraycopy(doubleArray, 0, dataValue, (iterationNumber * DOUBLE_BUFFER_SIZE), allocValue / 8);
3285                    byteBuffer.clear();
3286                    db.clear();
3287                    remainingSize = remainingSize - (DOUBLE_BUFFER_SIZE * 8);
3288                    iterationNumber++;
3289                } while (remainingSize > -(DOUBLE_BUFFER_SIZE * 8));
3290
3291                isValueChanged = true;
3292
3293            }
3294
3295        }
3296        catch (Exception es) {
3297            es.printStackTrace();
3298        }
3299        finally {
3300            try {
3301                in.close();
3302                inputFile.close();
3303            }
3304            catch (IOException ex) {
3305                log.debug("close binary file {}:", fname, ex);
3306            }
3307        }
3308        table.updateUI();
3309    }
3310
3311    /** Save data as text. */
3312    private void saveAsText ( ) throws Exception {
3313        final JFileChooser fchooser = new JFileChooser(dataset.getFile());
3314        fchooser.setFileFilter(DefaultFileFilter.getFileFilterText());
3315        // fchooser.changeToParentDirectory();
3316        fchooser.setDialogTitle("Save Current Data To Text File --- " + dataset.getName());
3317
3318        File choosedFile = new File(dataset.getName() + ".txt");
3319
3320        fchooser.setSelectedFile(choosedFile);
3321        int returnVal = fchooser.showSaveDialog(this);
3322
3323        if (returnVal != JFileChooser.APPROVE_OPTION) {
3324            return;
3325        }
3326
3327        choosedFile = fchooser.getSelectedFile();
3328        if (choosedFile == null) {
3329            return;
3330        }
3331        String fname = choosedFile.getAbsolutePath();
3332        log.trace("DefaultTableView saveAsText: file={}", fname);
3333
3334        // check if the file is in use
3335        List<?> fileList = viewer.getTreeView().getCurrentFiles();
3336        if (fileList != null) {
3337            FileFormat theFile = null;
3338            Iterator<?> iterator = fileList.iterator();
3339            while (iterator.hasNext()) {
3340                theFile = (FileFormat) iterator.next();
3341                if (theFile.getFilePath().equals(fname)) {
3342                    toolkit.beep();
3343                    JOptionPane.showMessageDialog(this, "Unable to save data to file \"" + fname + "\". \nThe file is being used.",
3344                            getTitle(), JOptionPane.ERROR_MESSAGE);
3345                    return;
3346                }
3347            }
3348        }
3349
3350        if (choosedFile.exists()) {
3351            int newFileFlag = JOptionPane.showConfirmDialog(this, "File exists. Do you want to replace it?", this.getTitle(),
3352                    JOptionPane.YES_NO_OPTION);
3353            if (newFileFlag == JOptionPane.NO_OPTION) {
3354                return;
3355            }
3356        }
3357
3358        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(choosedFile)));
3359
3360        String delName = ViewProperties.getDataDelimiter();
3361        String delimiter = "";
3362
3363        // delimiter must include a tab to be consistent with copy/paste for
3364        // compound fields
3365        if (dataset instanceof CompoundDS) delimiter = "\t";
3366
3367        if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_TAB)) {
3368            delimiter = "\t";
3369        }
3370        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SPACE)) {
3371            delimiter = " " + delimiter;
3372        }
3373        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COMMA)) {
3374            delimiter = "," + delimiter;
3375        }
3376        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COLON)) {
3377            delimiter = ":" + delimiter;
3378        }
3379        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SEMI_COLON)) {
3380            delimiter = ";" + delimiter;
3381        }
3382
3383        int cols = table.getColumnCount();
3384        int rows = table.getRowCount();
3385
3386        for (int i = 0; i < rows; i++) {
3387            out.print(table.getValueAt(i, 0));
3388            for (int j = 1; j < cols; j++) {
3389                out.print(delimiter);
3390                out.print(table.getValueAt(i, j));
3391            }
3392            out.println();
3393        }
3394
3395        out.flush();
3396        out.close();
3397
3398        viewer.showStatus("Data save to: " + fname);
3399    }
3400
3401    /** Save data as binary. */
3402    private void saveAsBinary ( ) throws Exception {
3403        final JFileChooser fchooser = new JFileChooser(dataset.getFile());
3404        fchooser.setFileFilter(DefaultFileFilter.getFileFilterBinary());
3405        // fchooser.changeToParentDirectory();
3406        fchooser.setDialogTitle("Save Current Data To Binary File --- " + dataset.getName());
3407
3408        File choosedFile = new File(dataset.getName() + ".bin");
3409        fchooser.setSelectedFile(choosedFile);
3410        int returnVal = fchooser.showSaveDialog(this);
3411
3412        if (returnVal != JFileChooser.APPROVE_OPTION) {
3413            return;
3414        }
3415
3416        choosedFile = fchooser.getSelectedFile();
3417        if (choosedFile == null) {
3418            return;
3419        }
3420        String fname = choosedFile.getAbsolutePath();
3421        log.trace("DefaultTableView saveAsBinary: file={}", fname);
3422
3423        // check if the file is in use
3424        List<?> fileList = viewer.getTreeView().getCurrentFiles();
3425        if (fileList != null) {
3426            FileFormat theFile = null;
3427            Iterator<?> iterator = fileList.iterator();
3428            while (iterator.hasNext()) {
3429                theFile = (FileFormat) iterator.next();
3430                if (theFile.getFilePath().equals(fname)) {
3431                    toolkit.beep();
3432                    JOptionPane.showMessageDialog(this, "Unable to save data to file \"" + fname + "\". \nThe file is being used.",
3433                            getTitle(), JOptionPane.ERROR_MESSAGE);
3434                    return;
3435                }
3436            }
3437        }
3438
3439        // check if the file exists
3440        if (choosedFile.exists()) {
3441            int newFileFlag = JOptionPane.showConfirmDialog(this, "File exists. Do you want to replace it?", this.getTitle(),
3442                    JOptionPane.YES_NO_OPTION);
3443            if (newFileFlag == JOptionPane.NO_OPTION) {
3444                return;
3445            }
3446        }
3447
3448        FileOutputStream outputFile = new FileOutputStream(choosedFile);
3449        DataOutputStream out = new DataOutputStream(outputFile);
3450
3451        if (dataset instanceof ScalarDS) {
3452            ((ScalarDS) dataset).convertToUnsignedC();
3453            Object data = dataset.getData();
3454            String cname = data.getClass().getName();
3455            char dname = cname.charAt(cname.lastIndexOf("[") + 1);
3456            ByteBuffer bb = null;
3457
3458            int size = Array.getLength(data);
3459
3460            if (dname == 'B') {
3461                byte[] bdata = new byte[size];
3462                bdata = (byte[]) data;
3463
3464                bb = ByteBuffer.allocate(BYTE_BUFFER_SIZE);
3465                if (binaryOrder == 1)
3466                    bb.order(ByteOrder.nativeOrder());
3467                else if (binaryOrder == 2)
3468                    bb.order(ByteOrder.LITTLE_ENDIAN);
3469                else if (binaryOrder == 3) bb.order(ByteOrder.BIG_ENDIAN);
3470
3471                int remainingSize = size - BYTE_BUFFER_SIZE;
3472                int allocValue = 0;
3473                int iterationNumber = 0;
3474                do {
3475                    if (remainingSize <= 0) {
3476                        allocValue = remainingSize + BYTE_BUFFER_SIZE;
3477                    }
3478                    else {
3479                        allocValue = BYTE_BUFFER_SIZE;
3480                    }
3481                    bb.clear();
3482                    bb.put(bdata, (iterationNumber * BYTE_BUFFER_SIZE), allocValue);
3483                    out.write(bb.array(), 0, allocValue);
3484                    remainingSize = remainingSize - BYTE_BUFFER_SIZE;
3485                    iterationNumber++;
3486                } while (remainingSize > -BYTE_BUFFER_SIZE);
3487
3488                out.flush();
3489                out.close();
3490            }
3491            else if (dname == 'S') {
3492                short[] sdata = new short[size];
3493                sdata = (short[]) data;
3494                bb = ByteBuffer.allocate(SHORT_BUFFER_SIZE * 2);
3495                if (binaryOrder == 1)
3496                    bb.order(ByteOrder.nativeOrder());
3497                else if (binaryOrder == 2)
3498                    bb.order(ByteOrder.LITTLE_ENDIAN);
3499                else if (binaryOrder == 3) bb.order(ByteOrder.BIG_ENDIAN);
3500
3501                ShortBuffer sb = bb.asShortBuffer();
3502                int remainingSize = size - SHORT_BUFFER_SIZE;
3503                int allocValue = 0;
3504                int iterationNumber = 0;
3505                do {
3506                    if (remainingSize <= 0) {
3507                        allocValue = remainingSize + SHORT_BUFFER_SIZE;
3508                    }
3509                    else {
3510                        allocValue = SHORT_BUFFER_SIZE;
3511                    }
3512                    bb.clear();
3513                    sb.clear();
3514                    sb.put(sdata, (iterationNumber * SHORT_BUFFER_SIZE), allocValue);
3515                    out.write(bb.array(), 0, allocValue * 2);
3516                    remainingSize = remainingSize - SHORT_BUFFER_SIZE;
3517                    iterationNumber++;
3518                } while (remainingSize > -SHORT_BUFFER_SIZE);
3519
3520                out.flush();
3521                out.close();
3522            }
3523            else if (dname == 'I') {
3524                int[] idata = new int[size];
3525                idata = (int[]) data;
3526                bb = ByteBuffer.allocate(INT_BUFFER_SIZE * 4);
3527                if (binaryOrder == 1)
3528                    bb.order(ByteOrder.nativeOrder());
3529                else if (binaryOrder == 2)
3530                    bb.order(ByteOrder.LITTLE_ENDIAN);
3531                else if (binaryOrder == 3) bb.order(ByteOrder.BIG_ENDIAN);
3532
3533                IntBuffer ib = bb.asIntBuffer();
3534                int remainingSize = size - INT_BUFFER_SIZE;
3535                int allocValue = 0;
3536                int iterationNumber = 0;
3537                do {
3538                    if (remainingSize <= 0) {
3539                        allocValue = remainingSize + INT_BUFFER_SIZE;
3540                    }
3541                    else {
3542                        allocValue = INT_BUFFER_SIZE;
3543                    }
3544                    bb.clear();
3545                    ib.clear();
3546                    ib.put(idata, (iterationNumber * INT_BUFFER_SIZE), allocValue);
3547                    out.write(bb.array(), 0, allocValue * 4);
3548                    remainingSize = remainingSize - INT_BUFFER_SIZE;
3549                    iterationNumber++;
3550                } while (remainingSize > -INT_BUFFER_SIZE);
3551
3552                out.flush();
3553                out.close();
3554            }
3555            else if (dname == 'J') {
3556                long[] ldata = new long[size];
3557                ldata = (long[]) data;
3558
3559                bb = ByteBuffer.allocate(LONG_BUFFER_SIZE * 8);
3560                if (binaryOrder == 1)
3561                    bb.order(ByteOrder.nativeOrder());
3562                else if (binaryOrder == 2)
3563                    bb.order(ByteOrder.LITTLE_ENDIAN);
3564                else if (binaryOrder == 3) bb.order(ByteOrder.BIG_ENDIAN);
3565
3566                LongBuffer lb = bb.asLongBuffer();
3567                int remainingSize = size - LONG_BUFFER_SIZE;
3568                int allocValue = 0;
3569                int iterationNumber = 0;
3570                do {
3571                    if (remainingSize <= 0) {
3572                        allocValue = remainingSize + LONG_BUFFER_SIZE;
3573                    }
3574                    else {
3575                        allocValue = LONG_BUFFER_SIZE;
3576                    }
3577                    bb.clear();
3578                    lb.clear();
3579                    lb.put(ldata, (iterationNumber * LONG_BUFFER_SIZE), allocValue);
3580                    out.write(bb.array(), 0, allocValue * 8);
3581                    remainingSize = remainingSize - LONG_BUFFER_SIZE;
3582                    iterationNumber++;
3583                } while (remainingSize > -LONG_BUFFER_SIZE);
3584
3585                out.flush();
3586                out.close();
3587            }
3588            else if (dname == 'F') {
3589                float[] fdata = new float[size];
3590                fdata = (float[]) data;
3591
3592                bb = ByteBuffer.allocate(FLOAT_BUFFER_SIZE * 4);
3593                if (binaryOrder == 1)
3594                    bb.order(ByteOrder.nativeOrder());
3595                else if (binaryOrder == 2)
3596                    bb.order(ByteOrder.LITTLE_ENDIAN);
3597                else if (binaryOrder == 3) bb.order(ByteOrder.BIG_ENDIAN);
3598
3599                FloatBuffer fb = bb.asFloatBuffer();
3600                int remainingSize = size - FLOAT_BUFFER_SIZE;
3601                int allocValue = 0;
3602                int iterationNumber = 0;
3603                do {
3604                    if (remainingSize <= 0) {
3605                        allocValue = remainingSize + FLOAT_BUFFER_SIZE;
3606                    }
3607                    else {
3608                        allocValue = FLOAT_BUFFER_SIZE;
3609                    }
3610                    bb.clear();
3611                    fb.clear();
3612                    fb.put(fdata, (iterationNumber * FLOAT_BUFFER_SIZE), allocValue);
3613                    out.write(bb.array(), 0, allocValue * 4);
3614                    remainingSize = remainingSize - FLOAT_BUFFER_SIZE;
3615                    iterationNumber++;
3616                } while (remainingSize > -FLOAT_BUFFER_SIZE);
3617
3618                out.flush();
3619                out.close();
3620            }
3621            else if (dname == 'D') {
3622                double[] ddata = new double[size];
3623                ddata = (double[]) data;
3624
3625                bb = ByteBuffer.allocate(DOUBLE_BUFFER_SIZE * 8);
3626                if (binaryOrder == 1)
3627                    bb.order(ByteOrder.nativeOrder());
3628                else if (binaryOrder == 2)
3629                    bb.order(ByteOrder.LITTLE_ENDIAN);
3630                else if (binaryOrder == 3) bb.order(ByteOrder.BIG_ENDIAN);
3631
3632                DoubleBuffer db = bb.asDoubleBuffer();
3633                int remainingSize = size - DOUBLE_BUFFER_SIZE;
3634                int allocValue = 0;
3635                int iterationNumber = 0;
3636                do {
3637                    if (remainingSize <= 0) {
3638                        allocValue = remainingSize + DOUBLE_BUFFER_SIZE;
3639                    }
3640                    else {
3641                        allocValue = DOUBLE_BUFFER_SIZE;
3642                    }
3643                    bb.clear();
3644                    db.clear();
3645                    db.put(ddata, (iterationNumber * DOUBLE_BUFFER_SIZE), allocValue);
3646                    out.write(bb.array(), 0, allocValue * 8);
3647                    remainingSize = remainingSize - DOUBLE_BUFFER_SIZE;
3648                    iterationNumber++;
3649                } while (remainingSize > -DOUBLE_BUFFER_SIZE);
3650
3651                out.flush();
3652                out.close();
3653            }
3654        }
3655
3656        viewer.showStatus("Data save to: " + fname);
3657    }
3658
3659    /**
3660     * update dataset value in file. The change will go to file.
3661     */
3662    @Override
3663    public void updateValueInFile ( ) {
3664        log.trace("DefaultTableView updateValueInFile enter");
3665        if (isReadOnly || showAsBin || showAsHex) {
3666            return;
3667        }
3668
3669        if (!isValueChanged) {
3670            return;
3671        }
3672
3673        try {
3674            log.trace("DefaultTableView updateValueInFile write");
3675            dataset.write();
3676        }
3677        catch (Exception ex) {
3678            toolkit.beep();
3679            JOptionPane.showMessageDialog(this, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
3680            return;
3681        }
3682
3683        isValueChanged = false;
3684        log.trace("DefaultTableView updateValueInFile exit");
3685    }
3686
3687    /**
3688     * Selects all rows, columns, and cells in the table.
3689     */
3690    private void selectAll ( ) throws Exception {
3691        table.selectAll();
3692    }
3693
3694    /**
3695     * Converting selected data based on predefined math functions.
3696     */
3697    private void mathConversion ( ) throws Exception {
3698        if (isReadOnly) {
3699            return;
3700        }
3701
3702        int cols = table.getSelectedColumnCount();
3703        // if (!(dataset instanceof ScalarDS)) return;
3704        if ((dataset instanceof CompoundDS) && (cols > 1)) {
3705            toolkit.beep();
3706            JOptionPane.showMessageDialog(this, "Please select one colunm a time for math conversion for compound dataset.",
3707                    getTitle(), JOptionPane.ERROR_MESSAGE);
3708            return;
3709        }
3710
3711        Object theData = getSelectedData();
3712        if (theData == null) {
3713            toolkit.beep();
3714            JOptionPane.showMessageDialog(this, "No data is selected.", getTitle(), JOptionPane.ERROR_MESSAGE);
3715            return;
3716        }
3717
3718        MathConversionDialog dialog = new MathConversionDialog((JFrame) viewer, theData);
3719        dialog.setVisible(true);
3720
3721        if (dialog.isConverted()) {
3722            if (dataset instanceof CompoundDS) {
3723                Object colData = null;
3724                try {
3725                    colData = ((List<?>) dataset.getData()).get(table.getSelectedColumn());
3726                }
3727                catch (Exception ex) {
3728                    log.debug("colData:", ex);
3729                }
3730
3731                if (colData != null) {
3732                    int size = Array.getLength(theData);
3733                    System.arraycopy(theData, 0, colData, 0, size);
3734                }
3735            }
3736            else {
3737                int rows = table.getSelectedRowCount();
3738                int r0 = table.getSelectedRow();
3739                int c0 = table.getSelectedColumn();
3740                int w = table.getColumnCount();
3741                int idx_src = 0;
3742                int idx_dst = 0;
3743                for (int i = 0; i < rows; i++) {
3744                    idx_dst = (r0 + i) * w + c0;
3745                    System.arraycopy(theData, idx_src, dataValue, idx_dst, cols);
3746                    idx_src += cols;
3747                }
3748            }
3749
3750            theData = null;
3751            System.gc();
3752            table.updateUI();
3753            isValueChanged = true;
3754        }
3755
3756    }
3757
3758    /**
3759     * update cell value in memory. It does not change the dataset value in file.
3760     *
3761     * @param cellValue
3762     *            the string value of input.
3763     * @param row
3764     *            the row of the editing cell.
3765     * @param col
3766     *            the column of the editing cell.
3767     */
3768    private void updateValueInMemory (String cellValue, int row, int col) throws Exception {
3769        log.trace("DefaultTableView updateValueInMemory");
3770        if (currentEditingCellValue != null) {
3771            // data values are the same, no need to change the data
3772            if (currentEditingCellValue.toString().equals(cellValue)) return;
3773        }
3774
3775        if (dataset instanceof ScalarDS) {
3776            updateScalarData(cellValue, row, col);
3777        }
3778        else if (dataset instanceof CompoundDS) {
3779            updateCompoundData(cellValue, row, col);
3780        }
3781    }
3782
3783    /**
3784     * update cell value in memory. It does not change the dataset value in file.
3785     *
3786     * @param cellValue
3787     *            the string value of input.
3788     * @param row
3789     *            the row of the editing cell.
3790     * @param col
3791     *            the column of the editing cell.
3792     */
3793    private void updateScalarData (String cellValue, int row, int col) throws Exception {
3794        if (!(dataset instanceof ScalarDS) || (cellValue == null) || ((cellValue = cellValue.trim()) == null) || showAsBin
3795                || showAsHex) {
3796            return;
3797        }
3798
3799        int i = 0;
3800        if (isDataTransposed) {
3801            i = col * table.getRowCount() + row;
3802        }
3803        else {
3804            i = row * table.getColumnCount() + col;
3805        }
3806        log.trace("DefaultTableView updateScalarData {} NT={}", cellValue, NT);
3807
3808        ScalarDS sds = (ScalarDS) dataset;
3809        boolean isUnsigned = sds.isUnsigned();
3810        String cname = dataset.getOriginalClass().getName();
3811        char dname = cname.charAt(cname.lastIndexOf("[") + 1);
3812        log.trace("updateScalarData isUnsigned={} cname={} dname={}", isUnsigned, cname, dname);
3813
3814        // check data range for unsigned datatype converted sizes!
3815        if (isUnsigned) {
3816            long lvalue = -1;
3817            long maxValue = Long.MAX_VALUE;
3818            if (dname == 'B') {
3819                maxValue = 255;
3820                lvalue = Long.parseLong(cellValue);
3821
3822                if (lvalue < 0) {
3823                    throw new NumberFormatException("Negative value for unsigned integer: " + lvalue);
3824                }
3825
3826                if (lvalue > maxValue) {
3827                    throw new NumberFormatException("Data value is out of range: " + lvalue);
3828                }
3829            }
3830            else if (dname == 'S') {
3831                maxValue = 65535;
3832                lvalue = Long.parseLong(cellValue);
3833
3834                if (lvalue < 0) {
3835                    throw new NumberFormatException("Negative value for unsigned integer: " + lvalue);
3836                }
3837
3838                if (lvalue > maxValue) {
3839                    throw new NumberFormatException("Data value is out of range: " + lvalue);
3840                }
3841            }
3842            else if (dname == 'I') {
3843                maxValue = 4294967295L;
3844                lvalue = Long.parseLong(cellValue);
3845
3846                if (lvalue < 0) {
3847                    throw new NumberFormatException("Negative value for unsigned integer: " + lvalue);
3848                }
3849
3850                if (lvalue > maxValue) {
3851                    throw new NumberFormatException("Data value is out of range: " + lvalue);
3852                }
3853            }
3854            else if (dname == 'J') {
3855                BigInteger Jmax = new BigInteger("18446744073709551615");
3856                BigInteger big = new BigInteger(cellValue);
3857                if (big.compareTo(Jmax) > 0) {
3858                    throw new NumberFormatException("Negative value for unsigned integer: " + cellValue);
3859                }
3860                if (big.compareTo(BigInteger.ZERO) < 0) {
3861                    throw new NumberFormatException("Data value is out of range: " + cellValue);
3862                }
3863            }
3864        }
3865
3866        switch (NT) {
3867            case 'B':
3868                byte bvalue = 0;
3869                bvalue = Byte.parseByte(cellValue);
3870                Array.setByte(dataValue, i, bvalue);
3871                break;
3872            case 'S':
3873                short svalue = 0;
3874                svalue = Short.parseShort(cellValue);
3875                Array.setShort(dataValue, i, svalue);
3876                break;
3877            case 'I':
3878                int ivalue = 0;
3879                ivalue = Integer.parseInt(cellValue);
3880                Array.setInt(dataValue, i, ivalue);
3881                break;
3882            case 'J':
3883                long lvalue = 0;
3884                if (dname == 'J') {
3885                    BigInteger big = new BigInteger(cellValue);
3886                    lvalue = big.longValue();
3887                }
3888                else
3889                    lvalue = Long.parseLong(cellValue);
3890                Array.setLong(dataValue, i, lvalue);
3891                break;
3892            case 'F':
3893                float fvalue = 0;
3894                fvalue = Float.parseFloat(cellValue);
3895                Array.setFloat(dataValue, i, fvalue);
3896                break;
3897            case 'D':
3898                double dvalue = 0;
3899                dvalue = Double.parseDouble(cellValue);
3900                Array.setDouble(dataValue, i, dvalue);
3901                break;
3902            default:
3903                Array.set(dataValue, i, cellValue);
3904                break;
3905        }
3906
3907        isValueChanged = true;
3908    }
3909
3910    private void updateCompoundData (String cellValue, int row, int col) throws Exception {
3911        if (!(dataset instanceof CompoundDS) || (cellValue == null) || ((cellValue = cellValue.trim()) == null)) {
3912            return;
3913        }
3914        log.trace("DefaultTableView updateCompoundData");
3915
3916        CompoundDS compDS = (CompoundDS) dataset;
3917        List<?> cdata = (List<?>) compDS.getData();
3918        int orders[] = compDS.getSelectedMemberOrders();
3919        Datatype types[] = compDS.getSelectedMemberTypes();
3920        int nFields = cdata.size();
3921        int nSubColumns = table.getColumnCount() / nFields;
3922        table.getRowCount();
3923        int column = col;
3924        int offset = 0;
3925        int morder = 1;
3926
3927        if (nSubColumns > 1) { // multi-dimension compound dataset
3928            int colIdx = col / nFields;
3929            column = col - colIdx * nFields;
3930            // //BUG 573: offset = row * orders[column] + colIdx * nRows *
3931            // orders[column];
3932            offset = row * orders[column] * nSubColumns + colIdx * orders[column];
3933        }
3934        else {
3935            offset = row * orders[column];
3936        }
3937        morder = orders[column];
3938
3939        Object mdata = cdata.get(column);
3940
3941        // strings
3942        if (Array.get(mdata, 0) instanceof String) {
3943            Array.set(mdata, offset, cellValue);
3944            isValueChanged = true;
3945            return;
3946        }
3947        else if (types[column].getDatatypeClass() == Datatype.CLASS_STRING) {
3948            // it is string but not converted, still byte array
3949            int strlen = types[column].getDatatypeSize();
3950            offset *= strlen;
3951            byte[] bytes = cellValue.getBytes();
3952            byte[] bData = (byte[]) mdata;
3953            int n = Math.min(strlen, bytes.length);
3954            System.arraycopy(bytes, 0, bData, offset, n);
3955            offset += n;
3956            n = strlen - bytes.length;
3957            // space padding
3958            for (int i = 0; i < n; i++) {
3959                bData[offset + i] = ' ';
3960            }
3961            isValueChanged = true;
3962            return;
3963        }
3964
3965        // Numeric data
3966        char mNT = ' ';
3967        String cName = mdata.getClass().getName();
3968        int cIndex = cName.lastIndexOf("[");
3969        if (cIndex >= 0) {
3970            mNT = cName.charAt(cIndex + 1);
3971        }
3972
3973        StringTokenizer st = new StringTokenizer(cellValue, ",");
3974        if (st.countTokens() < morder) {
3975            toolkit.beep();
3976            JOptionPane.showMessageDialog(this, "Number of data point < " + morder + ".", getTitle(), JOptionPane.ERROR_MESSAGE);
3977            return;
3978        }
3979
3980        String token = "";
3981        isValueChanged = true;
3982        switch (mNT) {
3983            case 'B':
3984                byte bvalue = 0;
3985                for (int i = 0; i < morder; i++) {
3986                    token = st.nextToken().trim();
3987                    bvalue = Byte.parseByte(token);
3988                    Array.setByte(mdata, offset + i, bvalue);
3989                }
3990                break;
3991            case 'S':
3992                short svalue = 0;
3993                for (int i = 0; i < morder; i++) {
3994                    token = st.nextToken().trim();
3995                    svalue = Short.parseShort(token);
3996                    Array.setShort(mdata, offset + i, svalue);
3997                }
3998                break;
3999            case 'I':
4000                int ivalue = 0;
4001                for (int i = 0; i < morder; i++) {
4002                    token = st.nextToken().trim();
4003                    ivalue = Integer.parseInt(token);
4004                    Array.setInt(mdata, offset + i, ivalue);
4005                }
4006                break;
4007            case 'J':
4008                long lvalue = 0;
4009                for (int i = 0; i < morder; i++) {
4010                    token = st.nextToken().trim();
4011                    BigInteger big = new BigInteger(token);
4012                    lvalue = big.longValue();
4013                    // lvalue = Long.parseLong(token);
4014                    Array.setLong(mdata, offset + i, lvalue);
4015                }
4016                break;
4017            case 'F':
4018                float fvalue = 0;
4019                for (int i = 0; i < morder; i++) {
4020                    token = st.nextToken().trim();
4021                    fvalue = Float.parseFloat(token);
4022                    Array.setFloat(mdata, offset + i, fvalue);
4023                }
4024                break;
4025            case 'D':
4026                double dvalue = 0;
4027                for (int i = 0; i < morder; i++) {
4028                    token = st.nextToken().trim();
4029                    dvalue = Double.parseDouble(token);
4030                    Array.setDouble(mdata, offset + i, dvalue);
4031                }
4032                break;
4033            default:
4034                isValueChanged = false;
4035        }
4036    }
4037
4038    private class LineplotOption extends JDialog implements ActionListener, ItemListener {
4039        private static final long serialVersionUID = -3457035832213978906L;
4040        public static final int   NO_PLOT          = -1;
4041        public static final int   ROW_PLOT         = 0;
4042        public static final int   COLUMN_PLOT      = 1;
4043
4044        private int               idx_xaxis        = -1, plotType = -1;
4045        private JRadioButton      rowButton, colButton;
4046        @SuppressWarnings("rawtypes")
4047        private JComboBox         rowBox, colBox;
4048
4049        @SuppressWarnings({ "rawtypes", "unchecked" })
4050        public LineplotOption(JFrame owner, String title, int nrow, int ncol) {
4051            super(owner, title, true);
4052
4053            rowBox = new JComboBox();
4054            rowBox.setEditable(false);
4055            colBox = new JComboBox();
4056            colBox.setEditable(false);
4057
4058            JPanel contentPane = (JPanel) this.getContentPane();
4059            contentPane.setPreferredSize(new Dimension(400, 150));
4060            contentPane.setLayout(new BorderLayout(10, 10));
4061
4062            long[] startArray = dataset.getStartDims();
4063            long[] strideArray = dataset.getStride();
4064            int[] selectedIndex = dataset.getSelectedIndex();
4065            int start = (int) startArray[selectedIndex[0]];
4066            int stride = (int) strideArray[selectedIndex[0]];
4067
4068            rowBox.addItem("array index");
4069            for (int i = 0; i < nrow; i++) {
4070                rowBox.addItem("row " + (start + indexBase + i * stride));
4071            }
4072
4073            colBox.addItem("array index");
4074            for (int i = 0; i < ncol; i++) {
4075                colBox.addItem("column " + table.getColumnName(i));
4076            }
4077
4078            rowButton = new JRadioButton("Row");
4079            colButton = new JRadioButton("Column", true);
4080            rowButton.addItemListener(this);
4081            colButton.addItemListener(this);
4082            ButtonGroup rgroup = new ButtonGroup();
4083            rgroup.add(rowButton);
4084            rgroup.add(colButton);
4085
4086            JPanel p1 = new JPanel();
4087            p1.setLayout(new GridLayout(2, 1, 5, 5));
4088            p1.add(new JLabel(" Series in:", SwingConstants.RIGHT));
4089            p1.add(new JLabel(" For abscissa use:", SwingConstants.RIGHT));
4090
4091            JPanel p2 = new JPanel();
4092            p2.setLayout(new GridLayout(2, 1, 5, 5));
4093            // p2.setBorder(new LineBorder(Color.lightGray));
4094            p2.add(colButton);
4095            p2.add(colBox);
4096
4097            JPanel p3 = new JPanel();
4098            p3.setLayout(new GridLayout(2, 1, 5, 5));
4099            // p3.setBorder(new LineBorder(Color.lightGray));
4100            p3.add(rowButton);
4101            p3.add(rowBox);
4102
4103            JPanel p = new JPanel();
4104            p.setBorder(new LineBorder(Color.lightGray));
4105            p.setLayout(new GridLayout(1, 3, 20, 5));
4106            p.add(p1);
4107            p.add(p2);
4108            p.add(p3);
4109
4110            JPanel bp = new JPanel();
4111
4112            JButton okButton = new JButton("Ok");
4113            okButton.addActionListener(this);
4114            okButton.setActionCommand("Ok");
4115            bp.add(okButton);
4116
4117            JButton cancelButton = new JButton("Cancel");
4118            cancelButton.addActionListener(this);
4119            cancelButton.setActionCommand("Cancel");
4120            bp.add(cancelButton);
4121
4122            contentPane.add(new JLabel(" Select plot options:"), BorderLayout.NORTH);
4123            contentPane.add(p, BorderLayout.CENTER);
4124            contentPane.add(bp, BorderLayout.SOUTH);
4125
4126            colBox.setEnabled(colButton.isSelected());
4127            rowBox.setEnabled(rowButton.isSelected());
4128
4129            Point l = getParent().getLocation();
4130            l.x += 450;
4131            l.y += 200;
4132            setLocation(l);
4133            pack();
4134        }
4135
4136        int getXindex ( ) {
4137            return idx_xaxis;
4138        }
4139
4140        int getPlotBy ( ) {
4141            return plotType;
4142        }
4143
4144        @Override
4145        public void actionPerformed (ActionEvent e) {
4146            e.getSource();
4147            String cmd = e.getActionCommand();
4148
4149            if (cmd.equals("Cancel")) {
4150                plotType = NO_PLOT;
4151                this.dispose(); // terminate the application
4152            }
4153            else if (cmd.equals("Ok")) {
4154                if (colButton.isSelected()) {
4155                    idx_xaxis = colBox.getSelectedIndex() - 1;
4156                    plotType = COLUMN_PLOT;
4157                }
4158                else {
4159                    idx_xaxis = rowBox.getSelectedIndex() - 1;
4160                    plotType = ROW_PLOT;
4161                }
4162
4163                this.dispose(); // terminate the application
4164            }
4165        }
4166
4167        @Override
4168        public void itemStateChanged (ItemEvent e) {
4169            Object source = e.getSource();
4170
4171            if (source.equals(colButton) || source.equals(rowButton)) {
4172                colBox.setEnabled(colButton.isSelected());
4173                rowBox.setEnabled(rowButton.isSelected());
4174            }
4175        }
4176    }
4177
4178    private class ColumnHeader extends JTableHeader {
4179        private static final long serialVersionUID   = -3179653809792147055L;
4180        private int               currentColumnIndex = -1;
4181        private int               lastColumnIndex    = -1;
4182        private JTable            parentTable;
4183
4184        public ColumnHeader(JTable theTable) {
4185            super(theTable.getColumnModel());
4186
4187            parentTable = theTable;
4188            setReorderingAllowed(false);
4189        }
4190
4191        @Override
4192        protected void processMouseMotionEvent (MouseEvent e) {
4193            super.processMouseMotionEvent(e);
4194
4195            if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
4196                // do not do anything, just resize the column
4197                if (getResizingColumn() != null) return;
4198
4199                int colEnd = columnAtPoint(e.getPoint());
4200
4201                if (colEnd < 0) {
4202                    colEnd = 0;
4203                }
4204                if (currentColumnIndex < 0) {
4205                    currentColumnIndex = 0;
4206                }
4207
4208                parentTable.clearSelection();
4209
4210                if (colEnd > currentColumnIndex) {
4211                    parentTable.setColumnSelectionInterval(currentColumnIndex, colEnd);
4212                }
4213                else {
4214                    parentTable.setColumnSelectionInterval(colEnd, currentColumnIndex);
4215                }
4216
4217                parentTable.setRowSelectionInterval(0, parentTable.getRowCount() - 1);
4218            }
4219        }
4220
4221        @Override
4222        protected void processMouseEvent (MouseEvent e) {
4223            super.processMouseEvent(e);
4224
4225            int mouseID = e.getID();
4226
4227            if (mouseID == MouseEvent.MOUSE_CLICKED) {
4228                if (currentColumnIndex < 0) {
4229                    return;
4230                }
4231
4232                if (e.isControlDown()) {
4233                    // select discontinuous columns
4234                    parentTable.addColumnSelectionInterval(currentColumnIndex, currentColumnIndex);
4235                }
4236                else if (e.isShiftDown()) {
4237                    // select continuous columns
4238                    if (lastColumnIndex < 0) {
4239                        parentTable.addColumnSelectionInterval(0, currentColumnIndex);
4240                    }
4241                    else if (lastColumnIndex < currentColumnIndex) {
4242                        parentTable.addColumnSelectionInterval(lastColumnIndex, currentColumnIndex);
4243                    }
4244                    else {
4245                        parentTable.addColumnSelectionInterval(currentColumnIndex, lastColumnIndex);
4246                    }
4247                }
4248                else {
4249                    // clear old selection and set new column selection
4250                    parentTable.clearSelection();
4251                    parentTable.setColumnSelectionInterval(currentColumnIndex, currentColumnIndex);
4252                }
4253
4254                lastColumnIndex = currentColumnIndex;
4255                parentTable.setRowSelectionInterval(0, parentTable.getRowCount() - 1);
4256            }
4257            else if (mouseID == MouseEvent.MOUSE_PRESSED) {
4258                currentColumnIndex = columnAtPoint(e.getPoint());
4259            }
4260        }
4261    } // private class ColumnHeader
4262
4263    /** RowHeader defines the row header component of the Spreadsheet. */
4264    private class RowHeader extends JTable {
4265        private static final long serialVersionUID = -1548007702499873626L;
4266        private int               currentRowIndex  = -1;
4267        private int               lastRowIndex     = -1;
4268        private JTable            parentTable;
4269
4270        public RowHeader(JTable pTable, Dataset dset) {
4271            // Create a JTable with the same number of rows as
4272            // the parent table and one column.
4273            // super( pTable.getRowCount(), 1 );
4274
4275            final long[] startArray = dset.getStartDims();
4276            final long[] strideArray = dset.getStride();
4277            final int[] selectedIndex = dset.getSelectedIndex();
4278            final int start = (int) startArray[selectedIndex[0]];
4279            final int stride = (int) strideArray[selectedIndex[0]];
4280            final int rowCount = pTable.getRowCount();
4281            parentTable = pTable;
4282
4283            AbstractTableModel tm = new AbstractTableModel() {
4284                private static final long serialVersionUID = -8117073107569884677L;
4285
4286                @Override
4287                public int getColumnCount ( ) {
4288                    return 1;
4289                }
4290
4291                @Override
4292                public int getRowCount ( ) {
4293                    return rowCount;
4294                }
4295
4296                @Override
4297                public String getColumnName (int col) {
4298                    return " ";
4299                }
4300
4301                @Override
4302                public Object getValueAt (int row, int column) {
4303                    log.trace("RowHeader:AbstractTableModel:getValueAt");
4304                    return String.valueOf(start + indexBase + row * stride);
4305                }
4306            };
4307
4308            this.setModel(tm);
4309
4310            // Get the only table column.
4311            TableColumn col = getColumnModel().getColumn(0);
4312
4313            // Use the cell renderer in the column.
4314            col.setCellRenderer(new RowHeaderRenderer());
4315        }
4316
4317        /** Overridden to return false since the headers are not editable. */
4318        @Override
4319        public boolean isCellEditable (int row, int col) {
4320            return false;
4321        }
4322
4323        /** This is called when the selection changes in the row headers. */
4324        @Override
4325        public void valueChanged (ListSelectionEvent e) {
4326            if (parentTable == null) {
4327                return;
4328            }
4329
4330            int rows[] = getSelectedRows();
4331            if ((rows == null) || (rows.length == 0)) {
4332                return;
4333            }
4334
4335            parentTable.clearSelection();
4336            parentTable.setRowSelectionInterval(rows[0], rows[rows.length - 1]);
4337            parentTable.setColumnSelectionInterval(0, parentTable.getColumnCount() - 1);
4338        }
4339
4340        @Override
4341        protected void processMouseMotionEvent (MouseEvent e) {
4342            if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
4343                int colEnd = rowAtPoint(e.getPoint());
4344
4345                if (colEnd < 0) {
4346                    colEnd = 0;
4347                }
4348                if (currentRowIndex < 0) {
4349                    currentRowIndex = 0;
4350                }
4351
4352                parentTable.clearSelection();
4353
4354                if (colEnd > currentRowIndex) {
4355                    parentTable.setRowSelectionInterval(currentRowIndex, colEnd);
4356                }
4357                else {
4358                    parentTable.setRowSelectionInterval(colEnd, currentRowIndex);
4359                }
4360
4361                parentTable.setColumnSelectionInterval(0, parentTable.getColumnCount() - 1);
4362            }
4363        }
4364
4365        @Override
4366        protected void processMouseEvent (MouseEvent e) {
4367            int mouseID = e.getID();
4368
4369            if (mouseID == MouseEvent.MOUSE_CLICKED) {
4370                if (currentRowIndex < 0) {
4371                    return;
4372                }
4373
4374                if (e.isControlDown()) {
4375                    // select discontinuous rows
4376                    parentTable.addRowSelectionInterval(currentRowIndex, currentRowIndex);
4377                }
4378                else if (e.isShiftDown()) {
4379                    // select contiguous columns
4380                    if (lastRowIndex < 0) {
4381                        parentTable.addRowSelectionInterval(0, currentRowIndex);
4382                    }
4383                    else if (lastRowIndex < currentRowIndex) {
4384                        parentTable.addRowSelectionInterval(lastRowIndex, currentRowIndex);
4385                    }
4386                    else {
4387                        parentTable.addRowSelectionInterval(currentRowIndex, lastRowIndex);
4388                    }
4389                }
4390                else {
4391                    // clear old selection and set new column selection
4392                    parentTable.clearSelection();
4393                    parentTable.setRowSelectionInterval(currentRowIndex, currentRowIndex);
4394                }
4395
4396                lastRowIndex = currentRowIndex;
4397
4398                parentTable.setColumnSelectionInterval(0, parentTable.getColumnCount() - 1);
4399            }
4400            else if (mouseID == MouseEvent.MOUSE_PRESSED) {
4401                currentRowIndex = rowAtPoint(e.getPoint());
4402            }
4403        }
4404    } // private class RowHeader extends JTable
4405
4406    /**
4407     * RowHeaderRenderer is a custom cell renderer that displays cells as buttons.
4408     */
4409    private class RowHeaderRenderer extends JLabel implements TableCellRenderer {
4410        private static final long serialVersionUID = -8963879626159783226L;
4411
4412        public RowHeaderRenderer( ) {
4413            super();
4414            setHorizontalAlignment(SwingConstants.CENTER);
4415
4416            setOpaque(true);
4417            setBorder(UIManager.getBorder("TableHeader.cellBorder"));
4418            setBackground(Color.lightGray);
4419        }
4420
4421        /** Configures the button for the current cell, and returns it. */
4422        @Override
4423        public Component getTableCellRendererComponent (JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
4424                int column) {
4425            setFont(table.getFont());
4426
4427            if (value != null) {
4428                setText(value.toString());
4429            }
4430
4431            return this;
4432        }
4433    } // private class RowHeaderRenderer extends JLabel implements
4434      // TableCellRenderer
4435
4436    @SuppressWarnings("rawtypes")
4437    private class MultiLineHeaderRenderer extends JList implements TableCellRenderer {
4438        private static final long    serialVersionUID = -3697496960833719169L;
4439        private final CompoundBorder subBorder        = new CompoundBorder(new MatteBorder(1, 0, 1, 0, java.awt.Color.darkGray),
4440                                                              new MatteBorder(1, 0, 1, 0, java.awt.Color.white));
4441        private final CompoundBorder majorBorder      = new CompoundBorder(new MatteBorder(1, 1, 1, 0, java.awt.Color.darkGray),
4442                                                              new MatteBorder(1, 2, 1, 0, java.awt.Color.white));
4443        Vector<String>               lines            = new Vector<String>();
4444        int                          nSubcolumns      = 1;
4445
4446        public MultiLineHeaderRenderer(int majorColumns, int subColumns) {
4447            nSubcolumns = subColumns;
4448            setOpaque(true);
4449            setForeground(UIManager.getColor("TableHeader.foreground"));
4450            setBackground(UIManager.getColor("TableHeader.background"));
4451        }
4452
4453        @Override
4454        public Component getTableCellRendererComponent (JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
4455                int column) {
4456            setFont(table.getFont());
4457            String str = (value == null) ? "" : value.toString();
4458            BufferedReader br = new BufferedReader(new StringReader(str));
4459            String line;
4460
4461            lines.clear();
4462            try {
4463                while ((line = br.readLine()) != null) {
4464                    lines.addElement(line);
4465                }
4466            }
4467            catch (IOException ex) {
4468                log.debug("string read:", ex);
4469            }
4470
4471            if ((column / nSubcolumns) * nSubcolumns == column) {
4472                setBorder(majorBorder);
4473            }
4474            else {
4475                setBorder(subBorder);
4476            }
4477            setListData(lines);
4478
4479            return this;
4480        }
4481    }
4482
4483    // ////////////////////////////////////////////////////////////////////////
4484    // //
4485    // The code below was added to deal with region references //
4486    // Peter Cao, 4/30/2009 //
4487    // //
4488    // ////////////////////////////////////////////////////////////////////////
4489
4490    @Override
4491    public void mouseClicked (MouseEvent e) {
4492        // only deal with reg. ref
4493        if (!(isRegRef || isObjRef)) return;
4494
4495        int eMod = e.getModifiers();
4496
4497        // provide two options here: double click to show data in table, or
4498        // right mouse to choose to show data in table or in image
4499
4500        // right mouse click
4501        if (e.isPopupTrigger()
4502                || (eMod == InputEvent.BUTTON3_MASK)
4503                || (System.getProperty("os.name").startsWith("Mac")
4504                && (eMod == (InputEvent.BUTTON1_MASK
4505                | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())))) {
4506            if (popupMenu != null) {
4507                popupMenu.show((JComponent) e.getSource(), e.getX(), e.getY());
4508            }
4509        }
4510        else if (e.getClickCount() == 2) {
4511            // double click
4512            viewType = ViewType.TABLE;
4513            Object theData = null;
4514            try {
4515                theData = ((Dataset) getDataObject()).getData();
4516            }
4517            catch (Exception ex) {
4518                JOptionPane.showMessageDialog(this, ex.getMessage(), getTitle(), JOptionPane.ERROR_MESSAGE);
4519            }
4520
4521            if (theData == null) {
4522                toolkit.beep();
4523                JOptionPane.showMessageDialog(this, "No data selected.", getTitle(), JOptionPane.ERROR_MESSAGE);
4524                return;
4525
4526            }
4527
4528            int[] selectedRows = table.getSelectedRows();
4529            if (selectedRows == null || selectedRows.length <= 0) {
4530                return;
4531            }
4532            int len = Array.getLength(selectedRows);
4533            for (int i = 0; i < len; i++) {
4534                if (isRegRef)
4535                    showRegRefData((String) Array.get(theData, selectedRows[i]));
4536                else if (isObjRef) showObjRefData(Array.getLong(theData, selectedRows[i]));
4537            }
4538        }
4539    }
4540
4541    @Override
4542    public void mouseEntered (MouseEvent e) {
4543    }
4544
4545    @Override
4546    public void mouseExited (MouseEvent e) {
4547    }
4548
4549    @Override
4550    public void mousePressed (MouseEvent e) {
4551    }
4552
4553    @Override
4554    public void mouseReleased (MouseEvent e) {
4555    }
4556
4557    /** creates a popup menu for a right mouse click on a data object */
4558    private JPopupMenu createPopupMenu ( ) {
4559        JPopupMenu menu = new JPopupMenu();
4560        JMenuItem item;
4561
4562        item = new JMenuItem("Show As Table");
4563        item.setMnemonic(KeyEvent.VK_T);
4564        item.addActionListener(this);
4565        item.setActionCommand("Show data as table");
4566        menu.add(item);
4567
4568        item = new JMenuItem("Show As Image");
4569        item.setMnemonic(KeyEvent.VK_I);
4570        item.addActionListener(this);
4571        item.setActionCommand("Show data as image");
4572        menu.add(item);
4573
4574        // item = new JMenuItem( "Show As Text");
4575        // item.setMnemonic(KeyEvent.VK_I);
4576        // item.addActionListener(this);
4577        // item.setActionCommand("Show data as text");
4578        // menu.add(item);
4579
4580        return menu;
4581    }
4582
4583    /**
4584     * Display data pointed by object references. Data of each object is shown in a separate
4585     * spreadsheet.
4586     *
4587     * @param ref
4588     *            the array of strings that contain the object reference information.
4589     *
4590     */
4591    private void showObjRefData (long ref) {
4592        long[] oid = { ref };
4593        log.trace("DefaultTableView showObjRefData: ref={}", ref);
4594
4595        HObject obj = FileFormat.findObject(dataset.getFileFormat(), oid);
4596        if (obj == null || !(obj instanceof ScalarDS)) return;
4597
4598        ScalarDS dset = (ScalarDS) obj;
4599        ScalarDS dset_copy = null;
4600
4601        // create an instance of the dataset constructor
4602        Constructor<? extends ScalarDS> constructor = null;
4603        Object[] paramObj = null;
4604        Object data = null;
4605
4606        try {
4607            Class[] paramClass = { FileFormat.class, String.class, String.class };
4608            constructor = dset.getClass().getConstructor(paramClass);
4609            paramObj = new Object[] { dset.getFileFormat(), dset.getName(), dset.getPath() };
4610            dset_copy = (ScalarDS) constructor.newInstance(paramObj);
4611            data = dset_copy.getData();
4612        }
4613        catch (Exception ex) {
4614            JOptionPane.showMessageDialog(this, ex, "Object Reference:" + getTitle(), JOptionPane.ERROR_MESSAGE);
4615            data = null;
4616        }
4617
4618        if (data == null) return;
4619
4620        JInternalFrame dataView = null;
4621        HashMap map = new HashMap(1);
4622        map.put(ViewProperties.DATA_VIEW_KEY.OBJECT, dset_copy);
4623        switch (viewType) {
4624            case TEXT:
4625                dataView = new DefaultTextView(viewer, map);
4626                break;
4627            case IMAGE:
4628                dataView = new DefaultImageView(viewer, map);
4629                break;
4630            default:
4631                dataView = new DefaultTableView(viewer, map);
4632                break;
4633        }
4634
4635        if (dataView != null) {
4636            viewer.addDataView((DataView) dataView);
4637        }
4638    }
4639
4640    /**
4641     * Display data pointed by region references. Data of each region is shown in a separate
4642     * spreadsheet. The reg. ref. information is stored in strings of the format below:
4643     * <p />
4644     * <ul>
4645     * <li>For point selections: "file_id:obj_id { <point1> <point2> ...) }", where <point1> is in
4646     * the form of (location_of_dim0, location_of_dim1, ...). For example, 0:800 { (0,1) (2,11)
4647     * (1,0) (2,4) }</li>
4648     * <li>For rectangle selections:
4649     * "file_id:obj_id { <corner coordinates1> <corner coordinates2> ... }", where <corner
4650     * coordinates1> is in the form of (start_corner)-(oposite_corner). For example, 0:800 {
4651     * (0,0)-(0,2) (0,11)-(0,13) (2,0)-(2,2) (2,11)-(2,13) }</li>
4652     * </ul>
4653     *
4654     * @param reg
4655     *            the array of strings that contain the reg. ref information.
4656     *
4657     */
4658    @SuppressWarnings({ "rawtypes", "unchecked" })
4659    private void showRegRefData (String reg) {
4660        boolean isPointSelection = false;
4661
4662        if (reg == null || (reg.length() <= 0) || (reg.compareTo("NULL") == 0)) return;
4663        log.trace("DefaultTableView showRegRefData: reg={}", reg);
4664
4665        isPointSelection = (reg.indexOf('-') <= 0);
4666
4667        // find the object location
4668        String oidStr = reg.substring(reg.indexOf('/'), reg.indexOf(' '));
4669        log.trace("DefaultTableView showRegRefData: isPointSelection={} oidStr={}", isPointSelection, oidStr);
4670
4671        // decode the region selection
4672        String regStr = reg.substring(reg.indexOf('{') + 1, reg.indexOf('}'));
4673        if (regStr == null || regStr.length() <= 0) return; // no selection
4674
4675        reg.substring(reg.indexOf('}') + 1);
4676
4677        StringTokenizer st = new StringTokenizer(regStr);
4678        int nSelections = st.countTokens();
4679        if (nSelections <= 0) return; // no selection
4680        log.trace("DefaultTableView showRegRefData: nSelections={}", nSelections);
4681
4682        HObject obj = FileFormat.findObject(dataset.getFileFormat(), oidStr);
4683        if (obj == null || !(obj instanceof ScalarDS)) return;
4684
4685        ScalarDS dset = (ScalarDS) obj;
4686        ScalarDS dset_copy = null;
4687
4688        // create an instance of the dataset constructor
4689        Constructor<? extends ScalarDS> constructor = null;
4690        Object[] paramObj = null;
4691        try {
4692            @SuppressWarnings("rawtypes")
4693            Class[] paramClass = { FileFormat.class, String.class, String.class };
4694            constructor = dset.getClass().getConstructor(paramClass);
4695            paramObj = new Object[] { dset.getFileFormat(), dset.getName(), dset.getPath() };
4696        }
4697        catch (Exception ex) {
4698            constructor = null;
4699        }
4700
4701        // load each selection into a separate dataset and display it in
4702        // a separate spreadsheet
4703        StringBuffer titleSB = new StringBuffer();
4704        log.trace("DefaultTableView showRegRefData: titleSB created");
4705
4706        while (st.hasMoreTokens()) {
4707            log.trace("DefaultTableView showRegRefData: st.hasMoreTokens() begin");
4708            try {
4709                dset_copy = (ScalarDS) constructor.newInstance(paramObj);
4710            }
4711            catch (Exception ex) {
4712                continue;
4713            }
4714
4715            if (dset_copy == null) continue;
4716
4717            try {
4718                dset_copy.init();
4719            }
4720            catch (Exception ex) {
4721                continue;
4722            }
4723
4724            dset_copy.getRank();
4725            long start[] = dset_copy.getStartDims();
4726            long count[] = dset_copy.getSelectedDims();
4727
4728            // set the selected dimension sizes based on the region selection
4729            // info.
4730            int idx = 0;
4731            String sizeStr = null;
4732            String token = st.nextToken();
4733
4734            titleSB.setLength(0);
4735            titleSB.append(token);
4736            titleSB.append(" at ");
4737            log.trace("DefaultTableView showRegRefData: titleSB={}", titleSB);
4738
4739            token = token.replace('(', ' ');
4740            token = token.replace(')', ' ');
4741            if (isPointSelection) {
4742                // point selection
4743                StringTokenizer tmp = new StringTokenizer(token, ",");
4744                while (tmp.hasMoreTokens()) {
4745                    count[idx] = 1;
4746                    sizeStr = tmp.nextToken().trim();
4747                    start[idx] = Long.valueOf(sizeStr);
4748                    idx++;
4749                }
4750            }
4751            else {
4752                // rectangle selection
4753                String startStr = token.substring(0, token.indexOf('-'));
4754                String endStr = token.substring(token.indexOf('-') + 1);
4755                StringTokenizer tmp = new StringTokenizer(startStr, ",");
4756                while (tmp.hasMoreTokens()) {
4757                    sizeStr = tmp.nextToken().trim();
4758                    start[idx] = Long.valueOf(sizeStr);
4759                    idx++;
4760                }
4761
4762                idx = 0;
4763                tmp = new StringTokenizer(endStr, ",");
4764                while (tmp.hasMoreTokens()) {
4765                    sizeStr = tmp.nextToken().trim();
4766                    count[idx] = Long.valueOf(sizeStr) - start[idx] + 1;
4767                    idx++;
4768                }
4769            }
4770            log.trace("DefaultTableView showRegRefData: selection inited");
4771
4772            try {
4773                dset_copy.getData();
4774            }
4775            catch (Exception ex) {
4776                JOptionPane.showMessageDialog(this, ex, "Region Reference:" + getTitle(), JOptionPane.ERROR_MESSAGE);
4777            }
4778
4779            JInternalFrame dataView = null;
4780            HashMap map = new HashMap(1);
4781            map.put(ViewProperties.DATA_VIEW_KEY.OBJECT, dset_copy);
4782            switch (viewType) {
4783                case TEXT:
4784                    dataView = new DefaultTextView(viewer, map);
4785                    break;
4786                case IMAGE:
4787                    dataView = new DefaultImageView(viewer, map);
4788                    break;
4789                default:
4790                    dataView = new DefaultTableView(viewer, map);
4791                    break;
4792            }
4793
4794            if (dataView != null) {
4795                viewer.addDataView((DataView) dataView);
4796                dataView.setTitle(dataView.getTitle() + "; " + titleSB.toString());
4797            }
4798            log.trace("DefaultTableView showRegRefData: st.hasMoreTokens() end");
4799        } // while (st.hasMoreTokens())
4800    } // private void showRegRefData(String reg)
4801}