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