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.object.h5;
016
017import java.lang.reflect.Array;
018import java.math.BigDecimal;
019import java.math.BigInteger;
020import java.math.MathContext;
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.BitSet;
024import java.util.HashMap;
025import java.util.Iterator;
026import java.util.List;
027import java.util.Map.Entry;
028import java.util.Objects;
029import java.util.Vector;
030
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034import hdf.hdf5lib.H5;
035import hdf.hdf5lib.HDF5Constants;
036import hdf.hdf5lib.HDFArray;
037import hdf.hdf5lib.HDFNativeData;
038import hdf.hdf5lib.exceptions.HDF5Exception;
039import hdf.hdf5lib.exceptions.HDF5LibraryException;
040import hdf.hdf5lib.structs.H5O_info_t;
041import hdf.hdf5lib.structs.H5O_token_t;
042
043import hdf.object.Attribute;
044import hdf.object.CompoundDS;
045import hdf.object.Datatype;
046import hdf.object.FileFormat;
047
048import hdf.object.h5.H5MetaDataContainer;
049
050/**
051 * This class defines HDF5 datatype characteristics and APIs for a data type. This class provides several methods to
052 * convert an HDF5 datatype identifier to a datatype object, and vice versa. A datatype object is described by four
053 * basic fields: datatype class, size, byte order, and sign, while an HDF5 datatype is presented by a datatype
054 * identifier.
055 *
056 * @version 1.1 9/4/2007
057 * @author Peter X. Cao
058 */
059public class H5Datatype extends Datatype
060{
061    private static final long serialVersionUID = -750546422258749792L;
062
063    private static final Logger log = LoggerFactory.getLogger(H5Datatype.class);
064
065    /**
066     * The metadata object for this data object. Members of the metadata are instances of Attribute.
067     */
068    private H5MetaDataContainer objMetadata;
069
070    /**
071     * The dimension sizes of the reference object
072     */
073    protected long[] refdims;
074
075    /** the datatype is an object reference */
076    private boolean isRefObj = false;
077
078    /** the datatype is a region reference */
079    private boolean isRegRef = false;
080
081    /** the datatype is a standard reference */
082    private boolean isStdRef = false;
083
084    /** the object properties */
085    private H5O_info_t objInfo;
086
087    /**
088     * The native class of the datatype.
089     */
090    private int nativeClass = -1;
091
092    /** The native Precision properties of the number datatype. */
093    private long nativePrecision = 0;
094    /** The native Offset properties of the number datatype. */
095    private int nativeOffset = -1;
096    /** The native PadLSB properties of the number datatype. */
097    private int nativePadLSB = -1;
098    /** The native PadMSB properties of the number datatype. */
099    private int nativePadMSB = -1;
100
101    /** The native ebias properties of the float datatype. */
102    private long nativeFPebias = 0;
103    /** The native spos properties of the float datatype. */
104    private long nativeFPspos = -1;
105    /** The native epos properties of the float datatype. */
106    private long nativeFPepos = -1;
107    /** The native esize properties of the float datatype. */
108    private long nativeFPesize = -1;
109    /** The native mpos properties of the float datatype. */
110    private long nativeFPmpos = -1;
111    /** The native msize properties of the float datatype. */
112    private long nativeFPmsize = -1;
113    /** The native norm properties of the float datatype. */
114    private int nativeFPnorm = -1;
115    /** The native inpad properties of the float datatype. */
116    private int nativeFPinpad = -1;
117
118    /** The native padding properties of the string datatype. */
119    private int nativeStrPad = -1;
120    /** The native CSET properties of the string datatype. */
121    private int nativeStrCSET = -1;
122
123    /**
124     * The tag for an opaque datatype.
125     */
126    private String opaqueTag = null;
127
128    /**
129     * Constructs an named HDF5 data type object for a given file, dataset name and group path. The datatype object
130     * represents an existing named datatype in file. For example,
131     *
132     * <pre>
133     * new H5Datatype(file, "dtype1", "/g0")
134     * </pre>
135     *
136     * constructs a datatype object that corresponds to the dataset,"dset1", at group "/g0".
137     *
138     * @param theFile
139     *                the file that contains the datatype.
140     * @param theName
141     *                the name of the dataset such as "dset1".
142     * @param thePath
143     *                the group path to the dataset such as "/g0/".
144     */
145    public H5Datatype(FileFormat theFile, String theName, String thePath) {
146        this(theFile, theName, thePath, null);
147    }
148
149    /**
150     * @deprecated Not for public use in the future. <br>
151     *             Using {@link #H5Datatype(FileFormat, String, String)}
152     * @param theFile
153     *                the file that contains the datatype.
154     * @param theName
155     *                the name of the dataset such as "dset1".
156     * @param thePath
157     *                the group path to the dataset such as "/g0/".
158     * @param oid
159     *                the oid of the dataset.
160     */
161    @Deprecated
162    public H5Datatype(FileFormat theFile, String theName, String thePath, long[] oid) {
163        super(theFile, theName, thePath, oid);
164        objMetadata = new H5MetaDataContainer(theFile, theName, thePath, this);
165
166        if (theFile != null) {
167            if (oid == null) {
168                // retrieve the object ID
169                byte[] refBuf = null;
170                try {
171                    refBuf = H5.H5Rcreate_object(theFile.getFID(), this.getFullName(), HDF5Constants.H5P_DEFAULT);
172                    this.oid = HDFNativeData.byteToLong(refBuf);
173                    log.trace("constructor REF {} to OID {}", refBuf, this.oid);
174                }
175                catch (Exception ex) {
176                    log.debug("constructor ID {} for {} failed H5Rcreate_object", theFile.getFID(), this.getFullName());
177                }
178                finally {
179                    if (refBuf != null)
180                        H5.H5Rdestroy(refBuf);
181                }
182            }
183            log.trace("constructor OID {}", this.oid);
184            try {
185                objInfo = H5.H5Oget_info_by_name(theFile.getFID(), this.getFullName(), HDF5Constants.H5O_INFO_BASIC,
186                        HDF5Constants.H5P_DEFAULT);
187            }
188            catch (Exception ex) {
189                objInfo = new H5O_info_t(-1L, null, 0, 0, 0L, 0L, 0L, 0L, 0L);
190            }
191
192            long tid = HDF5Constants.H5I_INVALID_HID;
193            try {
194                tid = open();
195            }
196            catch (Exception ex) {
197                log.debug("constructor H5Topen() failure");
198            }
199            finally {
200                close(tid);
201            }
202        }
203        else {
204            this.oid = null;
205            objInfo = new H5O_info_t(-1L, null, 0, 0, 0L, 0L, 0L, 0L, 0L);
206        }
207    }
208
209    /**
210     * Constructs a Datatype with specified class, size, byte order and sign. The following is a list of a few examples
211     * of H5Datatype.
212     * <ol>
213     * <li>to create unsigned native integer<br>
214     * H5Datatype type = new H5Dataype(Datatype.CLASS_INTEGER, Datatype.NATIVE, Datatype.NATIVE, Datatype.SIGN_NONE);
215     * <li>to create 16-bit signed integer with big endian<br>
216     * H5Datatype type = new H5Dataype(Datatype.CLASS_INTEGER, 2, Datatype.ORDER_BE, Datatype.NATIVE);
217     * <li>to create native float<br>
218     * H5Datatype type = new H5Dataype(Datatype.CLASS_FLOAT, Datatype.NATIVE, Datatype.NATIVE, Datatype.NATIVE);
219     * <li>to create 64-bit double<br>
220     * H5Datatype type = new H5Dataype(Datatype.CLASS_FLOAT, 8, Datatype.NATIVE, Datatype.NATIVE);
221     * </ol>
222     *
223     * @param tclass
224     *               the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
225     * @param tsize
226     *               the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4. Valid values are
227     *               NATIVE or a positive value. For string datatypes, -1 is also a valid value (to create a
228     *               variable-length string).
229     * @param torder
230     *               the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX, ORDER_NONE and
231     *               NATIVE.
232     * @param tsign
233     *               the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and NATIVE.
234     * @throws Exception
235     *                   if there is an error
236     */
237    public H5Datatype(int tclass, int tsize, int torder, int tsign) throws Exception {
238        this(tclass, tsize, torder, tsign, null);
239    }
240
241    /**
242     * Constructs a Datatype with specified class, size, byte order and sign. The following is a list of a few examples
243     * of H5Datatype.
244     * <ol>
245     * <li>to create unsigned native integer<br>
246     * H5Datatype type = new H5Dataype(Datatype.CLASS_INTEGER, Datatype.NATIVE, Datatype.NATIVE, Datatype.SIGN_NONE);
247     * <li>to create 16-bit signed integer with big endian<br>
248     * H5Datatype type = new H5Dataype(Datatype.CLASS_INTEGER, 2, Datatype.ORDER_BE, Datatype.NATIVE);
249     * <li>to create native float<br>
250     * H5Datatype type = new H5Dataype(Datatype.CLASS_FLOAT, Datatype.NATIVE, Datatype.NATIVE, Datatype.NATIVE);
251     * <li>to create 64-bit double<br>
252     * H5Datatype type = new H5Dataype(Datatype.CLASS_FLOAT, 8, Datatype.NATIVE, Datatype.NATIVE);
253     * </ol>
254     *
255     * @param tclass
256     *               the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
257     * @param tsize
258     *               the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4. Valid values are
259     *               NATIVE or a positive value. For string datatypes, -1 is also a valid value (to create a
260     *               variable-length string).
261     * @param torder
262     *               the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX, ORDER_NONE and
263     *               NATIVE.
264     * @param tsign
265     *               the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and NATIVE.
266     * @param tbase
267     *               the base datatype of the new datatype
268     * @throws Exception
269     *                   if there is an error
270     */
271    public H5Datatype(int tclass, int tsize, int torder, int tsign, Datatype tbase) throws Exception {
272        this(tclass, tsize, torder, tsign, tbase, null);
273    }
274
275    /**
276     * Constructs a Datatype with specified class, size, byte order and sign. The following is a list of a few examples
277     * of H5Datatype.
278     * <ol>
279     * <li>to create unsigned native integer<br>
280     * H5Datatype type = new H5Dataype(Datatype.CLASS_INTEGER, Datatype.NATIVE, Datatype.NATIVE, Datatype.SIGN_NONE);
281     * <li>to create 16-bit signed integer with big endian<br>
282     * H5Datatype type = new H5Dataype(Datatype.CLASS_INTEGER, 2, Datatype.ORDER_BE, Datatype.NATIVE);
283     * <li>to create native float<br>
284     * H5Datatype type = new H5Dataype(Datatype.CLASS_FLOAT, Datatype.NATIVE, Datatype.NATIVE, Datatype.NATIVE);
285     * <li>to create 64-bit double<br>
286     * H5Datatype type = new H5Dataype(Datatype.CLASS_FLOAT, 8, Datatype.NATIVE, Datatype.NATIVE);
287     * </ol>
288     *
289     * @param tclass
290     *               the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
291     * @param tsize
292     *               the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4. Valid values are
293     *               NATIVE or a positive value. For string datatypes, -1 is also a valid value (to create a
294     *               variable-length string).
295     * @param torder
296     *               the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX, ORDER_NONE and
297     *               NATIVE.
298     * @param tsign
299     *               the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and NATIVE.
300     * @param tbase
301     *               the base datatype of the new datatype
302     * @param pbase
303     *               the parent datatype of the new datatype
304     * @throws Exception
305     *                   if there is an error
306     */
307    public H5Datatype(int tclass, int tsize, int torder, int tsign, Datatype tbase, Datatype pbase) throws Exception {
308        super(tclass, tsize, torder, tsign, tbase, pbase);
309        datatypeDescription = getDescription();
310    }
311
312    /**
313     * Constructs a Datatype with a given native datatype identifier. For example, if the datatype identifier is a
314     * 32-bit unsigned integer created from HDF5,
315     *
316     * <pre>
317     * int tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_UNINT32);
318     * Datatype dtype = new Datatype(tid);
319     * </pre>
320     *
321     * will construct a datatype equivalent to new Datatype(Datatype.CLASS_INTEGER, 4, Datatype.NATIVE,
322     * Datatype.SIGN_NONE);
323     *
324     * @see #fromNative(long nativeID)
325     * @param theFile
326     *                 the file that contains the datatype.
327     * @param nativeID
328     *                 the native datatype identifier.
329     * @throws Exception
330     *                   if there is an error
331     */
332    public H5Datatype(FileFormat theFile, long nativeID) throws Exception {
333        this(theFile, nativeID, null);
334    }
335
336    /**
337     * Constructs a Datatype with a given native datatype identifier. For example, if the datatype identifier is a
338     * 32-bit unsigned integer created from HDF5,
339     *
340     * <pre>
341     * int tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_UNINT32);
342     * Datatype dtype = new Datatype(tid);
343     * </pre>
344     *
345     * will construct a datatype equivalent to new Datatype(Datatype.CLASS_INTEGER, 4, Datatype.NATIVE,
346     * Datatype.SIGN_NONE);
347     *
348     * @see #fromNative(long nativeID)
349     * @param theFile
350     *                 the file that contains the datatype.
351     * @param nativeID
352     *                 the native datatype identifier.
353     * @param pbase
354     *                 the parent datatype of the new datatype
355     * @throws Exception
356     *                   if there is an error
357     */
358    public H5Datatype(FileFormat theFile, long nativeID, Datatype pbase) throws Exception {
359        super(theFile, nativeID, pbase);
360        fromNative(nativeID);
361        datatypeDescription = getDescription();
362    }
363
364    /**
365     * Opens access to a named datatype. It calls H5.H5Topen(loc, name).
366     *
367     * @return the datatype identifier if successful; otherwise returns negative value.
368     * @see hdf.hdf5lib.H5#H5Topen(long, String, long)
369     */
370    @Override
371    public long open() {
372        long tid = HDF5Constants.H5I_INVALID_HID;
373
374        if (fileFormat != null) {
375            try {
376                tid = H5.H5Topen(getFID(), getFullName(), HDF5Constants.H5P_DEFAULT);
377                fromNative(tid);
378                log.trace("open(): tid={}", tid);
379            }
380            catch (HDF5Exception ex) {
381                log.debug("open(): Failed to open datatype {}", getFullName(), ex);
382                tid = HDF5Constants.H5I_INVALID_HID;
383            }
384        }
385
386        return tid;
387    }
388
389    /**
390     * Closes a datatype identifier. It calls H5.H5close(tid).
391     *
392     * @param tid
393     *            the datatype ID to close
394     */
395    @Override
396    public void close(long tid) {
397        if (tid >= 0) {
398            try {
399                H5.H5Tclose(tid);
400            }
401            catch (HDF5Exception ex) {
402                log.debug("close(): H5Tclose(tid {}) failure: ", tid, ex);
403            }
404        }
405    }
406
407    /**
408     * Get the token for this object.
409     *
410     * @return true if it has any attributes, false otherwise.
411     */
412    public long[] getToken() {
413        H5O_token_t token = objInfo.token;
414        return HDFNativeData.byteToLong(token.data);
415    }
416
417    /**
418     * Check if the object has any attributes attached.
419     *
420     * @return true if it has any attributes, false otherwise.
421     */
422    @Override
423    public boolean hasAttribute() {
424        objInfo.num_attrs = objMetadata.getObjectAttributeSize();
425
426        if (objInfo.num_attrs < 0) {
427            long tid = open();
428            if (tid > 0) {
429                try {
430                    objInfo = H5.H5Oget_info(tid);
431                }
432                catch (Exception ex) {
433                    objInfo.num_attrs = 0;
434                    log.debug("hasAttribute(): get object info failure: ", ex);
435                }
436                finally {
437                    close(tid);
438                }
439                objMetadata.setObjectAttributeSize((int) objInfo.num_attrs);
440            }
441            else {
442                log.debug("hasAttribute(): could not open group");
443            }
444        }
445
446        log.trace("hasAttribute(): nAttributes={}", objInfo.num_attrs);
447        return (objInfo.num_attrs > 0);
448    }
449
450    /**
451     * Converts values in an Enumeration Datatype to names. This method searches the identified enumeration datatype for
452     * the values appearing in <code>inValues</code> and returns the names corresponding to those values. If a given
453     * value is not found in the enumeration datatype, the name corresponding to that value will be set to
454     * <code>"ENUM ERR value"</code> in the string array that is returned. If the method fails in general, null will be
455     * returned instead of a String array. An empty <code>inValues</code> parameter would cause general failure.
456     *
457     * @param inValues
458     *                 The array of enumerations values to be converted.
459     * @return The string array of names if successful; otherwise return null.
460     * @throws HDF5Exception
461     *                       If there is an error at the HDF5 library level.
462     */
463    public String[] convertEnumValueToName(Object inValues)
464            throws HDF5Exception {
465        log.trace("convertEnumValueToName() inValues={} start", inValues);
466
467        if (inValues == null) {
468            log.debug("convertEnumValueToName() failure: in values null ");
469            return null;
470        }
471
472        int inSize = 0;
473        String[] outNames = null;
474        String cName = inValues.getClass().getName();
475        boolean isArray = cName.lastIndexOf('[') >= 0;
476        if (isArray)
477            inSize = Array.getLength(inValues);
478        else
479            inSize = 1;
480
481        if (inSize <= 0) {
482            log.debug("convertEnumValueToName() failure: inSize length invalid");
483            log.debug("convertEnumValueToName(): inValues={} inSize={}", inValues, inSize);
484            return null;
485        }
486
487        if (enumMembers == null
488                || enumMembers.size() <= 0) {
489            log.debug("convertEnumValueToName(): no members");
490            return null;
491        }
492
493        log.trace("convertEnumValueToName(): inSize={} nMembers={} enums={}", inSize, enumMembers.size(), enumMembers);
494        outNames = new String[inSize];
495        for (int i = 0; i < inSize; i++) {
496            if (isArray) {
497                if (enumMembers.containsKey(String.valueOf(Array.get(inValues, i))))
498                    outNames[i] = enumMembers.get(String.valueOf(Array.get(inValues, i)));
499                else
500                    outNames[i] = "**ENUM ERR " + Array.get(inValues, i) + "**";
501            }
502            else {
503                if (enumMembers.containsKey(String.valueOf(inValues)))
504                    outNames[i] = enumMembers.get(String.valueOf(inValues));
505                else
506                    outNames[i] = "**ENUM ERR " + inValues + "**";
507            }
508        }
509
510        return outNames;
511    }
512
513    /**
514     * Converts names in an Enumeration Datatype to values. This method searches the identified enumeration datatype for
515     * the names appearing in <code>inValues</code> and returns the values corresponding to those names.
516     *
517     * @param in
518     *           The array of enumerations names to be converted.
519     * @return The int array of values if successful; otherwise return null.
520     * @throws HDF5Exception
521     *                       If there is an error at the HDF5 library level.
522     */
523    public Object[] convertEnumNameToValue(String[] in)
524            throws HDF5Exception {
525        int size = 0;
526
527        if (in == null) {
528            log.debug("convertEnumNameToValue() failure: in values null");
529            return null;
530        }
531
532        if ((size = Array.getLength(in)) <= 0) {
533            log.debug("convertEnumNameToValue() failure: in size not valid");
534            return null;
535        }
536
537        if (enumMembers == null
538                || enumMembers.size() <= 0) {
539            log.debug("convertEnumNameToValue(): no members");
540            return null;
541        }
542
543        Object[] out = null;
544        if (datatypeSize == 1)
545            out = new Byte[size];
546        else if (datatypeSize == 2)
547            out = new Short[size];
548        else if (datatypeSize == 4)
549            out = new Integer[size];
550        else if (datatypeSize == 8)
551            out = new Long[size];
552        else
553            out = new Object[size];
554
555        for (int i = 0; i < size; i++) {
556            if (in[i] == null
557                    || in[i].length() <= 0)
558                continue;
559
560            for (Entry<String, String> entry : enumMembers.entrySet()) {
561                if (Objects.equals(in[i], entry.getValue())) {
562                    if (datatypeSize == 1) {
563                        log.trace("convertEnumNameToValue(): ENUM is H5T_NATIVE_INT8");
564                        out[i] = Byte.parseByte(entry.getKey());
565                    }
566                    else if (datatypeSize == 2) {
567                        log.trace("convertEnumNameToValue(): CLASS_INT-ENUM is H5T_NATIVE_INT16");
568                        out[i] = Short.parseShort(entry.getKey());
569                    }
570                    else if (datatypeSize == 4) {
571                        log.trace("convertEnumNameToValue(): CLASS_INT-ENUM is H5T_NATIVE_INT32");
572                        out[i] = Integer.parseInt(entry.getKey());
573                    }
574                    else if (datatypeSize == 8) {
575                        log.trace("convertEnumNameToValue(): CLASS_INT-ENUM is H5T_NATIVE_INT64");
576                        out[i] = Long.parseLong(entry.getKey());
577                    }
578                    else {
579                        log.debug("convertEnumNameToValue(): enum datatypeSize incorrect");
580                        out[i] = -1;
581                    }
582                    break;
583                }
584            }
585        }
586
587        return out;
588    }
589
590    /**
591     * Convert from an array of BigDecimal into an array of bytes
592     *
593     * @param start
594     *              The position in the input array of BigDecimal to start
595     * @param len
596     *              The number of 'BigDecimal' to convert
597     * @param data
598     *              The input array of BigDecimal
599     * @return an array of bytes
600     */
601    public byte[] bigDecimalToByte(int start, int len, BigDecimal[] data) {
602        int ii;
603        byte[] bd = new byte[(int) datatypeSize];
604        byte[] bdconv = new byte[(int) datatypeSize];
605        byte[] bdbytes = new byte[(int) datatypeSize * len];
606
607        for (ii = 0; ii < len; ii++) {
608            BigDecimal entry = data[start + ii];
609            bdconv = convertBigDecimalToByte(entry);
610            /* bitsets operate assuming LE order, BigInteger/BigDecimal expect BE */
611            if (datatypeOrder == ORDER_BE) {
612                int k = 0;
613                for (int j = (int) datatypeSize - 1; j >= 0; j--)
614                    bd[k++] = bdconv[j];
615            }
616            else {
617                try {
618                    System.arraycopy(bdconv, 0, bd, 0, (int) datatypeSize);
619                }
620                catch (Exception err) {
621                    log.trace("bigDecimalToByte(): arraycopy failure: ", err);
622                }
623            }
624            try {
625                System.arraycopy(bd, 0, bdbytes, ii * 16, 16);
626            }
627            catch (Exception err) {
628                log.trace("bigDecimalToByte(): arraycopy failure: ", err);
629            }
630        }
631        return bdbytes;
632    }
633
634    /**
635     * Convert from a single BigDecimal object from an array of BigDecimal into an array of bytes
636     *
637     * @param start
638     *              The position in the input array of BigDecimal to start
639     * @param data
640     *              The input Float
641     * @return an array of bytes
642     */
643    public byte[] bigDecimalToByte(BigDecimal[] data, int start) {
644        byte[] bdbytes = new byte[(int) datatypeSize];
645        bdbytes = bigDecimalToByte(start, 1, data);
646        return bdbytes;
647    }
648
649    /**
650     * Convert a BigDecimal to a byte array .
651     *
652     * @param num
653     *            The BigDecimal number to convert
654     * @return A byte array representing the BigDecimal.
655     */
656    public byte[] convertBigDecimalToByte(BigDecimal num) {
657        BigInteger sig = new BigInteger(num.unscaledValue().toString());
658        byte[] bsig = sig.toByteArray();
659        int scale = num.scale();
660        byte[] bscale = new byte[] { (byte) (scale >>> 24), (byte) (scale >>> 16), (byte) (scale >>> 8),
661                (byte) (scale) };
662        byte[] both = new byte[bscale.length + bsig.length];
663        try {
664            System.arraycopy(bscale, 0, both, 0, bscale.length);
665            System.arraycopy(bsig, 0, both, bscale.length, bsig.length);
666        }
667        catch (Exception err) {
668            log.trace("convertBigDecimalToByte(): arraycopy failure: ", err);
669        }
670        return both;
671    }
672
673    /**
674     * Convert a range from an array of bytes into an array of BigDecimal
675     *
676     * @param start
677     *              The position in the input array of bytes to start
678     * @param len
679     *              The number of 'BigDecimal' to convert
680     * @param data
681     *              The input array of bytes
682     * @return an array of 'len' BigDecimal
683     */
684    public BigDecimal[] byteToBigDecimal(int start, int len, byte[] data) {
685        int ii;
686        byte[] bd = new byte[(int) datatypeSize];
687        BigDecimal[] BDarray = new BigDecimal[len];
688
689        for (ii = 0; ii < len; ii++) {
690            int rawpos = (start + ii) * (int) datatypeSize;
691            /* bitsets operate assuming LE order, BigInteger/BigDecimal expect BE */
692            if (datatypeOrder == ORDER_BE) {
693                int k = 0;
694                for (int j = (int) datatypeSize - 1; j >= 0; j--)
695                    bd[k++] = data[rawpos + j];
696            }
697            else {
698                try {
699                    System.arraycopy(data, rawpos, bd, 0, (int) datatypeSize);
700                }
701                catch (Exception err) {
702                    log.trace("byteToBigDecimal(): arraycopy failure: ", err);
703                }
704            }
705            BDarray[ii] = convertByteToBigDecimal(bd);
706        }
707        return BDarray;
708    }
709
710    /**
711     * Convert 4 bytes from an array of bytes into a single BigDecimal
712     *
713     * @param start
714     *              The position in the input array of bytes to start
715     * @param data
716     *              The input array of bytes
717     * @return The BigDecimal value of the bytes.
718     */
719    public BigDecimal byteToBigDecimal(byte[] data, int start) {
720        BigDecimal[] bdval = new BigDecimal[1];
721        bdval = byteToBigDecimal(start, 1, data);
722        return (bdval[0]);
723    }
724
725    /**
726     * Convert byte array data to a BigDecimal.
727     *
728     * @param raw
729     *            The byte array to convert to a BigDecimal
730     * @return A BigDecimal representing the byte array.
731     */
732    public BigDecimal convertByteToBigDecimal(byte[] raw) {
733        BitSet rawset = BitSet.valueOf(raw);
734
735        boolean sign = rawset.get(nativeOffset + (int) nativeFPspos);
736        BitSet mantissaset = rawset.get(nativeOffset + (int) nativeFPmpos,
737                nativeOffset + (int) nativeFPmpos + (int) nativeFPmsize);
738        BitSet exponentset = rawset.get(nativeOffset + (int) nativeFPepos,
739                nativeOffset + (int) nativeFPepos + (int) nativeFPesize);
740        byte[] expraw = Arrays.copyOf(exponentset.toByteArray(), (int) (nativeFPesize + 7) / 8);
741        byte[] bexp = new byte[expraw.length];
742        /* bitsets operate assuming LE order, BigInteger/BigDecimal expect BE */
743        if (datatypeOrder == ORDER_LE) {
744            int k = 0;
745            for (int j = expraw.length - 1; j >= 0; j--)
746                bexp[k++] = expraw[j];
747        }
748        else {
749            try {
750                System.arraycopy(expraw, 0, bexp, 0, expraw.length);
751            }
752            catch (Exception err) {
753                log.trace("convertByteToBigDecimal(): arraycopy failure: ", err);
754            }
755        }
756        BigInteger bscale = new BigInteger(bexp);
757        long scale = bscale.longValue();
758        scale -= nativeFPebias;
759        double powscale = Math.pow(2, scale);
760
761        byte[] manraw = Arrays.copyOf(mantissaset.toByteArray(), (int) (nativeFPmsize + 7) / 8);
762        byte[] bman = new byte[manraw.length];
763        /* bitsets operate assuming LE order, BigInteger/BigDecimal expect BE */
764        if (datatypeOrder == ORDER_BE) {
765            int k = 0;
766            for (int j = manraw.length - 1; j >= 0; j--)
767                bman[k++] = manraw[j];
768        }
769        else {
770            try {
771                System.arraycopy(manraw, 0, bman, 0, manraw.length);
772            }
773            catch (Exception err) {
774                log.trace("convertByteToBigDecimal(): arraycopy failure: ", err);
775            }
776        }
777        BitSet manset = BitSet.valueOf(bman);
778
779        // calculate mantissa value
780        double val = 0.0;
781        for (int i = 0; i < (int) nativeFPmsize; i++) {
782            if (manset.get((int) nativeFPmsize - 1 - i))
783                val += Math.pow(2, -(i));
784        }
785        if (nativeFPnorm == HDF5Constants.H5T_NORM_IMPLIED
786                || nativeFPnorm == HDF5Constants.H5T_NORM_MSBSET)
787            val += 1;
788        BigDecimal sig = BigDecimal.valueOf(val);
789        if (sign)
790            sig.negate(MathContext.DECIMAL128);
791        return sig.multiply(new BigDecimal(powscale, MathContext.DECIMAL128));
792    }
793
794    /*
795     * (non-Javadoc)
796     * @see hdf.object.Datatype#fromNative(int)
797     */
798    @Override
799    public void fromNative(long tid) {
800        log.trace("fromNative(): start: tid={}", tid);
801        long tsize = -1;
802        int torder = -1;
803        boolean isChar = false;
804        boolean isUchar = false;
805
806        if (tid < 0) {
807            datatypeClass = CLASS_NO_CLASS;
808        }
809        else {
810            try {
811                nativeClass = H5.H5Tget_class(tid);
812                tsize = H5.H5Tget_size(tid);
813                isVariableStr = H5.H5Tis_variable_str(tid);
814                isVLEN = false;
815                log.trace("fromNative(): tclass={}, tsize={}, torder={}, isVLEN={}", nativeClass, tsize, torder,
816                        isVLEN);
817                if (H5.H5Tcommitted(tid)) {
818                    isNamed = true;
819                    try {
820                        setFullname(null, H5.H5Iget_name(tid));
821                    }
822                    catch (Exception nex) {
823                        log.debug("fromNative(): setName failure: {}", nex.getMessage());
824                    }
825                    log.trace("fromNative(): path={} name={}", this.getPath(), this.getName());
826                }
827                log.trace("fromNative(): isNamed={}", isNamed());
828            }
829            catch (Exception ex) {
830                log.debug("fromNative(): failure: ", ex);
831                datatypeClass = CLASS_NO_CLASS;
832            }
833
834            try {
835                isUchar = H5.H5Tequal(tid, HDF5Constants.H5T_NATIVE_UCHAR);
836                isChar = (H5.H5Tequal(tid, HDF5Constants.H5T_NATIVE_CHAR)
837                        || isUchar);
838                log.trace("fromNative(): tclass={}, tsize={}, torder={}, isUchar={}, isChar={}", nativeClass, tsize,
839                        torder, isUchar, isChar);
840            }
841            catch (Exception ex) {
842                log.debug("fromNative(): native char type failure: ", ex);
843            }
844
845            datatypeOrder = HDF5Constants.H5T_ORDER_NONE;
846            boolean IsAtomic = datatypeClassIsAtomic(nativeClass);
847            if (IsAtomic
848                    || (nativeClass == HDF5Constants.H5T_COMPOUND)) {
849                try {
850                    torder = H5.H5Tget_order(tid);
851                    datatypeOrder = (torder == HDF5Constants.H5T_ORDER_BE) ? ORDER_BE : ORDER_LE;
852                }
853                catch (Exception ex) {
854                    log.debug("fromNative(): get_order failure: ", ex);
855                }
856            }
857
858            if (IsAtomic
859                    && !datatypeClassIsOpaque(nativeClass)) {
860                try {
861                    nativePrecision = H5.H5Tget_precision_long(tid);
862                }
863                catch (Exception ex) {
864                    log.debug("fromNative(): get_precision failure: ", ex);
865                }
866
867                try {
868                    nativeOffset = H5.H5Tget_offset(tid);
869                }
870                catch (Exception ex) {
871                    log.debug("fromNative(): get_offset failure: ", ex);
872                }
873
874                try {
875                    int[] pads = new int[2];
876                    H5.H5Tget_pad(tid, pads);
877                    nativePadLSB = pads[0];
878                    nativePadMSB = pads[1];
879                }
880                catch (Exception ex) {
881                    log.debug("fromNative(): get_pad failure: ", ex);
882                }
883            }
884
885            log.trace("fromNative(): isUchar={}, nativePrecision={}, nativeOffset={}, nativePadLSB={}, nativePadMSB={}",
886                    isUchar, nativePrecision, nativeOffset, nativePadLSB, nativePadMSB);
887
888            datatypeSign = NATIVE; // default
889            if (nativeClass == HDF5Constants.H5T_ARRAY) {
890                long tmptid = HDF5Constants.H5I_INVALID_HID;
891                datatypeClass = CLASS_ARRAY;
892                try {
893                    int ndims = H5.H5Tget_array_ndims(tid);
894                    arrayDims = new long[ndims];
895                    H5.H5Tget_array_dims(tid, arrayDims);
896
897                    tmptid = H5.H5Tget_super(tid);
898                    int nativeBaseClass = H5.H5Tget_class(tmptid);
899                    if (nativeBaseClass == HDF5Constants.H5T_REFERENCE)
900                        baseType = new H5ReferenceType(this.fileFormat, 1, tmptid);
901                    else
902                        baseType = new H5Datatype(this.fileFormat, tmptid, this);
903                    if (baseType == null) {
904                        log.debug("fromNative(): ARRAY datatype has null base type");
905                        throw new Exception("Datatype (ARRAY) has no base datatype");
906                    }
907
908                    datatypeSign = baseType.getDatatypeSign();
909                }
910                catch (Exception ex) {
911                    log.debug("fromNative(): array type failure: ", ex);
912                }
913                finally {
914                    close(tmptid);
915                }
916            }
917            else if (nativeClass == HDF5Constants.H5T_COMPOUND) {
918                datatypeClass = CLASS_COMPOUND;
919
920                try {
921                    int nMembers = H5.H5Tget_nmembers(tid);
922                    compoundMemberNames = new Vector<>(nMembers);
923                    compoundMemberTypes = new Vector<>(nMembers);
924                    compoundMemberOffsets = new Vector<>(nMembers);
925                    log.trace("fromNative(): compound type nMembers={} start", nMembers);
926
927                    for (int i = 0; i < nMembers; i++) {
928                        String memberName = H5.H5Tget_member_name(tid, i);
929                        log.trace("fromNative(): compound type [{}] name={} start", i, memberName);
930                        long memberOffset = H5.H5Tget_member_offset(tid, i);
931                        long memberID = HDF5Constants.H5I_INVALID_HID;
932                        H5Datatype membertype = null;
933                        try {
934                            memberID = H5.H5Tget_member_type(tid, i);
935                            int nativeMemberClass = H5.H5Tget_class(memberID);
936                            if (nativeMemberClass == HDF5Constants.H5T_REFERENCE)
937                                membertype = new H5ReferenceType(this.fileFormat, 1, memberID);
938                            else
939                                membertype = new H5Datatype(this.fileFormat, memberID, this);
940                        }
941                        catch (Exception ex1) {
942                            log.debug("fromNative(): compound type failure: ", ex1);
943                        }
944                        finally {
945                            close(memberID);
946                        }
947
948                        compoundMemberNames.add(i, memberName);
949                        compoundMemberOffsets.add(i, memberOffset);
950                        compoundMemberTypes.add(i, membertype);
951                    }
952                }
953                catch (HDF5LibraryException ex) {
954                    log.debug("fromNative(): compound type failure: ", ex);
955                }
956            }
957            else if (nativeClass == HDF5Constants.H5T_INTEGER) {
958                datatypeClass = CLASS_INTEGER;
959                try {
960                    log.trace("fromNative(): integer type");
961                    int tsign = H5.H5Tget_sign(tid);
962                    datatypeSign = (tsign == HDF5Constants.H5T_SGN_NONE) ? SIGN_NONE : SIGN_2;
963                }
964                catch (Exception ex) {
965                    log.debug("fromNative(): int type failure: ", ex);
966                }
967            }
968            else if (nativeClass == HDF5Constants.H5T_FLOAT) {
969                datatypeClass = CLASS_FLOAT;
970                try {
971                    nativeFPebias = H5.H5Tget_ebias_long(tid);
972                }
973                catch (Exception ex) {
974                    log.debug("fromNative(): get_ebias failure: ", ex);
975                }
976                try {
977                    long[] fields = new long[5];
978                    H5.H5Tget_fields(tid, fields);
979                    nativeFPspos = fields[0];
980                    nativeFPepos = fields[1];
981                    nativeFPesize = fields[2];
982                    nativeFPmpos = fields[3];
983                    nativeFPmsize = fields[4];
984                }
985                catch (Exception ex) {
986                    log.debug("fromNative(): get_fields failure: ", ex);
987                }
988                try {
989                    nativeFPnorm = H5.H5Tget_norm(tid);
990                }
991                catch (Exception ex) {
992                    log.debug("fromNative(): get_norm failure: ", ex);
993                }
994                try {
995                    nativeFPinpad = H5.H5Tget_inpad(tid);
996                }
997                catch (Exception ex) {
998                    log.debug("fromNative(): get_inpad failure: ", ex);
999                }
1000            }
1001            else if (isChar) {
1002                datatypeClass = CLASS_CHAR;
1003                datatypeSign = (isUchar) ? SIGN_NONE : SIGN_2;
1004                log.trace("fromNative(): CLASS_CHAR:datatypeSign={}", datatypeSign);
1005            }
1006            else if (nativeClass == HDF5Constants.H5T_STRING) {
1007                datatypeClass = CLASS_STRING;
1008                try {
1009                    isVLEN = H5.H5Tdetect_class(tid, HDF5Constants.H5T_VLEN)
1010                            || isVariableStr;
1011                    log.trace("fromNative(): H5T_STRING:var str type={}", isVLEN);
1012                    nativeStrPad = H5.H5Tget_strpad(tid);
1013                }
1014                catch (Exception ex) {
1015                    log.debug("fromNative(): var str type failure: ", ex);
1016                }
1017                try {
1018                    nativeStrCSET = H5.H5Tget_cset(tid);
1019                }
1020                catch (Exception ex) {
1021                    log.debug("fromNative(): H5T_STRING:get_cset failure: ", ex);
1022                }
1023                log.trace("fromNative(): H5T_STRING:nativeStrPad={}, nativeStrCSET={}", nativeStrPad, nativeStrCSET);
1024            }
1025            else if (nativeClass == HDF5Constants.H5T_REFERENCE) {
1026                datatypeClass = CLASS_REFERENCE;
1027                log.trace("fromNative(): reference type");
1028                try {
1029                    isStdRef = H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF);
1030                    log.trace("fromNative(): reference type is orig StdRef:{}", isStdRef);
1031                    if (H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF))
1032                        tsize = HDF5Constants.H5R_REF_BUF_SIZE;
1033                }
1034                catch (Exception ex) {
1035                    log.debug("fromNative(): H5T_STD_REF: ", ex);
1036                }
1037                try {
1038                    isRegRef = H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF_DSETREG);
1039                    log.trace("fromNative(): reference type isRegRef:{}", isRegRef);
1040                    if (isRegRef)
1041                        tsize = HDF5Constants.H5R_DSET_REG_REF_BUF_SIZE;
1042                }
1043                catch (Exception ex) {
1044                    log.debug("fromNative(): H5T_STD_REF_DSETREG: ", ex);
1045                }
1046                try {
1047                    isRefObj = H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF_OBJ);
1048                    log.trace("fromNative(): reference type isRefObj:{}", isRefObj);
1049                    if (isRefObj)
1050                        tsize = HDF5Constants.H5R_OBJ_REF_BUF_SIZE;
1051                }
1052                catch (Exception ex) {
1053                    log.debug("fromNative(): H5T_STD_REF_OBJ: ", ex);
1054                }
1055            }
1056            else if (nativeClass == HDF5Constants.H5T_ENUM) {
1057                datatypeClass = CLASS_ENUM;
1058                long tmptid = HDF5Constants.H5I_INVALID_HID;
1059                long basetid = HDF5Constants.H5I_INVALID_HID;
1060                try {
1061                    log.trace("fromNative(): enum type");
1062                    basetid = H5.H5Tget_super(tid);
1063                    tmptid = basetid;
1064                    basetid = H5.H5Tget_native_type(tmptid);
1065                    log.trace("fromNative(): enum type basetid={}", basetid);
1066                    if (basetid >= 0) {
1067                        baseType = new H5Datatype(this.fileFormat, tmptid, this);
1068                        datatypeSign = baseType.getDatatypeSign();
1069                    }
1070                }
1071                catch (Exception ex) {
1072                    log.debug("fromNative(): enum type failure: ", ex);
1073                }
1074                finally {
1075                    close(tmptid);
1076                    close(basetid);
1077                }
1078                try {
1079                    int enumMemberCount = H5.H5Tget_nmembers(tid);
1080                    String name = null;
1081                    String enumStr = null;
1082                    byte[] val = new byte[(int) tsize];
1083                    enumMembers = new HashMap<>();
1084                    for (int i = 0; i < enumMemberCount; i++) {
1085                        name = H5.H5Tget_member_name(tid, i);
1086                        H5.H5Tget_member_value(tid, i, val);
1087                        switch ((int) H5.H5Tget_size(tid)) {
1088                        case 1:
1089                            enumStr = Byte.toString((HDFNativeData.byteToByte(val[0]))[0]);
1090                            break;
1091                        case 2:
1092                            enumStr = Short.toString((HDFNativeData.byteToShort(val))[0]);
1093                            break;
1094                        case 4:
1095                            enumStr = Integer.toString((HDFNativeData.byteToInt(val))[0]);
1096                            break;
1097                        case 8:
1098                            enumStr = Long.toString((HDFNativeData.byteToLong(val))[0]);
1099                            break;
1100                        default:
1101                            enumStr = "-1";
1102                            break;
1103                        }
1104                        enumMembers.put(enumStr, name);
1105                    }
1106                }
1107                catch (Exception ex) {
1108                    log.debug("fromNative(): enum type failure: ", ex);
1109                }
1110            }
1111            else if (nativeClass == HDF5Constants.H5T_VLEN) {
1112                long tmptid = HDF5Constants.H5I_INVALID_HID;
1113                datatypeClass = CLASS_VLEN;
1114                isVLEN = true;
1115                try {
1116                    log.trace("fromNative(): vlen type");
1117                    tmptid = H5.H5Tget_super(tid);
1118                    int nativeBaseClass = H5.H5Tget_class(tmptid);
1119                    if (nativeBaseClass == HDF5Constants.H5T_REFERENCE)
1120                        baseType = new H5ReferenceType(this.fileFormat, 1, tmptid);
1121                    else
1122                        baseType = new H5Datatype(this.fileFormat, tmptid, this);
1123                    if (baseType == null) {
1124                        log.debug("fromNative(): VLEN datatype has null base type");
1125                        throw new Exception("Datatype (VLEN) has no base datatype");
1126                    }
1127
1128                    datatypeSign = baseType.getDatatypeSign();
1129                }
1130                catch (Exception ex) {
1131                    log.debug("fromNative(): vlen type failure: ", ex);
1132                }
1133                finally {
1134                    close(tmptid);
1135                }
1136            }
1137            else if (nativeClass == HDF5Constants.H5T_BITFIELD) {
1138                datatypeClass = CLASS_BITFIELD;
1139            }
1140            else if (nativeClass == HDF5Constants.H5T_OPAQUE) {
1141                datatypeClass = CLASS_OPAQUE;
1142
1143                try {
1144                    opaqueTag = H5.H5Tget_tag(tid);
1145                }
1146                catch (Exception ex) {
1147                    log.debug("fromNative(): opaque type tag retrieval failed: ", ex);
1148                    opaqueTag = null;
1149                }
1150            }
1151            else {
1152                log.debug("fromNative(): datatypeClass is unknown");
1153            }
1154
1155            datatypeSize = (isVLEN
1156                    && !isVariableStr) ? HDF5Constants.H5T_VL_T : tsize;
1157        }
1158        if (datatypeSize == NATIVE)
1159            datatypeNATIVE = true;
1160        else
1161            datatypeNATIVE = false;
1162        log.trace("fromNative(): datatypeClass={} baseType={} datatypeSize={}", datatypeClass, baseType, datatypeSize);
1163    }
1164
1165    /**
1166     * @param tid
1167     *            the datatype identification disk.
1168     * @return the memory datatype identifier if successful, and negative otherwise.
1169     */
1170    public static long toNative(long tid) {
1171        // data type information
1172        log.trace("toNative(): tid={} start", tid);
1173        long nativeID = HDF5Constants.H5I_INVALID_HID;
1174
1175        try {
1176            nativeID = H5.H5Tget_native_type(tid);
1177        }
1178        catch (Exception ex) {
1179            log.debug("toNative(): H5Tget_native_type(tid {}) failure: ", tid, ex);
1180        }
1181
1182        try {
1183            if (H5.H5Tis_variable_str(tid))
1184                H5.H5Tset_size(nativeID, HDF5Constants.H5T_VARIABLE);
1185        }
1186        catch (Exception ex) {
1187            log.debug("toNative(): var str type size failure: ", ex);
1188        }
1189
1190        return nativeID;
1191    }
1192
1193    /*
1194     * (non-Javadoc)
1195     * @see hdf.object.Datatype#createNative()
1196     */
1197    @SuppressWarnings("rawtypes")
1198    @Override
1199    public long createNative() {
1200        long tid = HDF5Constants.H5I_INVALID_HID;
1201        long tmptid = HDF5Constants.H5I_INVALID_HID;
1202
1203        String the_path = getFullName();
1204        // isNamed == true should have non-null fileFormat
1205        if (isNamed()) {
1206            try {
1207                tid = H5.H5Topen(getFID(), the_path, HDF5Constants.H5P_DEFAULT);
1208            }
1209            catch (Exception ex) {
1210                log.debug("createNative(): name {} H5Topen failure: ", the_path, ex);
1211            }
1212        }
1213        else
1214            log.debug("createNative(): isNamed={} and named path={}", isNamed(), the_path);
1215
1216        if (tid >= 0)
1217            return tid;
1218
1219        log.trace("createNative(): datatypeClass={} datatypeSize={} baseType={}", datatypeClass, datatypeSize,
1220                baseType);
1221
1222        switch (datatypeClass) {
1223        case CLASS_ARRAY:
1224            try {
1225                if (baseType == null) {
1226                    log.debug("createNative(): CLASS_ARRAY base type is NULL");
1227                    break;
1228                }
1229
1230                if ((tmptid = baseType.createNative()) < 0) {
1231                    log.debug("createNative(): failed to create native datatype for ARRAY base datatype");
1232                    break;
1233                }
1234
1235                tid = H5.H5Tarray_create(tmptid, arrayDims.length, arrayDims);
1236            }
1237            catch (Exception ex) {
1238                log.debug("createNative(): native array datatype creation failed: ", ex);
1239                if (tid >= 0)
1240                    close(tid);
1241                tid = HDF5Constants.H5I_INVALID_HID;
1242            }
1243            finally {
1244                close(tmptid);
1245            }
1246
1247            break;
1248        case CLASS_COMPOUND:
1249            try {
1250                tid = H5.H5Tcreate(CLASS_COMPOUND, datatypeSize);
1251
1252                for (int i = 0; i < compoundMemberTypes.size(); i++) {
1253                    H5Datatype memberType = null;
1254                    String memberName = null;
1255                    long memberOffset = -1;
1256
1257                    try {
1258                        memberType = (H5Datatype) compoundMemberTypes.get(i);
1259                    }
1260                    catch (Exception ex) {
1261                        log.debug("createNative(): get compound member[{}] type failure: ", i, ex);
1262                        memberType = null;
1263                    }
1264
1265                    try {
1266                        memberName = compoundMemberNames.get(i);
1267                    }
1268                    catch (Exception ex) {
1269                        log.debug("createNative(): get compound member[{}] name failure: ", i, ex);
1270                        memberName = null;
1271                    }
1272
1273                    try {
1274                        memberOffset = compoundMemberOffsets.get(i);
1275                    }
1276                    catch (Exception ex) {
1277                        log.debug("createNative(): get compound member[{}] offset failure: ", i, ex);
1278                        memberOffset = -1;
1279                    }
1280
1281                    long memberID = HDF5Constants.H5I_INVALID_HID;
1282                    try {
1283                        memberID = memberType.createNative();
1284                        log.trace("createNative(): {} member[{}] with offset={} ID={}: ", memberName, i, memberOffset,
1285                                memberID);
1286
1287                        H5.H5Tinsert(tid, memberName, memberOffset, memberID);
1288                    }
1289                    catch (Exception ex) {
1290                        log.debug("createNative(): compound type member[{}] insertion failure: ", i, ex);
1291                    }
1292                    finally {
1293                        close(memberID);
1294                    }
1295                }
1296            }
1297            catch (Exception ex) {
1298                log.debug("createNative(): native compound datatype creation failed: ", ex);
1299                if (tid >= 0)
1300                    close(tid);
1301                tid = HDF5Constants.H5I_INVALID_HID;
1302            }
1303            break;
1304        case CLASS_INTEGER:
1305            log.trace("createNative(): CLASS_INT of size {}", datatypeSize);
1306
1307            try {
1308                switch ((int) datatypeSize) {
1309                case 1:
1310                    log.trace("createNative(): CLASS_INT is H5T_NATIVE_INT8");
1311                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT8);
1312                    break;
1313                case 2:
1314                    log.trace("createNative(): CLASS_INT is H5T_NATIVE_INT16");
1315                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT16);
1316                    break;
1317                case 4:
1318                    log.trace("createNative(): CLASS_INT is H5T_NATIVE_INT32");
1319                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT32);
1320                    break;
1321                case 8:
1322                    log.trace("createNative(): CLASS_INT is H5T_NATIVE_INT64");
1323                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT64);
1324                    break;
1325                default:
1326                    if (datatypeSize == NATIVE) {
1327                        datatypeNATIVE = true;
1328                        log.trace("createNative(): CLASS_INT is H5T_NATIVE_INT");
1329                        tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT);
1330                        datatypeSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_INT);
1331                    }
1332                    else {
1333                        datatypeNATIVE = false;
1334                        /* Custom sized integer */
1335                        tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT8);
1336                        H5.H5Tset_size(tid, datatypeSize);
1337                        H5.H5Tset_precision(tid, 8 * datatypeSize);
1338                    }
1339                    break;
1340                }
1341
1342                if (datatypeOrder == Datatype.ORDER_BE) {
1343                    log.trace("createNative(): CLASS_INT order is H5T_ORDER_BE");
1344                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_BE);
1345                }
1346                else if (datatypeOrder == Datatype.ORDER_LE) {
1347                    log.trace("createNative(): CLASS_INT order is H5T_ORDER_LE");
1348                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_LE);
1349                }
1350
1351                if (datatypeSign == Datatype.SIGN_NONE) {
1352                    log.trace("createNative(): CLASS_INT sign is H5T_SGN_NONE");
1353                    H5.H5Tset_sign(tid, HDF5Constants.H5T_SGN_NONE);
1354                }
1355            }
1356            catch (Exception ex) {
1357                log.debug("createNative(): native integer datatype creation failed: ", ex);
1358                if (tid >= 0)
1359                    close(tid);
1360                tid = -1;
1361            }
1362
1363            break;
1364        case CLASS_ENUM:
1365            log.trace("createNative(): CLASS_ENUM");
1366            try {
1367                if (baseType != null) {
1368                    if ((tmptid = baseType.createNative()) < 0) {
1369                        log.debug("createNative(): failed to create native type for ENUM base datatype");
1370                        break;
1371                    }
1372
1373                    tid = H5.H5Tenum_create(tmptid);
1374                }
1375                else {
1376                    if (datatypeSize == NATIVE) {
1377                        datatypeNATIVE = true;
1378                        datatypeSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_INT);
1379                    }
1380                    else
1381                        datatypeNATIVE = false;
1382
1383                    tid = H5.H5Tcreate(HDF5Constants.H5T_ENUM, datatypeSize);
1384                }
1385
1386                if (datatypeOrder == Datatype.ORDER_BE) {
1387                    log.trace("createNative(): CLASS_ENUM order is H5T_ORDER_BE");
1388                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_BE);
1389                }
1390                else if (datatypeOrder == Datatype.ORDER_LE) {
1391                    log.trace("createNative(): CLASS_ENUM order is H5T_ORDER_LE");
1392                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_LE);
1393                }
1394
1395                if (datatypeSign == Datatype.SIGN_NONE) {
1396                    log.trace("createNative(): CLASS_ENUM sign is H5T_SGN_NONE");
1397                    H5.H5Tset_sign(tid, HDF5Constants.H5T_SGN_NONE);
1398                }
1399            }
1400            catch (Exception ex) {
1401                log.debug("createNative(): native enum datatype creation failed: ", ex);
1402                if (tid >= 0)
1403                    close(tid);
1404                tid = HDF5Constants.H5I_INVALID_HID;
1405            }
1406            finally {
1407                close(tmptid);
1408            }
1409
1410            break;
1411        case CLASS_FLOAT:
1412            try {
1413                if (datatypeSize > 8)
1414                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_LDOUBLE);
1415                else
1416                    tid = H5.H5Tcopy(
1417                            (datatypeSize == 8) ? HDF5Constants.H5T_NATIVE_DOUBLE : HDF5Constants.H5T_NATIVE_FLOAT);
1418
1419                if (datatypeOrder == Datatype.ORDER_BE) {
1420                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_BE);
1421                }
1422                else if (datatypeOrder == Datatype.ORDER_LE) {
1423                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_LE);
1424                }
1425
1426                if (nativeFPebias > 0) {
1427                    H5.H5Tset_ebias(tid, nativeFPebias);
1428                }
1429
1430                if (nativeFPnorm >= 0) {
1431                    H5.H5Tset_norm(tid, nativeFPnorm);
1432                }
1433
1434                if (nativeFPinpad >= 0) {
1435                    H5.H5Tset_inpad(tid, nativeFPinpad);
1436                }
1437
1438                if ((nativeFPesize >= 0)
1439                        && (nativeFPmsize >= 0)) {
1440                    H5.H5Tset_fields(tid, nativeFPspos, nativeFPmpos, nativeFPesize, nativeFPmpos, nativeFPmsize);
1441                }
1442            }
1443            catch (Exception ex) {
1444                log.debug("createNative(): native floating-point datatype creation failed: ", ex);
1445                if (tid >= 0)
1446                    close(tid);
1447                tid = HDF5Constants.H5I_INVALID_HID;
1448            }
1449
1450            break;
1451        case CLASS_CHAR:
1452            try {
1453                tid = H5.H5Tcopy((datatypeSign == Datatype.SIGN_NONE) ? HDF5Constants.H5T_NATIVE_UCHAR
1454                        : HDF5Constants.H5T_NATIVE_CHAR);
1455            }
1456            catch (Exception ex) {
1457                log.debug("createNative(): native character datatype creation failed: ", ex);
1458                if (tid >= 0)
1459                    close(tid);
1460                tid = HDF5Constants.H5I_INVALID_HID;
1461            }
1462
1463            break;
1464        case CLASS_STRING:
1465            try {
1466                tid = H5.H5Tcopy(HDF5Constants.H5T_C_S1);
1467
1468                H5.H5Tset_size(tid, (isVLEN
1469                        || datatypeSize < 0) ? HDF5Constants.H5T_VARIABLE : datatypeSize);
1470
1471                log.trace("createNative(): isVlenStr={} nativeStrPad={} nativeStrCSET={}", isVLEN, nativeStrPad,
1472                        nativeStrCSET);
1473
1474                H5.H5Tset_strpad(tid, (nativeStrPad >= 0) ? nativeStrPad : HDF5Constants.H5T_STR_NULLTERM);
1475
1476                if (nativeStrCSET >= 0) {
1477                    H5.H5Tset_cset(tid, nativeStrCSET);
1478                }
1479            }
1480            catch (Exception ex) {
1481                log.debug("createNative(): native string datatype creation failed: ", ex);
1482                if (tid >= 0)
1483                    close(tid);
1484                tid = HDF5Constants.H5I_INVALID_HID;
1485            }
1486
1487            break;
1488        case CLASS_REFERENCE:
1489            try {
1490                long objRefTypeSize = H5.H5Tget_size(HDF5Constants.H5T_STD_REF_OBJ);
1491                long dsetRefTypeSize = H5.H5Tget_size(HDF5Constants.H5T_STD_REF_DSETREG);
1492                // use datatypeSize as which type to copy
1493                log.debug("createNative(): datatypeSize:{} ", datatypeSize);
1494                if (datatypeSize < 0
1495                        || datatypeSize > dsetRefTypeSize) {
1496                    tid = H5.H5Tcopy(HDF5Constants.H5T_STD_REF);
1497                    log.debug("createNative(): HDF5Constants.H5T_STD_REF");
1498                }
1499                else if (datatypeSize > objRefTypeSize) {
1500                    tid = H5.H5Tcopy(HDF5Constants.H5T_STD_REF_DSETREG);
1501                    log.debug("createNative(): HDF5Constants.H5T_STD_REF_DSETREG");
1502                }
1503                else {
1504                    tid = H5.H5Tcopy(HDF5Constants.H5T_STD_REF_OBJ);
1505                    log.debug("createNative(): HDF5Constants.H5T_STD_REF_OBJ");
1506                }
1507            }
1508            catch (Exception ex) {
1509                log.debug("createNative(): native reference datatype creation failed: ", ex);
1510                if (tid >= 0)
1511                    close(tid);
1512                tid = HDF5Constants.H5I_INVALID_HID;
1513            }
1514
1515            break;
1516        case CLASS_VLEN:
1517            try {
1518                if (baseType == null) {
1519                    log.debug("createNative(): CLASS_VLEN base type is NULL");
1520                    break;
1521                }
1522
1523                if ((tmptid = baseType.createNative()) < 0) {
1524                    log.debug("createNative(): failed to create native datatype for VLEN base datatype");
1525                    break;
1526                }
1527
1528                tid = H5.H5Tvlen_create(tmptid);
1529            }
1530            catch (Exception ex) {
1531                log.debug("createNative(): native variable-length datatype creation failed: ", ex);
1532                if (tid >= 0)
1533                    close(tid);
1534                tid = HDF5Constants.H5I_INVALID_HID;
1535            }
1536            finally {
1537                close(tmptid);
1538            }
1539
1540            break;
1541        case CLASS_BITFIELD:
1542            log.trace("createNative(): CLASS_BITFIELD size is {}", datatypeSize);
1543
1544            try {
1545                switch ((int) datatypeSize) {
1546                case 1:
1547                    log.trace("createNative(): CLASS_BITFIELD is H5T_NATIVE_B8");
1548                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B8);
1549                    break;
1550                case 2:
1551                    log.trace("createNative(): CLASS_BITFIELD is H5T_NATIVE_B16");
1552                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B16);
1553                    break;
1554                case 4:
1555                    log.trace("createNative(): CLASS_BITFIELD is H5T_NATIVE_B32");
1556                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B32);
1557                    break;
1558                case 8:
1559                    log.trace("createNative(): CLASS_BITFIELD is H5T_NATIVE_B64");
1560                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B64);
1561                    break;
1562                default:
1563                    if (datatypeSize == NATIVE) {
1564                        datatypeNATIVE = true;
1565                        datatypeSize = 1;
1566                    }
1567                    else
1568                        datatypeNATIVE = false;
1569
1570                    /* Custom sized bitfield */
1571                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B8);
1572                    H5.H5Tset_size(tid, datatypeSize);
1573                    H5.H5Tset_precision(tid, 8 * datatypeSize);
1574
1575                    break;
1576                }
1577
1578                if (datatypeOrder == Datatype.ORDER_BE) {
1579                    log.trace("createNative(): CLASS_BITFIELD order is H5T_ORDER_BE");
1580                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_BE);
1581                }
1582                else if (datatypeOrder == Datatype.ORDER_LE) {
1583                    log.trace("createNative(): CLASS_BITFIELD order is H5T_ORDER_LE");
1584                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_LE);
1585                }
1586            }
1587            catch (Exception ex) {
1588                log.debug("createNative(): native bitfield datatype creation failed: ", ex);
1589                if (tid >= 0)
1590                    close(tid);
1591                tid = HDF5Constants.H5I_INVALID_HID;
1592            }
1593
1594            break;
1595        case CLASS_OPAQUE:
1596            log.trace("createNative(): CLASS_OPAQUE is {}-byte H5T_OPAQUE", datatypeSize);
1597
1598            try {
1599                if (datatypeSize == NATIVE) {
1600                    datatypeNATIVE = true;
1601                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_OPAQUE);
1602                    datatypeSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_OPAQUE);
1603                }
1604                else {
1605                    datatypeNATIVE = false;
1606                    tid = H5.H5Tcreate(HDF5Constants.H5T_OPAQUE, datatypeSize);
1607                }
1608
1609                if (opaqueTag != null) {
1610                    H5.H5Tset_tag(tid, opaqueTag);
1611                }
1612            }
1613            catch (Exception ex) {
1614                log.debug("createNative(): native opaque datatype creation failed: ", ex);
1615                if (tid >= 0)
1616                    close(tid);
1617                tid = HDF5Constants.H5I_INVALID_HID;
1618            }
1619
1620            break;
1621        default:
1622            log.debug("createNative(): Unknown class");
1623            break;
1624        } // (tclass)
1625
1626        // set up enum members
1627        if ((datatypeClass == CLASS_ENUM)
1628                && (enumMembers != null)) {
1629            log.trace("createNative(): set up enum members");
1630            try {
1631                String memstr;
1632                String memname;
1633                byte[] memval = null;
1634
1635                Iterator entries = enumMembers.entrySet().iterator();
1636                while (entries.hasNext()) {
1637                    Entry thisEntry = (Entry) entries.next();
1638                    memstr = (String) thisEntry.getKey();
1639                    memname = (String) thisEntry.getValue();
1640
1641                    if (datatypeSize == 1) {
1642                        log.trace("createNative(): CLASS_ENUM is H5T_NATIVE_INT8");
1643                        Byte tval = Byte.parseByte(memstr);
1644                        memval = HDFNativeData.byteToByte(tval);
1645                    }
1646                    else if (datatypeSize == 2) {
1647                        log.trace("createNative(): CLASS_ENUM is H5T_NATIVE_INT16");
1648                        Short tval = Short.parseShort(memstr);
1649                        memval = HDFNativeData.shortToByte(tval);
1650                    }
1651                    else if (datatypeSize == 4) {
1652                        log.trace("createNative(): CLASS_ENUM is H5T_NATIVE_INT32");
1653                        Integer tval = Integer.parseInt(memstr);
1654                        memval = HDFNativeData.intToByte(tval);
1655                    }
1656                    else if (datatypeSize == 8) {
1657                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT64");
1658                        Long tval = Long.parseLong(memstr);
1659                        memval = HDFNativeData.longToByte(tval);
1660                    }
1661                    else {
1662                        log.debug("createNative(): enum datatypeSize incorrect");
1663                    }
1664                    log.trace("createNative(): H5Tenum_insert {} {}", memname, memval);
1665                    H5.H5Tenum_insert(tid, memname, memval);
1666                }
1667            }
1668            catch (Exception ex) {
1669                log.debug("createNative(): set up enum members failure: ", ex);
1670            }
1671        } // (datatypeClass == CLASS_ENUM)
1672
1673        try {
1674            tmptid = tid;
1675            tid = H5.H5Tget_native_type(tmptid);
1676        }
1677        catch (HDF5Exception ex) {
1678            log.debug("createNative(): H5Tget_native_type({}) failure: ", tmptid, ex);
1679        }
1680        finally {
1681            close(tmptid);
1682        }
1683
1684        return tid;
1685    }
1686
1687    /**
1688     * Allocates a one-dimensional array of byte, short, int, long, float, double, or String to store data in memory.
1689     * For example,
1690     *
1691     * <pre>
1692     * long tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT32);
1693     * int[] data = (int[]) H5Datatype.allocateArray(datatype, 100);
1694     * </pre>
1695     *
1696     * returns a 32-bit integer array of size 100.
1697     *
1698     * @param dtype
1699     *                  the type.
1700     * @param numPoints
1701     *                  the total number of data points of the array.
1702     * @return the array object if successful; otherwise, return null.
1703     * @throws OutOfMemoryError
1704     *                          If there is a failure.
1705     */
1706    public static final Object allocateArray(final H5Datatype dtype, int numPoints)
1707            throws OutOfMemoryError {
1708        log.trace("allocateArray(): start: numPoints={}", numPoints);
1709
1710        Object data = null;
1711        H5Datatype baseType = (H5Datatype) dtype.getDatatypeBase();
1712        int typeClass = dtype.getDatatypeClass();
1713        long typeSize = dtype.getDatatypeSize();
1714
1715        if (numPoints < 0) {
1716            log.debug("allocateArray(): numPoints < 0");
1717            return null;
1718        }
1719
1720        // Scalar members have dimensionality zero, i.e. size =0
1721        // what can we do about it, set the size to 1
1722        if (numPoints == 0)
1723            numPoints = 1;
1724
1725        log.trace("allocateArray(): tclass={} : tsize={}", typeClass, typeSize);
1726
1727        if (dtype.isVarStr()) {
1728            log.trace("allocateArray(): is_variable_str={}", dtype.isVarStr());
1729
1730            data = new String[numPoints];
1731            for (int i = 0; i < numPoints; i++)
1732                ((String[]) data)[i] = "";
1733        }
1734        else if (typeClass == HDF5Constants.H5T_INTEGER) {
1735            log.trace("allocateArray(): class H5T_INTEGER");
1736            if (typeSize == NATIVE)
1737                typeSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_INT);
1738
1739            switch ((int) typeSize) {
1740            case 1:
1741                data = new byte[numPoints];
1742                break;
1743            case 2:
1744                data = new short[numPoints];
1745                break;
1746            case 4:
1747                data = new int[numPoints];
1748                break;
1749            case 8:
1750                data = new long[numPoints];
1751                break;
1752            default:
1753                break;
1754            }
1755        }
1756        else if (typeClass == HDF5Constants.H5T_ENUM) {
1757            log.trace("allocateArray(): class H5T_ENUM");
1758
1759            if (baseType != null)
1760                data = H5Datatype.allocateArray(baseType, numPoints);
1761            else {
1762                if (typeSize == NATIVE)
1763                    typeSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_INT);
1764                data = new byte[(int) (numPoints * typeSize)];
1765            }
1766        }
1767        else if (typeClass == HDF5Constants.H5T_COMPOUND) {
1768            log.trace("allocateArray(): class H5T_COMPOUND");
1769
1770            data = new ArrayList<>(dtype.getCompoundMemberTypes().size());
1771        }
1772        else if (typeClass == HDF5Constants.H5T_FLOAT) {
1773            log.trace("allocateArray(): class H5T_FLOAT");
1774            if (typeSize == NATIVE)
1775                typeSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_FLOAT);
1776
1777            switch ((int) typeSize) {
1778            case 4:
1779                data = new float[numPoints];
1780                break;
1781            case 8:
1782                data = new double[numPoints];
1783                break;
1784            case 16:
1785                data = new byte[numPoints * 16];
1786                break;
1787            default:
1788                break;
1789            }
1790        }
1791        else if ((typeClass == HDF5Constants.H5T_STRING)
1792                || (typeClass == HDF5Constants.H5T_REFERENCE)) {
1793            log.trace("allocateArray(): class H5T_STRING || H5T_REFERENCE");
1794
1795            data = new byte[(int) (numPoints * typeSize)];
1796        }
1797        else if (dtype.isVLEN()) {
1798            log.trace("allocateArray(): isVLEN");
1799
1800            data = new ArrayList[numPoints];
1801            for (int j = 0; j < numPoints; j++)
1802                ((ArrayList[])data)[j] = new ArrayList<byte[]>();
1803                // if (baseType != null)
1804                // ((ArrayList<>)data).add(H5Datatype.allocateArray(baseType, numPoints));
1805        }
1806        else if (typeClass == HDF5Constants.H5T_ARRAY) {
1807            log.trace("allocateArray(): class H5T_ARRAY");
1808
1809            try {
1810                log.trace("allocateArray(): ArrayRank={}", dtype.getArrayDims().length);
1811
1812                // Use the base datatype to define the array
1813                long[] arrayDims = dtype.getArrayDims();
1814                int asize = numPoints;
1815                for (int j = 0; j < arrayDims.length; j++) {
1816                    log.trace("allocateArray(): Array dims[{}]={}", j, arrayDims[j]);
1817
1818                    asize *= arrayDims[j];
1819                }
1820
1821                if (baseType != null)
1822                    data = H5Datatype.allocateArray(baseType, asize);
1823            }
1824            catch (Exception ex) {
1825                log.debug("allocateArray(): H5T_ARRAY class failure: ", ex);
1826            }
1827        }
1828        else if ((typeClass == HDF5Constants.H5T_OPAQUE)
1829                || (typeClass == HDF5Constants.H5T_BITFIELD)) {
1830            log.trace("allocateArray(): class H5T_OPAQUE || H5T_BITFIELD");
1831            if (typeSize == NATIVE)
1832                typeSize = H5.H5Tget_size(typeClass);
1833
1834            data = new byte[(int) (numPoints * typeSize)];
1835        }
1836        else {
1837            log.debug("allocateArray(): class ???? ({})", typeClass);
1838
1839            data = null;
1840        }
1841
1842        return data;
1843    }
1844
1845    /**
1846     * Returns the size (in bytes) of a given datatype identifier. It basically just calls H5Tget_size(tid).
1847     *
1848     * @param tid
1849     *            The datatype identifier.
1850     * @return The size of the datatype in bytes.
1851     * @see hdf.hdf5lib.H5#H5Tget_size(long)
1852     */
1853    public static final long getDatatypeSize(long tid) {
1854        // data type information
1855        long tsize = -1;
1856
1857        try {
1858            tsize = H5.H5Tget_size(tid);
1859        }
1860        catch (Exception ex) {
1861            tsize = -1;
1862        }
1863
1864        return tsize;
1865    }
1866
1867    /*
1868     * (non-Javadoc)
1869     * @see hdf.object.Datatype#getDescription()
1870     */
1871    @Override
1872    public String getDescription() {
1873        log.trace("getDescription(): start - isNamed={}", isNamed());
1874
1875        if (datatypeDescription != null)
1876            return datatypeDescription;
1877
1878        StringBuilder description = new StringBuilder();
1879        long tid = HDF5Constants.H5I_INVALID_HID;
1880
1881        switch (datatypeClass) {
1882        case CLASS_CHAR:
1883            log.trace("getDescription(): Char");
1884            description.append("8-bit ").append(isUnsigned() ? "unsigned " : "").append("integer");
1885            break;
1886        case CLASS_INTEGER:
1887            log.trace("getDescription(): Int [{}]", datatypeNATIVE);
1888            if (datatypeNATIVE)
1889                description.append("native ").append(isUnsigned() ? "unsigned " : "").append("integer");
1890            else
1891                description.append(String.valueOf(datatypeSize * 8)).append("-bit ")
1892                .append(isUnsigned() ? "unsigned " : "").append("integer");
1893            break;
1894        case CLASS_FLOAT:
1895            log.trace("getDescription(): Float");
1896            if (datatypeNATIVE)
1897                description.append("native floating-point");
1898            else
1899                description.append(String.valueOf(datatypeSize * 8)).append("-bit floating-point");
1900            break;
1901        case CLASS_STRING:
1902            log.trace("getDescription(): String");
1903            description.append("String, length = ").append(isVarStr() ? "variable" : datatypeSize);
1904
1905            try {
1906                tid = createNative();
1907                if (tid >= 0) {
1908                    String strPadType;
1909                    String strCSETType;
1910                    int strPad = H5.H5Tget_strpad(tid);
1911                    int strCSET = H5.H5Tget_cset(tid);
1912
1913                    if (strPad == HDF5Constants.H5T_STR_NULLTERM)
1914                        strPadType = "H5T_STR_NULLTERM";
1915                    else if (strPad == HDF5Constants.H5T_STR_NULLPAD)
1916                        strPadType = "H5T_STR_NULLPAD";
1917                    else if (strPad == HDF5Constants.H5T_STR_SPACEPAD)
1918                        strPadType = "H5T_STR_SPACEPAD";
1919                    else
1920                        strPadType = null;
1921
1922                    if (strPadType != null)
1923                        description.append(", padding = ").append(strPadType);
1924
1925                    if (strCSET == HDF5Constants.H5T_CSET_ASCII)
1926                        strCSETType = "H5T_CSET_ASCII";
1927                    else if (strCSET == HDF5Constants.H5T_CSET_UTF8)
1928                        strCSETType = "H5T_CSET_UTF8";
1929                    else
1930                        strCSETType = null;
1931
1932                    if (strCSETType != null)
1933                        description.append(", cset = ").append(strCSETType);
1934                }
1935                else {
1936                    log.debug("createNative() failure");
1937                }
1938            }
1939            catch (Exception ex) {
1940                log.debug("H5Tget_strpad failure: ", ex);
1941            }
1942            finally {
1943                close(tid);
1944            }
1945            break;
1946        case CLASS_BITFIELD:
1947            log.trace("getDescription(): Bit");
1948            if (datatypeNATIVE)
1949                description.append("native bitfield");
1950            else
1951                description.append(String.valueOf(datatypeSize * 8)).append("-bit bitfield");
1952            break;
1953        case CLASS_OPAQUE:
1954            log.trace("getDescription(): Opaque");
1955            if (datatypeNATIVE)
1956                description.append("native Opaque");
1957            else
1958                description.append(String.valueOf(datatypeSize)).append("-byte Opaque");
1959
1960            if (opaqueTag != null) {
1961                description.append(", tag = ").append(opaqueTag);
1962            }
1963
1964            break;
1965        case CLASS_COMPOUND:
1966            log.trace("getDescription(): Compound");
1967            description.append("Compound");
1968
1969            if ((compoundMemberTypes != null)
1970                    && !compoundMemberTypes.isEmpty()) {
1971                Iterator<String> memberNames = null;
1972                Iterator<Datatype> memberTypes = compoundMemberTypes.iterator();
1973
1974                if (compoundMemberNames != null)
1975                    memberNames = compoundMemberNames.iterator();
1976
1977                description.append(" {");
1978
1979                while (memberTypes.hasNext()) {
1980                    if (memberNames != null
1981                            && memberNames.hasNext()) {
1982                        description.append(memberNames.next()).append(" = ");
1983                    }
1984
1985                    description.append(memberTypes.next().getDescription());
1986
1987                    if (memberTypes.hasNext())
1988                        description.append(", ");
1989                }
1990
1991                description.append("}");
1992            }
1993
1994            break;
1995        case CLASS_REFERENCE:
1996            log.trace("getDescription(): Ref");
1997            description.append("Reference");
1998
1999            try {
2000                boolean isRegionType = false;
2001
2002                tid = createNative();
2003                if (tid >= 0) {
2004                    if (!H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF)) {
2005                        isRegionType = H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF_DSETREG);
2006
2007                        description.setLength(0);
2008                        if (isRegionType) {
2009                            description.append("Dataset region reference");
2010                        }
2011                        else {
2012                            description.append("Object reference");
2013                        }
2014                    }
2015                }
2016            }
2017            catch (Exception ex) {
2018                log.debug("H5.H5Tequal failure: ", ex);
2019            }
2020            finally {
2021                close(tid);
2022            }
2023
2024            break;
2025        case CLASS_ENUM:
2026            log.trace("getDescription(): Enum");
2027            if (datatypeNATIVE)
2028                description.append("native enum");
2029            else
2030                description.append(String.valueOf(datatypeSize * 8)).append("-bit enum");
2031
2032            String members = getEnumMembersAsString();
2033            if (members != null)
2034                description.append(" (").append(members).append(")");
2035
2036            break;
2037        case CLASS_VLEN:
2038            log.trace("getDescription(): Var Len");
2039            description.append("Variable-length");
2040
2041            if (baseType != null)
2042                description.append(" of ").append(baseType.getDescription());
2043
2044            break;
2045        case CLASS_ARRAY:
2046            log.trace("getDescription(): Array");
2047            description.append("Array");
2048
2049            if (arrayDims != null) {
2050                description.append(" [");
2051                for (int i = 0; i < arrayDims.length; i++) {
2052                    description.append(arrayDims[i]);
2053                    if (i < arrayDims.length - 1)
2054                        description.append(" x ");
2055                }
2056                description.append("]");
2057            }
2058
2059            if (baseType != null)
2060                description.append(" of ").append(baseType.getDescription());
2061
2062            break;
2063        default:
2064            description.append("Unknown");
2065            break;
2066        }
2067        if (isNamed())
2068            description.append("->").append(getFullName());
2069
2070        return description.toString();
2071    }
2072
2073    /**
2074     * Checks if a datatype specified by the identifier is an unsigned integer.
2075     *
2076     * @param tid
2077     *            the datatype ID to be checked.
2078     * @return true is the datatype is an unsigned integer; otherwise returns false.
2079     */
2080    public static final boolean isUnsigned(long tid) {
2081        boolean unsigned = false;
2082
2083        if (tid >= 0) {
2084            try {
2085                int tclass = H5.H5Tget_class(tid);
2086                log.trace("isUnsigned(): tclass = {}", tclass);
2087                if (tclass != HDF5Constants.H5T_FLOAT
2088                        && tclass != HDF5Constants.H5T_STRING && tclass != HDF5Constants.H5T_REFERENCE
2089                        && tclass != HDF5Constants.H5T_BITFIELD && tclass != HDF5Constants.H5T_OPAQUE
2090                        && tclass != HDF5Constants.H5T_VLEN && tclass != HDF5Constants.H5T_COMPOUND
2091                        && tclass != HDF5Constants.H5T_ARRAY) {
2092                    int tsign = H5.H5Tget_sign(tid);
2093                    if (tsign == HDF5Constants.H5T_SGN_NONE)
2094                        unsigned = true;
2095                    else
2096                        log.trace("isUnsigned(): not unsigned");
2097                }
2098                else {
2099                    log.trace("isUnsigned(): tclass not integer type");
2100                }
2101            }
2102            catch (Exception ex) {
2103                log.debug("isUnsigned(): Datatype {} failure", tid, ex);
2104                unsigned = false;
2105            }
2106        }
2107        else {
2108            log.trace("isUnsigned(): not a valid datatype");
2109        }
2110
2111        return unsigned;
2112    }
2113
2114    /**
2115     * Removes all of the elements from metadata list. The list should be empty after this call returns.
2116     */
2117    @Override
2118    public void clear() {
2119        super.clear();
2120        objMetadata.clear();
2121    }
2122
2123    /**
2124     * Retrieves the object's metadata, such as attributes, from the file. Metadata, such as attributes, is stored in a
2125     * List.
2126     *
2127     * @return the list of metadata objects.
2128     * @throws HDF5Exception
2129     *                       if the metadata can not be retrieved
2130     */
2131    @Override
2132    public List<Attribute> getMetadata()
2133            throws HDF5Exception {
2134        int gmIndexType = 0;
2135        int gmIndexOrder = 0;
2136
2137        try {
2138            gmIndexType = fileFormat.getIndexType(null);
2139        }
2140        catch (Exception ex) {
2141            log.debug("getMetadata(): getIndexType failed: ", ex);
2142        }
2143        try {
2144            gmIndexOrder = fileFormat.getIndexOrder(null);
2145        }
2146        catch (Exception ex) {
2147            log.debug("getMetadata(): getIndexOrder failed: ", ex);
2148        }
2149        return this.getMetadata(gmIndexType, gmIndexOrder);
2150    }
2151
2152    /**
2153     * Retrieves the object's metadata, such as attributes, from the file. Metadata, such as attributes, is stored in a
2154     * List.
2155     *
2156     * @param attrPropList
2157     *                     the list of properties to get
2158     * @return the list of metadata objects.
2159     * @throws HDF5Exception
2160     *                       if the metadata can not be retrieved
2161     */
2162    public List<Attribute> getMetadata(int... attrPropList)
2163            throws HDF5Exception {
2164        try {
2165            this.linkTargetObjName = H5File.getLinkTargetName(this);
2166        }
2167        catch (Exception ex) {
2168            log.debug("getMetadata(): getLinkTargetName failed: ", ex);
2169        }
2170
2171        List<Attribute> attrlist = null;
2172        try {
2173            attrlist = objMetadata.getMetadata(attrPropList);
2174        }
2175        catch (Exception ex) {
2176            log.debug("getMetadata(): getMetadata failed: ", ex);
2177        }
2178        return attrlist;
2179    }
2180
2181    /**
2182     * Writes a specific piece of metadata (such as an attribute) into the file. If an HDF(4&amp;5) attribute exists in
2183     * the file, this method updates its value. If the attribute does not exist in the file, it creates the attribute in
2184     * the file and attaches it to the object. It will fail to write a new attribute to the object where an attribute
2185     * with the same name already exists. To update the value of an existing attribute in the file, one needs to get the
2186     * instance of the attribute by getMetadata(), change its values, then use writeMetadata() to write the value.
2187     *
2188     * @param info
2189     *             the metadata to write.
2190     * @throws Exception
2191     *                   if the metadata can not be written
2192     */
2193    @Override
2194    public void writeMetadata(Object info)
2195            throws Exception {
2196        try {
2197            objMetadata.writeMetadata(info);
2198        }
2199        catch (Exception ex) {
2200            log.debug("writeMetadata(): Object not an Attribute");
2201        }
2202    }
2203
2204    /**
2205     * Deletes an existing piece of metadata from this object.
2206     *
2207     * @param info
2208     *             the metadata to delete.
2209     * @throws HDF5Exception
2210     *                       if the metadata can not be removed
2211     */
2212    @Override
2213    public void removeMetadata(Object info)
2214            throws HDF5Exception {
2215        try {
2216            objMetadata.removeMetadata(info);
2217        }
2218        catch (Exception ex) {
2219            log.debug("removeMetadata(): Object not an Attribute");
2220            return;
2221        }
2222
2223        Attribute attr = (Attribute) info;
2224        log.trace("removeMetadata(): {}", attr.getAttributeName());
2225        long tid = open();
2226        if (tid >= 0) {
2227            try {
2228                H5.H5Adelete(tid, attr.getAttributeName());
2229            }
2230            catch (Exception ex) {
2231                log.debug("removeMetadata(): ", ex);
2232            }
2233            finally {
2234                close(tid);
2235            }
2236        }
2237        else {
2238            log.debug("removeMetadata(): failed to open datatype");
2239        }
2240    }
2241
2242    /**
2243     * Updates an existing piece of metadata attached to this object.
2244     *
2245     * @param info
2246     *             the metadata to update.
2247     * @throws HDF5Exception
2248     *                       if the metadata can not be updated
2249     */
2250    @Override
2251    public void updateMetadata(Object info)
2252            throws HDF5Exception {
2253        try {
2254            objMetadata.updateMetadata(info);
2255        }
2256        catch (Exception ex) {
2257            log.debug("updateMetadata(): Object not an Attribute");
2258            return;
2259        }
2260    }
2261
2262    /*
2263     * (non-Javadoc)
2264     * @see hdf.object.HObject#setName(java.lang.String)
2265     */
2266    @Override
2267    public void setName(String newName)
2268            throws Exception {
2269        if (newName == null)
2270            throw new IllegalArgumentException("The new name is NULL");
2271
2272        H5File.renameObject(this, newName);
2273        super.setName(newName);
2274    }
2275
2276    @Override
2277    public void setFullname(String newPath, String newName)
2278            throws Exception {
2279        H5File.renameObject(this, newPath, newName);
2280        super.setFullname(newPath, newName);
2281    }
2282
2283    @Override
2284    public boolean isText() {
2285        return (datatypeClass == Datatype.CLASS_STRING);
2286    }
2287
2288    /**
2289     * Checks if this datatype is an object reference type.
2290     *
2291     * @return true if the datatype is an object reference; false otherwise
2292     */
2293    public boolean isRefObj() {
2294        return isRefObj;
2295    }
2296
2297    /**
2298     * Checks if this datatype is a region reference type.
2299     *
2300     * @return true if the datatype is a region reference; false otherwise
2301     */
2302    public boolean isRegRef() {
2303        return isRegRef;
2304    }
2305
2306    /**
2307     * Checks if this datatype is a standard reference type.
2308     *
2309     * @return true if the datatype is a standard reference; false otherwise
2310     */
2311    public boolean isStdRef() {
2312        return isStdRef;
2313    }
2314
2315    /*
2316     * (non-Javadoc)
2317     * @see hdf.object.Datatype#getReferenceType()
2318     */
2319    @Override
2320    public long getReferenceType()
2321            throws HDF5Exception {
2322        if (isRegRef)
2323            return HDF5Constants.H5T_STD_REF_DSETREG;
2324        if (isRefObj)
2325            return HDF5Constants.H5T_STD_REF_OBJ;
2326        if (isStdRef)
2327            return HDF5Constants.H5T_STD_REF;
2328        return -1;
2329    }
2330
2331    /**
2332     * Describes the dataset object description for a 1.10 reference.
2333     *
2334     * @param container
2335     *                  the dataset/attribute with the reference
2336     * @param refarr
2337     *                  the reference datatype data to be checked.
2338     *
2339     * @return the dataset reference object description.
2340     */
2341    public static String descReferenceObject(long container, byte[] refarr) {
2342        String region_desc = H5.H5Rget_name_string(container, HDF5Constants.H5R_OBJECT, refarr);
2343        region_desc += " H5O_TYPE_OBJ_REF";
2344        log.trace("descReferenceObject region_desc={}:", region_desc);
2345        return region_desc;
2346    }
2347
2348    /**
2349     * Describes the dataset region description for a 1.10 reference.
2350     *
2351     * @param container
2352     *                  the dataset/attribute with the reference
2353     * @param refarr
2354     *                  the reference datatype data to be checked.
2355     *
2356     * @return the dataset region description.
2357     */
2358    public static String descRegionDataset(long container, byte[] refarr) {
2359        String region_desc = H5.H5Rget_name_string(container, HDF5Constants.H5R_DATASET_REGION, refarr);
2360        log.trace("descRegionDataset region_desc={}:", region_desc);
2361        long new_obj_id = HDF5Constants.H5I_INVALID_HID;
2362        try {
2363            log.trace("descRegionDataset refarr2={}:", refarr);
2364            new_obj_id = H5.H5Rdereference(container, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5R_DATASET_REGION,
2365                    refarr);
2366            long new_obj_sid = HDF5Constants.H5I_INVALID_HID;
2367            try {
2368                log.trace("descRegionDataset refarr3={}:", refarr);
2369                new_obj_sid = H5.H5Rget_region(container, HDF5Constants.H5R_DATASET_REGION, refarr);
2370                try {
2371                    int region_type = H5.H5Sget_select_type(new_obj_sid);
2372                    log.debug("descRegionDataset Reference Region Type {}", region_type);
2373                    long reg_ndims = H5.H5Sget_simple_extent_ndims(new_obj_sid);
2374                    StringBuilder sb = new StringBuilder();
2375                    if (HDF5Constants.H5S_SEL_POINTS == region_type) {
2376                        sb.append(" REGION_TYPE POINT ");
2377                        long reg_npoints = H5.H5Sget_select_elem_npoints(new_obj_sid);
2378                        long getcoord[] = new long[(int) (reg_ndims * reg_npoints)];
2379                        try {
2380                            H5.H5Sget_select_elem_pointlist(new_obj_sid, 0, reg_npoints, getcoord);
2381                        }
2382                        catch (Exception ex5) {
2383                            log.debug("descRegionDataset H5.H5Sget_select_elem_pointlist: ", ex5);
2384                        }
2385                        sb.append("{ ");
2386                        for (int i = 0; i < (int) reg_npoints; i++) {
2387                            if (i > 0)
2388                                sb.append(" ");
2389                            sb.append("(");
2390                            for (int j = 0; j < (int) reg_ndims; j++) {
2391                                if (j > 0)
2392                                    sb.append(",");
2393                                sb.append(getcoord[i * (int) reg_ndims + j]);
2394                            }
2395                            sb.append(")");
2396                        }
2397                        sb.append(" }");
2398                        region_desc += sb.toString();
2399                    }
2400                    else if (HDF5Constants.H5S_SEL_HYPERSLABS == region_type) {
2401                        sb.append(" REGION_TYPE BLOCK ");
2402                        long reg_nblocks = H5.H5Sget_select_hyper_nblocks(new_obj_sid);
2403                        long getblocks[] = new long[(int) (reg_ndims * reg_nblocks) * 2];
2404                        try {
2405                            H5.H5Sget_select_hyper_blocklist(new_obj_sid, 0, reg_nblocks, getblocks);
2406                        }
2407                        catch (Exception ex5) {
2408                            log.debug("descRegionDataset H5.H5Sget_select_hyper_blocklist: ", ex5);
2409                        }
2410                        sb.append("{ ");
2411                        for (int i = 0; i < (int) reg_nblocks; i++) {
2412                            if (i > 0)
2413                                sb.append(" ");
2414                            sb.append("(");
2415                            for (int j = 0; j < (int) reg_ndims; j++) {
2416                                if (j > 0)
2417                                    sb.append(",");
2418                                sb.append(getblocks[i * 2 * (int) reg_ndims + j]);
2419                            }
2420                            sb.append(")-(");
2421                            for (int j = 0; j < (int) reg_ndims; j++) {
2422                                if (j > 0)
2423                                    sb.append(",");
2424                                sb.append(getblocks[i * 2 * (int) reg_ndims + (int) reg_ndims + j]);
2425                            }
2426                            sb.append(")");
2427                        }
2428                        sb.append(" }");
2429                        region_desc += sb.toString();
2430                    }
2431                    else
2432                        region_desc += " REGION_TYPE UNKNOWN";
2433                }
2434                catch (Exception ex4) {
2435                    log.debug("descRegionDataset Region Type", ex4);
2436                }
2437            }
2438            catch (Exception ex3) {
2439                log.debug("descRegionDataset Space Open", ex3);
2440            }
2441            finally {
2442                H5.H5Sclose(new_obj_sid);
2443            }
2444            log.trace("descRegionDataset finish");
2445        }
2446        catch (Exception ex2) {
2447            log.debug("descRegionDataset ", ex2);
2448        }
2449        finally {
2450            H5.H5Dclose(new_obj_id);
2451        }
2452        return region_desc;
2453    }
2454
2455    /**
2456     * Gets the dataset reference type for a 1.10 reference.
2457     *
2458     * @param container
2459     *                  the dataset/attribute with the reference
2460     * @param refarr
2461     *                  the reference datatype data to be checked.
2462     *
2463     * @return the dataset reference type.
2464     */
2465    public static int typeObjectRef(long container, int obj_type, byte[] refarr) {
2466        int ref_type = -1;
2467        long new_obj_id = HDF5Constants.H5I_INVALID_HID;
2468        try {
2469            log.trace("typeObjectRef refarr2={}:", refarr);
2470            new_obj_id = H5.H5Rdereference(container, HDF5Constants.H5P_DEFAULT, obj_type, refarr);
2471            if (HDF5Constants.H5R_DATASET_REGION == obj_type) {
2472                long new_obj_sid = HDF5Constants.H5I_INVALID_HID;
2473                try {
2474                    log.trace("typeObjectRef refarr3={}:", refarr);
2475                    new_obj_sid = H5.H5Rget_region(container, HDF5Constants.H5R_DATASET_REGION, refarr);
2476                    try {
2477                        ref_type = H5.H5Sget_select_type(new_obj_sid);
2478                        log.debug("typeObjectRef Reference Region Type {}", ref_type);
2479                    }
2480                    catch (Exception ex4) {
2481                        log.debug("typeObjectRef Region Type", ex4);
2482                    }
2483                }
2484                catch (Exception ex3) {
2485                    log.debug("typeObjectRef Space Open", ex3);
2486                }
2487                finally {
2488                    H5.H5Sclose(new_obj_sid);
2489                }
2490            }
2491            else {
2492                H5O_info_t objInfo;
2493
2494                objInfo = H5.H5Oget_info(new_obj_id);
2495                ref_type = objInfo.type;
2496            }
2497            log.trace("typeObjectRef finish");
2498        }
2499        catch (Exception ex2) {
2500            log.debug("typeObjectRef ", ex2);
2501        }
2502        finally {
2503            H5.H5Dclose(new_obj_id);
2504        }
2505        return ref_type;
2506    }
2507
2508    /**
2509     * Checks if a reference datatype is all zero.
2510     *
2511     * @param refarr
2512     *               the reference datatype data to be checked.
2513     * @return true is the reference datatype data is all zero; otherwise returns false.
2514     */
2515    public static boolean zeroArrayCheck(final byte[] refarr) {
2516        for (byte b : refarr) {
2517            if (b != 0)
2518                return false;
2519        }
2520        return true;
2521    }
2522
2523    /**
2524     * Gets the string padding.
2525     *
2526     * @return the string padding value
2527     */
2528    public int getNativeStrPad() {
2529        return nativeStrPad;
2530    }
2531
2532    /**
2533     * Extracts compound information into flat structure. For example, compound datatype "nest" has {nest1{a, b, c}, d,
2534     * e} then extractCompoundInfo() will put the names of nested compound fields into a flat list as
2535     *
2536     * <pre>
2537     * nest.nest1.a
2538     * nest.nest1.b
2539     * nest.nest1.c
2540     * nest.d
2541     * nest.e
2542     * </pre>
2543     *
2544     * @param dtype
2545     *                      the datatype to extract compound info from
2546     * @param name
2547     *                      the name of the compound datatype
2548     * @param names
2549     *                      the list to store the member names of the compound datatype
2550     * @param flatListTypes
2551     *                      the list to store the nested member names of the compound datatype
2552     */
2553    public static void extractCompoundInfo(final H5Datatype dtype, String name, List<String> names,
2554            List<Datatype> flatListTypes) {
2555        log.trace("extractCompoundInfo(): start: name={}", name);
2556
2557        if (dtype.isArray()) {
2558            log.trace("extractCompoundInfo(): array type - extracting compound info from base datatype");
2559            H5Datatype.extractCompoundInfo((H5Datatype) dtype.getDatatypeBase(), name, names, flatListTypes);
2560        }
2561        else if (dtype.isVLEN()
2562                && !dtype.isVarStr()) {
2563            log.trace("extractCompoundInfo(): variable-length type - extracting compound info from base datatype");
2564            H5Datatype.extractCompoundInfo((H5Datatype) dtype.getDatatypeBase(), name, names, flatListTypes);
2565        }
2566        else if (dtype.isCompound()) {
2567            List<String> compoundMemberNames = dtype.getCompoundMemberNames();
2568            List<Datatype> compoundMemberTypes = dtype.getCompoundMemberTypes();
2569            Datatype mtype = null;
2570            String mname = null;
2571
2572            if (compoundMemberNames == null) {
2573                log.debug("extractCompoundInfo(): compoundMemberNames is null");
2574                return;
2575            }
2576
2577            if (compoundMemberNames.isEmpty()) {
2578                log.debug("extractCompoundInfo(): compound datatype has no members");
2579                return;
2580            }
2581
2582            log.trace("extractCompoundInfo(): nMembers={}", compoundMemberNames.size());
2583
2584            for (int i = 0; i < compoundMemberNames.size(); i++) {
2585                log.trace("extractCompoundInfo(): member[{}]:", i);
2586
2587                mtype = compoundMemberTypes.get(i);
2588
2589                log.trace("extractCompoundInfo(): type={} with size={}", mtype.getDescription(),
2590                        mtype.getDatatypeSize());
2591
2592                if (names != null) {
2593                    mname = name + compoundMemberNames.get(i);
2594                    log.trace("extractCompoundInfo(): mname={}, name={}", mname, name);
2595                }
2596
2597                if (mtype.isCompound()) {
2598                    H5Datatype.extractCompoundInfo((H5Datatype) mtype, mname + CompoundDS.SEPARATOR, names,
2599                            flatListTypes);
2600                    log.trace("extractCompoundInfo(): continue after recursive compound");
2601                    continue;
2602                }
2603
2604                if (names != null) {
2605                    names.add(mname);
2606                }
2607
2608                flatListTypes.add(mtype);
2609
2610                /*
2611                 * For ARRAY of COMPOUND and VLEN of COMPOUND types, we first add the top-level array or vlen type to
2612                 * the list of datatypes, and then follow that with a listing of the datatypes inside the nested
2613                 * compound.
2614                 */
2615                /*
2616                 * TODO: Don't flatten variable-length types until true variable-length support is implemented.
2617                 */
2618                if (mtype.isArray() /* || (mtype.isVLEN() && !mtype.isVarStr()) */) {
2619                    H5Datatype.extractCompoundInfo((H5Datatype) mtype, mname + CompoundDS.SEPARATOR, names,
2620                            flatListTypes);
2621                }
2622            }
2623        }
2624    }
2625
2626    /**
2627     * Creates a datatype of a compound with one field. This function is needed to read/write data field by field.
2628     *
2629     * @param memberName
2630     *                   The name of the datatype
2631     * @return the identifier of the compound datatype.
2632     * @throws HDF5Exception
2633     *                       If there is an error at the HDF5 library level.
2634     */
2635    public long createCompoundFieldType(String memberName)
2636            throws HDF5Exception {
2637        log.trace("createCompoundFieldType(): start member_name={}", memberName);
2638
2639        long topTID = HDF5Constants.H5I_INVALID_HID;
2640        long tmpTID1 = HDF5Constants.H5I_INVALID_HID;
2641
2642        try {
2643            if (this.isArray()) {
2644                log.trace("createCompoundFieldType(): array datatype");
2645
2646                if (baseType != null) {
2647                    log.trace("createCompoundFieldType(): creating compound field type from base datatype");
2648                    tmpTID1 = ((H5Datatype) baseType).createCompoundFieldType(memberName);
2649                }
2650
2651                log.trace("createCompoundFieldType(): creating container array datatype");
2652                topTID = H5.H5Tarray_create(tmpTID1, arrayDims.length, arrayDims);
2653            }
2654            else if (this.isVLEN()) {
2655                log.trace("createCompoundFieldType(): variable-length datatype");
2656
2657                if (baseType != null) {
2658                    log.trace("createCompoundFieldType(): creating compound field type from base datatype");
2659                    tmpTID1 = ((H5Datatype) baseType).createCompoundFieldType(memberName);
2660                }
2661
2662                log.trace("createCompoundFieldType(): creating container variable-length datatype");
2663                topTID = H5.H5Tvlen_create(tmpTID1);
2664            }
2665            else if (this.isCompound()) {
2666                log.trace("createCompoundFieldType(): compound datatype");
2667
2668                String insertedName = memberName;
2669
2670                int sep = memberName.indexOf(CompoundDS.SEPARATOR);
2671                if (sep >= 0) {
2672                    /*
2673                     * If a compound separator character is present in the supplied string, then there is an additional
2674                     * level of compound nesting. We will create a compound type to hold the nested compound type.
2675                     */
2676                    insertedName = memberName.substring(0, sep);
2677
2678                    log.trace("createCompoundFieldType(): member with name {} is nested inside compound", insertedName);
2679                }
2680
2681                /*
2682                 * Retrieve the index of the compound member by its name.
2683                 */
2684                int memberIndex = this.compoundMemberNames.indexOf(insertedName);
2685                if (memberIndex >= 0) {
2686                    H5Datatype memberType = (H5Datatype) this.compoundMemberTypes.get(memberIndex);
2687
2688                    log.trace("createCompoundFieldType(): Member {} is type {} of size={} with baseType={}",
2689                            insertedName, memberType.getDescription(), memberType.getDatatypeSize(),
2690                            memberType.getDatatypeBase());
2691
2692                    if (sep >= 0)
2693                        /*
2694                         * Additional compound nesting; create the nested compound type.
2695                         */
2696                        tmpTID1 = memberType.createCompoundFieldType(memberName.substring(sep + 1));
2697                    else
2698                        tmpTID1 = memberType.createNative();
2699
2700                    log.trace("createCompoundFieldType(): creating container compound datatype");
2701                    topTID = H5.H5Tcreate(HDF5Constants.H5T_COMPOUND, datatypeSize);
2702
2703                    log.trace("createCompoundFieldType(): inserting member {} into compound datatype", insertedName);
2704                    H5.H5Tinsert(topTID, insertedName, 0, tmpTID1);
2705
2706                    /*
2707                     * WARNING!!! This step is crucial. Without it, the compound type created might be larger than the
2708                     * size of the single datatype field we are inserting. Performing a read with a compound datatype of
2709                     * an incorrect size will corrupt JVM memory and cause strange behavior and crashes.
2710                     */
2711                    H5.H5Tpack(topTID);
2712                }
2713                else {
2714                    log.debug(
2715                            "createCompoundFieldType(): member name {} not found in compound datatype's member name list",
2716                            memberName);
2717                }
2718            }
2719        }
2720        catch (Exception ex) {
2721            log.debug("createCompoundFieldType(): creation of compound field type failed: ", ex);
2722            topTID = HDF5Constants.H5I_INVALID_HID;
2723        }
2724        finally {
2725            close(tmpTID1);
2726        }
2727
2728        return topTID;
2729    }
2730
2731    private boolean datatypeIsComplex(long tid) {
2732        long tclass = HDF5Constants.H5T_NO_CLASS;
2733
2734        try {
2735            tclass = H5.H5Tget_class(tid);
2736            log.trace("datatypeIsComplex():{}", tclass);
2737        }
2738        catch (Exception ex) {
2739            log.debug("datatypeIsComplex():", ex);
2740        }
2741
2742        boolean retVal = (tclass == HDF5Constants.H5T_COMPOUND);
2743        retVal |= (tclass == HDF5Constants.H5T_ENUM);
2744        retVal |= (tclass == HDF5Constants.H5T_VLEN);
2745        retVal |= (tclass == HDF5Constants.H5T_ARRAY);
2746
2747        return retVal;
2748    }
2749
2750    private boolean datatypeIsReference(long tid) {
2751        long tclass = HDF5Constants.H5T_NO_CLASS;
2752
2753        try {
2754            tclass = H5.H5Tget_class(tid);
2755            log.trace("datatypeIsReference():{}", tclass);
2756        }
2757        catch (Exception ex) {
2758            log.debug("datatypeIsReference():", ex);
2759        }
2760
2761        return (tclass == HDF5Constants.H5T_REFERENCE);
2762    }
2763
2764    private boolean datatypeIsAtomic(long tid) {
2765        boolean retVal = !(datatypeIsComplex(tid)
2766                | datatypeIsReference(tid) | isRef());
2767        retVal |= isOpaque();
2768        retVal |= isBitField();
2769
2770        return retVal;
2771    }
2772
2773    private boolean datatypeClassIsComplex(long tclass) {
2774        boolean retVal = (tclass == HDF5Constants.H5T_COMPOUND);
2775        retVal |= (tclass == HDF5Constants.H5T_ENUM);
2776        retVal |= (tclass == HDF5Constants.H5T_VLEN);
2777        retVal |= (tclass == HDF5Constants.H5T_ARRAY);
2778
2779        return retVal;
2780    }
2781
2782    private boolean datatypeClassIsReference(long tclass) {
2783        return (tclass == HDF5Constants.H5T_REFERENCE);
2784    }
2785
2786    private boolean datatypeClassIsOpaque(long tclass) {
2787        return (tclass == Datatype.CLASS_OPAQUE);
2788    }
2789
2790    private boolean datatypeClassIsAtomic(long tclass) {
2791        boolean retVal = !(datatypeClassIsComplex(tclass)
2792                | datatypeClassIsReference(tclass));
2793        retVal |= (tclass == Datatype.CLASS_OPAQUE);
2794        retVal |= (tclass == Datatype.CLASS_BITFIELD);
2795
2796        return retVal;
2797    }
2798}