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