001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see https://support.hdfgroup.org/products/licenses.html               *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view.MetaDataView;
016
017import java.io.File;
018import java.lang.reflect.Array;
019import java.math.BigInteger;
020import java.util.Iterator;
021import java.util.List;
022import java.util.StringTokenizer;
023
024import org.eclipse.jface.dialogs.MessageDialog;
025import org.eclipse.swt.SWT;
026import org.eclipse.swt.custom.ScrolledComposite;
027import org.eclipse.swt.events.MenuAdapter;
028import org.eclipse.swt.events.MenuEvent;
029import org.eclipse.swt.events.SelectionAdapter;
030import org.eclipse.swt.events.SelectionEvent;
031import org.eclipse.swt.graphics.Font;
032import org.eclipse.swt.graphics.Point;
033import org.eclipse.swt.graphics.Rectangle;
034import org.eclipse.swt.layout.GridData;
035import org.eclipse.swt.layout.GridLayout;
036import org.eclipse.swt.widgets.Button;
037import org.eclipse.swt.widgets.Combo;
038import org.eclipse.swt.widgets.Composite;
039import org.eclipse.swt.widgets.Dialog;
040import org.eclipse.swt.widgets.Display;
041import org.eclipse.swt.widgets.Event;
042import org.eclipse.swt.widgets.FileDialog;
043import org.eclipse.swt.widgets.Label;
044import org.eclipse.swt.widgets.Listener;
045import org.eclipse.swt.widgets.Menu;
046import org.eclipse.swt.widgets.MenuItem;
047import org.eclipse.swt.widgets.Shell;
048import org.eclipse.swt.widgets.TabFolder;
049import org.eclipse.swt.widgets.TabItem;
050import org.eclipse.swt.widgets.Table;
051import org.eclipse.swt.widgets.TableColumn;
052import org.eclipse.swt.widgets.TableItem;
053import org.eclipse.swt.widgets.Text;
054
055import hdf.hdf5lib.H5;
056import hdf.hdf5lib.HDF5Constants;
057import hdf.object.Attribute;
058import hdf.object.CompoundDS;
059import hdf.object.Dataset;
060import hdf.object.Datatype;
061import hdf.object.FileFormat;
062import hdf.object.Group;
063import hdf.object.HObject;
064import hdf.object.MetaDataContainer;
065import hdf.object.ScalarDS;
066import hdf.view.DefaultFileFilter;
067import hdf.view.Tools;
068import hdf.view.ViewProperties;
069import hdf.view.DataView.DataViewManager;
070import hdf.view.TreeView.DefaultTreeView;
071import hdf.view.TreeView.TreeView;
072import hdf.view.dialog.InputDialog;
073import hdf.view.dialog.NewAttributeDialog;
074
075/**
076 * DefaultBaseMetaDataView is a default implementation of the MetaDataView which
077 * is used to show data properties of an object. Data properties include
078 * attributes and general object information such as the object type, data type
079 * and data space.
080 *
081 * This base class is responsible for displaying an object's general information
082 * and attributes, since these are not object-specific. Subclasses of this class
083 * are responsible for displaying any extra object-specific content by
084 * overriding the addObjectSpecificContent() method.
085 *
086 * @author Jordan T. Henderson
087 * @version 1.0 4/20/2018
088 */
089public abstract class DefaultBaseMetaDataView implements MetaDataView {
090
091    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DefaultBaseMetaDataView.class);
092
093    protected final Display               display = Display.getDefault();
094
095    protected final DataViewManager       viewManager;
096
097    private final Composite               parent;
098
099    protected final TabFolder             contentTabFolder;
100
101    protected final Composite             attributeInfoPane;
102
103    protected final Composite             generalObjectInfoPane;
104
105    protected Font                        curFont;
106
107    /** The HDF data object */
108    protected HObject                     dataObject;
109
110    /* The table to hold the list of attributes attached to the HDF object */
111    private Table                         attrTable;
112
113    private Label                         attrNumberLabel;
114
115    private List<?>                       attrList;
116
117    private int                           numAttributes;
118
119    protected boolean                     isH5, isH4, isN3;
120
121    private static final String[]         attrTableColNames = { "Name", "Type", "Array Size", "Value[50](...)" };
122
123    private static final int              ATTR_TAB_INDEX = 0;
124    private static final int              GENERAL_TAB_INDEX = 1;
125
126    public DefaultBaseMetaDataView(Composite parentComposite, DataViewManager viewer, HObject theObj) {
127        this.parent = parentComposite;
128        this.viewManager = viewer;
129        this.dataObject = theObj;
130
131        numAttributes = 0;
132
133        isH5 = dataObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5));
134        isH4 = dataObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4));
135        isN3 = dataObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3));
136
137        try {
138            curFont = new Font(display, ViewProperties.getFontType(), ViewProperties.getFontSize(), SWT.NORMAL);
139        }
140        catch (Exception ex) {
141            curFont = null;
142        }
143
144        /* Get the metadata information before adding GUI components */
145        try {
146            attrList = ((MetaDataContainer) dataObject).getMetadata();
147            if (attrList != null) numAttributes = attrList.size();
148        }
149        catch (Exception ex) {
150            attrList = null;
151            log.debug("Error retrieving metadata of object '" + dataObject.getName() + "':", ex);
152        }
153
154        log.trace("dataObject={} isN3={} isH4={} isH5={} numAttributes={}", dataObject, isN3, isH4, isH5, numAttributes);
155
156        contentTabFolder = new TabFolder(parent, SWT.NONE);
157        contentTabFolder.addSelectionListener(new SelectionAdapter() {
158            @Override
159            public void widgetSelected(SelectionEvent e) {
160                switch (contentTabFolder.getSelectionIndex()) {
161                    case ATTR_TAB_INDEX:
162                        parent.setData("MetaDataView.LastTabIndex", ATTR_TAB_INDEX);
163                        break;
164                    case GENERAL_TAB_INDEX:
165                    default:
166                        parent.setData("MetaDataView.LastTabIndex", GENERAL_TAB_INDEX);
167                        break;
168                }
169            }
170        });
171
172        attributeInfoPane = createAttributeInfoPane(contentTabFolder, dataObject);
173        if (attributeInfoPane != null) {
174            TabItem attributeInfoItem = new TabItem(contentTabFolder, SWT.None, ATTR_TAB_INDEX);
175            attributeInfoItem.setText("Object Attribute Info");
176            attributeInfoItem.setControl(attributeInfoPane);
177        }
178
179        generalObjectInfoPane = createGeneralObjectInfoPane(contentTabFolder, dataObject);
180        if (generalObjectInfoPane != null) {
181            TabItem generalInfoItem = new TabItem(contentTabFolder, SWT.None, GENERAL_TAB_INDEX);
182            generalInfoItem.setText("General Object Info");
183            generalInfoItem.setControl(generalObjectInfoPane);
184        }
185
186        /* Add any extra information depending on the object type */
187        try {
188            addObjectSpecificContent();
189        }
190        catch (UnsupportedOperationException ex) {
191        }
192
193        if (parent instanceof ScrolledComposite)
194            ((ScrolledComposite) parent).setContent(contentTabFolder);
195
196        /*
197         * If the MetaDataView.LastTabIndex key data exists in the parent
198         * composite, retrieve its value to determine which remembered
199         * tab to select.
200         */
201        Object lastTabObject = parent.getData("MetaDataView.LastTabIndex");
202        if (lastTabObject != null) {
203            contentTabFolder.setSelection((int) lastTabObject);
204        }
205    }
206
207    protected abstract void addObjectSpecificContent();
208
209    private Composite createAttributeInfoPane(Composite parent, final HObject dataObject) {
210        if (parent == null || dataObject == null) return null;
211
212        org.eclipse.swt.widgets.Group attributeInfoGroup = null;
213
214        attributeInfoGroup = new org.eclipse.swt.widgets.Group(parent, SWT.NONE);
215        attributeInfoGroup.setFont(curFont);
216        attributeInfoGroup.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
217        attributeInfoGroup.setLayout(new GridLayout(3, false));
218        attributeInfoGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
219
220        if (isH5) {
221            StringBuilder objCreationStr = new StringBuilder("Creation Order NOT Tracked");
222            long ocplID = -1;
223            long oid = -1;
224            int creationOrder = 0;
225            try {
226                oid = dataObject.open();
227                if (oid >= 0) {
228                    if (dataObject instanceof Group) {
229                        ocplID = H5.H5Gget_create_plist(oid);
230                    }
231                    else if (dataObject instanceof Dataset) {
232                        ocplID = H5.H5Dget_create_plist(oid);
233                    }
234                    if (ocplID >= 0) {
235                        creationOrder = H5.H5Pget_attr_creation_order(ocplID);
236                        log.trace("createAttributeInfoPane(): creationOrder={}", creationOrder);
237                        if ((creationOrder & HDF5Constants.H5P_CRT_ORDER_TRACKED) > 0) {
238                            objCreationStr.setLength(0);
239                            objCreationStr.append("Creation Order Tracked");
240                            if ((creationOrder & HDF5Constants.H5P_CRT_ORDER_INDEXED) > 0)
241                                objCreationStr.append(" and Indexed");
242                        }
243                    }
244                }
245            }
246            finally {
247                H5.H5Pclose(ocplID);
248                dataObject.close(oid);
249            }
250
251            /* Creation order section */
252            Label label;
253            label = new Label(attributeInfoGroup, SWT.LEFT);
254            label.setFont(curFont);
255            label.setText("Attribute Creation Order: ");
256            label.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, false, false));
257
258            Text text;
259            text = new Text(attributeInfoGroup, SWT.SINGLE | SWT.BORDER);
260            text.setEditable(false);
261            text.setFont(curFont);
262            text.setText(objCreationStr.toString());
263            text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
264        }
265
266        log.trace("createAttributeInfoPane(): numAttributes={}", numAttributes);
267
268        attrNumberLabel = new Label(attributeInfoGroup, SWT.RIGHT);
269        attrNumberLabel.setFont(curFont);
270        attrNumberLabel.setText("Number of attributes = 0");
271        attrNumberLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, false, false));
272
273        Button addButton = new Button(attributeInfoGroup, SWT.PUSH);
274        addButton.setFont(curFont);
275        addButton.setText("Add Attribute");
276        addButton.setEnabled(!(dataObject.getFileFormat().isReadOnly()));
277        addButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
278        addButton.addSelectionListener(new SelectionAdapter() {
279            @Override
280            public void widgetSelected(SelectionEvent e) {
281                addAttribute(dataObject);
282            }
283        });
284
285        /* Deleting attributes is not supported by HDF4 */
286        Button delButton = new Button(attributeInfoGroup, SWT.PUSH);
287        delButton.setFont(curFont);
288        delButton.setText("Delete Attribute");
289        delButton.setEnabled(isH5 && !(dataObject.getFileFormat().isReadOnly()));
290        delButton.setLayoutData(new GridData(SWT.END, SWT.FILL, false, false));
291        delButton.addSelectionListener(new SelectionAdapter() {
292            @Override
293            public void widgetSelected(SelectionEvent e) {
294                deleteAttribute(dataObject);
295            }
296        });
297
298        attrTable = new Table(attributeInfoGroup, SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
299        attrTable.setLinesVisible(true);
300        attrTable.setHeaderVisible(true);
301        attrTable.setFont(curFont);
302        attrTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
303
304        Menu attrPopupMenu = createAttributePopupMenu(attrTable);
305        attrTable.setMenu(attrPopupMenu);
306
307        /*
308         * Add a double-click listener for editing attribute values in a separate
309         * TableView
310         */
311        attrTable.addListener(SWT.MouseDoubleClick, new Listener() {
312            @Override
313            public void handleEvent(Event arg0) {
314                int selectionIndex = attrTable.getSelectionIndex();
315                if (selectionIndex < 0) {
316                    Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected");
317                    return;
318                }
319
320                final TableItem item = attrTable.getItem(selectionIndex);
321
322                viewManager.getTreeView().setDefaultDisplayMode(true);
323
324                try {
325                    Display.getDefault().syncExec(new Runnable() {
326                        @Override
327                        public void run() {
328                            try {
329                                viewManager.getTreeView().showDataContent((HObject) item.getData());
330                            }
331                            catch (Exception ex) {
332                                log.debug("Attribute showDataContent failure: ", ex);
333                            }
334                        }
335                    });
336                }
337                catch (Exception e) {
338                    log.debug("Attribute showDataContent loading manually interrupted");
339                }
340            }
341        });
342
343        /*
344         * Add a right-click listener for showing a menu that has options for renaming
345         * an attribute, editing an attribute, or deleting an attribute
346         */
347        attrTable.addListener(SWT.MenuDetect, new Listener() {
348            @Override
349            public void handleEvent(Event arg0) {
350                int index = attrTable.getSelectionIndex();
351                if (index < 0) return;
352
353                attrTable.getMenu().setVisible(true);
354            }
355        });
356
357        for (int i = 0; i < attrTableColNames.length; i++) {
358            TableColumn column = new TableColumn(attrTable, SWT.NONE);
359            column.setText(attrTableColNames[i]);
360            column.setMoveable(false);
361
362            /*
363             * Make sure all columns show even when the object in question has no attributes
364             */
365            if (i == attrTableColNames.length - 1)
366                column.setWidth(200);
367            else
368                column.setWidth(50);
369        }
370
371        if (attrList != null) {
372            attrNumberLabel.setText("Number of attributes = " + numAttributes);
373
374            Attribute attr = null;
375            for (int i = 0; i < numAttributes; i++) {
376                attr = (Attribute) attrList.get(i);
377
378                log.trace("createAttributeInfoPane(): attr[{}] is {} of type {}", i, attr.getName(),
379                        attr.getDatatype().getDescription());
380
381                addAttributeTableItem(attrTable, attr);
382            }
383        }
384
385        for (int i = 0; i < attrTableColNames.length; i++) {
386            attrTable.getColumn(i).pack();
387        }
388
389        // Prevent attributes with many values, such as array types, from making
390        // the window too wide
391        attrTable.getColumn(3).setWidth(200);
392
393        return attributeInfoGroup;
394    }
395
396    private Composite createGeneralObjectInfoPane(Composite parent, final HObject dataObject) {
397        if (parent == null || dataObject == null) return null;
398
399        FileFormat theFile = dataObject.getFileFormat();
400        boolean isRoot = ((dataObject instanceof Group) && ((Group) dataObject).isRoot());
401        String objTypeStr = "Unknown";
402        Label label;
403        Text text;
404
405        /* Add an SWT Group to encompass all of the GUI components */
406        org.eclipse.swt.widgets.Group generalInfoGroup = new org.eclipse.swt.widgets.Group(parent, SWT.NONE);
407        generalInfoGroup.setFont(curFont);
408        generalInfoGroup.setLayout(new GridLayout(2, false));
409        generalInfoGroup.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
410
411        /* Object name section */
412        label = new Label(generalInfoGroup, SWT.LEFT);
413        label.setFont(curFont);
414        label.setText("Name: ");
415
416        text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
417        text.setEditable(false);
418        text.setFont(curFont);
419        text.setText(dataObject.getName());
420        text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
421
422        /* Object Path section */
423        label = new Label(generalInfoGroup, SWT.LEFT);
424        label.setFont(curFont);
425        label.setText("Path: ");
426
427        text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
428        text.setEditable(false);
429        text.setFont(curFont);
430        text.setText(dataObject.getPath() == null ? "/"
431                : dataObject.getPath()); /* TODO: temporary workaround until Object Library is fixed */
432        text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
433
434        /* Object Type section */
435        label = new Label(generalInfoGroup, SWT.LEFT);
436        label.setFont(curFont);
437        label.setText("Type: ");
438
439        if (isH5) {
440            if (dataObject instanceof Group) {
441                objTypeStr = "HDF5 Group";
442            }
443            else if (dataObject instanceof ScalarDS) {
444                objTypeStr = "HDF5 Dataset";
445            }
446            else if (dataObject instanceof CompoundDS) {
447                objTypeStr = "HDF5 Dataset";
448            }
449            else if (dataObject instanceof Datatype) {
450                objTypeStr = "HDF5 Named Datatype";
451            }
452            else {
453                log.debug("createGeneralObjectInfoPane(): unknown HDF5 dataObject");
454            }
455        }
456        else if (isH4) {
457            if (dataObject instanceof Group) {
458                objTypeStr = "HDF4 Group";
459            }
460            else if (dataObject instanceof ScalarDS) {
461                ScalarDS ds = (ScalarDS) dataObject;
462                if (ds.isImage()) {
463                    objTypeStr = "HDF4 Raster Image";
464                }
465                else {
466                    objTypeStr = "HDF4 SDS";
467                }
468            }
469            else if (dataObject instanceof CompoundDS) {
470                objTypeStr = "HDF4 Vdata";
471            }
472            else {
473                log.debug("createGeneralObjectInfoPane(): unknown HDF4 dataObject");
474            }
475        }
476        else if (isN3) {
477            if (dataObject instanceof Group) {
478                objTypeStr = "netCDF3 Group";
479            }
480            else if (dataObject instanceof ScalarDS) {
481                objTypeStr = "netCDF3 Dataset";
482            }
483            else {
484                log.debug("createGeneralObjectInfoPane(): unknown netCDF3 dataObject");
485            }
486        }
487        else {
488            if (dataObject instanceof Group) {
489                objTypeStr = "Group";
490            }
491            else if (dataObject instanceof ScalarDS) {
492                objTypeStr = "Dataset";
493            }
494            else if (dataObject instanceof CompoundDS) {
495                objTypeStr = "Dataset";
496            }
497            else {
498                log.debug("createGeneralObjectInfoPane(): unknown dataObject");
499            }
500        }
501
502        text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
503        text.setEditable(false);
504        text.setFont(curFont);
505        text.setText(objTypeStr);
506        text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
507
508        /* Object ID section */
509
510        // bug #926 to remove the OID, put it back on Nov. 20, 2008, --PC
511        String oidStr = null;
512        long[] oID = dataObject.getOID();
513        if (oID != null) {
514            oidStr = String.valueOf(oID[0]);
515            if (isH4) oidStr += ", " + oID[1];
516
517            if (isH5) {
518                label = new Label(generalInfoGroup, SWT.LEFT);
519                label.setFont(curFont);
520                label.setText("Object Ref:       ");
521            }
522            else {
523                label = new Label(generalInfoGroup, SWT.LEFT);
524                label.setFont(curFont);
525                label.setText("Tag, Ref:        ");
526            }
527
528            text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
529            text.setEditable(false);
530            text.setFont(curFont);
531            text.setText(oidStr);
532            text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
533        }
534
535        /*
536         * If this is the root group, add some special extra info, such as the Library
537         * Version bounds set for the file.
538         */
539        if (isRoot) {
540            /* Get the file's size */
541            long fileSize = 0;
542            try {
543                fileSize = (new File(dataObject.getFile())).length();
544            }
545            catch (Exception ex) {
546                fileSize = -1;
547            }
548            fileSize /= 1024;
549
550            /* Retrieve the number of subgroups and datasets in the root group */
551            HObject root = theFile.getRootObject();
552            HObject theObj = null;
553            Iterator<HObject> it = ((Group) root).depthFirstMemberList().iterator();
554            int groupCount = 0;
555            int datasetCount = 0;
556
557            while (it.hasNext()) {
558                theObj = it.next();
559
560                if (theObj instanceof Group) {
561                    groupCount++;
562                }
563                else {
564                    datasetCount++;
565                }
566            }
567
568            /* Append all of the file's information to the general object info pane */
569            String fileInfo = "";
570
571            fileInfo = "size=" + fileSize + "K,  groups=" + groupCount + ",  datasets=" + datasetCount;
572
573            /* File name section */
574            label = new Label(generalInfoGroup, SWT.LEFT);
575            label.setFont(curFont);
576            label.setText("File Name: ");
577
578            text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
579            text.setEditable(false);
580            text.setFont(curFont);
581            text.setText(dataObject.getFileFormat().getName());
582            text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
583
584            /* File Path section */
585            label = new Label(generalInfoGroup, SWT.LEFT);
586            label.setFont(curFont);
587            label.setText("File Path: ");
588
589            text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
590            text.setEditable(false);
591            text.setFont(curFont);
592            text.setText((new File(dataObject.getFile())).getParent());
593            text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
594
595            label = new Label(generalInfoGroup, SWT.LEFT);
596            label.setFont(curFont);
597            label.setText("File Type: ");
598
599            if (isH5) {
600                objTypeStr = "HDF5,  " + fileInfo;
601            }
602            else if (isH4) {
603                objTypeStr = "HDF4,  " + fileInfo;
604            }
605            else if (isN3) {
606                objTypeStr = "netCDF3,  " + fileInfo;
607            }
608            else {
609                objTypeStr = fileInfo;
610            }
611
612            text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
613            text.setEditable(false);
614            text.setFont(curFont);
615            text.setText(objTypeStr);
616            text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
617
618            if (isH5) {
619                log.trace("createGeneralObjectInfoPane(): get Library Version bounds info");
620                String libversion = dataObject.getFileFormat().getLibBoundsDescription();
621
622                if (libversion.length() > 0) {
623                    label = new Label(generalInfoGroup, SWT.LEFT);
624                    label.setFont(curFont);
625                    label.setText("Library version bounds: ");
626
627                    text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
628                    text.setEditable(false);
629                    text.setFont(curFont);
630                    text.setText(libversion);
631                    text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
632                }
633
634                Button userBlockButton = new Button(generalInfoGroup, SWT.PUSH);
635                userBlockButton.setText("Show User Block");
636                userBlockButton.addSelectionListener(new SelectionAdapter() {
637                    @Override
638                    public void widgetSelected(SelectionEvent e) {
639                        new UserBlockDialog(display.getShells()[0], SWT.NONE, dataObject).open();
640                    }
641                });
642            }
643        }
644
645        /* Add a dummy label to take up some vertical space between sections */
646        label = new Label(generalInfoGroup, SWT.LEFT);
647        label.setText("");
648        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
649
650        return generalInfoGroup;
651    }
652
653    @Override
654    public HObject getDataObject() {
655        return dataObject;
656    }
657
658    private Menu createAttributePopupMenu(final Table table) {
659        final Menu menu = new Menu(table);
660        MenuItem item;
661
662        item = new MenuItem(menu, SWT.PUSH);
663        item.setText("Rename Attribute");
664        item.addSelectionListener(new SelectionAdapter() {
665            @Override
666            public void widgetSelected(SelectionEvent e) {
667                int selectionIndex = table.getSelectionIndex();
668                if (selectionIndex < 0) {
669                    Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected");
670                    return;
671                }
672
673                HObject itemObj = (HObject) table.getItem(selectionIndex).getData();
674                String result = new InputDialog(Display.getDefault().getShells()[0],
675                        Display.getDefault().getShells()[0].getText() + " - Rename Attribute", "New Attribute Name",
676                        itemObj.getName()).open();
677
678                if ((result == null) || ((result = result.trim()) == null) || (result.length() < 1)) {
679                    return;
680                }
681
682                Attribute attr = (Attribute) attrTable.getItem(selectionIndex).getData();
683                renameAttribute(attr, result);
684            }
685        });
686
687        item = new MenuItem(menu, SWT.PUSH);
688        item.setText("View/Edit Attribute Value");
689        item.addSelectionListener(new SelectionAdapter() {
690            @Override
691            public void widgetSelected(SelectionEvent e) {
692                int selectionIndex = attrTable.getSelectionIndex();
693                if (selectionIndex < 0) {
694                    Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected");
695                    return;
696                }
697
698                final TableItem item = attrTable.getItem(selectionIndex);
699
700                viewManager.getTreeView().setDefaultDisplayMode(true);
701
702                try {
703                    Display.getDefault().syncExec(new Runnable() {
704                        @Override
705                        public void run() {
706                            try {
707                                viewManager.getTreeView().showDataContent((HObject) item.getData());
708                            }
709                            catch (Exception ex) {
710                                log.debug("Attribute showDataContent failure: ", ex);
711                            }
712                        }
713                    });
714                }
715                catch (Exception ex) {
716                    log.debug("Attribute showDataContent loading manually interrupted");
717                }
718            }
719        });
720
721        item = new MenuItem(menu, SWT.PUSH);
722        item.setText("Delete Attribute");
723        item.addSelectionListener(new SelectionAdapter() {
724            @Override
725            public void widgetSelected(SelectionEvent e) {
726                int selectionIndex = attrTable.getSelectionIndex();
727                if (selectionIndex < 0) {
728                    Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected");
729                    return;
730                }
731
732                deleteAttribute(dataObject);
733            }
734        });
735
736        menu.addMenuListener(new MenuAdapter() {
737            @Override
738            public void menuShown(MenuEvent e) {
739                /* 'Rename Attribute' MenuItem */
740                menu.getItem(0).setEnabled(!dataObject.getFileFormat().isReadOnly() && !isH4);
741
742                /* 'Delete Attribute' MenuItem */
743                menu.getItem(2).setEnabled(!dataObject.getFileFormat().isReadOnly() && !isH4);
744            }
745        });
746
747        return menu;
748    }
749
750    @Override
751    public Attribute addAttribute(HObject obj) {
752        if (obj == null) return null;
753
754        HObject root = obj.getFileFormat().getRootObject();
755        NewAttributeDialog dialog = new NewAttributeDialog(display.getShells()[0], obj,
756                ((Group) root).breadthFirstMemberList());
757        dialog.open();
758
759        Attribute attr = dialog.getAttribute();
760        if (attr == null) {
761            log.debug("addAttribute(): attr is null");
762            return null;
763        }
764
765        addAttributeTableItem(attrTable, attr);
766
767        numAttributes++;
768        attrNumberLabel.setText("Number of attributes = " + numAttributes);
769
770        if (viewManager.getTreeView() instanceof DefaultTreeView)
771            ((DefaultTreeView) viewManager.getTreeView()).updateItemIcon(obj);
772
773        return attr;
774    }
775
776    @Override
777    public Attribute deleteAttribute(HObject obj) {
778        if (obj == null) return null;
779
780        int idx = attrTable.getSelectionIndex();
781        if (idx < 0) {
782            log.debug("deleteAttribute(): no attribute is selected");
783            Tools.showError(display.getShells()[0], "Delete", "No attribute is selected.");
784            return null;
785        }
786
787        int answer = SWT.NO;
788        if (Tools.showConfirm(display.getShells()[0], "Delete",
789                "Do you want to delete the selected attribute?"))
790            answer = SWT.YES;
791        if (answer == SWT.NO) {
792            log.trace("deleteAttribute(): attribute deletion cancelled");
793            return null;
794        }
795
796        if (attrList == null) {
797            log.debug("deleteAttribute(): Attribute list was null; can't delete an attribute from it");
798            return null;
799        }
800
801        Attribute attr = (Attribute) attrList.get(idx);
802
803        log.trace("deleteAttribute(): Attribute selected for deletion: {}", attr.getName());
804
805        try {
806            ((MetaDataContainer) obj).removeMetadata(attr);
807        }
808        catch (Exception ex) {
809            log.debug("deleteAttribute(): attribute deletion failed for object '{}': ", obj.getName(), ex);
810        }
811
812        attrTable.remove(idx);
813        numAttributes--;
814
815        attrNumberLabel.setText("Number of attributes = " + numAttributes);
816
817        if (viewManager.getTreeView() instanceof DefaultTreeView)
818            ((DefaultTreeView) viewManager.getTreeView()).updateItemIcon(obj);
819
820        return attr;
821    }
822
823    private void renameAttribute(Attribute attr, String newName) {
824        if ((attr == null) || (newName == null) || (newName = newName.trim()) == null || (newName.length() < 1)) {
825            log.debug("renameAttribute(): Attribute is null or Attribute's new name is null");
826            return;
827        }
828
829        String attrName = attr.getName();
830
831        log.trace("renameAttribute(): oldName={} newName={}", attrName, newName);
832
833        if (isH5) {
834            try {
835                dataObject.getFileFormat().renameAttribute(dataObject, attrName, newName);
836            }
837            catch (Exception ex) {
838                log.debug("renameAttribute(): renaming failure:", ex);
839                Tools.showError(display.getShells()[0], "Delete", ex.getMessage());
840            }
841
842            /* Update the attribute table */
843            int selectionIndex = attrTable.getSelectionIndex();
844            if (selectionIndex < 0) {
845                Tools.showError(Display.getDefault().getShells()[0], "Delete", "No Attribute selected");
846                return;
847            }
848
849            attrTable.getItem(selectionIndex).setText(0, newName);
850        }
851        else {
852            log.debug("renameAttribute(): renaming attributes is only allowed for HDF5 files");
853        }
854
855        if (dataObject instanceof MetaDataContainer) {
856            try {
857                ((MetaDataContainer) dataObject).updateMetadata(attr);
858            }
859            catch (Exception ex) {
860                log.debug("renameAttribute(): updateMetadata() failure:", ex);
861                Tools.showError(display.getShells()[0], "Delete", ex.getMessage());
862            }
863        }
864    }
865
866    /**
867     * Update an attribute's value. Currently can only update a single data point.
868     *
869     * @param attr
870     *            the selected attribute.
871     * @param newValue
872     *            the string of the new value.
873     */
874    private void updateAttributeValue(Attribute attr, String newValue) {
875        if ((attr == null) || (newValue == null) || (newValue = newValue.trim()) == null || (newValue.length() < 1)) {
876            log.debug("updateAttributeValue(): Attribute is null or Attribute's new value is null");
877            return;
878        }
879
880        String attrName = attr.getName();
881        Object data;
882
883        log.trace("updateAttributeValue(): changing value of attribute '{}'", attrName);
884
885        try {
886            data = attr.getData();
887        }
888        catch (Exception ex) {
889            log.debug("updateAttributeValue(): getData() failure:", ex);
890            return;
891        }
892
893        if (data == null) {
894            log.debug("updateAttributeValue(): attribute's data was null");
895            return;
896        }
897
898        int arrayLength = Array.getLength(data);
899        StringTokenizer st = new StringTokenizer(newValue, ",");
900        if (st.countTokens() < arrayLength) {
901            log.debug("updateAttributeValue(): More data values needed: {}", newValue);
902            Tools.showError(display.getShells()[0], "Update", "More data values needed: " + newValue);
903            return;
904        }
905
906        char cNT = ' ';
907        String cName = data.getClass().getName();
908        int cIndex = cName.lastIndexOf('[');
909        if (cIndex >= 0) {
910            cNT = cName.charAt(cIndex + 1);
911        }
912        boolean isUnsigned = attr.getDatatype().isUnsigned();
913
914        log.trace("updateAttributeValue(): array_length={} cName={} NT={} isUnsigned={}", arrayLength, cName,
915                cNT, isUnsigned);
916
917        double d = 0;
918        String theToken = null;
919        long max = 0;
920        long min = 0;
921        for (int i = 0; i < arrayLength; i++) {
922            max = min = 0;
923            theToken = st.nextToken().trim();
924            try {
925                if (!(Array.get(data, i) instanceof String)) {
926                    d = Double.parseDouble(theToken);
927                }
928            }
929            catch (NumberFormatException ex) {
930                log.debug("updateAttributeValue(): NumberFormatException: ", ex);
931                Tools.showError(display.getShells()[0], "Update", ex.getMessage());
932                return;
933            }
934
935            if (isUnsigned && (d < 0)) {
936                log.debug("updateAttributeValue(): Negative value for unsigned integer: {}", theToken);
937                Tools.showError(display.getShells()[0], "Update", "Negative value for unsigned integer: " + theToken);
938                return;
939            }
940
941            switch (cNT) {
942                case 'B': {
943                    if (isUnsigned) {
944                        min = 0;
945                        max = 255;
946                    }
947                    else {
948                        min = Byte.MIN_VALUE;
949                        max = Byte.MAX_VALUE;
950                    }
951
952                    if ((d > max) || (d < min)) {
953                        Tools.showError(display.getShells()[0], "Update",
954                                "Data is out of range[" + min + ", " + max + "]: " + theToken);
955                    }
956                    else {
957                        Array.setByte(data, i, (byte) d);
958                    }
959                    break;
960                }
961                case 'S': {
962                    if (isUnsigned) {
963                        min = 0;
964                        max = 65535;
965                    }
966                    else {
967                        min = Short.MIN_VALUE;
968                        max = Short.MAX_VALUE;
969                    }
970
971                    if ((d > max) || (d < min)) {
972                        Tools.showError(display.getShells()[0], "Update",
973                                "Data is out of range[" + min + ", " + max + "]: " + theToken);
974                    }
975                    else {
976                        Array.setShort(data, i, (short) d);
977                    }
978                    break;
979                }
980                case 'I': {
981                    if (isUnsigned) {
982                        min = 0;
983                        max = 4294967295L;
984                    }
985                    else {
986                        min = Integer.MIN_VALUE;
987                        max = Integer.MAX_VALUE;
988                    }
989
990                    if ((d > max) || (d < min)) {
991                        Tools.showError(display.getShells()[0], "Update",
992                                "Data is out of range[" + min + ", " + max + "]: " + theToken);
993                    }
994                    else {
995                        Array.setInt(data, i, (int) d);
996                    }
997                    break;
998                }
999                case 'J':
1000                    long lvalue = 0;
1001                    if (isUnsigned) {
1002                        if (theToken != null) {
1003                            String theValue = theToken;
1004                            BigInteger maxJ = new BigInteger("18446744073709551615");
1005                            BigInteger big = new BigInteger(theValue);
1006                            if ((big.compareTo(maxJ) > 0) || (big.compareTo(BigInteger.ZERO) < 0)) {
1007                                Tools.showError(display.getShells()[0], "Update",
1008                                        "Data is out of range[" + min + ", " + max + "]: " + theToken);
1009                            }
1010                            lvalue = big.longValue();
1011                            log.trace("updateAttributeValue(): big.longValue={}", lvalue);
1012                            Array.setLong(data, i, lvalue);
1013                        }
1014                        else
1015                            Array.set(data, i, theToken);
1016                    }
1017                    else {
1018                        min = Long.MIN_VALUE;
1019                        max = Long.MAX_VALUE;
1020                        if ((d > max) || (d < min)) {
1021                            Tools.showError(display.getShells()[0], "Update",
1022                                    "Data is out of range[" + min + ", " + max + "]: " + theToken);
1023                        }
1024                        lvalue = (long) d;
1025                        log.trace("updateAttributeValue(): longValue={}", lvalue);
1026                        Array.setLong(data, i, lvalue);
1027                    }
1028                    break;
1029                case 'F':
1030                    Array.setFloat(data, i, (float) d);
1031                    break;
1032                case 'D':
1033                    Array.setDouble(data, i, d);
1034                    break;
1035                default:
1036                    Array.set(data, i, theToken);
1037                    break;
1038            }
1039        }
1040
1041        try {
1042            dataObject.getFileFormat().writeAttribute(dataObject, attr, true);
1043        }
1044        catch (Exception ex) {
1045            log.debug("updateAttributeValue(): writeAttribute failure: ", ex);
1046            Tools.showError(display.getShells()[0], "Update", ex.getMessage());
1047            return;
1048        }
1049
1050        /* Update the attribute table */
1051        int selectionIndex = attrTable.getSelectionIndex();
1052        if (selectionIndex < 0) {
1053            Tools.showError(Display.getDefault().getShells()[0], "Update", "No Attribute selected");
1054            return;
1055        }
1056
1057        attrTable.getItem(selectionIndex).setText(3, attr.toString(", "));
1058
1059        if (dataObject instanceof MetaDataContainer) {
1060            try {
1061                ((MetaDataContainer) dataObject).updateMetadata(attr);
1062            }
1063            catch (Exception ex) {
1064                log.debug("updateAttributeValue(): updateMetadata() failure:", ex);
1065                Tools.showError(display.getShells()[0], "Update", ex.getMessage());
1066            }
1067        }
1068    }
1069
1070    private void addAttributeTableItem(Table table, Attribute attr) {
1071        if (table == null || attr == null) {
1072            log.debug("addAttributeTableItem(): table or attribute is null");
1073            return;
1074        }
1075
1076        String attrName = attr.getName();
1077        String attrType = attr.getDatatype().getDescription();
1078        StringBuilder attrSize = new StringBuilder();
1079        String attrValue = attr.toString(", ", 50);
1080        String[] rowData = new String[attrTableColNames.length];
1081
1082        if (attrName == null) attrName = "null";
1083        if (attrType == null) attrType = "null";
1084        if (attrValue == null) attrValue = "null";
1085
1086        TableItem item = new TableItem(attrTable, SWT.NONE);
1087        item.setFont(curFont);
1088        item.setData(attr);
1089
1090        if (attr.getProperty("field") != null) {
1091            rowData[0] = attrName + " {Field: " + attr.getProperty("field") + "}";
1092        }
1093        else {
1094            rowData[0] = attrName;
1095        }
1096
1097        if (attr.isScalar()) {
1098            attrSize.append("Scalar");
1099        }
1100        else {
1101            long[] dims = attr.getDims();
1102            attrSize.append(String.valueOf(dims[0]));
1103            for (int j = 1; j < dims.length; j++) {
1104                attrSize.append(" x ").append(dims[j]);
1105            }
1106        }
1107
1108        rowData[1] = attrType;
1109        rowData[2] = attrSize.toString();
1110        rowData[3] = attrValue;
1111
1112        item.setText(rowData);
1113    }
1114
1115    private class UserBlockDialog extends Dialog {
1116        private Shell          shell;
1117
1118        private final HObject  obj;
1119
1120        private byte[]         userBlock;
1121
1122        private final String[] displayChoices = { "Text", "Binary", "Octal", "Hexadecimal", "Decimal" };
1123
1124        private Button         jamButton;
1125        private Text           userBlockArea;
1126
1127        public UserBlockDialog(Shell parent, int style, HObject obj) {
1128            super(parent, style);
1129
1130            this.obj = obj;
1131
1132            userBlock = Tools.getHDF5UserBlock(obj.getFile());
1133        }
1134
1135        public void open() {
1136            Shell openParent = getParent();
1137            shell = new Shell(openParent, SWT.DIALOG_TRIM | SWT.RESIZE);
1138            shell.setFont(curFont);
1139            shell.setText("User Block - " + obj);
1140            shell.setLayout(new GridLayout(5, false));
1141
1142            Label label = new Label(shell, SWT.RIGHT);
1143            label.setFont(curFont);
1144            label.setText("Display As: ");
1145
1146            Combo userBlockDisplayChoice = new Combo(shell, SWT.SINGLE | SWT.READ_ONLY);
1147            userBlockDisplayChoice.setFont(curFont);
1148            userBlockDisplayChoice.setItems(displayChoices);
1149            userBlockDisplayChoice.select(0);
1150            userBlockDisplayChoice.addSelectionListener(new SelectionAdapter() {
1151                @Override
1152                public void widgetSelected(SelectionEvent e) {
1153                    Combo source = (Combo) e.widget;
1154                    int type = 0;
1155
1156                    String typeName = source.getItem(source.getSelectionIndex());
1157
1158                    jamButton.setEnabled(false);
1159                    userBlockArea.setEditable(false);
1160
1161                    if (typeName.equalsIgnoreCase("Text")) {
1162                        type = 0;
1163                        jamButton.setEnabled(true);
1164                        userBlockArea.setEditable(true);
1165                    }
1166                    else if (typeName.equalsIgnoreCase("Binary")) {
1167                        type = 2;
1168                    }
1169                    else if (typeName.equalsIgnoreCase("Octal")) {
1170                        type = 8;
1171                    }
1172                    else if (typeName.equalsIgnoreCase("Hexadecimal")) {
1173                        type = 16;
1174                    }
1175                    else if (typeName.equalsIgnoreCase("Decimal")) {
1176                        type = 10;
1177                    }
1178
1179                    showUserBlockAs(type);
1180                }
1181            });
1182
1183            Label dummyLabel = new Label(shell, SWT.RIGHT);
1184            dummyLabel.setFont(curFont);
1185            dummyLabel.setText("");
1186            dummyLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
1187
1188            Label sizeLabel = new Label(shell, SWT.RIGHT);
1189            sizeLabel.setFont(curFont);
1190            sizeLabel.setText("Header Size (Bytes): 0");
1191
1192            jamButton = new Button(shell, SWT.PUSH);
1193            jamButton.setFont(curFont);
1194            jamButton.setText("Save User Block");
1195            jamButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
1196            jamButton.addSelectionListener(new SelectionAdapter() {
1197                @Override
1198                public void widgetSelected(SelectionEvent e) {
1199                    writeUserBlock();
1200                }
1201            });
1202
1203            ScrolledComposite userBlockScroller = new ScrolledComposite(shell, SWT.V_SCROLL | SWT.BORDER);
1204            userBlockScroller.setExpandHorizontal(true);
1205            userBlockScroller.setExpandVertical(true);
1206            userBlockScroller.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));
1207
1208            userBlockArea = new Text(userBlockScroller, SWT.MULTI | SWT.WRAP);
1209            userBlockArea.setEditable(true);
1210            userBlockArea.setFont(curFont);
1211            userBlockScroller.setContent(userBlockArea);
1212
1213            Button closeButton = new Button(shell, SWT.CENTER);
1214            closeButton.setFont(curFont);
1215            closeButton.setText(" &Close ");
1216            closeButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, false, 5, 1));
1217            closeButton.addSelectionListener(new SelectionAdapter() {
1218                @Override
1219                public void widgetSelected(SelectionEvent e) {
1220                    shell.dispose();
1221                }
1222            });
1223
1224            if (userBlock != null) {
1225                int headSize = showUserBlockAs(0);
1226                sizeLabel.setText("Header Size (Bytes): " + headSize);
1227            }
1228            else {
1229                userBlockDisplayChoice.setEnabled(false);
1230            }
1231
1232            shell.pack();
1233
1234            Rectangle parentBounds = openParent.getBounds();
1235
1236            Point shellSize = new Point((int) (0.5 * parentBounds.width), (int) (0.5 * parentBounds.height));
1237            shell.setSize(shellSize);
1238
1239            shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
1240                    (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
1241
1242            shell.open();
1243
1244            Display openDisplay = openParent.getDisplay();
1245            while (!shell.isDisposed()) {
1246                if (!openDisplay.readAndDispatch()) openDisplay.sleep();
1247            }
1248        }
1249
1250        private int showUserBlockAs(int radix) {
1251            if (userBlock == null) return 0;
1252
1253            int headerSize = 0;
1254
1255            String userBlockInfo = null;
1256            if ((radix == 2) || (radix == 8) || (radix == 16) || (radix == 10)) {
1257                StringBuilder sb = new StringBuilder();
1258                for (headerSize = 0; headerSize < userBlock.length; headerSize++) {
1259                    int intValue = userBlock[headerSize];
1260                    if (intValue < 0) {
1261                        intValue += 256;
1262                    }
1263                    else if (intValue == 0) {
1264                        break; // null end
1265                    }
1266
1267                    sb.append(Integer.toString(intValue, radix)).append(" ");
1268                }
1269                userBlockInfo = sb.toString();
1270            }
1271            else {
1272                userBlockInfo = new String(userBlock).trim();
1273                if (userBlockInfo != null) {
1274                    headerSize = userBlockInfo.length();
1275                }
1276            }
1277
1278            userBlockArea.setText(userBlockInfo);
1279
1280            return headerSize;
1281        }
1282
1283        private void writeUserBlock() {
1284            if (!obj.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
1285                return;
1286            }
1287
1288            int blkSize0 = 0;
1289            if (userBlock != null) {
1290                blkSize0 = userBlock.length;
1291                // The super block space is allocated by offset 0, 512, 1024, 2048, etc
1292                if (blkSize0 > 0) {
1293                    int offset = 512;
1294                    while (offset < blkSize0) {
1295                        offset *= 2;
1296                    }
1297                    blkSize0 = offset;
1298                }
1299            }
1300
1301            int blkSize1 = 0;
1302            String userBlockStr = userBlockArea.getText();
1303            if (userBlockStr == null) {
1304                if (blkSize0 <= 0) {
1305                    return; // nothing to write
1306                }
1307                else {
1308                    userBlockStr = " "; // want to wipe out old userblock content
1309                }
1310            }
1311            byte[] buf = null;
1312            buf = userBlockStr.getBytes();
1313
1314            blkSize1 = buf.length;
1315            if (blkSize1 <= blkSize0) {
1316                java.io.RandomAccessFile raf = null;
1317                try {
1318                    raf = new java.io.RandomAccessFile(obj.getFile(), "rw");
1319                }
1320                catch (Exception ex) {
1321                    Tools.showError(shell, "Save", "Can't open output file: " + obj.getFile());
1322                    return;
1323                }
1324
1325                try {
1326                    raf.seek(0);
1327                    raf.write(buf, 0, buf.length);
1328                    raf.seek(buf.length);
1329                    if (blkSize0 > buf.length) {
1330                        byte[] padBuf = new byte[blkSize0 - buf.length];
1331                        raf.write(padBuf, 0, padBuf.length);
1332                    }
1333                }
1334                catch (Exception ex) {
1335                    log.debug("raf write:", ex);
1336                }
1337                try {
1338                    raf.close();
1339                }
1340                catch (Exception ex) {
1341                    log.debug("raf close:", ex);
1342                }
1343
1344                Tools.showInformation(shell, "Save", "Saving user block is successful.");
1345            }
1346            else {
1347                // must rewrite the whole file
1348                MessageDialog confirm = new MessageDialog(shell, "Save", null,
1349                        "The user block to write is " + blkSize1 + " (bytes),\n"
1350                                + "which is larger than the user block space in file " + blkSize0 + " (bytes).\n"
1351                                + "To expand the user block, the file must be rewritten.\n\n"
1352                                + "Do you want to replace the current file? Click "
1353                                + "\n\"Yes\" to replace the current file," + "\n\"No\" to save to a different file, "
1354                                + "\n\"Cancel\" to quit without saving the change.\n\n ",
1355                                MessageDialog.QUESTION_WITH_CANCEL, new String[] { "Yes", "No", "Cancel" }, 0);
1356                int op = confirm.open();
1357
1358                if (op == 2) return;
1359
1360                String fin = obj.getFile();
1361
1362                String fout = fin + "~copy.h5";
1363                if (fin.endsWith(".h5")) {
1364                    fout = fin.substring(0, fin.length() - 3) + "~copy.h5";
1365                }
1366                else if (fin.endsWith(".hdf5")) {
1367                    fout = fin.substring(0, fin.length() - 5) + "~copy.h5";
1368                }
1369
1370                File outFile = null;
1371
1372                if (op == 1) {
1373                    FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
1374                    fChooser.setFileName(fout);
1375
1376                    DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF5();
1377                    fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() });
1378                    fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() });
1379                    fChooser.setFilterIndex(1);
1380
1381                    if (fChooser.open() == null) return;
1382
1383                    File chosenFile = new File(fChooser.getFileName());
1384
1385                    outFile = chosenFile;
1386                    fout = outFile.getAbsolutePath();
1387                }
1388                else {
1389                    outFile = new File(fout);
1390                }
1391
1392                if (!outFile.exists()) {
1393                    try {
1394                        if (!outFile.createNewFile())
1395                            log.debug("Error creating file {}", fout);
1396                    }
1397                    catch (Exception ex) {
1398                        Tools.showError(shell, "Save", "Failed to write user block into file.");
1399                        return;
1400                    }
1401                }
1402
1403                // close the file
1404                TreeView view = viewManager.getTreeView();
1405
1406                try {
1407                    view.closeFile(view.getSelectedFile());
1408                }
1409                catch (Exception ex) {
1410                    log.debug("Error closing file {}", fin);
1411                }
1412
1413                if (Tools.setHDF5UserBlock(fin, fout, buf)) {
1414                    if (op == 1) {
1415                        fin = fout; // open the new file
1416                    }
1417                    else {
1418                        File oldFile = new File(fin);
1419                        boolean status = oldFile.delete();
1420                        if (status) {
1421                            if (!outFile.renameTo(oldFile))
1422                                log.debug("Error renaming file {}", fout);
1423                        }
1424                        else {
1425                            Tools.showError(shell, "Save", "Cannot replace the current file.\nPlease save to a different file.");
1426                            outFile.delete();
1427                        }
1428                    }
1429                }
1430                else {
1431                    Tools.showError(shell, "Save", "Failed to write user block into file.");
1432                    outFile.delete();
1433                }
1434
1435                // reopen the file
1436                shell.dispose();
1437
1438                try {
1439                    view.openFile(fin, ViewProperties.isReadOnly() ? FileFormat.READ : FileFormat.WRITE);
1440                }
1441                catch (Exception ex) {
1442                    log.debug("Error opening file {}", fin);
1443                }
1444            }
1445        }
1446    }
1447}