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 files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see https://support.hdfgroup.org/products/licenses.html               *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.object.h5;
016
017import java.lang.reflect.Array;
018import java.util.HashMap;
019import java.util.Iterator;
020import java.util.List;
021import java.util.Map.Entry;
022import java.util.Objects;
023import java.util.Vector;
024
025import hdf.hdf5lib.H5;
026import hdf.hdf5lib.HDF5Constants;
027import hdf.hdf5lib.HDFNativeData;
028import hdf.hdf5lib.exceptions.HDF5Exception;
029import hdf.hdf5lib.exceptions.HDF5LibraryException;
030import hdf.hdf5lib.structs.H5O_info_t;
031import hdf.object.Attribute;
032import hdf.object.CompoundDS;
033import hdf.object.Datatype;
034import hdf.object.FileFormat;
035
036/**
037 * This class defines HDF5 datatype characteristics and APIs for a data type.
038 * <p>
039 * This class provides several methods to convert an HDF5 datatype identifier to a datatype object, and vice versa. A
040 * datatype object is described by four basic fields: datatype class, size, byte order, and sign, while an HDF5 datatype
041 * is presented by a datatype identifier.
042 *
043 * @version 1.1 9/4/2007
044 * @author Peter X. Cao
045 */
046public class H5Datatype extends Datatype {
047    private static final long serialVersionUID = -750546422258749792L;
048
049    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(H5Datatype.class);
050
051    /**
052     * The list of attributes of this data object.
053     */
054    private List<Attribute> attributeList;
055
056    /** Flag to indicate if this datatype is a named datatype */
057    private boolean isNamed = false;
058
059    private boolean is_ref_obj = false;
060
061    private boolean is_reg_ref = false;
062
063    private int nAttributes = -1;
064
065    private H5O_info_t obj_info;
066
067    /**
068     * The native class of the datatype.
069     */
070    private int nativeClass = -1;
071
072    private int nativeStrPad = -1;
073
074    /**
075     * The tag for an opaque datatype.
076     */
077    private String opaqueTag = null;
078
079    /**
080     * Constructs an named HDF5 data type object for a given file, dataset name and group path.
081     * <p>
082     * The datatype object represents an existing named datatype in file. For example,
083     *
084     * <pre>
085     * new H5Datatype(file, "dtype1", "/g0")
086     * </pre>
087     *
088     * constructs a datatype object that corresponds to the dataset,"dset1", at group "/g0".
089     *
090     * @param theFile
091     *            the file that contains the dataset.
092     * @param name
093     *            the name of the dataset such as "dset1".
094     * @param path
095     *            the group path to the dataset such as "/g0/".
096     */
097    public H5Datatype(FileFormat theFile, String name, String path) {
098        this(theFile, name, path, null);
099    }
100
101    /**
102     * @deprecated Not for public use in the future. <br>
103     *             Using {@link #H5Datatype(FileFormat, String, String)}
104     *
105     * @param theFile
106     *            the file that contains the dataset.
107     * @param name
108     *            the name of the dataset such as "dset1".
109     * @param path
110     *            the group path to the dataset such as "/g0/".
111     * @param oid
112     *            the oid of the dataset.
113     */
114    @Deprecated
115    public H5Datatype(FileFormat theFile, String name, String path, long[] oid) {
116        super(theFile, name, path, oid);
117        obj_info = new H5O_info_t(-1L, -1L, 0, 0, -1L, 0L, 0L, 0L, 0L, null, null, null);
118
119        if ((oid == null) && (theFile != null)) {
120            // retrieve the object ID
121            try {
122                byte[] ref_buf = H5.H5Rcreate(theFile.getFID(), this.getFullName(), HDF5Constants.H5R_OBJECT, -1);
123                this.oid = new long[1];
124                this.oid[0] = HDFNativeData.byteToLong(ref_buf, 0);
125            }
126            catch (Exception ex) {
127                log.debug("constructor ID {} for {} failed H5Rcreate", theFile.getFID(), this.getFullName());
128            }
129        }
130
131        long tid = -1;
132        try {
133            tid = H5.H5Topen(theFile.getFID(), this.getFullName(), HDF5Constants.H5P_DEFAULT);
134            fromNative(tid);
135        }
136        catch (Exception ex) {
137            log.debug("constructor H5Topen() failure");
138        }
139        finally {
140            close(tid);
141        }
142    }
143
144    /**
145     * Constructs a Datatype with specified class, size, byte order and sign.
146     * <p>
147     * The following is a list of a few example of H5Datatype.
148     * <ol>
149     * <li>to create unsigned native integer<br>
150     * H5Datatype type = new H5Dataype(CLASS_INTEGER, NATIVE, NATIVE, SIGN_NONE);
151     * <li>to create 16-bit signed integer with big endian<br>
152     * H5Datatype type = new H5Dataype(CLASS_INTEGER, 2, ORDER_BE, NATIVE);
153     * <li>to create native float<br>
154     * H5Datatype type = new H5Dataype(CLASS_FLOAT, NATIVE, NATIVE, -1);
155     * <li>to create 64-bit double<br>
156     * H5Datatype type = new H5Dataype(CLASS_FLOAT, 8, NATIVE, -1);
157     * </ol>
158     *
159     * @param tclass
160     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
161     * @param tsize
162     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4.
163     * @param torder
164     *            the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX and ORDER_NONE
165     * @param tsign
166     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and MSGN
167     */
168    public H5Datatype(int tclass, int tsize, int torder, int tsign) {
169        this(tclass, tsize, torder, tsign, null);
170    }
171
172    /**
173     * Constructs a Datatype with specified class, size, byte order and sign.
174     * <p>
175     * The following is a list of a few example of H5Datatype.
176     * <ol>
177     * <li>to create unsigned native integer<br>
178     * H5Datatype type = new H5Dataype(CLASS_INTEGER, NATIVE, NATIVE, SIGN_NONE);
179     * <li>to create 16-bit signed integer with big endian<br>
180     * H5Datatype type = new H5Dataype(CLASS_INTEGER, 2, ORDER_BE, NATIVE);
181     * <li>to create native float<br>
182     * H5Datatype type = new H5Dataype(CLASS_FLOAT, NATIVE, NATIVE, -1);
183     * <li>to create 64-bit double<br>
184     * H5Datatype type = new H5Dataype(CLASS_FLOAT, 8, NATIVE, -1);
185     * </ol>
186     *
187     * @param tclass
188     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
189     * @param tsize
190     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4.
191     * @param torder
192     *            the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX and ORDER_NONE
193     * @param tsign
194     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and MSGN
195     * @param tbase
196     *            the base datatype of the new datatype
197     */
198    public H5Datatype(int tclass, int tsize, int torder, int tsign, Datatype tbase) {
199        this(tclass, tsize, torder, tsign, tbase, null);
200    }
201
202    /**
203     * Constructs a Datatype with specified class, size, byte order and sign.
204     * <p>
205     * The following is a list of a few example of H5Datatype.
206     * <ol>
207     * <li>to create unsigned native integer<br>
208     * H5Datatype type = new H5Dataype(CLASS_INTEGER, NATIVE, NATIVE, SIGN_NONE);
209     * <li>to create 16-bit signed integer with big endian<br>
210     * H5Datatype type = new H5Dataype(CLASS_INTEGER, 2, ORDER_BE, NATIVE);
211     * <li>to create native float<br>
212     * H5Datatype type = new H5Dataype(CLASS_FLOAT, NATIVE, NATIVE, -1);
213     * <li>to create 64-bit double<br>
214     * H5Datatype type = new H5Dataype(CLASS_FLOAT, 8, NATIVE, -1);
215     * </ol>
216     *
217     * @param tclass
218     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
219     * @param tsize
220     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4.
221     * @param torder
222     *            the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX and
223     *            ORDER_NONE
224     * @param tsign
225     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and MSGN
226     * @param tbase
227     *            the base datatype of the new datatype
228     * @param pbase
229     *            the parent datatype of the new datatype
230     */
231    public H5Datatype(int tclass, int tsize, int torder, int tsign, Datatype tbase, Datatype pbase) {
232        this(tclass, tsize, torder, tsign, tbase, pbase, null);
233    }
234
235    /**
236     * Constructs a Datatype with specified class, size, byte order and sign.
237     * <p>
238     * The following is a list of a few example of H5Datatype.
239     * <ol>
240     * <li>to create unsigned native integer<br>
241     * H5Datatype type = new H5Dataype(CLASS_INTEGER, NATIVE, NATIVE, SIGN_NONE);
242     * <li>to create 16-bit signed integer with big endian<br>
243     * H5Datatype type = new H5Dataype(CLASS_INTEGER, 2, ORDER_BE, NATIVE);
244     * <li>to create native float<br>
245     * H5Datatype type = new H5Dataype(CLASS_FLOAT, NATIVE, NATIVE, -1);
246     * <li>to create 64-bit double<br>
247     * H5Datatype type = new H5Dataype(CLASS_FLOAT, 8, NATIVE, -1);
248     * </ol>
249     *
250     * @param tclass
251     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
252     * @param tsize
253     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4.
254     * @param torder
255     *            the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX and
256     *            ORDER_NONE
257     * @param tsign
258     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and MSGN
259     * @param tbase
260     *            the base datatype of the new datatype
261     * @param pbase
262     *            the parent datatype of the new datatype
263     * @param members
264     *            the list of member datatypes
265     */
266    public H5Datatype(int tclass, int tsize, int torder, int tsign, Datatype tbase, Datatype pbase, List<H5Datatype> members) {
267        super(tclass, tsize, torder, tsign, tbase, pbase);
268        if (members == null) {
269            compoundMemberTypes = new Vector<>();
270        }
271        else {
272            compoundMemberTypes = new Vector<>(members.size());
273            compoundMemberTypes.addAll(members);
274        }
275
276        datatypeDescription = getDescription();
277    }
278
279    /**
280     * Constructs a Datatype with a given native datatype identifier.
281     * <p>
282     * For example, if the datatype identifier is a 32-bit unsigned integer created from HDF5,
283     *
284     * <pre>
285     * int tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_UNINT32);
286     * Datatype dtype = new Datatype(tid);
287     * </pre>
288     *
289     * will construct a datatype equivalent to new Datatype(CLASS_INTEGER, 4, NATIVE, SIGN_NONE);
290     *
291     * @see #fromNative(long nativeID)
292     *
293     * @param nativeID
294     *            the native datatype identifier.
295     */
296    public H5Datatype(long nativeID) {
297        this(nativeID, null);
298    }
299
300    /**
301     * Constructs a Datatype with a given native datatype identifier.
302     * <p>
303     * For example, if the datatype identifier is a 32-bit unsigned integer created from HDF5,
304     *
305     * <pre>
306     * int tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_UNINT32);
307     * Datatype dtype = new Datatype(tid);
308     * </pre>
309     *
310     * will construct a datatype equivalent to new Datatype(CLASS_INTEGER, 4, NATIVE, SIGN_NONE);
311     *
312     * @see #fromNative(long nativeID)
313     *
314     * @param nativeID
315     *            the native datatype identifier.
316     * @param pbase
317     *            the parent datatype of the new datatype
318     */
319    public H5Datatype(long nativeID, Datatype pbase) {
320        super(nativeID, pbase);
321        fromNative(nativeID);
322        datatypeDescription = getDescription();
323    }
324
325    /**
326     * Opens access to a named datatype.
327     * <p>
328     * It calls H5.H5Topen(loc, name).
329     *
330     * @return the datatype identifier if successful; otherwise returns negative value.
331     *
332     * @see hdf.hdf5lib.H5#H5Topen(long, String, long)
333     */
334    @Override
335    public long open() {
336        log.trace("open(): start");
337        long tid = -1;
338
339        try {
340            tid = H5.H5Topen(getFID(), getPath() + getName(), HDF5Constants.H5P_DEFAULT);
341        }
342        catch (HDF5Exception ex) {
343            tid = -1;
344        }
345
346        log.trace("open(): finish");
347        return tid;
348    }
349
350    /**
351     * Closes a datatype identifier.
352     * <p>
353     * It calls H5.H5close(tid).
354     *
355     * @param tid
356     *            the datatype ID to close
357     */
358    @Override
359    public void close(long tid) {
360        if (tid >= 0) {
361            try {
362                H5.H5Tclose(tid);
363            }
364            catch (HDF5Exception ex) {
365                log.debug("close(): H5Tclose(tid {}) failure: ", tid, ex);
366            }
367        }
368    }
369
370    /*
371     * (non-Javadoc)
372     *
373     * @see hdf.object.DataFormat#hasAttribute()
374     */
375    @Override
376    public boolean hasAttribute() {
377        obj_info.num_attrs = nAttributes;
378
379        if (obj_info.num_attrs < 0) {
380            long tid = -1;
381            try {
382                tid = H5.H5Topen(getFID(), getPath() + getName(), HDF5Constants.H5P_DEFAULT);
383                fromNative(tid);
384                obj_info = H5.H5Oget_info(tid);
385                isNamed = true;
386            }
387            catch (Exception ex) {
388                obj_info.num_attrs = 0;
389            }
390            finally {
391                close(tid);
392            }
393        }
394
395        log.trace("hasAttribute(): nAttributes={}", obj_info.num_attrs);
396
397        return (obj_info.num_attrs > 0);
398    }
399
400    /**
401     * Converts values in an Enumeration Datatype to names.
402     * <p>
403     * This method searches the identified enumeration datatype for the values appearing in
404     * <code>inValues</code> and returns the names corresponding to those values. If a given value is
405     * not found in the enumeration datatype, the name corresponding to that value will be set to
406     * <code>"ENUM ERR value"</code> in the string array that is returned.
407     * <p>
408     * If the method fails in general, null will be returned instead of a String array. An empty
409     * <code>inValues</code> parameter would cause general failure.
410     *
411     * @param inValues
412     *            The array of enumerations values to be converted.
413     *
414     * @return The string array of names if successful; otherwise return null.
415     *
416     * @throws HDF5Exception
417     *             If there is an error at the HDF5 library level.
418     *
419     */
420    public String[] convertEnumValueToName(Object inValues) throws HDF5Exception {
421        log.trace("convertEnumValueToName() inValues={} start", inValues);
422        int inSize = 0;
423        String[] outNames = null;
424        String cName = inValues.getClass().getName();
425        boolean isArray = cName.lastIndexOf("[") >= 0;
426        if (isArray) {
427            inSize = Array.getLength(inValues);
428        }
429        else {
430            inSize = 1;
431        }
432
433        if ((inValues == null) || (inSize <= 0)) {
434            log.debug("convertEnumValueToName() failure: in values null or inSize length invalid");
435            log.debug("convertEnumValueToName(): inValues={} inSize={}", inValues, inSize);
436            log.trace("convertEnumValueToName(): finish");
437            return null;
438        }
439
440        if (enumMembers == null || enumMembers.size() <= 0) {
441            log.debug("convertEnumValueToName(): no members");
442            log.trace("convertEnumValueToName(): finish");
443            return null;
444        }
445
446        log.trace("convertEnumValueToName(): inSize={} nMembers={} enums={}", inSize, enumMembers.size(), enumMembers);
447        outNames = new String[inSize];
448        for (int i = 0; i < inSize; i++) {
449            if (isArray) {
450                if (enumMembers.containsKey(String.valueOf(Array.get(inValues, i)))) {
451                    outNames[i] = enumMembers.get(String.valueOf(Array.get(inValues, i)));
452                }
453                else {
454                    outNames[i] = "**ENUM ERR " + String.valueOf(Array.get(inValues, i)) + "**";
455                }
456            }
457            else {
458                if (enumMembers.containsKey(String.valueOf(inValues))) {
459                    outNames[i] = enumMembers.get(String.valueOf(inValues));
460                }
461                else {
462                    outNames[i] = "**ENUM ERR " + String.valueOf(inValues) + "**";
463                }
464            }
465        }
466
467        log.trace("convertEnumValueToName(): finish");
468        return outNames;
469    }
470
471    /**
472     * Converts names in an Enumeration Datatype to values.
473     * <p>
474     * This method searches the identified enumeration datatype for the names appearing in
475     * <code>inValues</code> and returns the values corresponding to those names.
476     *
477     * @param in
478     *            The array of enumerations names to be converted.
479     *
480     * @return The int array of values if successful; otherwise return null.
481     *
482     * @throws HDF5Exception
483     *             If there is an error at the HDF5 library level.
484     *
485     */
486    public Object[] convertEnumNameToValue(String[] in) throws HDF5Exception {
487        log.trace("convertEnumNameToValue() start");
488        int size = 0;
489
490        if ((in == null) || ((size = Array.getLength(in)) <= 0)) {
491            log.debug("convertEnumNameToValue() failure: in values null or in size not valid");
492            log.debug("convertEnumNameToValue(): in={} inSize={}", in.toString(), in.length);
493            log.trace("convertEnumNameToValue(): finish");
494            return null;
495        }
496
497        if (enumMembers == null || enumMembers.size() <= 0) {
498            log.debug("convertEnumNameToValue(): no members");
499            log.trace("convertEnumNameToValue(): finish");
500            return null;
501        }
502
503        Object[] out = null;
504        if (datatypeSize == 1) {
505            out = new Byte[size];
506        }
507        else if (datatypeSize == 2) {
508            out = new Short[size];
509        }
510        else if (datatypeSize == 4) {
511            out = new Integer[size];
512        }
513        else if (datatypeSize == 8) {
514            out = new Long[size];
515        }
516        else {
517            out = new Object[size];
518        }
519
520        for (int i = 0; i < size; i++) {
521            if (in[i] == null || in[i].length() <= 0)
522                continue;
523
524            for (Entry<String, String> entry : enumMembers.entrySet()) {
525                if (Objects.equals(in[i], entry.getValue())) {
526                    if (datatypeSize == 1) {
527                        log.trace("convertEnumNameToValue(): ENUM is H5T_NATIVE_INT8");
528                        out[i] = Byte.parseByte(entry.getKey());
529                    }
530                    else if (datatypeSize == 2) {
531                        log.trace("convertEnumNameToValue(): CLASS_INT-ENUM is H5T_NATIVE_INT16");
532                        out[i] = Short.parseShort(entry.getKey());
533                    }
534                    else if (datatypeSize == 4) {
535                        log.trace("convertEnumNameToValue(): CLASS_INT-ENUM is H5T_NATIVE_INT32");
536                        out[i] = Integer.parseInt(entry.getKey());
537                    }
538                    else if (datatypeSize == 8) {
539                        log.trace("convertEnumNameToValue(): CLASS_INT-ENUM is H5T_NATIVE_INT64");
540                        out[i] = Long.parseLong(entry.getKey());
541                    }
542                    else {
543                        log.debug("convertEnumNameToValue(): enum datatypeSize incorrect");
544                        out[i] = -1;
545                    }
546                    break;
547                }
548            }
549        }
550
551        log.trace("convertEnumNameToValue(): finish");
552        return out;
553    }
554
555    /*
556     * (non-Javadoc)
557     *
558     * @see hdf.object.Datatype#fromNative(int)
559     */
560    @Override
561    public void fromNative(long tid) {
562        log.trace("fromNative(): start: tid={}", tid);
563        long tsize = -1;
564        int torder = -1;
565        boolean isChar = false, isUchar = false;
566
567        if (tid < 0) {
568            datatypeClass = CLASS_NO_CLASS;
569        }
570        else {
571            try {
572                nativeClass = H5.H5Tget_class(tid);
573                tsize = H5.H5Tget_size(tid);
574                torder = H5.H5Tget_order(tid);
575                is_variable_str = H5.H5Tis_variable_str(tid);
576                is_VLEN = false;
577                log.trace("fromNative(): tclass={}, tsize={}, torder={}, isVLEN={}", nativeClass, tsize, torder, is_VLEN);
578            }
579            catch (Exception ex) {
580                log.debug("fromNative(): failure: ", ex);
581                datatypeClass = CLASS_NO_CLASS;
582            }
583
584            datatypeOrder = (torder == HDF5Constants.H5T_ORDER_BE) ? ORDER_BE : ORDER_LE;
585
586            try {
587                isUchar = H5.H5Tequal(tid, HDF5Constants.H5T_NATIVE_UCHAR);
588                isChar = (H5.H5Tequal(tid, HDF5Constants.H5T_NATIVE_CHAR) || isUchar);
589            }
590            catch (Exception ex) {
591                log.debug("fromNative(): native char type failure: ", ex);
592            }
593
594            datatypeSign = NSGN; // default
595            if (nativeClass == HDF5Constants.H5T_ARRAY) {
596                long tmptid = -1;
597                datatypeClass = CLASS_ARRAY;
598                try {
599                    int ndims = H5.H5Tget_array_ndims(tid);
600                    arrayDims = new long[ndims];
601                    H5.H5Tget_array_dims(tid, arrayDims);
602                    tmptid = H5.H5Tget_super(tid);
603                    baseType = new H5Datatype(tmptid);
604                    datatypeSign = baseType.getDatatypeSign();
605                }
606                catch (Exception ex) {
607                    log.debug("fromNative(): array type failure: ", ex);
608                }
609                finally {
610                    close(tmptid);
611                }
612                log.trace("fromNative(): array type finish");
613            }
614            else if (nativeClass == HDF5Constants.H5T_COMPOUND) {
615                datatypeClass = CLASS_COMPOUND;
616
617                try {
618                    int nMembers = H5.H5Tget_nmembers(tid);
619                    compoundMemberNames = new Vector<>(nMembers);
620                    compoundMemberTypes = new Vector<>(nMembers);
621                    compoundMemberOffsets = new Vector<>(nMembers);
622                    log.trace("fromNative(): compound type nMembers={} start", nMembers);
623
624                    for (int i = 0; i < nMembers; i++) {
625                        String memberName = H5.H5Tget_member_name(tid, i);
626                        log.trace("fromNative(): compound type [{}] name={} start", i, memberName);
627                        long memberOffset = H5.H5Tget_member_offset(tid, i);
628                        long memberID = -1;
629                        H5Datatype membertype = null;
630                        try {
631                            memberID = H5.H5Tget_member_type(tid, i);
632                            membertype = new H5Datatype(memberID, this);
633                        }
634                        catch (Exception ex1) {
635                            log.debug("fromNative(): compound type failure: ", ex1);
636                        }
637                        finally {
638                            close(memberID);
639                        }
640
641                        compoundMemberNames.add(i, memberName);
642                        compoundMemberOffsets.add(i, memberOffset);
643                        compoundMemberTypes.add(i, membertype);
644                        log.trace("fromNative(): compound type [{}] name={} finish", i, memberName);
645                    }
646                }
647                catch (HDF5LibraryException ex) {
648                    log.debug("fromNative(): compound type failure: ", ex);
649                }
650                log.trace("fromNative(): compound type finish");
651            }
652            else if (nativeClass == HDF5Constants.H5T_INTEGER) {
653                datatypeClass = CLASS_INTEGER;
654                try {
655                    log.trace("fromNative(): integer type");
656                    int tsign = H5.H5Tget_sign(tid);
657                    datatypeSign = (tsign == HDF5Constants.H5T_SGN_NONE) ? SIGN_NONE : SIGN_2;
658                }
659                catch (Exception ex) {
660                    log.debug("fromNative(): int type failure: ", ex);
661                }
662            }
663            else if (nativeClass == HDF5Constants.H5T_FLOAT) {
664                datatypeClass = CLASS_FLOAT;
665            }
666            else if (isChar) {
667                datatypeClass = CLASS_CHAR;
668                datatypeSign = (isUchar) ? SIGN_NONE : SIGN_2;
669            }
670            else if (nativeClass == HDF5Constants.H5T_STRING) {
671                datatypeClass = CLASS_STRING;
672                try {
673                    is_VLEN = H5.H5Tdetect_class(tid, HDF5Constants.H5T_VLEN) || is_variable_str;
674                    log.trace("fromNative(): var str type={}", is_VLEN);
675                    nativeStrPad = H5.H5Tget_strpad(tid);
676                }
677                catch (Exception ex) {
678                    log.debug("fromNative(): var str type failure: ", ex);
679                }
680            }
681            else if (nativeClass == HDF5Constants.H5T_REFERENCE) {
682                datatypeClass = CLASS_REFERENCE;
683                log.trace("fromNative(): reference type");
684                try {
685                    is_reg_ref = H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF_DSETREG);
686                }
687                catch (Exception ex) {
688                    log.debug("fromNative(): H5T_STD_REF_DSETREG: ", ex);
689                }
690                try {
691                    is_ref_obj = H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF_OBJ);
692                }
693                catch (Exception ex) {
694                    log.debug("fromNative(): H5T_STD_REF_OBJ: ", ex);
695                }
696            }
697            else if (nativeClass == HDF5Constants.H5T_ENUM) {
698                datatypeClass = CLASS_ENUM;
699                long tmptid = -1;
700                long basetid = -1;
701                try {
702                    log.trace("fromNative(): enum type");
703                    basetid = H5.H5Tget_super(tid);
704                    tmptid = basetid;
705                    basetid = H5.H5Tget_native_type(tmptid);
706                    log.trace("fromNative(): enum type basetid={}", basetid);
707                    if (basetid >= 0) {
708                        baseType = new H5Datatype(tmptid, this);
709                        datatypeSign = baseType.getDatatypeSign();
710                    }
711                }
712                catch (Exception ex) {
713                    log.debug("fromNative(): enum type failure: ", ex);
714                }
715                finally {
716                    close(tmptid);
717                    close(basetid);
718                }
719                try {
720                    int enumMemberCount = H5.H5Tget_nmembers(tid);
721                    String name = null;
722                    String enumStr = null;
723                    byte[] val = new byte[(int)tsize];
724                    enumMembers = new HashMap<>();
725                    for (int i = 0; i < enumMemberCount; i++) {
726                        name = H5.H5Tget_member_name(tid, i);
727                        H5.H5Tget_member_value(tid, i, val);
728                        switch ((int)H5.H5Tget_size(tid)) {
729                            case 1:
730                                enumStr = Byte.toString((HDFNativeData.byteToByte(val[0]))[0]);
731                                break;
732                            case 2:
733                                enumStr = Short.toString((HDFNativeData.byteToShort(val))[0]);
734                                break;
735                            case 4:
736                                enumStr = Integer.toString((HDFNativeData.byteToInt(val))[0]);
737                                break;
738                            case 8:
739                                enumStr = Long.toString((HDFNativeData.byteToLong(val))[0]);
740                                break;
741                            default:
742                                enumStr = "-1";
743                                break;
744                        }
745                        enumMembers.put(enumStr, name);
746                    }
747                }
748                catch (Exception ex) {
749                    log.debug("fromNative(): enum type failure: ", ex);
750                }
751            }
752            else if (nativeClass == HDF5Constants.H5T_VLEN) {
753                long tmptid = -1;
754                datatypeClass = CLASS_VLEN;
755                is_VLEN = true;
756                try {
757                    log.trace("fromNative(): vlen type");
758                    tmptid = H5.H5Tget_super(tid);
759                    baseType = new H5Datatype(tmptid);
760                    datatypeSign = baseType.getDatatypeSign();
761                }
762                catch (Exception ex) {
763                    log.debug("fromNative(): vlen type failure: ", ex);
764                }
765                finally {
766                    close(tmptid);
767                }
768            }
769            else if (nativeClass == HDF5Constants.H5T_BITFIELD) {
770                datatypeClass = CLASS_BITFIELD;
771            }
772            else if (nativeClass == HDF5Constants.H5T_OPAQUE) {
773                datatypeClass = CLASS_OPAQUE;
774
775                try {
776                    opaqueTag = H5.H5Tget_tag(tid);
777                }
778                catch (Exception ex) {
779                    log.debug("fromNative(): opaque type tag retrieval failed: ", ex);
780                    opaqueTag = null;
781                }
782            }
783            else {
784                log.debug("fromNative(): datatypeClass is unknown");
785            }
786
787            datatypeSize = (is_VLEN && !is_variable_str) ? HDF5Constants.H5T_VL_T : tsize;
788        }
789        log.trace("fromNative(): datatypeClass={} baseType={} datatypeSize={}", datatypeClass, baseType, datatypeSize);
790        log.trace("fromNative(): finish");
791    }
792
793    /**
794     * @param tid
795     *            the datatype identification disk.
796     *
797     * @return the memory datatype identifier if successful, and negative otherwise.
798     */
799    public static long toNative(long tid) {
800        // data type information
801        log.trace("toNative(): tid={} start", tid);
802        long native_id = -1;
803
804        try {
805            native_id = H5.H5Tget_native_type(tid);
806        }
807        catch (Exception ex) {
808            log.debug("toNative(): H5Tget_native_type(tid {}) failure: ", tid, ex);
809        }
810
811        try {
812            if (H5.H5Tis_variable_str(tid))
813                H5.H5Tset_size(native_id, HDF5Constants.H5T_VARIABLE);
814        }
815        catch (Exception ex) {
816            log.debug("toNative(): var str type size failure: ", ex);
817        }
818
819        return native_id;
820    }
821
822    /*
823     * (non-Javadoc)
824     *
825     * @see hdf.object.Datatype#createNative()
826     */
827    @SuppressWarnings("rawtypes")
828    @Override
829    public long createNative() {
830        log.trace("createNative(): start");
831
832        long tid = -1;
833        long tmptid = -1;
834
835        if (isNamed) {
836            try {
837                tid = H5.H5Topen(getFID(), getPath() + getName(), HDF5Constants.H5P_DEFAULT);
838            }
839            catch (Exception ex) {
840                log.debug("createNative(): name {} H5Topen failure: ", getPath() + getName(), ex);
841            }
842        }
843
844        if (tid >= 0) {
845            log.trace("createNative(): tid >= 0");
846            log.trace("createNative(): finish");
847            return tid;
848        }
849
850        // figure the datatype
851        try {
852            log.trace("createNative(): datatypeClass={} datatypeSize={} baseType={}", datatypeClass, datatypeSize, baseType);
853            switch (datatypeClass) {
854                case CLASS_ARRAY:
855                    if (baseType != null) {
856                        if ((tmptid = baseType.createNative()) >= 0) {
857                            try {
858                                tid = H5.H5Tarray_create(tmptid, arrayDims.length, arrayDims);
859                            }
860                            finally {
861                                close(tmptid);
862                            }
863                        }
864                    }
865                    else {
866                        log.debug("createNative(): CLASS_ARRAY base type is NULL");
867                    }
868                    break;
869                case CLASS_COMPOUND:
870                    try {
871                        tid = H5.H5Tcreate(CLASS_COMPOUND, datatypeSize);
872
873                        for (int i = 0; i < compoundMemberTypes.size(); i++) {
874                            H5Datatype memberType = null;
875                            String memberName = null;
876                            long memberOffset = -1;
877
878                            try {
879                                memberType = (H5Datatype) compoundMemberTypes.get(i);
880                            }
881                            catch (Exception ex) {
882                                log.debug("createNative(): get compound member[{}] type failure: ", i, ex);
883                                memberType = null;
884                            }
885
886                            try {
887                                memberName = compoundMemberNames.get(i);
888                            }
889                            catch (Exception ex) {
890                                log.debug("createNative(): get compound member[{}] name failure: ", i, ex);
891                                memberName = null;
892                            }
893
894                            try {
895                                memberOffset = compoundMemberOffsets.get(i);
896                            }
897                            catch (Exception ex) {
898                                log.debug("createNative(): get compound member[{}] offset failure: ", i, ex);
899                                memberOffset = -1;
900                            }
901
902                            long memberID = -1;
903                            try {
904                                memberID = memberType.createNative();
905                                log.trace("createNative(): {} member[{}] with offset={} ID={}: ", memberName, i, memberOffset, memberID);
906
907                                H5.H5Tinsert(tid, memberName, memberOffset, memberID);
908                            }
909                            catch (Exception ex) {
910                                log.debug("createNative(): compound type member[{}] insertion failure: ", i, ex);
911                            }
912                            finally {
913                                close(memberID);
914                            }
915                        }
916                    }
917                    catch (Exception ex) {
918                        log.debug("createNative(): failure: ", ex);
919                    }
920                    break;
921                case CLASS_INTEGER:
922                case CLASS_ENUM:
923                    if (datatypeSize == 1) {
924                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT8");
925                        tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT8);
926                    }
927                    else if (datatypeSize == 2) {
928                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT16");
929                        tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT16);
930                    }
931                    else if (datatypeSize == 4) {
932                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT32");
933                        tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT32);
934                    }
935                    else if (datatypeSize == 8) {
936                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT64");
937                        tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT64);
938                    }
939                    else {
940                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT");
941                        tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT);
942                    }
943
944                    if (datatypeOrder == Datatype.ORDER_BE) {
945                        log.trace("createNative(): CLASS_INT-ENUM order is H5T_ORDER_BE");
946                        H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_BE);
947                    }
948                    else if (datatypeOrder == Datatype.ORDER_LE) {
949                        log.trace("createNative(): CLASS_INT-ENUM order is H5T_ORDER_LE");
950                        H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_LE);
951                    }
952
953                    if (datatypeSign == Datatype.SIGN_NONE) {
954                        log.trace("createNative(): CLASS_INT-ENUM is H5T_SGN_NONE");
955                        H5.H5Tset_sign(tid, HDF5Constants.H5T_SGN_NONE);
956                    }
957                    break;
958                case CLASS_FLOAT:
959                    tid = H5.H5Tcopy((datatypeSize == 8) ? HDF5Constants.H5T_NATIVE_DOUBLE : HDF5Constants.H5T_NATIVE_FLOAT);
960
961                    if (datatypeOrder == Datatype.ORDER_BE) {
962                        H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_BE);
963                    }
964                    else if (datatypeOrder == Datatype.ORDER_LE) {
965                        H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_LE);
966                    }
967
968                    break;
969                case CLASS_CHAR:
970                    tid = H5.H5Tcopy((datatypeSign == Datatype.SIGN_NONE) ? HDF5Constants.H5T_NATIVE_UCHAR : HDF5Constants.H5T_NATIVE_CHAR);
971                    break;
972                case CLASS_STRING:
973                    tid = H5.H5Tcopy(HDF5Constants.H5T_C_S1);
974                    H5.H5Tset_size(tid, (is_VLEN || datatypeSize < 0) ? HDF5Constants.H5T_VARIABLE : datatypeSize);
975
976                    log.trace("createNative(): isVlenStr={}", is_VLEN);
977
978                    H5.H5Tset_strpad(tid, (nativeStrPad >= 0) ? nativeStrPad : HDF5Constants.H5T_STR_NULLTERM);
979
980                    break;
981                case CLASS_REFERENCE:
982                    long obj_ref_type_size = H5.H5Tget_size(HDF5Constants.H5T_STD_REF_OBJ);
983                    tid = H5.H5Tcopy((datatypeSize > obj_ref_type_size) ? HDF5Constants.H5T_STD_REF_DSETREG : HDF5Constants.H5T_STD_REF_OBJ);
984                    break;
985                case CLASS_VLEN:
986                    if (baseType != null) {
987                        if ((tmptid = baseType.createNative()) >= 0) {
988                            try {
989                                tid = H5.H5Tvlen_create(tmptid);
990                            }
991                            finally {
992                                close(tmptid);
993                            }
994                        }
995                    }
996                    else {
997                        log.debug("createNative(): CLASS_VLEN base type is NULL");
998                    }
999                    break;
1000                case CLASS_BITFIELD:
1001                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B8);
1002
1003                    log.trace("createNative(): CLASS_BITFIELD size is " + datatypeSize);
1004                    if (datatypeSize > 0) H5.H5Tset_size(tid, datatypeSize);
1005
1006                    if (datatypeOrder == Datatype.ORDER_BE) {
1007                        log.trace("createNative(): CLASS_BITFIELD-OPAQUE order is H5T_ORDER_BE");
1008                        H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_BE);
1009                    }
1010                    else if (datatypeOrder == Datatype.ORDER_LE) {
1011                        log.trace("createNative(): CLASS_BITFIELD-OPAQUE order is H5T_ORDER_LE");
1012                        H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_LE);
1013                    }
1014
1015                    break;
1016                case CLASS_OPAQUE:
1017                    tid = H5.H5Tcreate(HDF5Constants.H5T_OPAQUE, datatypeSize);
1018
1019                    if (opaqueTag != null) {
1020                        H5.H5Tset_tag(tid, opaqueTag);
1021                    }
1022
1023                    break;
1024                default:
1025                    log.debug("createNative(): Unknown class");
1026                    break;
1027            } // switch (tclass)
1028        }
1029        catch (Exception ex) {
1030            log.debug("createNative(): Error figuring the datatype: ", ex);
1031            tid = -1;
1032        }
1033
1034        // set up enum members
1035        if (datatypeClass == CLASS_ENUM) {
1036            long ptid = tid;
1037            try {
1038                tid = H5.H5Tenum_create(ptid);
1039                datatypeSize = H5.H5Tget_size(tid);
1040            }
1041            catch (Exception ex) {
1042                log.debug("createNative(): create members failure: ", ex);
1043                tid = -1;
1044            }
1045
1046            try {
1047                String memstr, memname;
1048                byte[] memval = null;
1049                if (datatypeSize == 1) {
1050                    memval = HDFNativeData.byteToByte(new Byte((byte) 0));
1051                }
1052                else if (datatypeSize == 2) {
1053                    memval = HDFNativeData.shortToByte(new Short((short) 0));
1054                }
1055                else if (datatypeSize == 4) {
1056                    memval = HDFNativeData.intToByte(new Integer(0));
1057                }
1058                else if (datatypeSize == 8) {
1059                    memval = HDFNativeData.longToByte(new Long(0));
1060                }
1061
1062                // using "0" and "1" as default
1063                if (enumMembers == null) {
1064                    enumMembers = new HashMap<>();
1065                    enumMembers.put("1", "0");
1066                    enumMembers.put("2", "1");
1067                    log.trace("createNative(): default string");
1068                }
1069                Iterator entries = enumMembers.entrySet().iterator();
1070                while (entries.hasNext()) {
1071                    Entry thisEntry = (Entry) entries.next();
1072                    memstr = (String) thisEntry.getKey();
1073                    memname = (String) thisEntry.getValue();
1074
1075                    if (datatypeSize == 1) {
1076                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT8");
1077                        Byte tval = Byte.parseByte(memstr);
1078                        memval = HDFNativeData.byteToByte(tval);
1079                    }
1080                    else if (datatypeSize == 2) {
1081                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT16");
1082                        Short tval = Short.parseShort(memstr);
1083                        memval = HDFNativeData.shortToByte(tval);
1084                    }
1085                    else if (datatypeSize == 4) {
1086                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT32");
1087                        Integer tval = Integer.parseInt(memstr);
1088                        memval = HDFNativeData.intToByte(tval);
1089                    }
1090                    else if (datatypeSize == 8) {
1091                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT64");
1092                        Long tval = Long.parseLong(memstr);
1093                        memval = HDFNativeData.longToByte(tval);
1094                    }
1095                    else {
1096                        log.debug("createNative(): enum datatypeSize incorrect");
1097                    }
1098                    log.trace("createNative(): H5Tenum_insert {} {}", memname, memval);
1099                    H5.H5Tenum_insert(tid, memname, memval);
1100                }
1101            }
1102            catch (Exception ex) {
1103                log.debug("createNative(): set up enum members failure: ", ex);
1104            }
1105            finally {
1106                close(ptid);
1107            }
1108        } // if (datatypeClass == CLASS_ENUM) {
1109
1110        try {
1111            tmptid = tid;
1112            tid = H5.H5Tget_native_type(tmptid);
1113        }
1114        catch (HDF5Exception ex) {
1115            log.debug("createNative(): H5Tget_native_type{} failure: ", tmptid, ex);
1116        }
1117        finally {
1118            close(tmptid);
1119        }
1120
1121        return tid;
1122    }
1123
1124    /**
1125     * Allocates a one-dimensional array of byte, short, int, long, float, double,
1126     * or String to store data in memory.
1127     *
1128     * For example,
1129     *
1130     * <pre>
1131     * long tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT32);
1132     * int[] data = (int[]) allocateArray(100);
1133     * </pre>
1134     *
1135     * returns a 32-bit integer array of size 100.
1136     *
1137     * @param spaceSize
1138     *            the total number of data points of the array.
1139     *
1140     * @return the array object if successful; otherwise, return null.
1141     *
1142     * @throws OutOfMemoryError
1143     *             If there is a failure.
1144     */
1145    public Object allocateArray(int spaceSize) throws OutOfMemoryError {
1146        log.trace("allocateArray(): start: spaceSize={}", spaceSize);
1147        Object data = null;
1148
1149        if (spaceSize < 0) {
1150            log.debug("allocateArray(): spaceSize < 0");
1151            log.trace("allocateArray(): finish");
1152            return null;
1153        }
1154
1155        // Scalar members have dimensionality zero, i.e. size =0
1156        // what can we do about it, set the size to 1
1157        if (spaceSize == 0) {
1158            spaceSize = 1;
1159        }
1160
1161        log.trace("allocateArray(): tclass={} : tsize={}", nativeClass, datatypeSize);
1162
1163        if (is_variable_str || is_VLEN || is_reg_ref) {
1164            log.trace("allocateArray(): is_variable_str={} || isVL={} || is_reg_ref={}", is_variable_str, is_VLEN, is_reg_ref);
1165            data = new String[spaceSize];
1166            for (int i = 0; i < spaceSize; i++) {
1167                ((String[]) data)[i] = "";
1168            }
1169        }
1170        else if (nativeClass == HDF5Constants.H5T_INTEGER) {
1171            log.trace("allocateArray(): class.H5T_INTEGER={}", nativeClass);
1172            if (datatypeSize == 1) {
1173                data = new byte[spaceSize];
1174            }
1175            else if (datatypeSize == 2) {
1176                data = new short[spaceSize];
1177            }
1178            else if (datatypeSize == 4) {
1179                data = new int[spaceSize];
1180            }
1181            else if (datatypeSize == 8) {
1182                data = new long[spaceSize];
1183            }
1184        }
1185        else if (nativeClass == HDF5Constants.H5T_ENUM) {
1186            log.trace("allocateArray(): class.H5T_ENUM={}", nativeClass);
1187            if (baseType != null)
1188                data = ((H5Datatype) baseType).allocateArray(spaceSize);
1189            else
1190                data = new byte[(int) (spaceSize * datatypeSize)];
1191        }
1192        else if (nativeClass == HDF5Constants.H5T_COMPOUND) {
1193            log.trace("allocateArray(): class.H5T_COMPOUND={}", nativeClass);
1194            if (baseType != null)
1195                data = ((H5Datatype) baseType).allocateArray((spaceSize));
1196            else
1197                data = new byte[(int) (spaceSize * datatypeSize)];
1198        }
1199        else if (nativeClass == HDF5Constants.H5T_FLOAT) {
1200            log.trace("allocateArray(): class.H5T_FLOAT={}", nativeClass);
1201            if (datatypeSize == 4) {
1202                data = new float[spaceSize];
1203            }
1204            else if (datatypeSize == 8) {
1205                data = new double[spaceSize];
1206            }
1207        }
1208        else if ((nativeClass == HDF5Constants.H5T_STRING) || (nativeClass == HDF5Constants.H5T_REFERENCE)) {
1209            log.trace("allocateArray(): class.H5T_STRING || H5T_REFERENCE={}", nativeClass);
1210            data = new byte[(int) (spaceSize * datatypeSize)];
1211        }
1212        else if (nativeClass == HDF5Constants.H5T_ARRAY) {
1213            // use the base datatype to define the array
1214            try {
1215                log.trace("allocateArray(): ArrayRank={}", arrayDims.length);
1216                int asize = spaceSize;
1217                for (int j = 0; j < arrayDims.length; j++) {
1218                    log.trace("allocateArray(): ArrayRank={} : dims[{}]={}", arrayDims.length, j, arrayDims[j]);
1219                    asize *= arrayDims[j];
1220                }
1221                log.trace("allocateArray(): class.H5T_ARRAY={} : members={} : asize={} : datatypeSize={}", nativeClass, arrayDims.length, asize, datatypeSize);
1222
1223                if (baseType != null) {
1224                    // data = new byte[(int) (size * asize * datatypeSize)];
1225                    data = ((H5Datatype) baseType).allocateArray(asize);
1226                }
1227            }
1228            catch (Exception ex) {
1229                log.debug("allocateArray(): H5T_ARRAY class failure: ", ex);
1230            }
1231        }
1232        else if ((nativeClass == HDF5Constants.H5T_OPAQUE) || (nativeClass == HDF5Constants.H5T_BITFIELD)) {
1233            log.trace("allocateArray(): class.H5T_OPAQUE || H5T_BITFIELD={}", nativeClass);
1234            data = new byte[(int) (spaceSize * datatypeSize)];
1235        }
1236        else {
1237            log.debug("allocateArray(): class.????={}", nativeClass);
1238            data = null;
1239        }
1240
1241        return data;
1242    }
1243
1244    /**
1245     * Returns the size (in bytes) of a given datatype identifier.
1246     * <p>
1247     * It basically just calls H5Tget_size(tid).
1248     *
1249     * @param tid
1250     *            The datatype identifier.
1251     *
1252     * @return The size of the datatype in bytes.
1253     *
1254     * @see hdf.hdf5lib.H5#H5Tget_size(long)
1255     */
1256    public static final long getDatatypeSize(long tid) {
1257        // data type information
1258        long tsize = -1;
1259
1260        try {
1261            tsize = H5.H5Tget_size(tid);
1262        }
1263        catch (Exception ex) {
1264            tsize = -1;
1265        }
1266
1267        return tsize;
1268    }
1269
1270    /*
1271     * (non-Javadoc)
1272     *
1273     * @see hdf.object.Datatype#getDescription()
1274     */
1275    @Override
1276    public String getDescription() {
1277        log.trace("getDescription(): start");
1278
1279        if (datatypeDescription != null) {
1280            log.trace("getDescription(): finish");
1281            return datatypeDescription;
1282        }
1283
1284        String description = null;
1285        long tid = -1;
1286
1287        switch (datatypeClass) {
1288            case CLASS_CHAR:
1289                description = "8-bit " + (isUnsigned() ? "unsigned " : "") + "integer";
1290                break;
1291            case CLASS_INTEGER:
1292                description = String.valueOf(datatypeSize * 8) + "-bit " + (isUnsigned() ? "unsigned " : "") + "integer";
1293                break;
1294            case CLASS_FLOAT:
1295                description = String.valueOf(datatypeSize * 8) + "-bit floating-point";
1296                break;
1297            case CLASS_STRING:
1298                description = "String, length = " + (isVarStr() ? "variable" : datatypeSize);
1299
1300                try {
1301                    tid = createNative();
1302                    if (tid >= 0) {
1303                        String strPadType;
1304                        int strPad = H5.H5Tget_strpad(tid);
1305
1306                        if (strPad == HDF5Constants.H5T_STR_NULLTERM)
1307                            strPadType = "H5T_STR_NULLTERM";
1308                        else if (strPad == HDF5Constants.H5T_STR_NULLPAD)
1309                            strPadType = "H5T_STR_NULLPAD";
1310                        else if (strPad == HDF5Constants.H5T_STR_SPACEPAD)
1311                            strPadType = "H5T_STR_SPACEPAD";
1312                        else
1313                            strPadType = null;
1314
1315                        if (strPadType != null)
1316                            description += ", string padding = " + strPadType;
1317                    }
1318                    else {
1319                        log.debug("createNative() failure");
1320                    }
1321                }
1322                catch (Exception ex) {
1323                    log.debug("H5Tget_strpad failure: ", ex);
1324                }
1325                finally {
1326                    close(tid);
1327                }
1328                break;
1329            case CLASS_BITFIELD:
1330                description = String.valueOf(datatypeSize * 8) + "-bit bitfield";
1331                break;
1332            case CLASS_OPAQUE:
1333                description = String.valueOf(datatypeSize) + "-byte Opaque";
1334
1335                if (opaqueTag != null) {
1336                    description += ", tag = " + opaqueTag;
1337                }
1338
1339                break;
1340            case CLASS_COMPOUND:
1341                description = "Compound";
1342
1343                if ((compoundMemberTypes != null) && (compoundMemberTypes.size() > 0)) {
1344                    Iterator<String> member_names = null;
1345                    Iterator<Datatype> member_types = compoundMemberTypes.iterator();
1346
1347                    if (compoundMemberNames != null)
1348                        member_names = compoundMemberNames.iterator();
1349
1350                    description += " {";
1351
1352                    while (member_types.hasNext()) {
1353                        if (member_names != null && member_names.hasNext()) {
1354                            description += member_names.next() + " = ";
1355                        }
1356
1357                        description += member_types.next().getDescription();
1358
1359                        if (member_types.hasNext())
1360                            description += ", ";
1361                    }
1362
1363                    description += "}";
1364                }
1365
1366                break;
1367            case CLASS_REFERENCE:
1368                description = "Reference";
1369
1370                try {
1371                    boolean is_reg_ref = false;
1372
1373                    tid = createNative();
1374                    if (tid >= 0) {
1375                        is_reg_ref = H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF_DSETREG);
1376
1377                        if (is_reg_ref) {
1378                            description = "Dataset region reference";
1379                        }
1380                        else {
1381                            description = "Object reference";
1382                        }
1383                    }
1384                }
1385                catch (Exception ex) {
1386                    log.debug("H5.H5Tequal failure: ", ex);
1387                }
1388                finally {
1389                    close(tid);
1390                }
1391
1392                break;
1393            case CLASS_ENUM:
1394                description = String.valueOf(datatypeSize * 8) + "-bit enum";
1395
1396                String members = getEnumMembersAsString();
1397                if (members != null)
1398                    description += " (" + members + ")";
1399
1400                break;
1401            case CLASS_VLEN:
1402                description = "Variable-length";
1403
1404                if (baseType != null) {
1405                    description += " of " + baseType.getDescription();
1406                }
1407
1408                break;
1409            case CLASS_ARRAY:
1410                description = "Array";
1411
1412                if (baseType != null) {
1413                    description += " of " + baseType.getDescription();
1414                }
1415
1416                if (arrayDims != null) {
1417                    description += " [";
1418                    for (int i = 0; i < arrayDims.length; i++) {
1419                        description += arrayDims[i];
1420                        if (i < arrayDims.length - 1)
1421                            description += " x ";
1422                    }
1423                    description += "]";
1424                }
1425
1426                break;
1427            default:
1428                description = "Unknown";
1429                break;
1430        }
1431
1432        log.trace("getDescription(): finish");
1433        return description;
1434    }
1435
1436    /**
1437     * Checks if a datatype specified by the identifier is an unsigned integer.
1438     *
1439     * @param tid
1440     *            the datatype ID to be checked.
1441     *
1442     * @return true is the datatype is an unsigned integer; otherwise returns false.
1443     */
1444    public static final boolean isUnsigned(long tid) {
1445        boolean unsigned = false;
1446
1447        if (tid >= 0) {
1448            try {
1449                int tclass = H5.H5Tget_class(tid);
1450                log.trace("isUnsigned(): tclass = {}", tclass);
1451                if (tclass != HDF5Constants.H5T_FLOAT && tclass != HDF5Constants.H5T_STRING
1452                        && tclass != HDF5Constants.H5T_REFERENCE && tclass != HDF5Constants.H5T_BITFIELD
1453                        && tclass != HDF5Constants.H5T_OPAQUE && tclass != HDF5Constants.H5T_VLEN
1454                        && tclass != HDF5Constants.H5T_COMPOUND && tclass != HDF5Constants.H5T_ARRAY) {
1455                    int tsign = H5.H5Tget_sign(tid);
1456                    if (tsign == HDF5Constants.H5T_SGN_NONE) {
1457                        unsigned = true;
1458                    }
1459                    else {
1460                        log.trace("isUnsigned(): not unsigned");
1461                    }
1462                }
1463                else {
1464                    log.trace("isUnsigned(): tclass not integer type");
1465                }
1466            }
1467            catch (Exception ex) {
1468                log.debug("isUnsigned(): Datatype {} failure", tid, ex);
1469                unsigned = false;
1470            }
1471        }
1472        else {
1473            log.trace("isUnsigned(): not a valid datatype");
1474        }
1475
1476        return unsigned;
1477    }
1478
1479    /*
1480     * (non-Javadoc)
1481     *
1482     * @see hdf.object.Datatype#getMetadata()
1483     */
1484    @Override
1485    public List<Attribute> getMetadata() throws HDF5Exception {
1486        return this.getMetadata(fileFormat.getIndexType(null), fileFormat.getIndexOrder(null));
1487    }
1488
1489    /*
1490     * (non-Javadoc)
1491     *
1492     * @see hdf.object.DataFormat#getMetadata(int...)
1493     */
1494    public List<Attribute> getMetadata(int... attrPropList) throws HDF5Exception {
1495        log.trace("getMetadata(): start");
1496        // load attributes first
1497        if (attributeList == null) {
1498            int indxType = fileFormat.getIndexType(null);
1499            int order = fileFormat.getIndexOrder(null);
1500
1501            if (attrPropList.length > 0) {
1502                indxType = attrPropList[0];
1503                if (attrPropList.length > 1) {
1504                    order = attrPropList[1];
1505                }
1506            }
1507
1508            try {
1509                attributeList = H5File.getAttribute(this, indxType, order);
1510            }
1511            catch (Exception ex) {
1512                log.debug("getMetadata(): H5File.getAttribute failure: ", ex);
1513            }
1514        } // if (attributeList == null)
1515
1516        try {
1517            this.linkTargetObjName = H5File.getLinkTargetName(this);
1518        }
1519        catch (Exception ex) {
1520            log.debug("getMetadata(): H5File.linkTargetObjName failure: ", ex);
1521        }
1522
1523        log.trace("getMetadata(): finish");
1524        return attributeList;
1525    }
1526
1527    /*
1528     * (non-Javadoc)
1529     *
1530     * @see hdf.object.Datatype#writeMetadata(java.lang.Object)
1531     */
1532    @Override
1533    public void writeMetadata(Object info) throws Exception {
1534        log.trace("writeMetadata(): start");
1535
1536        // only attribute metadata is supported.
1537        if (!(info instanceof Attribute)) {
1538            log.debug("writeMetadata(): Object not an Attribute");
1539            log.trace("writeMetadata(): finish");
1540            return;
1541        }
1542
1543        boolean attrExisted = false;
1544        Attribute attr = (Attribute) info;
1545
1546        if (attributeList == null) {
1547            this.getMetadata();
1548        }
1549
1550        if (attributeList != null)
1551            attrExisted = attributeList.contains(attr);
1552
1553        getFileFormat().writeAttribute(this, attr, attrExisted);
1554
1555        // add the new attribute into attribute list
1556        if (!attrExisted) {
1557            attributeList.add(attr);
1558            nAttributes = attributeList.size();
1559        }
1560        log.trace("writeMetadata(): finish");
1561    }
1562
1563    /*
1564     * (non-Javadoc)
1565     *
1566     * @see hdf.object.Datatype#removeMetadata(java.lang.Object)
1567     */
1568    @Override
1569    public void removeMetadata(Object info) throws HDF5Exception {
1570        log.trace("removeMetadata(): start");
1571
1572        // only attribute metadata is supported.
1573        if (!(info instanceof Attribute)) {
1574            log.debug("removeMetadata(): Object not an attribute");
1575            log.trace("removeMetadata(): finish");
1576            return;
1577        }
1578
1579        Attribute attr = (Attribute) info;
1580        long tid = open();
1581        try {
1582            H5.H5Adelete(tid, attr.getName());
1583            List<Attribute> attrList = getMetadata();
1584            attrList.remove(attr);
1585            nAttributes = attributeList.size();
1586        }
1587        catch (Exception ex) {
1588            log.debug("removeMetadata(): ", ex);
1589        }
1590        finally {
1591            close(tid);
1592        }
1593        log.trace("removeMetadata(): finish");
1594    }
1595
1596    @Override
1597    public void setName(String newName) throws Exception {
1598        H5File.renameObject(this, newName);
1599        super.setName(newName);
1600    }
1601
1602    @Override
1603    public boolean isText() {
1604        return (datatypeClass == Datatype.CLASS_STRING);
1605    }
1606
1607    public boolean isRefObj() {
1608        return is_ref_obj;
1609    }
1610
1611    public boolean isRegRef() {
1612        return is_reg_ref;
1613    }
1614
1615    public int getNativeStrPad() {
1616        return nativeStrPad;
1617    }
1618
1619    /**
1620     * Extracts compound information into flat structure.
1621     * <p>
1622     * For example, compound datatype "nest" has {nest1{a, b, c}, d, e} then extractCompoundInfo() will
1623     * put the names of nested compound fields into a flat list as
1624     *
1625     * <pre>
1626     * nest.nest1.a
1627     * nest.nest1.b
1628     * nest.nest1.c
1629     * nest.d
1630     * nest.e
1631     * </pre>
1632     *
1633     * @param name
1634     *            the name of the compound datatype
1635     * @param names
1636     *            the list to store the member names of the compound datatype
1637     * @param flatListTypes
1638     *            the list to store the nested member names of the compound datatype
1639     */
1640    public void extractCompoundInfo(String name, List<String> names, List<Datatype> flatListTypes) {
1641        log.trace("extractCompoundInfo(): start: name={}", name);
1642
1643        if (isArray() || isVLEN()) {
1644            log.trace("extractCompoundInfo(): top-level types is ARRAY or VLEN; extracting compound info from base type");
1645            ((H5Datatype) getDatatypeBase()).extractCompoundInfo(name, names, flatListTypes);
1646        }
1647        else {
1648            if (compoundMemberNames == null) {
1649                log.debug("extractCompoundInfo(): compoundMemberNames is null");
1650                log.trace("extractCompoundInfo(): finish");
1651                return;
1652            }
1653
1654            Datatype mtype = null;
1655            String mname = null;
1656
1657            log.trace("extractCompoundInfo(): nMembers={}", compoundMemberNames.size());
1658
1659            if (compoundMemberNames.size() <= 0) {
1660                log.debug("extractCompoundInfo(): datatype has no members");
1661                log.trace("extractCompoundInfo(): finish");
1662                return;
1663            }
1664
1665            for (int i = 0; i < compoundMemberNames.size(); i++) {
1666                log.trace("extractCompoundInfo(): nMembers[{}]", i);
1667
1668                mtype = compoundMemberTypes.get(i);
1669                log.trace("extractCompoundInfo():[{}] mtype={} with size={}", i, mtype.getDescription(), mtype.getDatatypeSize());
1670
1671                if (names != null) {
1672                    mname = name + compoundMemberNames.get(i);
1673                    log.trace("extractCompoundInfo():[{}] mname={}, name={}", i, mname, name);
1674                }
1675
1676                if (mtype.isCompound()) {
1677                    ((H5Datatype) mtype).extractCompoundInfo(mname + CompoundDS.separator, names, flatListTypes);
1678                    log.debug("extractCompoundInfo(): continue after recursive H5T_COMPOUND[{}]:", i);
1679                    continue;
1680                }
1681
1682                if (names != null) {
1683                    names.add(mname);
1684                }
1685                flatListTypes.add(mtype);
1686
1687            } // for (int i=0; i<nMembers; i++)
1688        }
1689
1690        log.trace("extractCompoundInfo(): finish");
1691    } // extractCompoundInfo
1692
1693    /**
1694     * Creates a datatype of a compound with one field.
1695     * <p>
1696     * This function is needed to read/write data field by field.
1697     *
1698     * @param member_name
1699     *            The name of the datatype
1700     *
1701     * @return the identifier of the compound datatype.
1702     *
1703     * @throws HDF5Exception
1704     *             If there is an error at the HDF5 library level.
1705     */
1706    public long createCompoundFieldType(String member_name) throws HDF5Exception {
1707        log.trace("createCompoundFieldType(): start member_name={}", member_name);
1708        long nested_tid = -1;
1709        long tmp_tid1 = -1;
1710        try {
1711            log.trace("createCompoundFieldType(): {} Member is type {} of size={} with baseType={}", member_name, getDescription(), getDatatypeSize(), getDatatypeBase());
1712            tmp_tid1 = createNative();
1713
1714            /*
1715             * If this member is nested inside a compound, keep inserting
1716             * it into a newly-created compound datatype until we reach
1717             * the top-level compound type.
1718             */
1719            int sep = member_name.lastIndexOf(CompoundDS.separator);
1720            while (sep > 0) {
1721                String theName = member_name.substring(sep + 1);
1722
1723                log.trace("createCompoundFieldType(): member with name {} is nested inside compound", theName);
1724
1725                nested_tid = H5.H5Tcreate(HDF5Constants.H5T_COMPOUND, datatypeSize);
1726                H5.H5Tinsert(nested_tid, theName, 0, tmp_tid1);
1727                close(tmp_tid1);
1728                tmp_tid1 = nested_tid;
1729                member_name = member_name.substring(0, sep);
1730                sep = member_name.lastIndexOf(CompoundDS.separator);
1731            }
1732
1733            nested_tid = H5.H5Tcreate(HDF5Constants.H5T_COMPOUND, datatypeSize);
1734
1735            H5.H5Tinsert(nested_tid, member_name, 0, tmp_tid1);
1736        }
1737        finally {
1738            close(tmp_tid1);
1739        }
1740
1741        log.trace("createCompoundFieldType(): finish");
1742        return nested_tid;
1743    }
1744}