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