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 org.slf4j.Logger;
022import org.slf4j.LoggerFactory;
023
024import org.eclipse.swt.SWT;
025import org.eclipse.swt.events.SelectionAdapter;
026import org.eclipse.swt.events.SelectionEvent;
027import org.eclipse.swt.graphics.Font;
028import org.eclipse.swt.layout.GridData;
029import org.eclipse.swt.layout.GridLayout;
030import org.eclipse.swt.widgets.Button;
031import org.eclipse.swt.widgets.Combo;
032import org.eclipse.swt.widgets.Dialog;
033import org.eclipse.swt.widgets.Display;
034import org.eclipse.swt.widgets.Label;
035import org.eclipse.swt.widgets.Shell;
036import org.eclipse.swt.widgets.Text;
037
038import hdf.object.Datatype;
039import hdf.object.FileFormat;
040import hdf.object.Group;
041import hdf.object.HObject;
042import hdf.object.h5.H5Datatype;
043import hdf.view.Tools;
044import hdf.view.ViewProperties;
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        super(parent, SWT.APPLICATION_MODAL);
129
130        try {
131            curFont = new Font(
132                    Display.getCurrent(),
133                    ViewProperties.getFontType(),
134                    ViewProperties.getFontSize(),
135                    SWT.NORMAL);
136        }
137        catch (Exception ex) {
138            curFont = null;
139        }
140
141        newObject = null;
142        parentObj = pGroup;
143        objList = objs;
144
145        fileFormat = pGroup.getFileFormat();
146        isH5 = pGroup.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5));
147    }
148
149    /** the new dataset properties to be created. */
150    public void createDatatypeWidget() {
151        Label label;
152
153        // Create Datatype region
154        org.eclipse.swt.widgets.Group datatypeGroup = new org.eclipse.swt.widgets.Group(shell, SWT.NONE);
155        datatypeGroup.setFont(curFont);
156        datatypeGroup.setText("Datatype");
157        datatypeGroup.setLayout(new GridLayout(4, true));
158        datatypeGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
159
160        useCommittedType = new Button(datatypeGroup, SWT.CHECK);
161        useCommittedType.setFont(curFont);
162        useCommittedType.setText("Use Committed Datatype");
163        useCommittedType.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
164        useCommittedType.setEnabled(isH5);
165
166        if(isH5) {
167            label = new Label(datatypeGroup, SWT.LEFT);
168            label.setFont(curFont);
169            label.setText("Committed Datatype: ");
170
171            namedChoice = new Combo(datatypeGroup, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);
172            namedChoice.setFont(curFont);
173            namedChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
174            namedChoice.addSelectionListener(new SelectionAdapter() {
175                @Override
176                public void widgetSelected(SelectionEvent e) {
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                int idx = classChoice.getSelectionIndex();
226                sizeChoice.select(0);
227                endianChoice.select(0);
228                lengthField.setEnabled(false);
229
230                if ((idx == 0) || (idx == 6)) { // INTEGER
231                    sizeChoice.setEnabled(true);
232                    endianChoice.setEnabled(isH5);
233                    checkUnsigned.setEnabled(true);
234
235                    if (sizeChoice.getItemCount() == 3) {
236                        sizeChoice.remove("32");
237                        sizeChoice.remove("64");
238                        sizeChoice.add("8");
239                        sizeChoice.add("16");
240                        sizeChoice.add("32");
241                        sizeChoice.add("64");
242                    }
243                }
244                else if ((idx == 1) || (idx == 7)) { // FLOAT
245                    sizeChoice.setEnabled(true);
246                    endianChoice.setEnabled(isH5);
247                    checkUnsigned.setEnabled(false);
248
249                    if (sizeChoice.getItemCount() == 5) {
250                        sizeChoice.remove("16");
251                        sizeChoice.remove("8");
252                    }
253                }
254                else if (idx == 2) { // CHAR
255                    sizeChoice.setEnabled(false);
256                    endianChoice.setEnabled(isH5);
257                    checkUnsigned.setEnabled(true);
258                }
259                else if (idx == 3) { // STRING
260                    sizeChoice.setEnabled(false);
261                    endianChoice.setEnabled(false);
262                    checkUnsigned.setEnabled(false);
263                    lengthField.setEnabled(true);
264                    lengthField.setText("String length");
265                }
266                else if (idx == 4) { // REFERENCE
267                    sizeChoice.setEnabled(false);
268                    endianChoice.setEnabled(false);
269                    checkUnsigned.setEnabled(false);
270                    lengthField.setEnabled(false);
271                }
272                else if (idx == 5) { // ENUM
273                    sizeChoice.setEnabled(true);
274                    checkUnsigned.setEnabled(true);
275                    lengthField.setEnabled(true);
276                    lengthField.setText("0=R,1=G,#=TXT,...");
277                }
278                else if (idx == 8) {
279                    sizeChoice.setEnabled(false);
280                    endianChoice.setEnabled(false);
281                    checkUnsigned.setEnabled(false);
282                    lengthField.setEnabled(false);
283                }
284            }
285        });
286
287        classChoice.add("INTEGER");
288        classChoice.add("FLOAT");
289        classChoice.add("CHAR");
290
291        if(isH5) {
292            classChoice.add("STRING");
293            classChoice.add("REFERENCE");
294            classChoice.add("ENUM");
295            classChoice.add("VLEN_INTEGER");
296            classChoice.add("VLEN_FLOAT");
297            classChoice.add("VLEN_STRING");
298        }
299
300        sizeChoice = new Combo(datatypeGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
301        sizeChoice.setFont(curFont);
302        sizeChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
303        sizeChoice.addSelectionListener(new SelectionAdapter() {
304            @Override
305            public void widgetSelected(SelectionEvent e) {
306                if (classChoice.getSelectionIndex() == 0) {
307                    checkUnsigned.setEnabled(true);
308                }
309            }
310        });
311
312        if(isH5) {
313            sizeChoice.add("NATIVE");
314        }
315        else {
316            sizeChoice.add("DEFAULT");
317        }
318
319        sizeChoice.add("8");
320        sizeChoice.add("16");
321        sizeChoice.add("32");
322        sizeChoice.add("64");
323
324        endianChoice = new Combo(datatypeGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
325        endianChoice.setFont(curFont);
326        endianChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
327        endianChoice.setEnabled(isH5);
328
329        if(isH5) {
330            endianChoice.add("NATIVE");
331            endianChoice.add("LITTLE ENDIAN");
332            endianChoice.add("BIG ENDIAN");
333        }
334        else {
335            endianChoice.add("DEFAULT");
336        }
337
338        lengthField = new Text(datatypeGroup, SWT.SINGLE | SWT.BORDER);
339        lengthField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
340        lengthField.setFont(curFont);
341        lengthField.setText("String Length");
342        lengthField.setEnabled(false);
343
344        classChoice.select(0);
345        sizeChoice.select(0);
346        endianChoice.select(0);
347
348        refObject = null;
349        classChoice.setEnabled(true);
350        sizeChoice.setEnabled(true);
351        endianChoice.setEnabled(isH5);
352        checkUnsigned.setEnabled(true);
353    }
354
355    /**
356     * Create the datatype according to the settings.
357     *
358     * @param name
359     *        the datatype name
360     *
361     * @return the new object created.
362     */
363    public Datatype createNewDatatype(String name) {
364        Datatype datatype = null;
365
366        tclass = Datatype.CLASS_NO_CLASS;
367        tsize = Datatype.NATIVE;
368        torder = Datatype.NATIVE;
369        tsign = Datatype.NATIVE;
370        isEnum = false;
371        strEnumMap = null;
372        isVLen = false;
373        isVlenStr = false;
374
375        if (useCommittedType.getSelection()) {
376            datatype = (Datatype)refObject;
377        }
378        else {
379           // idx is set to datatype class
380            int idx = classChoice.getSelectionIndex();
381            if (idx == 0) {
382                tclass = Datatype.CLASS_INTEGER;
383                if (checkUnsigned.getSelection()) {
384                    tsign = Datatype.SIGN_NONE;
385                }
386            }
387            else if (idx == 1) {
388                tclass = Datatype.CLASS_FLOAT;
389            }
390            else if (idx == 2) {
391                tclass = Datatype.CLASS_CHAR;
392                if (checkUnsigned.getSelection()) {
393                    tsign = Datatype.SIGN_NONE;
394                }
395            }
396            else if (idx == 3) {
397                tclass = Datatype.CLASS_STRING;
398            }
399            else if (idx == 4) {
400                tclass = Datatype.CLASS_REFERENCE;
401            }
402            else if (idx == 5) {
403                isEnum = true;
404                tclass = Datatype.CLASS_ENUM;
405            }
406            else if (idx == 6) {
407                isVLen = true;
408                tclass = Datatype.CLASS_INTEGER;
409                if (checkUnsigned.getSelection()) {
410                    tsign = Datatype.SIGN_NONE;
411                }
412            }
413            else if (idx == 7) {
414                isVLen = true;
415                tclass = Datatype.CLASS_FLOAT;
416            }
417            else if (idx == 8) {
418                tclass = Datatype.CLASS_STRING;
419                isVlenStr = true;
420                tsize = -1;
421            }
422
423            // idx is set to datatype size
424            idx = sizeChoice.getSelectionIndex();
425            if (tclass == Datatype.CLASS_STRING) {
426                if (!isVlenStr) {
427                    int stringLength = 0;
428                    try {
429                        stringLength = Integer.parseInt(lengthField.getText());
430                    }
431                    catch (NumberFormatException ex) {
432                        stringLength = -1;
433                    }
434
435                    if (stringLength <= 0) {
436                        shell.getDisplay().beep();
437                        Tools.showError(shell, "Create", "Invalid string length: " + lengthField.getText());
438                        return null;
439                    }
440                    tsize = stringLength;
441                }
442            }
443            else if (tclass == Datatype.CLASS_REFERENCE) {
444                tsize = 1;
445                torder = Datatype.NATIVE;
446            }
447            else if (idx == 0) {
448                tsize = Datatype.NATIVE;
449            }
450            else if (tclass == Datatype.CLASS_FLOAT) {
451                tsize = idx * 4;
452            }
453            else if (tclass == Datatype.CLASS_INTEGER || tclass == Datatype.CLASS_ENUM) {
454                // Note that idx == 0 is set to either
455                //       "NATIVE" if isH5 is true
456                //       "DEFAULT" otherwise
457                switch(idx) {
458                    case 1:
459                        tsize = 1;
460                        break;
461                    case 2:
462                        tsize = 2;
463                        break;
464                    case 3:
465                        tsize = 4;
466                        break;
467                    case 4:
468                        tsize = 8;
469                        break;
470                    default:
471                        tsize = -1;
472                        break;
473                }
474                log.trace("CLASS_INTEGER or CLASS_ENUM: tsize={}", tsize);
475            }
476            else if (tclass == Datatype.CLASS_FLOAT) {
477                tsize = (idx + 1) * 4;
478                log.trace("CLASS_FLOAT: tsize={}", tsize);
479            }
480            else {
481                tsize = 1 << (idx - 1);
482            }
483
484            if ((tsize == 8) && !isH5 && (tclass == Datatype.CLASS_INTEGER)) {
485                shell.getDisplay().beep();
486                Tools.showError(shell, "Create", "HDF4 does not support 64-bit integer.");
487                return null;
488            }
489
490            // set order
491            idx = endianChoice.getSelectionIndex();
492            if (idx == 0) {
493                torder = Datatype.NATIVE;
494            }
495            else if (idx == 1) {
496                torder = Datatype.ORDER_LE;
497            }
498            else {
499                torder = Datatype.ORDER_BE;
500            }
501
502            Datatype thedatatype = null;
503            try {
504                Datatype basedatatype = null;
505                if (isVLen) {
506                    basedatatype = fileFormat.createDatatype(tclass, tsize, torder, tsign);
507                    tclass = Datatype.CLASS_VLEN;
508                    thedatatype = fileFormat.createDatatype(tclass, tsize, torder, tsign, basedatatype);
509                }
510                else if (isEnum && isH5) {
511                    strEnumMap = lengthField.getText();
512                    if ((strEnumMap == null) || (strEnumMap.length() < 1) || strEnumMap.endsWith("...")) {
513                        shell.getDisplay().beep();
514                        Tools.showError(shell, "Create", "Invalid member values: " + lengthField.getText());
515                        return null;
516                    }
517                    log.trace("CLASS_ENUM enumStr={}", strEnumMap);
518
519                    thedatatype = fileFormat.createDatatype(tclass, tsize, torder, tsign);
520                    thedatatype.setEnumMembers(strEnumMap);
521                }
522                else
523                    thedatatype = fileFormat.createDatatype(tclass, tsize, torder, tsign);
524
525                if(isH5)
526                    datatype = fileFormat.createNamedDatatype(thedatatype, name);
527                else
528                    datatype = thedatatype;
529            }
530            catch (Exception ex) {
531                shell.getDisplay().beep();
532                Tools.showError(shell, "Create", ex.getMessage());
533                return null;
534            }
535        }
536        return datatype;
537    }
538
539    /** @return the new object created. */
540    public HObject getObject() {
541        return newObject;
542    }
543
544    /** @return the parent group of the new dataset. */
545    public Group getParentGroup() {
546        return (Group) parentObj;
547    }
548}