    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.dialog;
    016
    017import java.util.Iterator;
    018import java.util.List;
    019import java.util.StringTokenizer;
    020import java.util.Vector;
    021
    022import org.slf4j.Logger;
    023import org.slf4j.LoggerFactory;
    024
    025import org.eclipse.swt.SWT;
    026import org.eclipse.swt.custom.CCombo;
    027import org.eclipse.swt.custom.TableEditor;
    028import org.eclipse.swt.events.DisposeEvent;
    029import org.eclipse.swt.events.DisposeListener;
    030import org.eclipse.swt.events.ModifyEvent;
    031import org.eclipse.swt.events.ModifyListener;
    032import org.eclipse.swt.events.SelectionAdapter;
    033import org.eclipse.swt.events.SelectionEvent;
    034import org.eclipse.swt.events.TraverseEvent;
    035import org.eclipse.swt.events.TraverseListener;
    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.Display;
    044import org.eclipse.swt.widgets.Event;
    045import org.eclipse.swt.widgets.Label;
    046import org.eclipse.swt.widgets.Listener;
    047import org.eclipse.swt.widgets.Shell;
    048import org.eclipse.swt.widgets.Table;
    049import org.eclipse.swt.widgets.TableColumn;
    050import org.eclipse.swt.widgets.TableItem;
    051import org.eclipse.swt.widgets.Text;
    052
    053import hdf.object.CompoundDS;
    054import hdf.object.Dataset;
    055import hdf.object.Datatype;
    056import hdf.object.Group;
    057import hdf.object.HObject;
    058
    059import hdf.object.h5.H5CompoundDS;
    060
    061import hdf.view.Tools;
    062import hdf.view.ViewProperties;
    063
    064/**
    065 * NewCompoundDatasetDialog shows a message dialog requesting user input for creating
    066 * a new HDF4/5 compound dataset.
    067 *
    068 * @author Jordan T. Henderson
    069 * @version 2.4 1/7/2015
    070 */
    071public class NewCompoundDatasetDialog extends NewDataObjectDialog {
    072
    073    private static final Logger log = LoggerFactory.getLogger(NewCompoundDatasetDialog.class);
    074
    075    private static final String[] DATATYPE_NAMES   = {
    076        "byte (8-bit)", // 0
    077        "short (16-bit)", // 1
    078        "int (32-bit)", // 2
    079        "unsigned byte (8-bit)", // 3
    080        "unsigned short (16-bit)", // 4
    081        "unsigned int (32-bit)", // 5
    082        "long (64-bit)", // 6
    083        "float", // 7
    084        "double", // 8
    085        "string", // 9
    086        "enum", // 10
    087        "unsigned long (64-bit)" // 11
    088    };
    089
    090    private Combo                 parentChoice, nFieldBox, templateChoice;
    091
    092    /** A list of current groups */
    093    private Vector<Group>         groupList;
    094    private Vector<CompoundDS>    compoundDSList;
    095
    096    private int                   numberOfMembers;
    097
    098    private Table                 table;
    099
    100    private TableEditor[][]       editors;
    101
    102    private Text                  nameField, currentSizeField, maxSizeField, chunkSizeField;
    103
    104    private Combo                 compressionLevel, rankChoice;
    105    private Button                checkCompression;
    106    private Button                checkContiguous, checkChunked;
    107
    108    /**
    109     * Constructs a NewCompoundDatasetDialog with specified list of possible parent
    110     * groups.
    111     *
    112     * @param parent
    113     *            the parent shell of the dialog
    114     * @param pGroup
    115     *            the parent group which the new group is added to.
    116     * @param objs
    117     *            the list of all objects.
    118     */
    119    public NewCompoundDatasetDialog(Shell parent, Group pGroup, List<?> objs) {
    120        super(parent, pGroup, objs);
    121
    122        numberOfMembers = 2;
    123
    124        groupList = new Vector<>(objs.size());
    125        compoundDSList = new Vector<>(objs.size());
    126    }
    127
    128    /**
    129     * Open the NewCompoundDatasetDialog for adding a new compound dataset.
    130     */
    131    public void open() {
    132        Shell parent = getParent();
    133        shell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
    134        shell.setFont(curFont);
    135        shell.setText("New Compound Dataset...");
    136        shell.setImages(ViewProperties.getHdfIcons());
    137        shell.setLayout(new GridLayout(1, false));
    138
    139
    140        // Create Name/Parent Group/Import field region
    141        Composite fieldComposite = new Composite(shell, SWT.NONE);
    142        GridLayout layout = new GridLayout(2, false);
    143        layout.verticalSpacing = 0;
    144        fieldComposite.setLayout(layout);
    145        fieldComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    146
    147        Label label = new Label(fieldComposite, SWT.LEFT);
    148        label.setFont(curFont);
    149        label.setText("Dataset name: ");
    150
    151        nameField = new Text(fieldComposite, SWT.SINGLE | SWT.BORDER);
    152        nameField.setFont(curFont);
    153        nameField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    154
    155        label = new Label(fieldComposite, SWT.LEFT);
    156        label.setFont(curFont);
    157        label.setText("Parent group: ");
    158
    159        parentChoice = new Combo(fieldComposite, SWT.DROP_DOWN | SWT.READ_ONLY);
    160        parentChoice.setFont(curFont);
    161        parentChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    162        parentChoice.addSelectionListener(new SelectionAdapter() {
    163            @Override
    164            public void widgetSelected(SelectionEvent e) {
    165                parentObj = groupList.get(parentChoice.getSelectionIndex());
    166            }
    167        });
    168
    169        Object obj = null;
    170        Iterator<?> iterator = objList.iterator();
    171
    172        while (iterator.hasNext()) {
    173            obj = iterator.next();
    174            if (obj instanceof Group) {
    175                Group g = (Group) obj;
    176                groupList.add(g);
    177                if (g.isRoot()) {
    178                    parentChoice.add(HObject.SEPARATOR);
    179                }
    180                else {
    181                    parentChoice.add(g.getPath() + g.getName() + HObject.SEPARATOR);
    182                }
    183            }
    184            else if (obj instanceof CompoundDS) {
    185                compoundDSList.add((CompoundDS) obj);
    186            }
    187        }
    188
    189        if (((Group) parentObj).isRoot()) {
    190            parentChoice.select(parentChoice.indexOf(HObject.SEPARATOR));
    191        }
    192        else {
    193            parentChoice.select(parentChoice.indexOf(parentObj.getPath() + parentObj.getName() + HObject.SEPARATOR));
    194        }
    195
    196        label = new Label(fieldComposite, SWT.LEFT);
    197        label.setFont(curFont);
    198        label.setText("Import template: ");
    199
    200        templateChoice = new Combo(fieldComposite, SWT.DROP_DOWN | SWT.READ_ONLY);
    201        templateChoice.setFont(curFont);
    202        templateChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    203        templateChoice.addSelectionListener(new SelectionAdapter() {
    204            @Override
    205            public void widgetSelected(SelectionEvent e) {
    206                CompoundDS dset = null;
    207                String name = templateChoice.getItem(templateChoice.getSelectionIndex());
    208
    209                log.trace("templateChoice start name={}", name);
    210                for (CompoundDS ds : compoundDSList)
    211                    if (ds.getName().equals(name))
    212                        dset = ds;
    213
    214                if (dset == null) return;
    215
    216                if (!dset.isInited())
    217                    dset.init();
    218
    219                int rank = dset.getRank();
    220                rankChoice.select(rank - 1);
    221                long[] dims = dset.getDims();
    222                final String[] mNames = dset.getMemberNames();
    223                int[] mOrders = dset.getMemberOrders();
    224                Datatype[] mTypes = dset.getMemberTypes();
    225
    226                String sizeStr = String.valueOf(dims[0]);
    227                for (int i = 1; i < rank; i++) {
    228                    sizeStr += "x" + dims[i];
    229                }
    230                currentSizeField.setText(sizeStr);
    231
    232                try {
    233                    ((H5CompoundDS)dset).getMetadata();
    234                } // get chunking and compression info
    235                catch (Exception ex) {
    236                    log.debug("get chunking and compression info:", ex);
    237                }
    238                long[] chunks = dset.getChunkSize();
    239                if (chunks != null) {
    240                    checkChunked.setSelection(true);
    241                    sizeStr = String.valueOf(chunks[0]);
    242                    for (int i = 1; i < rank; i++) {
    243                        sizeStr += "x" + chunks[i];
    244                    }
    245                    chunkSizeField.setText(sizeStr);
    246                }
    247
    248                String compression = dset.getCompression();
    249                if (compression != null) {
    250                    int clevel = -1;
    251                    int comp_pos = Dataset.COMPRESSION_GZIP_TXT.length();
    252                    int idx = compression.indexOf(Dataset.COMPRESSION_GZIP_TXT);
    253                    if (idx >= 0) {
    254                        try {
    255                            clevel = Integer.parseInt(compression.substring(idx + comp_pos, idx + comp_pos +1));
    256                        }
    257                        catch (NumberFormatException ex) {
    258                            clevel = -1;
    259                        }
    260                    }
    261                    if (clevel > 0) {
    262                        checkCompression.setSelection(true);
    263                        compressionLevel.select(clevel);
    264                    }
    265                }
    266
    267                nFieldBox.select(dset.getMemberCount() - 1);
    268                nFieldBox.notifyListeners(SWT.Selection, new Event());
    269                for (int i = 0; i < numberOfMembers; i++) {
    270                    ((Text) editors[i][0].getEditor()).setText(mNames[i]);
    271
    272                    log.trace("mNames[{}] = {}", i, mNames[i]);
    273                    int typeIdx = -1;
    274                    int tclass = mTypes[i].getDatatypeClass();
    275                    long tsize = mTypes[i].getDatatypeSize();
    276                    int tsigned = mTypes[i].getDatatypeSign();
    277                    if (tclass == Datatype.CLASS_ARRAY) {
    278                        tclass = mTypes[i].getDatatypeBase().getDatatypeClass();
    279                        tsize = mTypes[i].getDatatypeBase().getDatatypeSize();
    280                        tsigned = mTypes[i].getDatatypeBase().getDatatypeSign();
    281                    }
    282                    if (tclass == Datatype.CLASS_CHAR) {
    283                        if (tsigned == Datatype.SIGN_NONE) {
    284                            if (tsize == 1) {
    285                                typeIdx = 3;
    286                            }
    287                        }
    288                        else {
    289                            if (tsize == 1) {
    290                                typeIdx = 0;
    291                            }
    292                        }
    293                    }
    294                    if (tclass == Datatype.CLASS_INTEGER) {
    295                        if (tsigned == Datatype.SIGN_NONE) {
    296                            if (tsize == 1) {
    297                                typeIdx = 3;
    298                            }
    299                            else if (tsize == 2) {
    300                                typeIdx = 4;
    301                            }
    302                            else if (tsize == 4) {
    303                                typeIdx = 5;
    304                            }
    305                            else {
    306                                typeIdx = 11;
    307                            }
    308                        }
    309                        else {
    310                            if (tsize == 1) {
    311                                typeIdx = 0;
    312                            }
    313                            else if (tsize == 2) {
    314                                typeIdx = 1;
    315                            }
    316                            else if (tsize == 4) {
    317                                typeIdx = 2;
    318                            }
    319                            else {
    320                                typeIdx = 6;
    321                            }
    322                        }
    323                    }
    324                    else if (tclass == Datatype.CLASS_FLOAT) {
    325                        if (tsize == 4) {
    326                            typeIdx = 7;
    327                        }
    328                        else {
    329                            typeIdx = 8;
    330                        }
    331                    }
    332                    else if (tclass == Datatype.CLASS_STRING) {
    333                        typeIdx = 9;
    334                    }
    335                    else if (tclass == Datatype.CLASS_ENUM) {
    336                        typeIdx = 10;
    337                    }
    338                    if (typeIdx < 0) {
    339                        continue;
    340                    }
    341                    log.trace("typeIdx={}", typeIdx);
    342
    343                    CCombo typeCombo = ((CCombo) editors[i][1].getEditor());
    344                    typeCombo.select(typeIdx);
    345                    typeCombo.notifyListeners(SWT.Selection, new Event());
    346
    347                    //TODO: Array size is wrong for enums and for array types. Array types such as 8x8
    348                    // show as size 64, not 8x8
    349                    if (tclass == Datatype.CLASS_STRING) {
    350                        ((Text) editors[i][2].getEditor()).setText(String.valueOf(tsize));
    351                    }
    352                    else if (tclass == Datatype.CLASS_ENUM) {
    353                        ((Text) editors[i][2].getEditor()).setText(mTypes[i].getEnumMembersAsString());
    354                        table.getItem(i).setText(2, mTypes[i].getEnumMembersAsString());
    355                    }
    356                    else {
    357                        ((Text) editors[i][2].getEditor()).setText(String.valueOf(mOrders[i]));
    358                    }
    359                } //  (int i=0; i<numberOfMembers; i++)
    360            }
    361        });
    362
    363        Iterator<CompoundDS> it = compoundDSList.iterator();
    364        while(it.hasNext()) {
    365            templateChoice.add(it.next().getName());
    366        }
    367
    368
    369        // Create Dataspace region
    370        org.eclipse.swt.widgets.Group dataspaceGroup = new org.eclipse.swt.widgets.Group(shell, SWT.NONE);
    371        dataspaceGroup.setLayout(new GridLayout(3, true));
    372        dataspaceGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    373        dataspaceGroup.setFont(curFont);
    374        dataspaceGroup.setText("Dataspace");
    375
    376        label = new Label(dataspaceGroup, SWT.LEFT);
    377        label.setFont(curFont);
    378        label.setText("No. of dimensions");
    379
    380        label = new Label(dataspaceGroup, SWT.LEFT);
    381        label.setFont(curFont);
    382        label.setText("Current size");
    383
    384        label = new Label(dataspaceGroup, SWT.LEFT);
    385        label.setFont(curFont);
    386        label.setText("Max size (-1 for unlimited)");
    387
    388        rankChoice = new Combo(dataspaceGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
    389        rankChoice.setFont(curFont);
    390        rankChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    391        rankChoice.addSelectionListener(new SelectionAdapter() {
    392            @Override
    393            public void widgetSelected(SelectionEvent e) {
    394                int rank = rankChoice.getSelectionIndex() + 1;
    395                String currentSizeStr = "1";
    396                String maxSizeStr = "0";
    397
    398                for (int i = 1; i < rank; i++) {
    399                    currentSizeStr += " x 1";
    400                    maxSizeStr += " x 0";
    401                }
    402
    403                currentSizeField.setText(currentSizeStr);
    404                maxSizeField.setText(maxSizeStr);
    405
    406                String currentStr = currentSizeField.getText();
    407                int idx = currentStr.lastIndexOf('x');
    408                String chunkStr = "1";
    409
    410                if (rank <= 1) {
    411                    chunkStr = currentStr;
    412                }
    413                else {
    414                    for (int i = 1; i < rank - 1; i++) {
    415                        chunkStr += " x 1";
    416                    }
    417                    if (idx > 0) {
    418                        chunkStr += " x " + currentStr.substring(idx + 1);
    419                    }
    420                }
    421
    422                chunkSizeField.setText(chunkStr);
    423            }
    424        });
    425
    426        for (int i = 1; i < 33; i++) {
    427            rankChoice.add(String.valueOf(i));
    428        }
    429
    430        currentSizeField = new Text(dataspaceGroup, SWT.SINGLE | SWT.BORDER);
    431        currentSizeField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    432        currentSizeField.setFont(curFont);
    433        currentSizeField.setText("1");
    434
    435        maxSizeField = new Text(dataspaceGroup, SWT.SINGLE | SWT.BORDER);
    436        maxSizeField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    437        maxSizeField.setFont(curFont);
    438        maxSizeField.setText("0");
    439
    440
    441        // Create Data Layout/Compression region
    442        org.eclipse.swt.widgets.Group layoutGroup = new org.eclipse.swt.widgets.Group(shell, SWT.NONE);
    443        layoutGroup.setLayout(new GridLayout(7, false));
    444        layoutGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    445        layoutGroup.setFont(curFont);
    446        layoutGroup.setText("Data Layout and Compression");
    447
    448        label = new Label(layoutGroup, SWT.LEFT);
    449        label.setFont(curFont);
    450        label.setText("Storage layout: ");
    451
    452        checkContiguous = new Button(layoutGroup, SWT.RADIO);
    453        checkContiguous.setFont(curFont);
    454        checkContiguous.setText("Contiguous");
    455        checkContiguous.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    456        checkContiguous.addSelectionListener(new SelectionAdapter() {
    457            @Override
    458            public void widgetSelected(SelectionEvent e) {
    459                chunkSizeField.setEnabled(false);
    460            }
    461        });
    462
    463        // Dummy labels
    464        label = new Label(layoutGroup, SWT.LEFT);
    465        label.setFont(curFont);
    466        label.setText("");
    467        label = new Label(layoutGroup, SWT.LEFT);
    468        label.setFont(curFont);
    469        label.setText("");
    470
    471        checkChunked = new Button(layoutGroup, SWT.RADIO);
    472        checkChunked.setFont(curFont);
    473        checkChunked.setText("Chunked");
    474        checkChunked.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    475        checkChunked.addSelectionListener(new SelectionAdapter() {
    476            @Override
    477            public void widgetSelected(SelectionEvent e) {
    478                chunkSizeField.setEnabled(true);
    479
    480                String currentStr = currentSizeField.getText();
    481                int idx = currentStr.lastIndexOf('x');
    482                String chunkStr = "1";
    483
    484                int rank = rankChoice.getSelectionIndex() + 1;
    485                if (rank <= 1) {
    486                    chunkStr = currentStr;
    487                }
    488                else {
    489                    for (int i = 1; i < rank - 1; i++) {
    490                        chunkStr += " x 1";
    491                    }
    492                    if (idx > 0) {
    493                        chunkStr += " x " + currentStr.substring(idx + 1);
    494                    }
    495                }
    496
    497                chunkSizeField.setText(chunkStr);
    498            }
    499        });
    500
    501        label = new Label(layoutGroup, SWT.LEFT);
    502        label.setFont(curFont);
    503        label.setText("Size: ");
    504
    505        chunkSizeField = new Text(layoutGroup, SWT.SINGLE | SWT.BORDER);
    506        chunkSizeField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    507        chunkSizeField.setFont(curFont);
    508        chunkSizeField.setText("1");
    509        chunkSizeField.setEnabled(false);
    510
    511        label = new Label(layoutGroup, SWT.LEFT);
    512        label.setFont(curFont);
    513        label.setText("Compression: ");
    514
    515        checkCompression = new Button(layoutGroup, SWT.CHECK);
    516        checkCompression.setFont(curFont);
    517        checkCompression.setText("gzip");
    518        checkCompression.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    519        checkCompression.addSelectionListener(new SelectionAdapter() {
    520            @Override
    521            public void widgetSelected(SelectionEvent e) {
    522                boolean isCompressed = checkCompression.getSelection();
    523
    524                if (isCompressed) {
    525                    if (!checkChunked.getSelection()) {
    526                        String currentStr = currentSizeField.getText();
    527                        int idx = currentStr.lastIndexOf('x');
    528                        String chunkStr = "1";
    529
    530                        int rank = rankChoice.getSelectionIndex() + 1;
    531                        if (rank <= 1) {
    532                            chunkStr = currentStr;
    533                        }
    534                        else {
    535                            for (int i = 1; i < rank - 1; i++) {
    536                                chunkStr += " x 1";
    537                            }
    538                            if (idx > 0) {
    539                                chunkStr += " x " + currentStr.substring(idx + 1);
    540                            }
    541                        }
    542
    543                        chunkSizeField.setText(chunkStr);
    544                    }
    545
    546                    compressionLevel.setEnabled(true);
    547                    checkContiguous.setEnabled(false);
    548                    checkContiguous.setSelection(false);
    549                    checkChunked.setSelection(true);
    550                    chunkSizeField.setEnabled(true);
    551                }
    552                else {
    553                    compressionLevel.setEnabled(false);
    554                    checkContiguous.setEnabled(true);
    555                }
    556            }
    557        });
    558
    559        label = new Label(layoutGroup, SWT.LEFT);
    560        label.setFont(curFont);
    561        label.setText("Level: ");
    562
    563        compressionLevel = new Combo(layoutGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
    564        compressionLevel.setFont(curFont);
    565        compressionLevel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    566        compressionLevel.setEnabled(false);
    567
    568        for (int i = 0; i < 10; i++) {
    569            compressionLevel.add(String.valueOf(i));
    570        }
    571
    572        label = new Label(layoutGroup, SWT.LEFT);
    573        label.setFont(curFont);
    574        label.setText("");
    575
    576        label = new Label(layoutGroup, SWT.LEFT);
    577        label.setFont(curFont);
    578        label.setText("");
    579
    580        label = new Label(layoutGroup, SWT.LEFT);
    581        label.setFont(curFont);
    582        label.setText("");
    583
    584
    585        // Create Properties region
    586        org.eclipse.swt.widgets.Group propertiesGroup = new org.eclipse.swt.widgets.Group(shell, SWT.NONE);
    587        propertiesGroup.setLayout(new GridLayout(2, false));
    588        propertiesGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    589        propertiesGroup.setFont(curFont);
    590        propertiesGroup.setText("Compound Datatype Properties");
    591
    592        label = new Label(propertiesGroup, SWT.LEFT);
    593        label.setFont(curFont);
    594        label.setText("Number of Members:");
    595
    596        nFieldBox = new Combo(propertiesGroup, SWT.DROP_DOWN);
    597        nFieldBox.setFont(curFont);
    598        nFieldBox.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    599        nFieldBox.addSelectionListener(new SelectionAdapter() {
    600            @Override
    601            public void widgetSelected(SelectionEvent e) {
    602                updateMemberTableItems();
    603            }
    604        });
    605        nFieldBox.addTraverseListener(new TraverseListener() {
    606            @Override
    607            public void keyTraversed(TraverseEvent e) {
    608                if (e.detail == SWT.TRAVERSE_RETURN) updateMemberTableItems();
    609            }
    610        });
    611
    612        for (int i = 1; i <= 100; i++) {
    613            nFieldBox.add(String.valueOf(i));
    614        }
    615
    616        table = new Table(propertiesGroup, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
    617        table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
    618        table.setLinesVisible(false);
    619        table.setHeaderVisible(true);
    620        table.setFont(curFont);
    621
    622        editors = new TableEditor[nFieldBox.getItemCount()][3];
    623
    624        String[] colNames = { "Name", "Datatype", "Array size / String length / Enum names" };
    625
    626        TableColumn column = new TableColumn(table, SWT.NONE);
    627        column.setText(colNames[0]);
    628
    629        column = new TableColumn(table, SWT.NONE);
    630        column.setText(colNames[1]);
    631
    632        column = new TableColumn(table, SWT.NONE);
    633        column.setText(colNames[2]);
    634
    635        for (int i = 0; i < 2; i++) {
    636            TableEditor[] editor = addMemberTableItem(table);
    637            editors[i][0] = editor[0];
    638            editors[i][1] = editor[1];
    639            editors[i][2] = editor[2];
    640        }
    641
    642        for(int i = 0; i < table.getColumnCount(); i++) {
    643            table.getColumn(i).pack();
    644        }
    645
    646        // Last table column always expands to fill remaining table size
    647        table.addListener(SWT.Resize, new Listener() {
    648            @Override
    649            public void handleEvent(Event e) {
    650                Table table = (Table) e.widget;
    651                Rectangle area = table.getClientArea();
    652                int columnCount = table.getColumnCount();
    653                int totalGridLineWidth = (columnCount - 1) * table.getGridLineWidth();
    654
    655                int totalColumnWidth = 0;
    656                for (TableColumn column : table.getColumns()) {
    657                    totalColumnWidth += column.getWidth();
    658                }
    659
    660                int diff = area.width - (totalColumnWidth + totalGridLineWidth);
    661
    662                TableColumn col = table.getColumns()[columnCount - 1];
    663                col.setWidth(diff + col.getWidth());
    664            }
    665        });
    666
    667        // Disable table selection highlighting
    668        table.addListener(SWT.EraseItem, new Listener() {
    669            @Override
    670            public void handleEvent(Event e) {
    671                if ((e.detail & SWT.SELECTED) != 0) {
    672                    e.detail &= ~SWT.SELECTED;
    673                }
    674            }
    675        });
    676
    677        // Create Ok/Cancel button region
    678        Composite buttonComposite = new Composite(shell, SWT.NONE);
    679        buttonComposite.setLayout(new GridLayout(2, true));
    680        buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
    681
    682        Button okButton = new Button(buttonComposite, SWT.PUSH);
    683        okButton.setFont(curFont);
    684        okButton.setText("   &OK   ");
    685        okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
    686        okButton.addSelectionListener(new SelectionAdapter() {
    687            @Override
    688            public void widgetSelected(SelectionEvent e) {
    689                try {
    690                    newObject = createCompoundDS();
    691                }
    692                catch (Exception ex) {
    693                    Tools.showError(shell, "Create", ex.getMessage());
    694                }
    695
    696                if (newObject != null) {
    697                    shell.dispose();
    698                }
    699            }
    700        });
    701
    702        Button cancelButton = new Button(buttonComposite, SWT.PUSH);
    703        cancelButton.setFont(curFont);
    704        cancelButton.setText(" &Cancel ");
    705        cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
    706        cancelButton.addSelectionListener(new SelectionAdapter() {
    707            @Override
    708            public void widgetSelected(SelectionEvent e) {
    709                newObject = null;
    710                shell.dispose();
    711                (groupList).setSize(0);
    712            }
    713        });
    714
    715        templateChoice.deselectAll();
    716        rankChoice.select(0);
    717        checkContiguous.setSelection(true);
    718        compressionLevel.select(6);
    719        nFieldBox.select(nFieldBox.indexOf(String.valueOf(numberOfMembers)));
    720
    721        shell.pack();
    722
    723        table.getColumn(0).setWidth(table.getClientArea().width / 3);
    724        table.getColumn(1).setWidth(table.getClientArea().width / 3);
    725
    726        shell.addDisposeListener(new DisposeListener() {
    727            @Override
    728            public void widgetDisposed(DisposeEvent e) {
    729                if (curFont != null) curFont.dispose();
    730            }
    731        });
    732
    733        shell.setMinimumSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
    734
    735        Rectangle parentBounds = parent.getBounds();
    736        Point shellSize = shell.getSize();
    737        shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
    738                          (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
    739
    740        shell.open();
    741
    742        Display display = shell.getDisplay();
    743        while (!shell.isDisposed())
    744            if (!display.readAndDispatch())
    745                display.sleep();
    746    }
    747
    748    private HObject createCompoundDS() throws Exception {
    749        HObject obj = null;
    750        long dims[], maxdims[], chunks[];
    751        int rank;
    752
    753        maxdims = chunks = null;
    754        String dname = nameField.getText();
    755        if ((dname == null) || (dname.length() <= 0)) {
    756            throw new IllegalArgumentException("Dataset name is empty");
    757        }
    758
    759        Group pgroup = groupList.get(parentChoice.getSelectionIndex());
    760        if (pgroup == null) {
    761            throw new IllegalArgumentException("Invalid parent group");
    762        }
    763
    764        int n = table.getItemCount();
    765        if (n <= 0) {
    766            return null;
    767        }
    768
    769        String[] mNames = new String[n];
    770        Datatype[] mDatatypes = new Datatype[n];
    771        int[] mOrders = new int[n];
    772
    773        for (int i = 0; i < n; i++) {
    774            String name = (String) table.getItem(i).getData("MemberName");
    775            if ((name == null) || (name.length() <= 0)) {
    776                throw new IllegalArgumentException("Member name is empty");
    777            }
    778            mNames[i] = name;
    779            log.trace("createCompoundDS member[{}] name = {}", i, mNames[i]);
    780
    781            int order = 1;
    782            String orderStr = (String) table.getItem(i).getData("MemberSize");
    783            if (orderStr != null) {
    784                try {
    785                    order = Integer.parseInt(orderStr);
    786                }
    787                catch (Exception ex) {
    788                    log.debug("compound order:", ex);
    789                }
    790            }
    791            mOrders[i] = order;
    792
    793            String typeName = (String) table.getItem(i).getData("MemberType");
    794            log.trace("createCompoundDS type[{}] name = {}", i, typeName);
    795            Datatype type = null;
    796            if (DATATYPE_NAMES[0].equals(typeName)) {
    797                type = fileFormat.createDatatype(Datatype.CLASS_INTEGER, 1, Datatype.NATIVE, Datatype.NATIVE);
    798            }
    799            else if (DATATYPE_NAMES[1].equals(typeName)) {
    800                type = fileFormat.createDatatype(Datatype.CLASS_INTEGER, 2, Datatype.NATIVE, Datatype.NATIVE);
    801            }
    802            else if (DATATYPE_NAMES[2].equals(typeName)) {
    803                type = fileFormat.createDatatype(Datatype.CLASS_INTEGER, 4, Datatype.NATIVE, Datatype.NATIVE);
    804            }
    805            else if (DATATYPE_NAMES[3].equals(typeName)) {
    806                type = fileFormat.createDatatype(Datatype.CLASS_INTEGER, 1, Datatype.NATIVE, Datatype.SIGN_NONE);
    807            }
    808            else if (DATATYPE_NAMES[4].equals(typeName)) {
    809                type = fileFormat.createDatatype(Datatype.CLASS_INTEGER, 2, Datatype.NATIVE, Datatype.SIGN_NONE);
    810            }
    811            else if (DATATYPE_NAMES[5].equals(typeName)) {
    812                type = fileFormat.createDatatype(Datatype.CLASS_INTEGER, 4, Datatype.NATIVE, Datatype.SIGN_NONE);
    813            }
    814            else if (DATATYPE_NAMES[6].equals(typeName)) {
    815                type = fileFormat.createDatatype(Datatype.CLASS_INTEGER, 8, Datatype.NATIVE, Datatype.NATIVE);
    816            }
    817            else if (DATATYPE_NAMES[7].equals(typeName)) {
    818                type = fileFormat.createDatatype(Datatype.CLASS_FLOAT, 4, Datatype.NATIVE, Datatype.NATIVE);
    819            }
    820            else if (DATATYPE_NAMES[8].equals(typeName)) {
    821                type = fileFormat.createDatatype(Datatype.CLASS_FLOAT, 8, Datatype.NATIVE, Datatype.NATIVE);
    822            }
    823            else if (DATATYPE_NAMES[9].equals(typeName)) {
    824                type = fileFormat.createDatatype(Datatype.CLASS_STRING, order, Datatype.NATIVE, Datatype.NATIVE);
    825            }
    826            else if (DATATYPE_NAMES[10].equals(typeName)) { // enum
    827                type = fileFormat.createDatatype(Datatype.CLASS_ENUM, 4, Datatype.NATIVE, Datatype.NATIVE);
    828                if ((orderStr == null) || (orderStr.length() < 1) || orderStr.endsWith("...")) {
    829                    shell.getDisplay().beep();
    830                    Tools.showError(shell, "Create", "Invalid member values: " + orderStr);
    831                    return null;
    832                }
    833                else {
    834                    type.setEnumMembers(orderStr);
    835                }
    836            }
    837            else if (DATATYPE_NAMES[11].equals(typeName)) {
    838                type = fileFormat.createDatatype(Datatype.CLASS_INTEGER, 8, Datatype.NATIVE, Datatype.SIGN_NONE);
    839            }
    840            else {
    841                throw new IllegalArgumentException("Invalid data type.");
    842            }
    843            mDatatypes[i] = type;
    844        } //  (int i=0; i<n; i++)
    845
    846        rank = rankChoice.getSelectionIndex() + 1;
    847        log.trace("createCompoundDS rank={}", rank);
    848        StringTokenizer st = new StringTokenizer(currentSizeField.getText(), "x");
    849        if (st.countTokens() < rank) {
    850            shell.getDisplay().beep();
    851            Tools.showError(shell, "Create", "Number of values in the current dimension size is less than " + rank);
    852            return null;
    853        }
    854
    855        long l = 0;
    856        dims = new long[rank];
    857        String token = null;
    858        for (int i = 0; i < rank; i++) {
    859            token = st.nextToken().trim();
    860            try {
    861                l = Long.parseLong(token);
    862            }
    863            catch (NumberFormatException ex) {
    864                shell.getDisplay().beep();
    865                Tools.showError(shell, "Create", "Invalid dimension size: " + currentSizeField.getText());
    866                return null;
    867            }
    868
    869            if (l <= 0) {
    870                shell.getDisplay().beep();
    871                Tools.showError(shell, "Create", "Dimension size must be greater than zero.");
    872                return null;
    873            }
    874
    875            dims[i] = l;
    876        }
    877
    878        st = new StringTokenizer(maxSizeField.getText(), "x");
    879        if (st.countTokens() < rank) {
    880            shell.getDisplay().beep();
    881            Tools.showError(shell, "Create", "Number of values in the max dimension size is less than " + rank);
    882            return null;
    883        }
    884
    885        l = 0;
    886        maxdims = new long[rank];
    887        for (int i = 0; i < rank; i++) {
    888            token = st.nextToken().trim();
    889            try {
    890                l = Long.parseLong(token);
    891            }
    892            catch (NumberFormatException ex) {
    893                shell.getDisplay().beep();
    894                Tools.showError(shell, "Create", "Invalid max dimension size: " + maxSizeField.getText());
    895                return null;
    896            }
    897
    898            if (l < -1) {
    899                shell.getDisplay().beep();
    900                Tools.showError(shell, "Create", "Dimension size cannot be less than -1.");
    901                return null;
    902            }
    903            else if (l == 0) {
    904                l = dims[i];
    905            }
    906
    907            maxdims[i] = l;
    908        }
    909
    910        chunks = null;
    911        if (checkChunked.getSelection()) {
    912            st = new StringTokenizer(chunkSizeField.getText(), "x");
    913            if (st.countTokens() < rank) {
    914                shell.getDisplay().beep();
    915                Tools.showError(shell, "Create", "Number of values in the chunk size is less than " + rank);
    916                return null;
    917            }
    918
    919            l = 0;
    920            chunks = new long[rank];
    921            token = null;
    922            for (int i = 0; i < rank; i++) {
    923                token = st.nextToken().trim();
    924                try {
    925                    l = Long.parseLong(token);
    926                }
    927                catch (NumberFormatException ex) {
    928                    shell.getDisplay().beep();
    929                    Tools.showError(shell, "Create", "Invalid chunk dimension size: " + chunkSizeField.getText());
    930                    return null;
    931                }
    932
    933                if (l < 1) {
    934                    shell.getDisplay().beep();
    935                    Tools.showError(shell, "Create", "Chunk size cannot be less than 1.");
    936                    return null;
    937                }
    938
    939                chunks[i] = l;
    940            } //  (int i=0; i<rank; i++)
    941
    942            long tchunksize = 1, tdimsize = 1;
    943            for (int i = 0; i < rank; i++) {
    944                tchunksize *= chunks[i];
    945                tdimsize *= dims[i];
    946            }
    947
    948            if (tchunksize >= tdimsize) {
    949                shell.getDisplay().beep();
    950                if(!Tools.showConfirm(shell, "Create", "Chunk size is equal/greater than the current size. "
    951                        + "\nAre you sure you want to set chunk size to " + chunkSizeField.getText() + "?")) {
    952                    return null;
    953                }
    954            }
    955
    956            if (tchunksize == 1) {
    957                shell.getDisplay().beep();
    958                if(!Tools.showConfirm(shell, "Create", "Chunk size is one, which may cause large memory overhead for large dataset."
    959                        + "\nAre you sure you want to set chunk size to " + chunkSizeField.getText() + "?")) {
    960                    return null;
    961                }
    962            }
    963
    964        } //  (checkChunked.getSelection())
    965
    966        int gzip = 0;
    967        if (checkCompression.getSelection()) {
    968            gzip = compressionLevel.getSelectionIndex();
    969        }
    970
    971        if (checkChunked.getSelection()) {
    972            obj = fileFormat.createCompoundDS(dname, pgroup, dims, maxdims, chunks, gzip, mNames, mDatatypes, mOrders, null);
    973        }
    974        else {
    975            obj = fileFormat.createCompoundDS(dname, pgroup, dims, maxdims, null, -1, mNames, mDatatypes, mOrders, null);
    976        }
    977        return obj;
    978    }
    979
    980    private void updateMemberTableItems() {
    981        int n = 0;
    982
    983        try {
    984            n = Integer.valueOf(nFieldBox.getItem(nFieldBox.getSelectionIndex())).intValue();
    985        }
    986        catch (Exception ex) {
    987            log.debug("Change number of members:", ex);
    988            return;
    989        }
    990
    991        if (n == numberOfMembers) {
    992            return;
    993        }
    994
    995        if(n > numberOfMembers) {
    996            try {
    997                for (int i = numberOfMembers; i < n; i++) {
    998                    TableEditor[] editor = addMemberTableItem(table);
    999                    editors[i][0] = editor[0];
    1000                    editors[i][1] = editor[1];
    1001                    editors[i][2] = editor[2];
    1002                }
    1003            }
    1004            catch (Exception ex) {
    1005                log.debug("Error adding member table items: ", ex);
    1006                return;
    1007            }
    1008        } else {
    1009            try {
    1010                for(int i = numberOfMembers - 1; i >= n; i--) {
    1011                    table.remove(i);
    1012                }
    1013            }
    1014            catch (Exception ex) {
    1015                log.debug("Error removing member table items: ", ex);
    1016                return;
    1017            }
    1018        }
    1019
    1020        table.setItemCount(n);
    1021        numberOfMembers = n;
    1022    }
    1023
    1024    private TableEditor[] addMemberTableItem(Table table) {
    1025        final TableItem item = new TableItem(table, SWT.NONE);
    1026        final TableEditor[] editor = new TableEditor[3];
    1027
    1028        for (int i = 0; i < editor.length; i++) editor[i] = new TableEditor(table);
    1029
    1030        final Text nameText = new Text(table, SWT.SINGLE | SWT.BORDER);
    1031        nameText.setFont(curFont);
    1032
    1033        editor[0].grabHorizontal = true;
    1034        editor[0].grabVertical = true;
    1035        editor[0].horizontalAlignment = SWT.LEFT;
    1036        editor[0].verticalAlignment = SWT.TOP;
    1037        editor[0].setEditor(nameText, item, 0);
    1038
    1039        nameText.addModifyListener(new ModifyListener() {
    1040            @Override
    1041            public void modifyText(ModifyEvent e) {
    1042                Text text = (Text) e.widget;
    1043                item.setData("MemberName", text.getText());
    1044            }
    1045        });
    1046
    1047        final CCombo typeCombo = new CCombo(table, SWT.DROP_DOWN | SWT.READ_ONLY);
    1048        typeCombo.setFont(curFont);
    1049        typeCombo.setItems(DATATYPE_NAMES);
    1050
    1051        editor[1].grabHorizontal = true;
    1052        editor[1].grabVertical = true;
    1053        editor[1].horizontalAlignment = SWT.LEFT;
    1054        editor[1].verticalAlignment = SWT.TOP;
    1055        editor[1].setEditor(typeCombo, item, 1);
    1056
    1057        typeCombo.addSelectionListener(new SelectionAdapter() {
    1058            @Override
    1059            public void widgetSelected(SelectionEvent e) {
    1060                CCombo combo = (CCombo) e.widget;
    1061                item.setData("MemberType", combo.getItem(combo.getSelectionIndex()));
    1062            }
    1063        });
    1064
    1065        final Text sizeText = new Text(table, SWT.SINGLE | SWT.BORDER);
    1066        sizeText.setFont(curFont);
    1067
    1068        editor[2].grabHorizontal = true;
    1069        editor[2].grabVertical = true;
    1070        editor[2].horizontalAlignment = SWT.LEFT;
    1071        editor[2].verticalAlignment = SWT.TOP;
    1072        editor[2].setEditor(sizeText, item, 2);
    1073
    1074        sizeText.addModifyListener(new ModifyListener() {
    1075            @Override
    1076            public void modifyText(ModifyEvent e) {
    1077                Text text = (Text) e.widget;
    1078                item.setData("MemberSize", text.getText());
    1079            }
    1080        });
    1081
    1082        item.setData("MemberName", "");
    1083        item.setData("MemberType", "");
    1084        item.setData("MemberSize", "");
    1085
    1086        item.addDisposeListener(new DisposeListener() {
    1087            @Override
    1088            public void widgetDisposed(DisposeEvent e) {
    1089                editor[0].dispose();
    1090                editor[1].dispose();
    1091                editor[2].dispose();
    1092                nameText.dispose();
    1093                typeCombo.dispose();
    1094                sizeText.dispose();
    1095            }
    1096        });
    1097
    1098        return editor;
    1099    }
    1100}