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 COPYING file, which can be found  *
009 * at the root of the source code distribution tree,                         *
010 * or in https://www.hdfgroup.org/licenses.                                  *
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                                HObject selectedObject = (HObject) item.getData();
382                                if ((selectedObject instanceof Dataset) && !((Dataset) selectedObject).isNULL()) {
383                                    viewManager.getTreeView().showDataContent(selectedObject);
384                                }
385                                else {
386                                    Tools.showInformation(Display.getDefault().getShells()[0], "Open",
387                                            "No data to display in an object with a NULL dataspace.");
388                                }
389                            }
390                            catch (Exception ex) {
391                                log.debug("Attribute showDataContent failure: ", ex);
392                            }
393                        }
394                    });
395                }
396                catch (Exception e) {
397                    log.debug("Attribute showDataContent loading manually interrupted");
398                }
399            }
400        });
401
402        /*
403         * Add a right-click listener for showing a menu that has options for renaming
404         * an attribute, editing an attribute, or deleting an attribute
405         */
406        attrTable.addListener(SWT.MenuDetect, new Listener() {
407            @Override
408            public void handleEvent(Event arg0) {
409                int index = attrTable.getSelectionIndex();
410                if (index < 0) return;
411
412                attrTable.getMenu().setVisible(true);
413            }
414        });
415
416        for (int i = 0; i < attrTableColNames.length; i++) {
417            TableColumn column = new TableColumn(attrTable, SWT.NONE);
418            column.setText(attrTableColNames[i]);
419            column.setMoveable(false);
420
421            /*
422             * Make sure all columns show even when the object in question has no attributes
423             */
424            if (i == attrTableColNames.length - 1)
425                column.setWidth(200);
426            else
427                column.setWidth(50);
428        }
429
430        if (attrList != null) {
431            attrNumberLabel.setText("Number of attributes = " + numAttributes);
432
433            Attribute attr = null;
434            for (int i = 0; i < numAttributes; i++) {
435                attr = (Attribute) attrList.get(i);
436
437                log.trace("createAttributeInfoPane(): attr[{}] is {} of type {}", i, attr.getAttributeName(),
438                        attr.getAttributeDatatype().getDescription());
439
440                addAttributeTableItem(attrTable, attr);
441            }
442        }
443
444        for (int i = 0; i < attrTableColNames.length; i++) {
445            attrTable.getColumn(i).pack();
446        }
447
448        // Prevent attributes with many values, such as array types, from making
449        // the window too wide
450        attrTable.getColumn(3).setWidth(200);
451
452        return attributeInfoGroup;
453    }
454
455    private Composite createGeneralObjectInfoPane(Composite parent, final HObject dataObject) {
456        if (parent == null || dataObject == null) return null;
457
458        FileFormat theFile = dataObject.getFileFormat();
459        boolean isRoot = ((dataObject instanceof Group) && ((Group) dataObject).isRoot());
460        String objTypeStr = "Unknown";
461        Label label;
462        Text text;
463
464        /* Add an SWT Group to encompass all of the GUI components */
465        org.eclipse.swt.widgets.Group generalInfoGroup = new org.eclipse.swt.widgets.Group(parent, SWT.NONE);
466        generalInfoGroup.setFont(curFont);
467        generalInfoGroup.setLayout(new GridLayout(2, false));
468        generalInfoGroup.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
469
470        /* Object name section */
471        label = new Label(generalInfoGroup, SWT.LEFT);
472        label.setFont(curFont);
473        label.setText("Name: ");
474
475        text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
476        text.setEditable(false);
477        text.setFont(curFont);
478        text.setText(dataObject.getName());
479        text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
480
481        /* Object Path section */
482        label = new Label(generalInfoGroup, SWT.LEFT);
483        label.setFont(curFont);
484        label.setText("Path: ");
485
486        text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
487        text.setEditable(false);
488        text.setFont(curFont);
489        text.setText(dataObject.getPath() == null ? "/"
490                : dataObject.getPath()); /* TODO: temporary workaround until Object Library is fixed */
491        text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
492
493        /* Object Type section */
494        label = new Label(generalInfoGroup, SWT.LEFT);
495        label.setFont(curFont);
496        label.setText("Type: ");
497
498        if (isH5) {
499            if (dataObject instanceof Group) {
500                objTypeStr = "HDF5 Group";
501            }
502            else if (dataObject instanceof ScalarDS) {
503                objTypeStr = "HDF5 Dataset";
504            }
505            else if (dataObject instanceof CompoundDS) {
506                objTypeStr = "HDF5 Dataset";
507            }
508            else if (dataObject instanceof Datatype) {
509                objTypeStr = "HDF5 Named Datatype";
510            }
511            else {
512                log.debug("createGeneralObjectInfoPane(): unknown HDF5 dataObject");
513            }
514        }
515        else if (isH4) {
516            if (dataObject instanceof Group) {
517                objTypeStr = "HDF4 Group";
518            }
519            else if (dataObject instanceof ScalarDS) {
520                ScalarDS ds = (ScalarDS) dataObject;
521                if (ds.isImage()) {
522                    objTypeStr = "HDF4 Raster Image";
523                }
524                else {
525                    objTypeStr = "HDF4 SDS";
526                }
527            }
528            else if (dataObject instanceof CompoundDS) {
529                objTypeStr = "HDF4 Vdata";
530            }
531            else {
532                log.debug("createGeneralObjectInfoPane(): unknown HDF4 dataObject");
533            }
534        }
535        else if (isN3) {
536            if (dataObject instanceof Group) {
537                objTypeStr = "netCDF3 Group";
538            }
539            else if (dataObject instanceof ScalarDS) {
540                objTypeStr = "netCDF3 Dataset";
541            }
542            else {
543                log.debug("createGeneralObjectInfoPane(): unknown netCDF3 dataObject");
544            }
545        }
546        else {
547            if (dataObject instanceof Group) {
548                objTypeStr = "Group";
549            }
550            else if (dataObject instanceof ScalarDS) {
551                objTypeStr = "Dataset";
552            }
553            else if (dataObject instanceof CompoundDS) {
554                objTypeStr = "Dataset";
555            }
556            else {
557                log.debug("createGeneralObjectInfoPane(): unknown dataObject");
558            }
559        }
560
561        text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
562        text.setEditable(false);
563        text.setFont(curFont);
564        text.setText(objTypeStr);
565        text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
566
567        /* Object ID section */
568
569        // bug #926 to remove the OID, put it back on Nov. 20, 2008, --PC
570        String oidStr = null;
571        long[] oID = dataObject.getOID();
572        if (oID != null) {
573            oidStr = String.valueOf(oID[0]);
574            if (isH4)
575                oidStr += ", " + oID[1];
576
577            if (isH5) {
578                label = new Label(generalInfoGroup, SWT.LEFT);
579                label.setFont(curFont);
580                label.setText("Object Ref:       ");
581            }
582            else {
583                label = new Label(generalInfoGroup, SWT.LEFT);
584                label.setFont(curFont);
585                label.setText("Tag, Ref:        ");
586            }
587
588            text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
589            text.setEditable(false);
590            text.setFont(curFont);
591            text.setText(oidStr);
592            text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
593        }
594
595        /*
596         * If this is the root group, add some special extra info, such as the Library
597         * Version bounds set for the file.
598         */
599        if (isRoot) {
600            /* Get the file's size */
601            long fileSize = 0;
602            try {
603                fileSize = (new File(dataObject.getFile())).length();
604            }
605            catch (Exception ex) {
606                fileSize = -1;
607            }
608            fileSize /= 1024;
609
610            /* Retrieve the number of subgroups and datasets in the root group */
611            HObject root = theFile.getRootObject();
612            HObject theObj = null;
613            Iterator<HObject> it = ((Group) root).depthFirstMemberList().iterator();
614            int groupCount = 0;
615            int datasetCount = 0;
616
617            while (it.hasNext()) {
618                theObj = it.next();
619
620                if (theObj instanceof Group)
621                    groupCount++;
622                else
623                    datasetCount++;
624            }
625
626            /* Append all of the file's information to the general object info pane */
627            String fileInfo = "";
628
629            fileInfo = "size=" + fileSize + "K,  groups=" + groupCount + ",  datasets=" + datasetCount;
630
631            /* File name section */
632            label = new Label(generalInfoGroup, SWT.LEFT);
633            label.setFont(curFont);
634            label.setText("File Name: ");
635
636            text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
637            text.setEditable(false);
638            text.setFont(curFont);
639            text.setText(dataObject.getFileFormat().getName());
640            text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
641
642            /* File Path section */
643            label = new Label(generalInfoGroup, SWT.LEFT);
644            label.setFont(curFont);
645            label.setText("File Path: ");
646
647            text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
648            text.setEditable(false);
649            text.setFont(curFont);
650            text.setText((new File(dataObject.getFile())).getParent());
651            text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
652
653            label = new Label(generalInfoGroup, SWT.LEFT);
654            label.setFont(curFont);
655            label.setText("File Type: ");
656
657            if (isH5)
658                objTypeStr = "HDF5,  " + fileInfo;
659            else if (isH4)
660                objTypeStr = "HDF4,  " + fileInfo;
661            else if (isN3)
662                objTypeStr = "netCDF3,  " + fileInfo;
663            else
664                objTypeStr = fileInfo;
665
666            text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
667            text.setEditable(false);
668            text.setFont(curFont);
669            text.setText(objTypeStr);
670            text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
671
672            if (isH5) {
673                log.trace("createGeneralObjectInfoPane(): get Library Version bounds info");
674                String libversion = "";
675                try {
676                    libversion = dataObject.getFileFormat().getLibBoundsDescription();
677                }
678                catch (Exception ex) {
679                    log.debug("Get Library Bounds Description failure: ", ex);
680                }
681
682                if (libversion.length() > 0) {
683                    label = new Label(generalInfoGroup, SWT.LEFT);
684                    label.setFont(curFont);
685                    label.setText("Library version bounds: ");
686
687                    text = new Text(generalInfoGroup, SWT.SINGLE | SWT.BORDER);
688                    text.setEditable(false);
689                    text.setFont(curFont);
690                    text.setText(libversion);
691                    text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
692                }
693
694                Button userBlockButton = new Button(generalInfoGroup, SWT.PUSH);
695                userBlockButton.setText("Show User Block");
696                userBlockButton.addSelectionListener(new SelectionAdapter() {
697                    @Override
698                    public void widgetSelected(SelectionEvent e) {
699                        new UserBlockDialog(display.getShells()[0], SWT.NONE, dataObject).open();
700                    }
701                });
702            }
703        }
704
705        /* Add a dummy label to take up some vertical space between sections */
706        label = new Label(generalInfoGroup, SWT.LEFT);
707        label.setText("");
708        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
709
710        return generalInfoGroup;
711    }
712
713    @Override
714    public HObject getDataObject() {
715        return dataObject;
716    }
717
718    private Menu createAttributePopupMenu(final Table table) {
719        final Menu menu = new Menu(table);
720        MenuItem item;
721
722        item = new MenuItem(menu, SWT.PUSH);
723        item.setText("Rename Attribute");
724        item.addSelectionListener(new SelectionAdapter() {
725            @Override
726            public void widgetSelected(SelectionEvent e) {
727                int selectionIndex = table.getSelectionIndex();
728                if (selectionIndex < 0) {
729                    Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected");
730                    return;
731                }
732
733                HObject itemObj = (HObject) table.getItem(selectionIndex).getData();
734                String result = new InputDialog(Display.getDefault().getShells()[0],
735                        Display.getDefault().getShells()[0].getText() + " - Rename Attribute", "New Attribute Name",
736                        itemObj.getName()).open();
737
738                if ((result == null) || ((result = result.trim()) == null) || (result.length() < 1)) {
739                    return;
740                }
741
742                Attribute attr = (Attribute) attrTable.getItem(selectionIndex).getData();
743                renameAttribute(attr, result);
744            }
745        });
746
747        item = new MenuItem(menu, SWT.PUSH);
748        item.setText("View/Edit Attribute Value");
749        item.addSelectionListener(new SelectionAdapter() {
750            @Override
751            public void widgetSelected(SelectionEvent e) {
752                int selectionIndex = attrTable.getSelectionIndex();
753                if (selectionIndex < 0) {
754                    Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected");
755                    return;
756                }
757
758                final TableItem item = attrTable.getItem(selectionIndex);
759
760                viewManager.getTreeView().setDefaultDisplayMode(true);
761
762                try {
763                    Display.getDefault().syncExec(new Runnable() {
764                        @Override
765                        public void run() {
766                            try {
767                                HObject selectedObject = (HObject) item.getData();
768                                if ((selectedObject instanceof Dataset) && !((Dataset) selectedObject).isNULL()) {
769                                    viewManager.getTreeView().showDataContent(selectedObject);
770                                }
771                                else {
772                                    Tools.showInformation(Display.getDefault().getShells()[0], "Open",
773                                            "No data to display in an object with a NULL dataspace.");
774                                }
775                            }
776                            catch (Exception ex) {
777                                log.debug("Attribute showDataContent failure: ", ex);
778                            }
779                        }
780                    });
781                }
782                catch (Exception ex) {
783                    log.debug("Attribute showDataContent loading manually interrupted");
784                }
785            }
786        });
787
788        item = new MenuItem(menu, SWT.PUSH);
789        item.setText("Delete Attribute");
790        item.addSelectionListener(new SelectionAdapter() {
791            @Override
792            public void widgetSelected(SelectionEvent e) {
793                int selectionIndex = attrTable.getSelectionIndex();
794                if (selectionIndex < 0) {
795                    Tools.showError(Display.getDefault().getShells()[0], "Select", "No Attribute selected");
796                    return;
797                }
798
799                deleteAttribute(dataObject);
800            }
801        });
802
803        menu.addMenuListener(new MenuAdapter() {
804            @Override
805            public void menuShown(MenuEvent e) {
806                /* 'Rename Attribute' MenuItem */
807                menu.getItem(0).setEnabled(!dataObject.getFileFormat().isReadOnly() && !isH4);
808
809                /* 'Delete Attribute' MenuItem */
810                menu.getItem(2).setEnabled(!dataObject.getFileFormat().isReadOnly() && !isH4);
811            }
812        });
813
814        return menu;
815    }
816
817    @Override
818    public Attribute addAttribute(HObject obj) {
819        if (obj == null) return null;
820
821        HObject root = obj.getFileFormat().getRootObject();
822        Attribute attr = null;
823        if (isH5) {
824            NewScalarAttributeDialog dialog = new NewScalarAttributeDialog(display.getShells()[0], obj,
825                    ((Group) root).breadthFirstMemberList());
826            dialog.open();
827            attr = dialog.getAttribute();
828        }
829        else {
830            NewStringAttributeDialog dialog = new NewStringAttributeDialog(display.getShells()[0], obj,
831                    ((Group) root).breadthFirstMemberList());
832            dialog.open();
833            attr = dialog.getAttribute();
834        }
835
836        if (attr == null) {
837            log.debug("addAttribute(): attr is null");
838            return null;
839        }
840
841        addAttributeTableItem(attrTable, attr);
842
843        numAttributes++;
844        attrNumberLabel.setText("Number of attributes = " + numAttributes);
845
846        if (viewManager.getTreeView() instanceof DefaultTreeView)
847            ((DefaultTreeView) viewManager.getTreeView()).updateItemIcon(obj);
848
849        return attr;
850    }
851
852    @Override
853    public Attribute deleteAttribute(HObject obj) {
854        if (obj == null) return null;
855
856        int idx = attrTable.getSelectionIndex();
857        if (idx < 0) {
858            log.debug("deleteAttribute(): no attribute is selected");
859            Tools.showError(display.getShells()[0], "Delete", "No attribute is selected.");
860            return null;
861        }
862
863        int answer = SWT.NO;
864        if (Tools.showConfirm(display.getShells()[0], "Delete",
865                "Do you want to delete the selected attribute?"))
866            answer = SWT.YES;
867        if (answer == SWT.NO) {
868            log.trace("deleteAttribute(): attribute deletion cancelled");
869            return null;
870        }
871
872        if (attrList == null) {
873            log.debug("deleteAttribute(): Attribute list was null; can't delete an attribute from it");
874            return null;
875        }
876
877        Attribute attr = (Attribute) attrList.get(idx);
878
879        log.trace("deleteAttribute(): Attribute selected for deletion: {}", attr.getAttributeName());
880
881        try {
882            ((MetaDataContainer) obj).removeMetadata(attr);
883        }
884        catch (Exception ex) {
885            log.debug("deleteAttribute(): attribute deletion failed for object '{}': ", obj.getName(), ex);
886        }
887
888        attrTable.remove(idx);
889        numAttributes--;
890
891        attrNumberLabel.setText("Number of attributes = " + numAttributes);
892
893        if (viewManager.getTreeView() instanceof DefaultTreeView)
894            ((DefaultTreeView) viewManager.getTreeView()).updateItemIcon(obj);
895
896        return attr;
897    }
898
899    private void renameAttribute(Attribute attr, String newName) {
900        if ((attr == null) || (newName == null) || (newName = newName.trim()) == null || (newName.length() < 1)) {
901            log.debug("renameAttribute(): Attribute is null or Attribute's new name is null");
902            return;
903        }
904
905        String attrName = attr.getAttributeName();
906
907        log.trace("renameAttribute(): oldName={} newName={}", attrName, newName);
908
909        if (isH5) {
910            try {
911                dataObject.getFileFormat().renameAttribute(dataObject, attrName, newName);
912            }
913            catch (Exception ex) {
914                log.debug("renameAttribute(): renaming failure:", ex);
915                Tools.showError(display.getShells()[0], "Delete", ex.getMessage());
916            }
917
918            /* Update the attribute table */
919            int selectionIndex = attrTable.getSelectionIndex();
920            if (selectionIndex < 0) {
921                Tools.showError(Display.getDefault().getShells()[0], "Delete", "No Attribute selected");
922                return;
923            }
924
925            attrTable.getItem(selectionIndex).setText(0, newName);
926        }
927        else {
928            log.debug("renameAttribute(): renaming attributes is only allowed for HDF5 files");
929        }
930
931        if (dataObject instanceof MetaDataContainer) {
932            try {
933                ((MetaDataContainer) dataObject).updateMetadata(attr);
934            }
935            catch (Exception ex) {
936                log.debug("renameAttribute(): updateMetadata() failure:", ex);
937                Tools.showError(display.getShells()[0], "Delete", ex.getMessage());
938            }
939        }
940    }
941
942    /**
943     * Update an attribute's value. Currently can only update a single data point.
944     *
945     * @param attr
946     *            the selected attribute.
947     * @param newValue
948     *            the string of the new value.
949     */
950    private void updateAttributeValue(Attribute attr, String newValue) {
951        if ((attr == null) || (newValue == null) || (newValue = newValue.trim()) == null || (newValue.length() < 1)) {
952            log.debug("updateAttributeValue(): Attribute is null or Attribute's new value is null");
953            return;
954        }
955
956        String attrName = attr.getAttributeName();
957        Object data;
958
959        log.trace("updateAttributeValue(): changing value of attribute '{}'", attrName);
960
961        try {
962            data = attr.getAttributeData();
963        }
964        catch (Exception ex) {
965            log.debug("updateAttributeValue(): getData() failure:", ex);
966            return;
967        }
968
969        if (data == null) {
970            log.debug("updateAttributeValue(): attribute's data was null");
971            return;
972        }
973
974        int arrayLength = Array.getLength(data);
975        StringTokenizer st = new StringTokenizer(newValue, ",");
976        if (st.countTokens() < arrayLength) {
977            log.debug("updateAttributeValue(): More data values needed: {}", newValue);
978            Tools.showError(display.getShells()[0], "Update", "More data values needed: " + newValue);
979            return;
980        }
981
982        char cNT = ' ';
983        String cName = data.getClass().getName();
984        int cIndex = cName.lastIndexOf('[');
985        if (cIndex >= 0) {
986            cNT = cName.charAt(cIndex + 1);
987        }
988        boolean isUnsigned = attr.getAttributeDatatype().isUnsigned();
989
990        log.trace("updateAttributeValue(): array_length={} cName={} NT={} isUnsigned={}", arrayLength, cName,
991                cNT, isUnsigned);
992
993        double d = 0;
994        String theToken = null;
995        long max = 0;
996        long min = 0;
997        for (int i = 0; i < arrayLength; i++) {
998            max = min = 0;
999            theToken = st.nextToken().trim();
1000            try {
1001                if (!(Array.get(data, i) instanceof String)) {
1002                    d = Double.parseDouble(theToken);
1003                }
1004            }
1005            catch (NumberFormatException ex) {
1006                log.debug("updateAttributeValue(): NumberFormatException: ", ex);
1007                Tools.showError(display.getShells()[0], "Update", ex.getMessage());
1008                return;
1009            }
1010
1011            if (isUnsigned && (d < 0)) {
1012                log.debug("updateAttributeValue(): Negative value for unsigned integer: {}", theToken);
1013                Tools.showError(display.getShells()[0], "Update", "Negative value for unsigned integer: " + theToken);
1014                return;
1015            }
1016
1017            switch (cNT) {
1018                case 'B': {
1019                    if (isUnsigned) {
1020                        min = 0;
1021                        max = 255;
1022                    }
1023                    else {
1024                        min = Byte.MIN_VALUE;
1025                        max = Byte.MAX_VALUE;
1026                    }
1027
1028                    if ((d > max) || (d < min)) {
1029                        Tools.showError(display.getShells()[0], "Update",
1030                                "Data is out of range[" + min + ", " + max + "]: " + theToken);
1031                    }
1032                    else {
1033                        Array.setByte(data, i, (byte) d);
1034                    }
1035                    break;
1036                }
1037                case 'S': {
1038                    if (isUnsigned) {
1039                        min = 0;
1040                        max = 65535;
1041                    }
1042                    else {
1043                        min = Short.MIN_VALUE;
1044                        max = Short.MAX_VALUE;
1045                    }
1046
1047                    if ((d > max) || (d < min)) {
1048                        Tools.showError(display.getShells()[0], "Update",
1049                                "Data is out of range[" + min + ", " + max + "]: " + theToken);
1050                    }
1051                    else {
1052                        Array.setShort(data, i, (short) d);
1053                    }
1054                    break;
1055                }
1056                case 'I': {
1057                    if (isUnsigned) {
1058                        min = 0;
1059                        max = 4294967295L;
1060                    }
1061                    else {
1062                        min = Integer.MIN_VALUE;
1063                        max = Integer.MAX_VALUE;
1064                    }
1065
1066                    if ((d > max) || (d < min)) {
1067                        Tools.showError(display.getShells()[0], "Update",
1068                                "Data is out of range[" + min + ", " + max + "]: " + theToken);
1069                    }
1070                    else {
1071                        Array.setInt(data, i, (int) d);
1072                    }
1073                    break;
1074                }
1075                case 'J':
1076                    long lvalue = 0;
1077                    if (isUnsigned) {
1078                        if (theToken != null) {
1079                            String theValue = theToken;
1080                            BigInteger maxJ = new BigInteger("18446744073709551615");
1081                            BigInteger big = new BigInteger(theValue);
1082                            if ((big.compareTo(maxJ) > 0) || (big.compareTo(BigInteger.ZERO) < 0)) {
1083                                Tools.showError(display.getShells()[0], "Update",
1084                                        "Data is out of range[" + min + ", " + max + "]: " + theToken);
1085                            }
1086                            lvalue = big.longValue();
1087                            log.trace("updateAttributeValue(): big.longValue={}", lvalue);
1088                            Array.setLong(data, i, lvalue);
1089                        }
1090                        else
1091                            Array.set(data, i, theToken);
1092                    }
1093                    else {
1094                        min = Long.MIN_VALUE;
1095                        max = Long.MAX_VALUE;
1096                        if ((d > max) || (d < min)) {
1097                            Tools.showError(display.getShells()[0], "Update",
1098                                    "Data is out of range[" + min + ", " + max + "]: " + theToken);
1099                        }
1100                        lvalue = (long) d;
1101                        log.trace("updateAttributeValue(): longValue={}", lvalue);
1102                        Array.setLong(data, i, lvalue);
1103                    }
1104                    break;
1105                case 'F':
1106                    Array.setFloat(data, i, (float) d);
1107                    break;
1108                case 'D':
1109                    Array.setDouble(data, i, d);
1110                    break;
1111                default:
1112                    Array.set(data, i, theToken);
1113                    break;
1114            }
1115        }
1116
1117        try {
1118            dataObject.getFileFormat().writeAttribute(dataObject, attr, true);
1119        }
1120        catch (Exception ex) {
1121            log.debug("updateAttributeValue(): writeAttribute failure: ", ex);
1122            Tools.showError(display.getShells()[0], "Update", ex.getMessage());
1123            return;
1124        }
1125
1126        /* Update the attribute table */
1127        int selectionIndex = attrTable.getSelectionIndex();
1128        if (selectionIndex < 0) {
1129            Tools.showError(Display.getDefault().getShells()[0], "Update", "No Attribute selected");
1130            return;
1131        }
1132
1133        attrTable.getItem(selectionIndex).setText(3, attr.toAttributeString(", "));
1134
1135        if (dataObject instanceof MetaDataContainer) {
1136            try {
1137                ((MetaDataContainer) dataObject).updateMetadata(attr);
1138            }
1139            catch (Exception ex) {
1140                log.debug("updateAttributeValue(): updateMetadata() failure:", ex);
1141                Tools.showError(display.getShells()[0], "Update", ex.getMessage());
1142            }
1143        }
1144    }
1145
1146    private void addAttributeTableItem(Table table, Attribute attr) {
1147        if (table == null || attr == null) {
1148            log.debug("addAttributeTableItem(): table or attribute is null");
1149            return;
1150        }
1151
1152        String attrName = attr.getAttributeName();
1153        String attrType = attr.getAttributeDatatype().getDescription();
1154        StringBuilder attrSize = new StringBuilder();
1155        String attrValue = attr.toAttributeString(", ", 50);
1156        String[] rowData = new String[attrTableColNames.length];
1157
1158        if (attrName == null) attrName = "null";
1159        if (attrType == null) attrType = "null";
1160        if (attrValue == null) attrValue = "null";
1161
1162        TableItem item = new TableItem(attrTable, SWT.NONE);
1163        item.setFont(curFont);
1164        item.setData(attr);
1165
1166        if (attr.getProperty("field") != null) {
1167            rowData[0] = attrName + " {Field: " + attr.getProperty("field") + "}";
1168        }
1169        else {
1170            rowData[0] = attrName;
1171        }
1172
1173        if (attr.isAttributeNULL()) {
1174            attrSize.append("NULL");
1175        }
1176        else if (attr.isAttributeScalar()) {
1177            attrSize.append("Scalar");
1178        }
1179        else {
1180            long[] dims = attr.getAttributeDims();
1181            attrSize.append(String.valueOf(dims[0]));
1182            for (int j = 1; j < dims.length; j++) {
1183                attrSize.append(" x ").append(dims[j]);
1184            }
1185        }
1186
1187        rowData[1] = attrType;
1188        rowData[2] = attrSize.toString();
1189        if (attr.isAttributeNULL())
1190            rowData[3] = "NULL";
1191        else
1192            rowData[3] = attrValue;
1193
1194        item.setText(rowData);
1195    }
1196
1197    /**
1198     * Updates the current font.
1199     *
1200     * @param font the new font
1201     */
1202    public void updateFont(Font font) {
1203        if (curFont != null)
1204            curFont.dispose();
1205
1206        log.trace("updateFont():");
1207        curFont = font;
1208
1209        attributeInfoPane.setFont(font);
1210        attributeInfoPane.pack();
1211        attributeInfoPane.requestLayout();
1212
1213        generalObjectInfoPane.setFont(font);
1214        generalObjectInfoPane.pack();
1215        generalObjectInfoPane.requestLayout();
1216    }
1217
1218    private class UserBlockDialog extends Dialog {
1219        private Shell          shell;
1220
1221        private final HObject  obj;
1222
1223        private byte[]         userBlock;
1224
1225        private final String[] displayChoices = { "Text", "Binary", "Octal", "Hexadecimal", "Decimal" };
1226
1227        private Button         jamButton;
1228        private Text           userBlockArea;
1229
1230        public UserBlockDialog(Shell parent, int style, HObject obj) {
1231            super(parent, style);
1232
1233            this.obj = obj;
1234
1235            userBlock = Tools.getHDF5UserBlock(obj.getFile());
1236        }
1237
1238        public void open() {
1239            Shell openParent = getParent();
1240            shell = new Shell(openParent, SWT.DIALOG_TRIM | SWT.RESIZE);
1241            shell.setFont(curFont);
1242            shell.setText("User Block - " + obj);
1243            shell.setLayout(new GridLayout(5, false));
1244
1245            Label label = new Label(shell, SWT.RIGHT);
1246            label.setFont(curFont);
1247            label.setText("Display As: ");
1248
1249            Combo userBlockDisplayChoice = new Combo(shell, SWT.SINGLE | SWT.READ_ONLY);
1250            userBlockDisplayChoice.setFont(curFont);
1251            userBlockDisplayChoice.setItems(displayChoices);
1252            userBlockDisplayChoice.select(0);
1253            userBlockDisplayChoice.addSelectionListener(new SelectionAdapter() {
1254                @Override
1255                public void widgetSelected(SelectionEvent e) {
1256                    Combo source = (Combo) e.widget;
1257                    int type = 0;
1258
1259                    String typeName = source.getItem(source.getSelectionIndex());
1260
1261                    jamButton.setEnabled(false);
1262                    userBlockArea.setEditable(false);
1263
1264                    if (typeName.equalsIgnoreCase("Text")) {
1265                        type = 0;
1266                        jamButton.setEnabled(true);
1267                        userBlockArea.setEditable(true);
1268                    }
1269                    else if (typeName.equalsIgnoreCase("Binary")) {
1270                        type = 2;
1271                    }
1272                    else if (typeName.equalsIgnoreCase("Octal")) {
1273                        type = 8;
1274                    }
1275                    else if (typeName.equalsIgnoreCase("Hexadecimal")) {
1276                        type = 16;
1277                    }
1278                    else if (typeName.equalsIgnoreCase("Decimal")) {
1279                        type = 10;
1280                    }
1281
1282                    showUserBlockAs(type);
1283                }
1284            });
1285
1286            Label dummyLabel = new Label(shell, SWT.RIGHT);
1287            dummyLabel.setFont(curFont);
1288            dummyLabel.setText("");
1289            dummyLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
1290
1291            Label sizeLabel = new Label(shell, SWT.RIGHT);
1292            sizeLabel.setFont(curFont);
1293            sizeLabel.setText("Header Size (Bytes): 0");
1294
1295            jamButton = new Button(shell, SWT.PUSH);
1296            jamButton.setFont(curFont);
1297            jamButton.setText("Save User Block");
1298            jamButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
1299            jamButton.addSelectionListener(new SelectionAdapter() {
1300                @Override
1301                public void widgetSelected(SelectionEvent e) {
1302                    writeUserBlock();
1303                }
1304            });
1305
1306            ScrolledComposite userBlockScroller = new ScrolledComposite(shell, SWT.V_SCROLL | SWT.BORDER);
1307            userBlockScroller.setExpandHorizontal(true);
1308            userBlockScroller.setExpandVertical(true);
1309            userBlockScroller.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));
1310
1311            userBlockArea = new Text(userBlockScroller, SWT.MULTI | SWT.WRAP);
1312            userBlockArea.setEditable(true);
1313            userBlockArea.setFont(curFont);
1314            userBlockScroller.setContent(userBlockArea);
1315
1316            Button closeButton = new Button(shell, SWT.CENTER);
1317            closeButton.setFont(curFont);
1318            closeButton.setText(" &Close ");
1319            closeButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, false, 5, 1));
1320            closeButton.addSelectionListener(new SelectionAdapter() {
1321                @Override
1322                public void widgetSelected(SelectionEvent e) {
1323                    shell.dispose();
1324                }
1325            });
1326
1327            if (userBlock != null) {
1328                int headSize = showUserBlockAs(0);
1329                sizeLabel.setText("Header Size (Bytes): " + headSize);
1330            }
1331            else {
1332                userBlockDisplayChoice.setEnabled(false);
1333            }
1334
1335            shell.pack();
1336
1337            Rectangle parentBounds = openParent.getBounds();
1338
1339            Point shellSize = new Point((int) (0.5 * parentBounds.width), (int) (0.5 * parentBounds.height));
1340            shell.setSize(shellSize);
1341
1342            shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
1343                    (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
1344
1345            shell.open();
1346
1347            Display openDisplay = openParent.getDisplay();
1348            while (!shell.isDisposed()) {
1349                if (!openDisplay.readAndDispatch()) openDisplay.sleep();
1350            }
1351        }
1352
1353        private int showUserBlockAs(int radix) {
1354            if (userBlock == null) return 0;
1355
1356            int headerSize = 0;
1357
1358            String userBlockInfo = null;
1359            if ((radix == 2) || (radix == 8) || (radix == 16) || (radix == 10)) {
1360                StringBuilder sb = new StringBuilder();
1361                for (headerSize = 0; headerSize < userBlock.length; headerSize++) {
1362                    int intValue = userBlock[headerSize];
1363                    if (intValue < 0) {
1364                        intValue += 256;
1365                    }
1366                    else if (intValue == 0) {
1367                        break; // null end
1368                    }
1369
1370                    sb.append(Integer.toString(intValue, radix)).append(" ");
1371                }
1372                userBlockInfo = sb.toString();
1373            }
1374            else {
1375                userBlockInfo = new String(userBlock).trim();
1376                if (userBlockInfo != null) {
1377                    headerSize = userBlockInfo.length();
1378                }
1379            }
1380
1381            userBlockArea.setText(userBlockInfo);
1382
1383            return headerSize;
1384        }
1385
1386        private void writeUserBlock() {
1387            if (!obj.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
1388                return;
1389            }
1390
1391            int blkSize0 = 0;
1392            if (userBlock != null) {
1393                blkSize0 = userBlock.length;
1394                // The super block space is allocated by offset 0, 512, 1024, 2048, etc
1395                if (blkSize0 > 0) {
1396                    int offset = 512;
1397                    while (offset < blkSize0) {
1398                        offset *= 2;
1399                    }
1400                    blkSize0 = offset;
1401                }
1402            }
1403
1404            int blkSize1 = 0;
1405            String userBlockStr = userBlockArea.getText();
1406            if (userBlockStr == null) {
1407                if (blkSize0 <= 0) {
1408                    return; // nothing to write
1409                }
1410                else {
1411                    userBlockStr = " "; // want to wipe out old userblock content
1412                }
1413            }
1414            byte[] buf = null;
1415            buf = userBlockStr.getBytes();
1416
1417            blkSize1 = buf.length;
1418            if (blkSize1 <= blkSize0) {
1419                java.io.RandomAccessFile raf = null;
1420                try {
1421                    raf = new java.io.RandomAccessFile(obj.getFile(), "rw");
1422                }
1423                catch (Exception ex) {
1424                    Tools.showError(shell, "Save", "Can't open output file: " + obj.getFile());
1425                    return;
1426                }
1427
1428                try {
1429                    raf.seek(0);
1430                    raf.write(buf, 0, buf.length);
1431                    raf.seek(buf.length);
1432                    if (blkSize0 > buf.length) {
1433                        byte[] padBuf = new byte[blkSize0 - buf.length];
1434                        raf.write(padBuf, 0, padBuf.length);
1435                    }
1436                }
1437                catch (Exception ex) {
1438                    log.debug("raf write:", ex);
1439                }
1440                try {
1441                    raf.close();
1442                }
1443                catch (Exception ex) {
1444                    log.debug("raf close:", ex);
1445                }
1446
1447                Tools.showInformation(shell, "Save", "Saving user block is successful.");
1448            }
1449            else {
1450                // must rewrite the whole file
1451                MessageDialog confirm = new MessageDialog(shell, "Save", null,
1452                        "The user block to write is " + blkSize1 + " (bytes),\n"
1453                                + "which is larger than the user block space in file " + blkSize0 + " (bytes).\n"
1454                                + "To expand the user block, the file must be rewritten.\n\n"
1455                                + "Do you want to replace the current file? Click "
1456                                + "\n\"Yes\" to replace the current file," + "\n\"No\" to save to a different file, "
1457                                + "\n\"Cancel\" to quit without saving the change.\n\n ",
1458                                MessageDialog.QUESTION_WITH_CANCEL, new String[] { "Yes", "No", "Cancel" }, 0);
1459                int op = confirm.open();
1460
1461                if (op == 2) return;
1462
1463                String fin = obj.getFile();
1464
1465                String fout = fin + "~copy.h5";
1466                if (fin.endsWith(".h5")) {
1467                    fout = fin.substring(0, fin.length() - 3) + "~copy.h5";
1468                }
1469                else if (fin.endsWith(".hdf5")) {
1470                    fout = fin.substring(0, fin.length() - 5) + "~copy.h5";
1471                }
1472
1473                File outFile = null;
1474
1475                if (op == 1) {
1476                    FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
1477                    fChooser.setFileName(fout);
1478
1479                    DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF5();
1480                    fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() });
1481                    fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() });
1482                    fChooser.setFilterIndex(1);
1483
1484                    if (fChooser.open() == null) return;
1485
1486                    File chosenFile = new File(fChooser.getFileName());
1487
1488                    outFile = chosenFile;
1489                    fout = outFile.getAbsolutePath();
1490                }
1491                else {
1492                    outFile = new File(fout);
1493                }
1494
1495                if (!outFile.exists()) {
1496                    try {
1497                        if (!outFile.createNewFile())
1498                            log.debug("Error creating file {}", fout);
1499                    }
1500                    catch (Exception ex) {
1501                        Tools.showError(shell, "Save", "Failed to write user block into file.");
1502                        return;
1503                    }
1504                }
1505
1506                // close the file
1507                TreeView view = viewManager.getTreeView();
1508
1509                try {
1510                    view.closeFile(view.getSelectedFile());
1511                }
1512                catch (Exception ex) {
1513                    log.debug("Error closing file {}", fin);
1514                }
1515
1516                if (Tools.setHDF5UserBlock(fin, fout, buf)) {
1517                    if (op == 1) {
1518                        fin = fout; // open the new file
1519                    }
1520                    else {
1521                        File oldFile = new File(fin);
1522                        boolean status = oldFile.delete();
1523                        if (status) {
1524                            if (!outFile.renameTo(oldFile))
1525                                log.debug("Error renaming file {}", fout);
1526                        }
1527                        else {
1528                            Tools.showError(shell, "Save", "Cannot replace the current file.\nPlease save to a different file.");
1529                            outFile.delete();
1530                        }
1531                    }
1532                }
1533                else {
1534                    Tools.showError(shell, "Save", "Failed to write user block into file.");
1535                    outFile.delete();
1536                }
1537
1538                // reopen the file
1539                shell.dispose();
1540
1541                try {
1542                    int access_mode = FileFormat.WRITE;
1543                    if (ViewProperties.isReadOnly())
1544                        access_mode = FileFormat.READ;
1545                    else if (ViewProperties.isReadSWMR())
1546                        access_mode = FileFormat.READ | FileFormat.MULTIREAD;
1547                    view.openFile(fin, access_mode);
1548                }
1549                catch (Exception ex) {
1550                    log.debug("Error opening file {}", fin);
1551                }
1552            }
1553        }
1554    }
1555}