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