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