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