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