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.Vector;
020
021import hdf.object.Datatype;
022import hdf.object.FileFormat;
023import hdf.object.Group;
024import hdf.object.HObject;
025import hdf.object.h5.H5Datatype;
026import hdf.view.Tools;
027import hdf.view.ViewProperties;
028
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032import org.eclipse.swt.SWT;
033import org.eclipse.swt.events.SelectionAdapter;
034import org.eclipse.swt.events.SelectionEvent;
035import org.eclipse.swt.graphics.Font;
036import org.eclipse.swt.layout.GridData;
037import org.eclipse.swt.layout.GridLayout;
038import org.eclipse.swt.widgets.Button;
039import org.eclipse.swt.widgets.Combo;
040import org.eclipse.swt.widgets.Dialog;
041import org.eclipse.swt.widgets.Display;
042import org.eclipse.swt.widgets.Label;
043import org.eclipse.swt.widgets.Shell;
044import org.eclipse.swt.widgets.Text;
045
046/**
047 * NewDataDialog is an intermediate class for creating data types.
048 */
049public class NewDataObjectDialog extends Dialog {
050
051    private static final Logger log = LoggerFactory.getLogger(NewDataObjectDialog.class);
052
053    /** the visual shell for the dialog */
054    protected Shell shell;
055
056    /** the current font */
057    protected Font curFont;
058
059    /** the object which the this object is attached */
060    protected HObject parentObj;
061
062    /** the object referenced */
063    protected HObject refObject;
064
065    /** the object created */
066    protected HObject newObject;
067
068    /** TextField for entering the length of the data array or string. */
069    protected Text lengthField;
070
071    /** The Choice of the datatypes */
072    /** The named datatype combobox for the object */
073    protected Combo namedChoice;
074    /** The class combobox for the object */
075    protected Combo classChoice;
076    /** The size combobox for the object */
077    protected Combo sizeChoice;
078    /** The endianess combobox for the object */
079    protected Combo endianChoice;
080
081    /** The Choice of the object list */
082    /** The committed datatype button for the object */
083    protected Button useCommittedType;
084    /** The unsigned data button for the object */
085    protected Button checkUnsigned;
086    /** The list of objects for the object */
087    protected List<?> objList;
088    /** The list of datatypes for the object */
089    protected List<Datatype> namedList;
090    /** The length label for the object */
091    protected Label arrayLengthLabel;
092
093    /** The attributes of the datatype */
094    /** The default class for the object */
095    public int tclass = Datatype.CLASS_NO_CLASS;
096    /** The default size for the object */
097    public int tsize = Datatype.NATIVE;
098    /** The default  byte order for the object */
099    public int torder = Datatype.NATIVE;
100    /** The default sign for the object */
101    public int tsign = Datatype.NATIVE;
102    /** If the object is an enum object */
103    public boolean isEnum = false;
104    /** The enum mapping for the object */
105    public String strEnumMap = null;
106    /** If the object is a variable length data object */
107    public boolean isVLen = false;
108    /** If the object is a variable length string */
109    public boolean isVlenStr = false;
110
111    /** The file format associated with this object */
112    protected FileFormat fileFormat;
113
114    /** If the object should be attached to a hdf5 object */
115    protected boolean isH5;
116
117    /**
118     * The NewDataObjectDialog constructor.
119     *
120     * @param parent
121     *        the dialog parent shell
122     * @param pGroup
123     *        the dialog parent group object
124     * @param objs
125     *        the list of objects
126     */
127    public NewDataObjectDialog(Shell parent, HObject pGroup, List<?> objs)
128    {
129        super(parent, SWT.APPLICATION_MODAL);
130
131        try {
132            curFont = new Font(Display.getCurrent(), ViewProperties.getFontType(),
133                               ViewProperties.getFontSize(), SWT.NORMAL);
134        }
135        catch (Exception ex) {
136            curFont = null;
137        }
138
139        newObject = null;
140        parentObj = pGroup;
141        objList   = objs;
142
143        fileFormat = pGroup.getFileFormat();
144        isH5       = pGroup.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5));
145    }
146
147    /** the new dataset properties to be created. */
148    public void createDatatypeWidget()
149    {
150        Label label;
151
152        // Create Datatype region
153        org.eclipse.swt.widgets.Group datatypeGroup = new org.eclipse.swt.widgets.Group(shell, SWT.NONE);
154        datatypeGroup.setFont(curFont);
155        datatypeGroup.setText("Datatype");
156        datatypeGroup.setLayout(new GridLayout(4, true));
157        datatypeGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
158
159        useCommittedType = new Button(datatypeGroup, SWT.CHECK);
160        useCommittedType.setFont(curFont);
161        useCommittedType.setText("Use Committed Datatype");
162        useCommittedType.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
163        useCommittedType.setEnabled(isH5);
164
165        if (isH5) {
166            label = new Label(datatypeGroup, SWT.LEFT);
167            label.setFont(curFont);
168            label.setText("Committed Datatype: ");
169
170            namedChoice = new Combo(datatypeGroup, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);
171            namedChoice.setFont(curFont);
172            namedChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
173            namedChoice.addSelectionListener(new SelectionAdapter() {
174                @Override
175                public void widgetSelected(SelectionEvent e)
176                {
177                    refObject = namedList.get(namedChoice.getSelectionIndex());
178                }
179            });
180
181            // Dummy label
182            label = new Label(datatypeGroup, SWT.LEFT);
183            label.setFont(curFont);
184            label.setText("");
185
186            namedList            = new Vector<>(objList.size());
187            Object obj           = null;
188            Iterator<?> iterator = objList.iterator();
189            while (iterator.hasNext()) {
190                obj = iterator.next();
191                if (obj instanceof Datatype) {
192                    H5Datatype ndt = (H5Datatype)obj;
193                    namedList.add(ndt);
194                    namedChoice.add(((Datatype)obj).getFullName());
195                }
196            }
197
198            if (refObject != null)
199                namedChoice.select(namedChoice.indexOf(((Datatype)refObject).getFullName()));
200        }
201
202        label = new Label(datatypeGroup, SWT.LEFT);
203        label.setFont(curFont);
204        label.setText("Datatype Class");
205
206        label = new Label(datatypeGroup, SWT.LEFT);
207        label.setFont(curFont);
208        label.setText("Size (bits)");
209
210        label = new Label(datatypeGroup, SWT.LEFT);
211        label.setFont(curFont);
212        label.setText("Byte Ordering");
213
214        checkUnsigned = new Button(datatypeGroup, SWT.CHECK);
215        checkUnsigned.setFont(curFont);
216        checkUnsigned.setText("Unsigned");
217        checkUnsigned.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
218
219        classChoice = new Combo(datatypeGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
220        classChoice.setFont(curFont);
221        classChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
222        classChoice.addSelectionListener(new SelectionAdapter() {
223            @Override
224            public void widgetSelected(SelectionEvent e)
225            {
226                int idx = classChoice.getSelectionIndex();
227                sizeChoice.select(0);
228                endianChoice.select(0);
229                lengthField.setEnabled(false);
230
231                if ((idx == 0) || (idx == 6)) { // INTEGER
232                    sizeChoice.setEnabled(true);
233                    endianChoice.setEnabled(isH5);
234                    checkUnsigned.setEnabled(true);
235
236                    if (sizeChoice.getItemCount() == 3) {
237                        sizeChoice.remove("32");
238                        sizeChoice.remove("64");
239                        sizeChoice.add("8");
240                        sizeChoice.add("16");
241                        sizeChoice.add("32");
242                        sizeChoice.add("64");
243                    }
244                }
245                else if ((idx == 1) || (idx == 7)) { // FLOAT
246                    sizeChoice.setEnabled(true);
247                    endianChoice.setEnabled(isH5);
248                    checkUnsigned.setEnabled(false);
249
250                    if (sizeChoice.getItemCount() == 5) {
251                        sizeChoice.remove("16");
252                        sizeChoice.remove("8");
253                    }
254                }
255                else if (idx == 2) { // CHAR
256                    sizeChoice.setEnabled(false);
257                    endianChoice.setEnabled(isH5);
258                    checkUnsigned.setEnabled(true);
259                }
260                else if (idx == 3) { // STRING
261                    sizeChoice.setEnabled(false);
262                    endianChoice.setEnabled(false);
263                    checkUnsigned.setEnabled(false);
264                    lengthField.setEnabled(true);
265                    lengthField.setText("String length");
266                }
267                else if (idx == 4) { // REFERENCE
268                    sizeChoice.setEnabled(false);
269                    endianChoice.setEnabled(false);
270                    checkUnsigned.setEnabled(false);
271                    lengthField.setEnabled(false);
272                }
273                else if (idx == 5) { // ENUM
274                    sizeChoice.setEnabled(true);
275                    checkUnsigned.setEnabled(true);
276                    lengthField.setEnabled(true);
277                    lengthField.setText("0=R,1=G,#=TXT,...");
278                }
279                else if (idx == 8) {
280                    sizeChoice.setEnabled(false);
281                    endianChoice.setEnabled(false);
282                    checkUnsigned.setEnabled(false);
283                    lengthField.setEnabled(false);
284                }
285            }
286        });
287
288        classChoice.add("INTEGER");
289        classChoice.add("FLOAT");
290        classChoice.add("CHAR");
291
292        if (isH5) {
293            classChoice.add("STRING");
294            classChoice.add("REFERENCE");
295            classChoice.add("ENUM");
296            classChoice.add("VLEN_INTEGER");
297            classChoice.add("VLEN_FLOAT");
298            classChoice.add("VLEN_STRING");
299        }
300
301        sizeChoice = new Combo(datatypeGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
302        sizeChoice.setFont(curFont);
303        sizeChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
304        sizeChoice.addSelectionListener(new SelectionAdapter() {
305            @Override
306            public void widgetSelected(SelectionEvent e)
307            {
308                if (classChoice.getSelectionIndex() == 0) {
309                    checkUnsigned.setEnabled(true);
310                }
311            }
312        });
313
314        if (isH5) {
315            sizeChoice.add("NATIVE");
316        }
317        else {
318            sizeChoice.add("DEFAULT");
319        }
320
321        sizeChoice.add("8");
322        sizeChoice.add("16");
323        sizeChoice.add("32");
324        sizeChoice.add("64");
325
326        endianChoice = new Combo(datatypeGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
327        endianChoice.setFont(curFont);
328        endianChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
329        endianChoice.setEnabled(isH5);
330
331        if (isH5) {
332            endianChoice.add("NATIVE");
333            endianChoice.add("LITTLE ENDIAN");
334            endianChoice.add("BIG ENDIAN");
335        }
336        else {
337            endianChoice.add("DEFAULT");
338        }
339
340        lengthField = new Text(datatypeGroup, SWT.SINGLE | SWT.BORDER);
341        lengthField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
342        lengthField.setFont(curFont);
343        lengthField.setText("String Length");
344        lengthField.setEnabled(false);
345
346        classChoice.select(0);
347        sizeChoice.select(0);
348        endianChoice.select(0);
349
350        refObject = null;
351        classChoice.setEnabled(true);
352        sizeChoice.setEnabled(true);
353        endianChoice.setEnabled(isH5);
354        checkUnsigned.setEnabled(true);
355    }
356
357    /**
358     * Create the datatype according to the settings.
359     *
360     * @param name
361     *        the datatype name
362     *
363     * @return the new object created.
364     */
365    public Datatype createNewDatatype(String name)
366    {
367        Datatype datatype = null;
368
369        tclass     = Datatype.CLASS_NO_CLASS;
370        tsize      = Datatype.NATIVE;
371        torder     = Datatype.NATIVE;
372        tsign      = Datatype.NATIVE;
373        isEnum     = false;
374        strEnumMap = null;
375        isVLen     = false;
376        isVlenStr  = false;
377
378        if (useCommittedType.getSelection()) {
379            datatype = (Datatype)refObject;
380        }
381        else {
382            // idx is set to datatype class
383            int idx = classChoice.getSelectionIndex();
384            if (idx == 0) {
385                tclass = Datatype.CLASS_INTEGER;
386                if (checkUnsigned.getSelection()) {
387                    tsign = Datatype.SIGN_NONE;
388                }
389            }
390            else if (idx == 1) {
391                tclass = Datatype.CLASS_FLOAT;
392            }
393            else if (idx == 2) {
394                tclass = Datatype.CLASS_CHAR;
395                if (checkUnsigned.getSelection()) {
396                    tsign = Datatype.SIGN_NONE;
397                }
398            }
399            else if (idx == 3) {
400                tclass = Datatype.CLASS_STRING;
401            }
402            else if (idx == 4) {
403                tclass = Datatype.CLASS_REFERENCE;
404            }
405            else if (idx == 5) {
406                isEnum = true;
407                tclass = Datatype.CLASS_ENUM;
408            }
409            else if (idx == 6) {
410                isVLen = true;
411                tclass = Datatype.CLASS_INTEGER;
412                if (checkUnsigned.getSelection()) {
413                    tsign = Datatype.SIGN_NONE;
414                }
415            }
416            else if (idx == 7) {
417                isVLen = true;
418                tclass = Datatype.CLASS_FLOAT;
419            }
420            else if (idx == 8) {
421                tclass    = Datatype.CLASS_STRING;
422                isVlenStr = true;
423                tsize     = -1;
424            }
425
426            // idx is set to datatype size
427            idx = sizeChoice.getSelectionIndex();
428            if (tclass == Datatype.CLASS_STRING) {
429                if (!isVlenStr) {
430                    int stringLength = 0;
431                    try {
432                        stringLength = Integer.parseInt(lengthField.getText());
433                    }
434                    catch (NumberFormatException ex) {
435                        stringLength = -1;
436                    }
437
438                    if (stringLength <= 0) {
439                        shell.getDisplay().beep();
440                        Tools.showError(shell, "Create", "Invalid string length: " + lengthField.getText());
441                        return null;
442                    }
443                    tsize = stringLength;
444                }
445            }
446            else if (tclass == Datatype.CLASS_REFERENCE) {
447                tsize  = 1;
448                torder = Datatype.NATIVE;
449            }
450            else if (idx == 0) {
451                tsize = Datatype.NATIVE;
452            }
453            else if (tclass == Datatype.CLASS_FLOAT) {
454                tsize = idx * 4;
455            }
456            else if (tclass == Datatype.CLASS_INTEGER || tclass == Datatype.CLASS_ENUM) {
457                // Note that idx == 0 is set to either
458                //       "NATIVE" if isH5 is true
459                //       "DEFAULT" otherwise
460                switch (idx) {
461                case 1:
462                    tsize = 1;
463                    break;
464                case 2:
465                    tsize = 2;
466                    break;
467                case 3:
468                    tsize = 4;
469                    break;
470                case 4:
471                    tsize = 8;
472                    break;
473                default:
474                    tsize = -1;
475                    break;
476                }
477                log.trace("CLASS_INTEGER or CLASS_ENUM: tsize={}", tsize);
478            }
479            else if (tclass == Datatype.CLASS_FLOAT) {
480                tsize = (idx + 1) * 4;
481                log.trace("CLASS_FLOAT: tsize={}", tsize);
482            }
483            else {
484                tsize = 1 << (idx - 1);
485            }
486
487            if ((tsize == 8) && !isH5 && (tclass == Datatype.CLASS_INTEGER)) {
488                shell.getDisplay().beep();
489                Tools.showError(shell, "Create", "HDF4 does not support 64-bit integer.");
490                return null;
491            }
492
493            // set order
494            idx = endianChoice.getSelectionIndex();
495            if (idx == 0) {
496                torder = Datatype.NATIVE;
497            }
498            else if (idx == 1) {
499                torder = Datatype.ORDER_LE;
500            }
501            else {
502                torder = Datatype.ORDER_BE;
503            }
504
505            Datatype thedatatype = null;
506            try {
507                Datatype basedatatype = null;
508                if (isVLen) {
509                    basedatatype = fileFormat.createDatatype(tclass, tsize, torder, tsign);
510                    tclass       = Datatype.CLASS_VLEN;
511                    thedatatype  = fileFormat.createDatatype(tclass, tsize, torder, tsign, basedatatype);
512                }
513                else if (isEnum && isH5) {
514                    strEnumMap = lengthField.getText();
515                    if ((strEnumMap == null) || (strEnumMap.length() < 1) || strEnumMap.endsWith("...")) {
516                        shell.getDisplay().beep();
517                        Tools.showError(shell, "Create", "Invalid member values: " + lengthField.getText());
518                        return null;
519                    }
520                    log.trace("CLASS_ENUM enumStr={}", strEnumMap);
521
522                    thedatatype = fileFormat.createDatatype(tclass, tsize, torder, tsign);
523                    thedatatype.setEnumMembers(strEnumMap);
524                }
525                else
526                    thedatatype = fileFormat.createDatatype(tclass, tsize, torder, tsign);
527
528                if (isH5)
529                    datatype = fileFormat.createNamedDatatype(thedatatype, name);
530                else
531                    datatype = thedatatype;
532            }
533            catch (Exception ex) {
534                shell.getDisplay().beep();
535                Tools.showError(shell, "Create", ex.getMessage());
536                return null;
537            }
538        }
539        return datatype;
540    }
541
542    /**
543     * Get the new object created.
544     *
545     * @return the new object created.
546     */
547    public HObject getObject() { return newObject; }
548
549    /**
550     * Get the parent group of the new dataset.
551     *
552     * @return the parent group of the new dataset.
553     */
554    public Group getParentGroup() { return (Group)parentObj; }
555}