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}