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