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