    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}