001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see http://hdfgroup.org/products/hdf-java/doc/Copyright.html.         *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view;
016
017import java.awt.BorderLayout;
018import java.awt.Dimension;
019import java.awt.GridLayout;
020import java.awt.Insets;
021import java.awt.Point;
022import java.awt.event.ActionEvent;
023import java.awt.event.ActionListener;
024import java.awt.event.KeyEvent;
025import java.io.File;
026import java.lang.reflect.Array;
027import java.math.BigInteger;
028import java.util.Enumeration;
029import java.util.List;
030import java.util.StringTokenizer;
031
032import javax.swing.BorderFactory;
033import javax.swing.CellEditor;
034import javax.swing.JButton;
035import javax.swing.JComboBox;
036import javax.swing.JDialog;
037import javax.swing.JFileChooser;
038import javax.swing.JFrame;
039import javax.swing.JInternalFrame;
040import javax.swing.JLabel;
041import javax.swing.JOptionPane;
042import javax.swing.JPanel;
043import javax.swing.JScrollPane;
044import javax.swing.JSplitPane;
045import javax.swing.JTabbedPane;
046import javax.swing.JTable;
047import javax.swing.JTextArea;
048import javax.swing.JTextField;
049import javax.swing.ListSelectionModel;
050import javax.swing.border.TitledBorder;
051import javax.swing.event.ChangeEvent;
052import javax.swing.table.DefaultTableModel;
053import javax.swing.tree.DefaultMutableTreeNode;
054
055import hdf.object.Attribute;
056import hdf.object.CompoundDS;
057import hdf.object.Dataset;
058import hdf.object.Datatype;
059import hdf.object.FileFormat;
060import hdf.object.Group;
061import hdf.object.HObject;
062import hdf.object.ScalarDS;
063
064/**
065 * DefaultMetadataView is an dialog window used to show data properties. Data
066 * properties include attributes and general information such as object type,
067 * data type and data space.
068 *
069 * @author Peter X. Cao
070 * @version 2.4 9/6/2007
071 */
072public class DefaultMetaDataView extends JDialog implements ActionListener, MetaDataView {
073    private static final long serialVersionUID = 7891048909810508761L;
074
075    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultMetaDataView.class);
076
077    /**
078     * The main HDFView.
079     */
080    private ViewManager       viewer;
081
082    /** The HDF data object */
083    private HObject           hObject;
084
085    private JTabbedPane       tabbedPane       = null;
086    private JTextArea         attrContentArea;
087    private JTable            attrTable;  // table to hold a list of attributes
088    private DefaultTableModel attrTableModel;
089    private JLabel            attrNumberLabel;
090    private int               numAttributes;
091    private boolean           isH5, isH4;
092    private byte[]            userBlock;
093    private JTextArea         userBlockArea;
094    private JButton           jamButton;
095
096    private JTextField        linkField        = null;
097
098    private FileFormat        fileFormat;
099    private String            LinkTObjName;
100
101    private int[]             libver;
102
103    /**
104     * Constructs a DefaultMetadataView with the given HDFView.
105     */
106    public DefaultMetaDataView(ViewManager theView) {
107        super((JFrame) theView, false);
108        setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
109
110        setName("DefaultMetaDataView");
111        viewer = theView;
112        hObject = viewer.getTreeView().getCurrentObject();
113        fileFormat = hObject.getFileFormat();
114        numAttributes = 0;
115        userBlock = null;
116        userBlockArea = null;
117        libver = new int[2];
118
119        if (hObject == null) {
120            dispose();
121        }
122        else if (hObject.getPath() == null) {
123            setTitle("Properties - " + hObject.getName());
124        }
125        else {
126            setTitle("Properties - " + hObject.getPath() + hObject.getName());
127        }
128
129        isH5 = hObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5));
130        isH4 = hObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4));
131
132        tabbedPane = new JTabbedPane();
133        // get the metadata information before add GUI components */
134        try {
135            log.trace("DefaultMetaDataView: start");
136            hObject.getMetadata();
137        }
138        catch (Exception ex) {
139            log.debug("get the metadata information before add GUI components:", ex);
140        }
141        tabbedPane.addTab("General", createGeneralPropertyPanel());
142        tabbedPane.addTab("Attributes", createAttributePanel());
143
144        boolean isRoot = ((hObject instanceof Group) && ((Group) hObject).isRoot());
145        if (isH5 && isRoot) {
146            // add panel to display user block
147            tabbedPane.addTab("User Block", createUserBlockPanel());
148        }
149        tabbedPane.setSelectedIndex(0);
150
151        if (isH5) {
152            if (hObject.getLinkTargetObjName() != null) {
153                LinkTObjName = hObject.getLinkTargetObjName();
154            }
155        }
156        JPanel bPanel = new JPanel();
157        bPanel.setName("MetaDataClose");
158        JButton b = new JButton("  Close  ");
159        b.setName("Close");
160        b.setMnemonic(KeyEvent.VK_C);
161        b.setActionCommand("Close");
162        b.addActionListener(this);
163        bPanel.add(b);
164
165        // Add the tabbed pane to this panel.
166        JPanel contentPane = (JPanel) getContentPane();
167        contentPane.setName("MetaDataContent");
168        contentPane.setLayout(new BorderLayout());
169        contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
170        contentPane.setPreferredSize(new Dimension(620, 400));
171
172        contentPane.add("Center", tabbedPane);
173        contentPane.add("South", bPanel);
174
175        // locate the H5Property dialog
176        Point l = getParent().getLocation();
177        l.x += 250;
178        l.y += 80;
179        setLocation(l);
180        pack();
181        setVisible(true);
182    }
183
184    @SuppressWarnings("rawtypes")
185    public void actionPerformed(ActionEvent e) {
186        Object source = e.getSource();
187        String cmd = e.getActionCommand();
188
189        if (cmd.equals("Close")) {
190            if (isH5 && linkField != null) checkLinkTargetChanged();
191
192            dispose();
193        }
194        else if (cmd.equals("Add attribute")) {
195            addAttribute(hObject);
196        }
197        else if (cmd.equals("Delete attribute")) {
198            deleteAttribute(hObject);
199        }
200        else if (cmd.equals("Jam user block")) {
201            writeUserBlock();
202        }
203        else if (cmd.equals("Display user block as")) {
204            int type = 0;
205            String typeName = (String) ((JComboBox) source).getSelectedItem();
206            jamButton.setEnabled(false);
207            userBlockArea.setEditable(false);
208
209            if (typeName.equalsIgnoreCase("Text")) {
210                type = 0;
211                jamButton.setEnabled(true);
212                userBlockArea.setEditable(true);
213            }
214            else if (typeName.equalsIgnoreCase("Binary")) {
215                type = 2;
216            }
217            else if (typeName.equalsIgnoreCase("Octal")) {
218                type = 8;
219            }
220            else if (typeName.equalsIgnoreCase("Hexadecimal")) {
221                type = 16;
222            }
223            else if (typeName.equalsIgnoreCase("Decimal")) {
224                type = 10;
225            }
226
227            showUserBlockAs(type);
228        }
229    }
230
231    private final void checkLinkTargetChanged() {
232        Group pgroup = null;
233        try {
234            pgroup = (Group) hObject.getFileFormat().get(hObject.getPath());
235        }
236        catch (Exception ex) {
237            log.debug("parent group:", ex);
238        }
239        if (pgroup == null) {
240            JOptionPane.showMessageDialog(this, "Parent group is null.", getTitle(), JOptionPane.ERROR_MESSAGE);
241            return;
242        }
243
244        String target_name = linkField.getText();
245        if (target_name != null) target_name = target_name.trim();
246
247        int linkType = Group.LINK_TYPE_SOFT;
248        if (LinkTObjName.contains(FileFormat.FILE_OBJ_SEP))
249            linkType = Group.LINK_TYPE_EXTERNAL;
250        else if (target_name.equals("/")) // do not allow to link to the root
251            return;
252
253        // no change
254        if (target_name.equals(hObject.getLinkTargetObjName())) return;
255
256        // invalid name
257        if (target_name == null || target_name.length() < 1) return;
258
259        try {
260            fileFormat.createLink(pgroup, hObject.getName(), target_name, linkType);
261            hObject.setLinkTargetObjName(target_name);
262        }
263        catch (Exception ex) {
264            JOptionPane.showMessageDialog(this, ex, getTitle(), JOptionPane.ERROR_MESSAGE);
265        }
266    }
267
268    /** returns the data object displayed in this data viewer */
269    public HObject getDataObject() {
270        return hObject;
271    }
272
273    /** Disposes of this dataobserver. */
274    public void dispose() {
275        super.dispose();
276    }
277
278    /** add an attribute to a data object. */
279    public Attribute addAttribute(HObject obj) {
280        if (obj == null) {
281            return null;
282        }
283
284        DefaultMutableTreeNode node = (DefaultMutableTreeNode) obj.getFileFormat().getRootNode();
285        NewAttributeDialog dialog = new NewAttributeDialog(this, obj, node.breadthFirstEnumeration());
286        dialog.setVisible(true);
287
288        Attribute attr = dialog.getAttribute();
289        if (attr == null) {
290            return null;
291        }
292
293        String rowData[] = new String[4]; // name, value, type, size
294
295        rowData[0] = attr.getName();
296        rowData[2] = attr.getType().getDatatypeDescription();
297
298        rowData[1] = attr.toString(", ");
299
300        long dims[] = attr.getDataDims();
301
302        rowData[3] = String.valueOf(dims[0]);
303        for (int j = 1; j < dims.length; j++) {
304            rowData[3] += " x " + dims[j];
305        }
306
307        attrTableModel.addRow(rowData);
308        attrTableModel.fireTableRowsInserted(attrTableModel.getRowCount() - 1, attrTableModel.getRowCount() - 1);
309        numAttributes++;
310        attrContentArea.setText("");
311        attrNumberLabel.setText("Number of attributes = " + numAttributes);
312
313        return attr;
314    }
315
316    /** delete an attribute from a data object. */
317    public Attribute deleteAttribute(HObject obj) {
318        if (obj == null) {
319            return null;
320        }
321
322        int idx = attrTable.getSelectedRow();
323        if (idx < 0) {
324            JOptionPane.showMessageDialog(getOwner(), "No attribute is selected.", getTitle(),
325                    JOptionPane.ERROR_MESSAGE);
326            return null;
327        }
328
329        int option = JOptionPane.showConfirmDialog(this, "Do you want to delete the selected attribute?", getTitle(),
330                JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
331
332        if (option == JOptionPane.NO_OPTION) {
333            return null;
334        }
335
336        List<?> attrList = null;
337        try {
338            attrList = obj.getMetadata();
339        }
340        catch (Exception ex) {
341            attrList = null;
342        }
343
344        if (attrList == null) {
345            return null;
346        }
347
348        Attribute attr = (Attribute) attrList.get(idx);
349        try {
350            obj.removeMetadata(attr);
351        }
352        catch (Exception ex) {
353            log.debug("delete an attribute from a data object:", ex);
354        }
355
356        attrTableModel.removeRow(idx);
357        numAttributes--;
358        attrTableModel.fireTableRowsDeleted(idx, idx);
359
360        attrContentArea.setText("");
361        attrNumberLabel.setText("Number of attributes = " + numAttributes);
362
363        return attr;
364    }
365
366    /**
367     * Creates a panel used to display general information of HDF object.
368     */
369    private JPanel createGeneralPropertyPanel() {
370        JPanel panel = new JPanel();
371        panel.setLayout(new BorderLayout(10, 10));
372        panel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
373        boolean isRoot = ((hObject instanceof Group) && ((Group) hObject).isRoot());
374        FileFormat theFile = hObject.getFileFormat();
375
376        JPanel topPanel = new JPanel();
377        topPanel.setLayout(new BorderLayout());
378
379        JPanel lp = new JPanel();
380        lp.setLayout(new GridLayout(5, 1));
381
382        if (isRoot) {
383            lp.add(new JLabel("File Name: "));
384            lp.add(new JLabel("File Path: "));
385            lp.add(new JLabel("File Type: "));
386            if (isH5) {
387                try {
388                    libver = hObject.getFileFormat().getLibBounds();
389                }
390                catch (Exception ex) {
391                    ex.printStackTrace();
392                }
393                if (((libver[0] == 0) || (libver[0] == 1)) && (libver[1] == 1))
394                    lp.add(new JLabel("Library version: "));
395            }
396        }
397        else {
398            lp.add(new JLabel("Name: "));
399            if (isH5) {
400                if (hObject.getLinkTargetObjName() != null) lp.add(new JLabel("Link To Target: "));
401            }
402            lp.add(new JLabel("Path: "));
403            lp.add(new JLabel("Type: "));
404
405            /* bug #926 to remove the OID, put it back on Nov. 20, 2008, --PC */
406            if (isH4) {
407                lp.add(new JLabel("Tag, Ref:        "));
408            }
409            else {
410                lp.add(new JLabel("Object Ref:       "));
411            }
412        }
413
414        JPanel rp = new JPanel();
415        rp.setLayout(new GridLayout(5, 1));
416
417        JLabel nameField = new JLabel(hObject.getName());
418        nameField.setName("namefield");
419        rp.add(nameField);
420
421        JPanel targetObjPanel = new JPanel();
422        JButton ChangeTargetObjButton = new JButton("Change");
423        ChangeTargetObjButton.setActionCommand("Change link target");
424        ChangeTargetObjButton.addActionListener(this);
425
426        if (isH5) {
427            if (hObject.getLinkTargetObjName() != null) {
428                linkField = new JTextField(hObject.getLinkTargetObjName());
429                linkField.setName("linkField");
430                targetObjPanel.setLayout(new BorderLayout());
431                targetObjPanel.add(linkField, BorderLayout.CENTER);
432                // targetObjPanel.add(ChangeTargetObjButton, BorderLayout.EAST);
433                rp.add(targetObjPanel);
434            }
435        }
436
437        JLabel pathField = new JLabel();
438        if (isRoot) {
439            pathField.setText((new File(hObject.getFile())).getParent());
440        }
441        else {
442            pathField.setText(hObject.getPath());
443        }
444        rp.add(pathField);
445
446        String typeStr = "Unknown";
447        String fileInfo = "";
448        if (isRoot) {
449            long size = 0;
450            try {
451                size = (new File(hObject.getFile())).length();
452            }
453            catch (Exception ex) {
454                size = -1;
455            }
456            size /= 1024;
457
458            int groupCount = 0, datasetCount = 0;
459            DefaultMutableTreeNode root = (DefaultMutableTreeNode) theFile.getRootNode();
460            DefaultMutableTreeNode theNode = null;
461            Enumeration<?> local_enum = root.depthFirstEnumeration();
462            while (local_enum.hasMoreElements()) {
463                theNode = (DefaultMutableTreeNode) local_enum.nextElement();
464                if (theNode.getUserObject() instanceof Group) {
465                    groupCount++;
466                }
467                else {
468                    datasetCount++;
469                }
470            }
471            fileInfo = "size=" + size + "K,  groups=" + groupCount + ",  datasets=" + datasetCount;
472        }
473
474        if (isRoot) {
475            if (isH5) {
476                typeStr = "HDF5,  " + fileInfo;
477            }
478            else if (isH4) {
479                typeStr = "HDF4,  " + fileInfo;
480            }
481            else {
482                typeStr = fileInfo;
483            }
484        }
485        else if (isH5) {
486            if (hObject instanceof Group) {
487                typeStr = "HDF5 Group";
488            }
489            else if (hObject instanceof ScalarDS) {
490                typeStr = "HDF5 Scalar Dataset";
491            }
492            else if (hObject instanceof CompoundDS) {
493                typeStr = "HDF5 Compound Dataset";
494            }
495            else if (hObject instanceof Datatype) {
496                typeStr = "HDF5 Named Datatype";
497            }
498        }
499        else if (isH4) {
500            if (hObject instanceof Group) {
501                typeStr = "HDF4 Group";
502            }
503            else if (hObject instanceof ScalarDS) {
504                ScalarDS ds = (ScalarDS) hObject;
505                if (ds.isImage()) {
506                    typeStr = "HDF4 Raster Image";
507                }
508                else {
509                    typeStr = "HDF4 SDS";
510                }
511            }
512            else if (hObject instanceof CompoundDS) {
513                typeStr = "HDF4 Vdata";
514            }
515        }
516        else {
517            if (hObject instanceof Group) {
518                typeStr = "Group";
519            }
520            else if (hObject instanceof ScalarDS) {
521                typeStr = "Scalar Dataset";
522            }
523            else if (hObject instanceof CompoundDS) {
524                typeStr = "Compound Dataset";
525            }
526        }
527
528        JLabel typeField = new JLabel(typeStr);
529        rp.add(typeField);
530
531        if (isRoot && isH5) {
532            String libversion = null;
533            if ((libver[0] == 0) && (libver[1] == 1))
534                libversion = "Earliest and Latest";
535            else if ((libver[0] == 1) && (libver[1] == 1)) libversion = "Latest and Latest";
536            JLabel libverbound = new JLabel(libversion);
537            libverbound.setName("libverbound");
538            rp.add(libverbound);
539        }
540
541        /* bug #926 to remove the OID, put it back on Nov. 20, 2008, --PC */
542        String oidStr = null;
543        long[] OID = hObject.getOID();
544        if (OID != null) {
545            oidStr = String.valueOf(OID[0]);
546            for (int i = 1; i < OID.length; i++) {
547                oidStr += ", " + OID[i];
548            }
549        }
550
551        if (!isRoot) {
552            JLabel oidField = new JLabel(oidStr);
553            rp.add(oidField);
554        }
555
556        JPanel tmpP = new JPanel();
557        tmpP.setLayout(new BorderLayout());
558        tmpP.add("West", lp);
559        tmpP.add("Center", rp);
560        tmpP.setBorder(new TitledBorder(""));
561
562        topPanel.add("North", new JLabel(""));
563        topPanel.add("Center", tmpP);
564
565        JPanel infoPanel = null;
566        if (hObject instanceof Group) {
567            infoPanel = createGroupInfoPanel((Group) hObject);
568        }
569        else if (hObject instanceof Dataset) {
570            infoPanel = createDatasetInfoPanel((Dataset) hObject);
571        }
572        else if (hObject instanceof Datatype) {
573            infoPanel = createNamedDatatypeInfoPanel((Datatype) hObject);
574        }
575
576        panel.add(topPanel, BorderLayout.NORTH);
577        if (infoPanel != null) {
578            panel.add(infoPanel, BorderLayout.CENTER);
579        }
580
581        return panel;
582    }
583
584    /**
585     * Creates a panel used to display HDF group information.
586     */
587    private JPanel createGroupInfoPanel(Group g) {
588        JPanel panel = new JPanel();
589
590        List<?> mlist = g.getMemberList();
591        if (mlist == null) {
592            return panel;
593        }
594
595        int n = mlist.size();
596        if (n <= 0) {
597            return panel;
598        }
599
600        String rowData[][] = new String[n][2];
601        for (int i = 0; i < n; i++) {
602            HObject theObj = (HObject) mlist.get(i);
603            rowData[i][0] = theObj.getName();
604            if (theObj instanceof Group) {
605                rowData[i][1] = "Group";
606            }
607            else if (theObj instanceof Dataset) {
608                rowData[i][1] = "Dataset";
609            }
610        }
611
612        String[] columnNames = { "Name", "Type" };
613        JTable table = new JTable(rowData, columnNames) {
614            private static final long serialVersionUID = -834321929059590629L;
615
616            public boolean isCellEditable(int row, int column) {
617                return false;
618            }
619        };
620        table.setName("GroupInfo");
621        table.setCellSelectionEnabled(false);
622
623        // set cell height for large fonts
624        int cellRowHeight = Math.max(16, table.getFontMetrics(table.getFont()).getHeight());
625        table.setRowHeight(cellRowHeight);
626
627        table.getTableHeader().setReorderingAllowed(false);
628        JScrollPane scroller = new JScrollPane(table);
629
630        panel.setLayout(new BorderLayout());
631        if (g.getNumberOfMembersInFile() < ViewProperties.getMaxMembers()) {
632            panel.add(new JLabel("Number of members: " + n), BorderLayout.NORTH);
633        }
634        else {
635            panel.add(new JLabel("Number of members: " + n + " (in memory), " + g.getNumberOfMembersInFile()
636                    + " (in file)"), BorderLayout.NORTH);
637        }
638        panel.add(scroller, BorderLayout.CENTER);
639        panel.setBorder(new TitledBorder("Group Members"));
640
641        return panel;
642    }
643
644    private JPanel createNamedDatatypeInfoPanel(Datatype t) {
645        JPanel panel = new JPanel();
646        panel.setLayout(new BorderLayout());
647        JTextArea infoArea = new JTextArea(t.getDatatypeDescription());
648        infoArea.setEditable(false);
649
650        panel.add(infoArea, BorderLayout.CENTER);
651
652        return panel;
653    }
654
655    /**
656     * Creates a panel used to display HDF dataset information.
657     */
658    private JPanel createDatasetInfoPanel(Dataset d) {
659        JPanel lp = new JPanel();
660        lp.setLayout(new GridLayout(4, 1));
661        lp.add(new JLabel("No. of Dimension(s): "));
662        lp.add(new JLabel("Dimension Size(s): "));
663        lp.add(new JLabel("Max Dimension Size(s): "));
664        lp.add(new JLabel("Data Type: "));
665
666        JPanel rp = new JPanel();
667        rp.setLayout(new GridLayout(4, 1));
668        log.trace("createDatasetInfoPanel: start");
669
670        if (d.getRank() <= 0) {
671            d.init();
672        }
673        JTextField txtf = new JTextField("" + d.getRank());
674        txtf.setName("dimensions");
675        txtf.setEditable(false);
676        rp.add(txtf);
677        log.trace("createDatasetInfoPanel inited");
678
679        String dimStr = null;
680        String maxDimStr = null;
681        long dims[] = d.getDims();
682        long maxDims[] = d.getMaxDims();
683        if (dims != null) {
684            String[] dimNames = d.getDimNames();
685            boolean hasDimNames = ((dimNames != null) && (dimNames.length == dims.length));
686            StringBuffer sb = new StringBuffer();
687            StringBuffer sb2 = new StringBuffer();
688
689            sb.append(dims[0]);
690            if (hasDimNames) {
691                sb.append(" (");
692                sb.append(dimNames[0]);
693                sb.append(")");
694            }
695
696            if (maxDims[0] < 0)
697                sb2.append("Unlimited");
698            else
699                sb2.append(maxDims[0]);
700
701            for (int i = 1; i < dims.length; i++) {
702                sb.append(" x ");
703                sb.append(dims[i]);
704                if (hasDimNames) {
705                    sb.append(" (");
706                    sb.append(dimNames[i]);
707                    sb.append(")");
708                }
709
710                sb2.append(" x ");
711                if (maxDims[i] < 0)
712                    sb2.append("Unlimited");
713                else
714                    sb2.append(maxDims[i]);
715
716            }
717            dimStr = sb.toString();
718            maxDimStr = sb2.toString();
719        }
720        txtf = new JTextField(dimStr);
721        txtf.setName("dimensionsize");
722        txtf.setEditable(false);
723        rp.add(txtf);
724
725        txtf = new JTextField(maxDimStr);
726        txtf.setEditable(false);
727        rp.add(txtf);
728
729        String typeStr = null;
730        if (d instanceof ScalarDS) {
731            ScalarDS sd = (ScalarDS) d;
732            typeStr = sd.getDatatype().getDatatypeDescription();
733        }
734        else if (d instanceof CompoundDS) {
735            if (isH4) {
736                typeStr = "Vdata";
737            }
738            else {
739                typeStr = "Compound";
740            }
741        }
742
743        txtf = new JTextField(typeStr);
744        txtf.setEditable(false);
745        rp.add(txtf);
746
747        JPanel infoP = new JPanel();
748        infoP.setLayout(new BorderLayout());
749        infoP.add(lp, BorderLayout.WEST);
750        infoP.add(rp, BorderLayout.CENTER);
751        infoP.setBorder(new TitledBorder(""));
752
753        JPanel panel = new JPanel();
754        panel.setLayout(new BorderLayout());
755        panel.add(infoP, BorderLayout.NORTH);
756        panel.setBorder(new TitledBorder("Dataspace and Datatype"));
757
758        // add compound datatype information
759        if (d instanceof CompoundDS) {
760            CompoundDS compound = (CompoundDS) d;
761
762            int n = compound.getMemberCount();
763            if (n > 0) {
764                String rowData[][] = new String[n][3];
765                String names[] = compound.getMemberNames();
766                Datatype types[] = compound.getMemberTypes();
767                int orders[] = compound.getMemberOrders();
768
769                for (int i = 0; i < n; i++) {
770                    rowData[i][0] = names[i];
771                    int mDims[] = compound.getMemberDims(i);
772                    if (mDims == null) {
773                        rowData[i][2] = String.valueOf(orders[i]);
774
775                        if (isH4 && types[i].getDatatypeClass() == Datatype.CLASS_STRING) {
776                            rowData[i][2] = String.valueOf(types[i].getDatatypeSize());
777                        }
778                    }
779                    else {
780                        String mStr = String.valueOf(mDims[0]);
781                        int m = mDims.length;
782                        for (int j = 1; j < m; j++) {
783                            mStr += " x " + mDims[j];
784                        }
785                        rowData[i][2] = mStr;
786                    }
787                    rowData[i][1] = types[i].getDatatypeDescription();
788                }
789
790                String[] columnNames = { "Name", "Type", "Array Size" };
791                JTable table = new JTable(rowData, columnNames) {
792                    private static final long serialVersionUID = -1517773307922536859L;
793
794                    public boolean isCellEditable(int row, int column) {
795                        return false;
796                    }
797                };
798                table.setName("CompoundMetaData");
799                table.setCellSelectionEnabled(false);
800                table.getTableHeader().setReorderingAllowed(false);
801                panel.add(new JScrollPane(table), BorderLayout.CENTER);
802
803                // set cell height for large fonts
804                int cellRowHeight = Math.max(16, table.getFontMetrics(table.getFont()).getHeight());
805                table.setRowHeight(cellRowHeight);
806            } // if (n > 0)
807        } // if (d instanceof Compound)
808
809        // // add compression and data layout information
810        // try { d.getMetadata(); } catch (Exception ex) {}
811        String chunkInfo = "";
812        long[] chunks = d.getChunkSize();
813        if (chunks == null) {
814            chunkInfo = "NONE";
815        }
816        else {
817            int n = chunks.length;
818            chunkInfo = String.valueOf(chunks[0]);
819            for (int i = 1; i < n; i++) {
820                chunkInfo += " X " + chunks[i];
821            }
822        }
823
824        JPanel bPanel = new JPanel();
825        bPanel.setBorder(new TitledBorder(""));
826        bPanel.setLayout(new BorderLayout());
827        lp = new JPanel();
828        lp.setName("genmetainfo");
829        lp.setLayout(new GridLayout(5, 1));
830        lp.add(new JLabel("Chunking: "));
831        lp.add(new JLabel("Compression: "));
832        lp.add(new JLabel("Filters: "));
833        lp.add(new JLabel("Storage: "));
834        lp.add(new JLabel("Fill value: "));
835        bPanel.add(lp, BorderLayout.WEST);
836
837        Object fillValue = null;
838        String fillValueInfo = "NONE";
839        if (d instanceof ScalarDS) fillValue = ((ScalarDS) d).getFillValue();
840        if (fillValue != null) {
841            if (fillValue.getClass().isArray()) {
842                int len = Array.getLength(fillValue);
843                fillValueInfo = Array.get(fillValue, 0).toString();
844                for (int i = 1; i < len; i++) {
845                    fillValueInfo += ", ";
846                    fillValueInfo += Array.get(fillValue, i).toString();
847                }
848            }
849            else
850                fillValueInfo = fillValue.toString();
851        }
852
853        rp = new JPanel();
854        rp.setName("genmetadata");
855        rp.setLayout(new GridLayout(5, 1));
856        JLabel rpchunk = new JLabel(chunkInfo);
857        rpchunk.setName("chunkdata");
858        rp.add(rpchunk);
859        JLabel rpcomp = new JLabel(d.getCompression());
860        rpcomp.setName("compressiondata");
861        rp.add(rpcomp);
862        JLabel rpfilt = new JLabel(d.getFilters());
863        rpfilt.setName("filterdata");
864        rp.add(rpfilt);
865        JLabel rpstore = new JLabel(d.getStorage());
866        rpstore.setName("storagedata");
867        rp.add(rpstore);
868        JLabel rpfillval = new JLabel(fillValueInfo);
869        rpfillval.setName("fillvaluedata");
870        rp.add(rpfillval);
871        bPanel.add(rp, BorderLayout.CENTER);
872
873        panel.add(bPanel, BorderLayout.SOUTH);
874        log.trace("createDatasetInfoPanel: finish");
875
876        return panel;
877    }
878
879    /**
880     * Creates a panel used to display attribute information.
881     */
882    private JPanel createAttributePanel() {
883        JPanel panel = new JPanel();
884        panel.setLayout(new BorderLayout());
885        // panel.setBorder(BorderFactory.createEmptyBorder(10,0,0,0));
886
887        JPanel topPanel = new JPanel();
888        topPanel.setLayout(new BorderLayout());
889        attrNumberLabel = new JLabel("Number of attributes = 0");
890        topPanel.add(attrNumberLabel, BorderLayout.WEST);
891
892        FileFormat theFile = hObject.getFileFormat();
893        JPanel bPanel = new JPanel();
894        JButton b = new JButton(" Add ");
895        b.setName("Add");
896        b.setMnemonic('A');
897        b.addActionListener(this);
898        b.setActionCommand("Add attribute");
899        bPanel.add(b);
900        b.setEnabled(!theFile.isReadOnly());
901
902        if (isH5) {
903            // deleting is not supported by HDF4
904            b = new JButton("Delete");
905            b.setName("Delete");
906            b.setMnemonic('D');
907            b.addActionListener(this);
908            b.setActionCommand("Delete attribute");
909            bPanel.add(b);
910            b.setEnabled(!theFile.isReadOnly());
911        }
912        topPanel.add(bPanel, BorderLayout.EAST);
913
914        panel.add(topPanel, BorderLayout.NORTH);
915
916        List<?> attrList = null;
917        log.trace("createAttributePanel: start");
918
919        try {
920            attrList = hObject.getMetadata();
921        }
922        catch (Exception ex) {
923            attrList = null;
924        }
925        if (attrList != null) {
926            numAttributes = attrList.size();
927        }
928        log.trace("createAttributePanel:  isH5={} numAttributes={}", isH5, numAttributes);
929
930        String[] columnNames = { "Name", "Value", "Type", "Array Size" };
931        attrTableModel = new DefaultTableModel(columnNames, numAttributes);
932
933        attrTable = new JTable(attrTableModel) {
934            private static final long serialVersionUID = 2590244645972259454L;
935            int                       lastSelectedRow  = -1;
936            int                       lastSelectedCol  = -1;
937
938            public boolean isCellEditable(int row, int column) {
939                return ((column == 1) || (isH5 && (column == 0)));
940                // only attribute value and name can be changed
941            }
942
943            public void editingStopped(ChangeEvent e) {
944                int row = getEditingRow();
945                int col = getEditingColumn();
946                String oldValue = (String) getValueAt(row, col);
947
948                super.editingStopped(e);
949
950                Object source = e.getSource();
951
952                if (source instanceof CellEditor) {
953                    CellEditor editor = (CellEditor) source;
954                    String newValue = (String) editor.getCellEditorValue();
955                    setValueAt(oldValue, row, col); // set back to what it is
956                    updateAttributeValue(newValue, row, col);
957                }
958            }
959
960            public boolean isCellSelected(int row, int col) {
961
962                if ((getSelectedRow() == row) && (getSelectedColumn() == col)
963                        && !((lastSelectedRow == row) && (lastSelectedCol == col))) {
964                    // selection is changed
965                    Object attrV = getValueAt(row, col);
966                    if (attrV != null) {
967                        attrContentArea.setText(attrV.toString());
968                    }
969                    lastSelectedRow = row;
970                    lastSelectedCol = col;
971                }
972
973                return super.isCellSelected(row, col);
974            }
975        };
976
977        attrTable.setName("attributes");
978        attrTable.setRowSelectionAllowed(false);
979        attrTable.setCellSelectionEnabled(true);
980        attrTable.getTableHeader().setReorderingAllowed(false);
981        attrTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
982
983        // set cell height for large fonts
984        int cellRowHeight = Math.max(16, attrTable.getFontMetrics(attrTable.getFont()).getHeight());
985        attrTable.setRowHeight(cellRowHeight);
986
987        JScrollPane scroller1 = new JScrollPane(attrTable);
988        attrContentArea = new JTextArea();
989        attrContentArea.setLineWrap(true);
990        attrContentArea.setEditable(false);
991        Insets m = new Insets(5, 5, 5, 5);
992        attrContentArea.setMargin(m);
993
994        JScrollPane scroller2 = new JScrollPane(attrContentArea);
995        JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, scroller1, scroller2);
996
997        // set the divider location
998        int h = Math.min((numAttributes + 2) * attrTable.getRowHeight(), scroller1.getPreferredSize().height - 40);
999        splitPane.setDividerLocation(h);
1000        panel.add(splitPane, BorderLayout.CENTER);
1001
1002        if (attrList == null) {
1003            log.trace("createAttributePanel:  attrList == null");
1004            return panel;
1005        }
1006
1007        Attribute attr = null;
1008        String name, type, size;
1009        attrNumberLabel.setText("Number of attributes = " + numAttributes);
1010        for (int i = 0; i < numAttributes; i++) {
1011            attr = (Attribute) attrList.get(i);
1012            name = attr.getName();
1013            type = attr.getType().getDatatypeDescription();
1014            log.trace("createAttributePanel:  attr[{}] is {} as {}", i, name, type);
1015
1016            if (attr.isScalar()) {
1017                size = "Scalar";
1018            }
1019            else {
1020                long dims[] = attr.getDataDims();
1021                size = String.valueOf(dims[0]);
1022                for (int j = 1; j < dims.length; j++) {
1023                    size += " x " + dims[j];
1024                }
1025            }
1026
1027            if (attr.getProperty("field") !=null) {
1028                String fieldInfo = " {Field: "+attr.getProperty("field")+"}";
1029                attrTable.setValueAt(name+fieldInfo, i, 0);
1030            }
1031            else
1032                attrTable.setValueAt(name, i, 0);
1033
1034            attrTable.setValueAt(attr.toString(", "), i, 1);
1035            attrTable.setValueAt(type, i, 2);
1036            attrTable.setValueAt(size, i, 3);
1037        } // for (int i=0; i<n; i++)
1038
1039        log.trace("createAttributePanel: finish");
1040        return panel;
1041    }
1042
1043    /**
1044     * Creates a panel used to display HDF5 user block.
1045     */
1046    @SuppressWarnings({ "rawtypes", "unchecked" })
1047    private JPanel createUserBlockPanel() {
1048        JPanel panel = new JPanel();
1049
1050        userBlock = DefaultFileFilter.getHDF5UserBlock(hObject.getFile());
1051
1052        panel.setLayout(new BorderLayout(10, 10));
1053        panel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
1054        userBlockArea = new JTextArea();
1055        userBlockArea.setEditable(true);
1056        userBlockArea.setLineWrap(true);
1057        Insets m = new Insets(5, 5, 5, 5);
1058        userBlockArea.setMargin(m);
1059
1060        String[] displayChoices = { "Text", "Binary", "Octal", "Hexadecimal", "Decimal" };
1061        JComboBox userBlockDisplayChoice = new JComboBox(displayChoices);
1062        userBlockDisplayChoice.setSelectedIndex(0);
1063        userBlockDisplayChoice.addActionListener(this);
1064        userBlockDisplayChoice.setEditable(false);
1065        userBlockDisplayChoice.setActionCommand("Display user block as");
1066
1067        JLabel sizeLabel = new JLabel("Header Size (Bytes): 0");
1068        jamButton = new JButton("Save User Block");
1069        jamButton.setActionCommand("Jam user block");
1070        jamButton.addActionListener(this);
1071        JPanel topPane = new JPanel();
1072        topPane.setLayout(new BorderLayout(10, 5));
1073        topPane.add(new JLabel("Display As:"), BorderLayout.WEST);
1074        JPanel topPane0 = new JPanel();
1075        topPane0.setLayout(new GridLayout(1, 2, 10, 5));
1076        topPane0.add(userBlockDisplayChoice);
1077        topPane0.add(sizeLabel);
1078        topPane.add(topPane0, BorderLayout.CENTER);
1079        topPane.add(jamButton, BorderLayout.EAST);
1080
1081        JScrollPane scroller = new JScrollPane(userBlockArea);
1082        panel.add(topPane, BorderLayout.NORTH);
1083        panel.add(scroller, BorderLayout.CENTER);
1084
1085        int headSize = 0;
1086        if (userBlock != null) {
1087            headSize = showUserBlockAs(0);
1088            sizeLabel.setText("Header Size (Bytes): " + headSize);
1089        }
1090        else {
1091            userBlockDisplayChoice.setEnabled(false);
1092        }
1093
1094        return panel;
1095    }
1096
1097    private int showUserBlockAs(int radix) {
1098        int headerSize = 0;
1099
1100        if (userBlock == null) {
1101            return 0;
1102        }
1103
1104        String userBlockInfo = null;
1105        if ((radix == 2) || (radix == 8) || (radix == 16) || (radix == 10)) {
1106            StringBuffer sb = new StringBuffer();
1107            for (headerSize = 0; headerSize < userBlock.length; headerSize++) {
1108                int intValue = (int) userBlock[headerSize];
1109                if (intValue < 0) {
1110                    intValue += 256;
1111                }
1112                else if (intValue == 0) {
1113                    break; // null end
1114                }
1115                sb.append(Integer.toString(intValue, radix));
1116                sb.append(" ");
1117            }
1118            userBlockInfo = sb.toString();
1119        }
1120        else {
1121            userBlockInfo = new String(userBlock).trim();
1122            if (userBlockInfo != null) {
1123                headerSize = userBlockInfo.length();
1124            }
1125        }
1126
1127        userBlockArea.setText(userBlockInfo);
1128
1129        return headerSize;
1130    }
1131
1132    /**
1133     * update attribute value. Currently can only update single data point.
1134     *
1135     * @param newValue
1136     *            the string of the new value.
1137     * @param row
1138     *            the row number of the selected cell.
1139     * @param col
1140     *            the column number of the selected cell.
1141     */
1142    private void updateAttributeValue(String newValue, int row, int col) {
1143        log.trace("updateAttributeValue:start value={}[{},{}]", newValue, row, col);
1144
1145        String attrName = (String) attrTable.getValueAt(row, 0);
1146        List<?> attrList = null;
1147        try {
1148            attrList = hObject.getMetadata();
1149        }
1150        catch (Exception ex) {
1151            JOptionPane.showMessageDialog(getOwner(), ex.getMessage(), getTitle(), JOptionPane.ERROR_MESSAGE);
1152            return;
1153        }
1154
1155        Attribute attr = (Attribute) attrList.get(row);
1156
1157        if (col == 1) { // To change attribute value
1158            log.trace("updateAttributeValue: change attribute value");
1159            Object data = attr.getValue();
1160            if (data == null) {
1161                return;
1162            }
1163
1164            int array_length = Array.getLength(data);
1165            StringTokenizer st = new StringTokenizer(newValue, ",");
1166            if (st.countTokens() < array_length) {
1167                JOptionPane.showMessageDialog(getOwner(), "More data value needed: " + newValue, getTitle(),
1168                        JOptionPane.ERROR_MESSAGE);
1169                return;
1170            }
1171
1172            char NT = ' ';
1173            String cName = data.getClass().getName();
1174            int cIndex = cName.lastIndexOf("[");
1175            if (cIndex >= 0) {
1176                NT = cName.charAt(cIndex + 1);
1177            }
1178            boolean isUnsigned = attr.isUnsigned();
1179            log.trace("updateAttributeValue:start array_length={} cName={} NT={} isUnsigned={}", array_length, cName, NT, isUnsigned);
1180
1181            double d = 0;
1182            String theToken = null;
1183            long max = 0, min = 0;
1184            for (int i = 0; i < array_length; i++) {
1185                max = min = 0;
1186                theToken = st.nextToken().trim();
1187                try {
1188                    if (!(Array.get(data, i) instanceof String)) {
1189                        d = Double.parseDouble(theToken);
1190                    }
1191                }
1192                catch (NumberFormatException ex) {
1193                    JOptionPane.showMessageDialog(getOwner(), ex.getMessage(), getTitle(), JOptionPane.ERROR_MESSAGE);
1194                    return;
1195                }
1196
1197                if (isUnsigned && (d < 0)) {
1198                    JOptionPane.showMessageDialog(getOwner(), "Negative value for unsigned integer: " + theToken,
1199                            getTitle(), JOptionPane.ERROR_MESSAGE);
1200                    return;
1201                }
1202
1203                switch (NT) {
1204                    case 'B': {
1205                        if (isUnsigned) {
1206                            min = 0;
1207                            max = 255;
1208                        }
1209                        else {
1210                            min = Byte.MIN_VALUE;
1211                            max = Byte.MAX_VALUE;
1212                        }
1213
1214                        if ((d > max) || (d < min)) {
1215                            JOptionPane.showMessageDialog(getOwner(), "Data is out of range[" + min + ", " + max
1216                                    + "]: " + theToken, getTitle(), JOptionPane.ERROR_MESSAGE);
1217                        }
1218                        else {
1219                            Array.setByte(data, i, (byte) d);
1220                        }
1221                        break;
1222                    }
1223                    case 'S': {
1224                        if (isUnsigned) {
1225                            min = 0;
1226                            max = 65535;
1227                        }
1228                        else {
1229                            min = Short.MIN_VALUE;
1230                            max = Short.MAX_VALUE;
1231                        }
1232
1233                        if ((d > max) || (d < min)) {
1234                            JOptionPane.showMessageDialog(getOwner(), "Data is out of range[" + min + ", " + max
1235                                    + "]: " + theToken, getTitle(), JOptionPane.ERROR_MESSAGE);
1236                        }
1237                        else {
1238                            Array.setShort(data, i, (short) d);
1239                        }
1240                        break;
1241                    }
1242                    case 'I': {
1243                        if (isUnsigned) {
1244                            min = 0;
1245                            max = 4294967295L;
1246                        }
1247                        else {
1248                            min = Integer.MIN_VALUE;
1249                            max = Integer.MAX_VALUE;
1250                        }
1251
1252                        if ((d > max) || (d < min)) {
1253                            JOptionPane.showMessageDialog(getOwner(), "Data is out of range[" + min + ", " + max
1254                                    + "]: " + theToken, getTitle(), JOptionPane.ERROR_MESSAGE);
1255                        }
1256                        else {
1257                            Array.setInt(data, i, (int) d);
1258                        }
1259                        break;
1260                    }
1261                    case 'J':
1262                        long lvalue = 0;
1263                        if (isUnsigned) {
1264                            if (theToken != null) {
1265                                String theValue = theToken;
1266                                BigInteger Jmax = new BigInteger("18446744073709551615");
1267                                BigInteger big = new BigInteger(theValue);
1268                                if ((big.compareTo(Jmax)>0) || (big.compareTo(BigInteger.ZERO)<0)) {
1269                                    JOptionPane.showMessageDialog(getOwner(), "Data is out of range[" + min + ", " + max
1270                                            + "]: " + theToken, getTitle(), JOptionPane.ERROR_MESSAGE);
1271                                }
1272                                lvalue = big.longValue();
1273                                log.trace("updateAttributeValue: big.longValue={}", lvalue);
1274                                Array.setLong(data, i, lvalue);
1275                            }
1276                            else
1277                                Array.set(data, i, (Object)theToken);
1278                        }
1279                        else {
1280                            min = Long.MIN_VALUE;
1281                            max = Long.MAX_VALUE;
1282                            if ((d > max) || (d < min)) {
1283                                JOptionPane.showMessageDialog(getOwner(), "Data is out of range[" + min + ", " + max
1284                                        + "]: " + theToken, getTitle(), JOptionPane.ERROR_MESSAGE);
1285                            }
1286                            lvalue = (long)d;
1287                            log.trace("updateAttributeValue: longValue={}", lvalue);
1288                            Array.setLong(data, i, lvalue);
1289                        }
1290                        break;
1291                    case 'F':
1292                        Array.setFloat(data, i, (float) d);
1293                        break;
1294                    case 'D':
1295                        Array.setDouble(data, i, d);
1296                        break;
1297                    default:
1298                        Array.set(data, i, (Object)theToken);
1299                        break;
1300                }
1301            }
1302
1303            try {
1304                hObject.getFileFormat().writeAttribute(hObject, attr, true);
1305            }
1306            catch (Exception ex) {
1307                JOptionPane.showMessageDialog(getOwner(), ex.getMessage(), getTitle(), JOptionPane.ERROR_MESSAGE);
1308                return;
1309            }
1310
1311            // update the attribute table
1312            attrTable.setValueAt(attr.toString(", "), row, 1);
1313        }
1314
1315        if ((col == 0) && isH5) { // To change attribute name
1316            log.trace("updateAttributeValue: change attribute name");
1317            try {
1318                hObject.getFileFormat().renameAttribute(hObject, attrName, newValue);
1319            }
1320            catch (Exception ex) {
1321                JOptionPane.showMessageDialog(getOwner(), ex.getMessage(), getTitle(), JOptionPane.ERROR_MESSAGE);
1322                return;
1323            }
1324
1325            // update the attribute table
1326            attrTable.setValueAt(newValue, row, 0);
1327        }
1328        if (hObject instanceof ScalarDS) {
1329            ScalarDS ds = (ScalarDS) hObject;
1330            try {
1331                log.trace("updateAttributeValue: ScalarDS:updateMetadata");
1332                ds.updateMetadata(attr);
1333            }
1334            catch (Exception ex) {
1335                JOptionPane.showMessageDialog(getOwner(), ex.getMessage(), getTitle(), JOptionPane.ERROR_MESSAGE);
1336            }
1337        }
1338        else {
1339            log.trace("updateAttributeValue: hObject is not instanceof ScalarDS");
1340        }
1341    }
1342
1343    private void writeUserBlock() {
1344        if (!isH5) {
1345            return;
1346        }
1347
1348        int blkSize0 = 0;
1349        if (userBlock != null) {
1350            blkSize0 = userBlock.length;
1351            // The super block space is allocated by offset 0, 512, 1024, 2048,
1352            // etc
1353            if (blkSize0 > 0) {
1354                int offset = 512;
1355                while (offset < blkSize0) {
1356                    offset *= 2;
1357                }
1358                blkSize0 = offset;
1359            }
1360        }
1361
1362        int blkSize1 = 0;
1363        String userBlockStr = userBlockArea.getText();
1364        if (userBlockStr == null) {
1365            if (blkSize0 <= 0) {
1366                return; // nothing to write
1367            }
1368            else {
1369                userBlockStr = " "; // want to wipe out old userblock content
1370            }
1371        }
1372        byte buf[] = null;
1373        buf = userBlockStr.getBytes();
1374
1375        blkSize1 = buf.length;
1376        if (blkSize1 <= blkSize0) {
1377            java.io.RandomAccessFile raf = null;
1378            try {
1379                raf = new java.io.RandomAccessFile(hObject.getFile(), "rw");
1380            }
1381            catch (Exception ex) {
1382                JOptionPane.showMessageDialog(this, "Can't open output file: " + hObject.getFile(), getTitle(),
1383                        JOptionPane.ERROR_MESSAGE);
1384                return;
1385            }
1386
1387            try {
1388                raf.seek(0);
1389                raf.write(buf, 0, buf.length);
1390                raf.seek(buf.length);
1391                if (blkSize0 > buf.length) {
1392                    byte[] padBuf = new byte[blkSize0 - buf.length];
1393                    raf.write(padBuf, 0, padBuf.length);
1394                }
1395            }
1396            catch (Exception ex) {
1397                log.debug("raf write:", ex);
1398            }
1399            try {
1400                raf.close();
1401            }
1402            catch (Exception ex) {
1403                log.debug("raf close:", ex);
1404            }
1405
1406            JOptionPane.showMessageDialog(this, "Saving user block is successful.", getTitle(),
1407                    JOptionPane.INFORMATION_MESSAGE);
1408        }
1409        else {
1410            // must rewrite the whole file
1411            // must rewrite the whole file
1412            int op = JOptionPane.showConfirmDialog(this,
1413                    "The user block to write is " + blkSize1 + " (bytes),\n"
1414                            + "which is larger than the user block space in file " + blkSize0 + " (bytes).\n"
1415                            + "To expand the user block, the file must be rewriten.\n\n"
1416                            + "Do you want to replace the current file? Click "
1417                            + "\n\"Yes\" to replace the current file," + "\n\"No\" to save to a different file, "
1418                            + "\n\"Cancel\" to quit without saving the change.\n\n ", getTitle(),
1419                            JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
1420
1421            if (op == JOptionPane.CANCEL_OPTION) {
1422                return;
1423            }
1424
1425            String fin = hObject.getFile();
1426
1427            String fout = fin + "~copy.h5";
1428            if (fin.endsWith(".h5")) {
1429                fout = fin.substring(0, fin.length() - 3) + "~copy.h5";
1430            }
1431            else if (fin.endsWith(".hdf5")) {
1432                fout = fin.substring(0, fin.length() - 5) + "~copy.h5";
1433            }
1434
1435            File outFile = null;
1436
1437            if (op == JOptionPane.NO_OPTION) {
1438                JFileChooser fchooser = new JFileChooser();
1439                fchooser.setFileFilter(DefaultFileFilter.getFileFilterHDF5());
1440                fchooser.setSelectedFile(new File(fout));
1441
1442                int returnVal = fchooser.showSaveDialog(this);
1443                if (returnVal != JFileChooser.APPROVE_OPTION) {
1444                    return;
1445                }
1446
1447                File choosedFile = fchooser.getSelectedFile();
1448                if (choosedFile == null) {
1449                    return;
1450                }
1451
1452                outFile = choosedFile;
1453                fout = outFile.getAbsolutePath();
1454            }
1455            else {
1456                outFile = new File(fout);
1457            }
1458
1459            if (!outFile.exists()) {
1460                try {
1461                    outFile.createNewFile();
1462                }
1463                catch (Exception ex) {
1464                    JOptionPane.showMessageDialog(this, "Fail to write user block into file. ", getTitle(),
1465                            JOptionPane.ERROR_MESSAGE);
1466                    return;
1467                }
1468            }
1469
1470            // close the file
1471            ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Close file");
1472            ((HDFView) viewer).actionPerformed(e);
1473
1474            if (DefaultFileFilter.setHDF5UserBlock(fin, fout, buf)) {
1475                if (op == JOptionPane.NO_OPTION) {
1476                    fin = fout; // open the new file
1477                }
1478                else {
1479                    File oldFile = new File(fin);
1480                    boolean status = oldFile.delete();
1481                    if (status) {
1482                        outFile.renameTo(oldFile);
1483                    }
1484                    else {
1485                        JOptionPane.showMessageDialog(this,
1486                                "Cannot replace the current file.\nPlease save to a different file.", getTitle(),
1487                                JOptionPane.ERROR_MESSAGE);
1488                        outFile.delete();
1489                    }
1490                }
1491            }
1492            else {
1493                JOptionPane.showMessageDialog(this, "Fail to write user block into file. ", getTitle(),
1494                        JOptionPane.ERROR_MESSAGE);
1495                outFile.delete();
1496            }
1497
1498            // reopen the file
1499            dispose();
1500            e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Open file://" + fin);
1501            ((HDFView) viewer).actionPerformed(e);
1502        }
1503    }
1504
1505}