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