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
1424                    tid = H5.H5Tcopy((datatypeSize == 8) ? HDF5Constants.H5T_NATIVE_DOUBLE
1425                                                         : HDF5Constants.H5T_NATIVE_FLOAT);
1426
1427                if (datatypeOrder == Datatype.ORDER_BE) {
1428                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_BE);
1429                }
1430                else if (datatypeOrder == Datatype.ORDER_LE) {
1431                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_LE);
1432                }
1433
1434                if (nativeFPebias > 0) {
1435                    H5.H5Tset_ebias(tid, nativeFPebias);
1436                }
1437
1438                if (nativeFPnorm >= 0) {
1439                    H5.H5Tset_norm(tid, nativeFPnorm);
1440                }
1441
1442                if (nativeFPinpad >= 0) {
1443                    H5.H5Tset_inpad(tid, nativeFPinpad);
1444                }
1445
1446                if ((nativeFPesize >= 0) && (nativeFPmsize >= 0)) {
1447                    H5.H5Tset_fields(tid, nativeFPspos, nativeFPmpos, nativeFPesize, nativeFPmpos,
1448                                     nativeFPmsize);
1449                }
1450            }
1451            catch (Exception ex) {
1452                log.debug("createNative(): native floating-point datatype creation failed: ", ex);
1453                if (tid >= 0)
1454                    close(tid);
1455                tid = HDF5Constants.H5I_INVALID_HID;
1456            }
1457
1458            break;
1459        case CLASS_CHAR:
1460            try {
1461                tid = H5.H5Tcopy((datatypeSign == Datatype.SIGN_NONE) ? HDF5Constants.H5T_NATIVE_UCHAR
1462                                                                      : HDF5Constants.H5T_NATIVE_CHAR);
1463            }
1464            catch (Exception ex) {
1465                log.debug("createNative(): native character datatype creation failed: ", ex);
1466                if (tid >= 0)
1467                    close(tid);
1468                tid = HDF5Constants.H5I_INVALID_HID;
1469            }
1470
1471            break;
1472        case CLASS_STRING:
1473            try {
1474                tid = H5.H5Tcopy(HDF5Constants.H5T_C_S1);
1475
1476                H5.H5Tset_size(tid, (isVLEN || datatypeSize < 0) ? HDF5Constants.H5T_VARIABLE : datatypeSize);
1477
1478                log.trace("createNative(): isVlenStr={} nativeStrPad={} nativeStrCSET={}", isVLEN,
1479                          nativeStrPad, nativeStrCSET);
1480
1481                H5.H5Tset_strpad(tid, (nativeStrPad >= 0) ? nativeStrPad : HDF5Constants.H5T_STR_NULLTERM);
1482
1483                if (nativeStrCSET >= 0) {
1484                    H5.H5Tset_cset(tid, nativeStrCSET);
1485                }
1486            }
1487            catch (Exception ex) {
1488                log.debug("createNative(): native string datatype creation failed: ", ex);
1489                if (tid >= 0)
1490                    close(tid);
1491                tid = HDF5Constants.H5I_INVALID_HID;
1492            }
1493
1494            break;
1495        case CLASS_REFERENCE:
1496            try {
1497                long objRefTypeSize  = H5.H5Tget_size(HDF5Constants.H5T_STD_REF_OBJ);
1498                long dsetRefTypeSize = H5.H5Tget_size(HDF5Constants.H5T_STD_REF_DSETREG);
1499                // use datatypeSize as which type to copy
1500                log.debug("createNative(): datatypeSize:{} ", datatypeSize);
1501                if (datatypeSize < 0 || datatypeSize > dsetRefTypeSize) {
1502                    tid = H5.H5Tcopy(HDF5Constants.H5T_STD_REF);
1503                    log.debug("createNative(): HDF5Constants.H5T_STD_REF");
1504                }
1505                else if (datatypeSize > objRefTypeSize) {
1506                    tid = H5.H5Tcopy(HDF5Constants.H5T_STD_REF_DSETREG);
1507                    log.debug("createNative(): HDF5Constants.H5T_STD_REF_DSETREG");
1508                }
1509                else {
1510                    tid = H5.H5Tcopy(HDF5Constants.H5T_STD_REF_OBJ);
1511                    log.debug("createNative(): HDF5Constants.H5T_STD_REF_OBJ");
1512                }
1513            }
1514            catch (Exception ex) {
1515                log.debug("createNative(): native reference datatype creation failed: ", ex);
1516                if (tid >= 0)
1517                    close(tid);
1518                tid = HDF5Constants.H5I_INVALID_HID;
1519            }
1520
1521            break;
1522        case CLASS_VLEN:
1523            try {
1524                if (baseType == null) {
1525                    log.debug("createNative(): CLASS_VLEN base type is NULL");
1526                    break;
1527                }
1528
1529                if ((tmptid = baseType.createNative()) < 0) {
1530                    log.debug("createNative(): failed to create native datatype for VLEN base datatype");
1531                    break;
1532                }
1533
1534                tid = H5.H5Tvlen_create(tmptid);
1535            }
1536            catch (Exception ex) {
1537                log.debug("createNative(): native variable-length datatype creation failed: ", ex);
1538                if (tid >= 0)
1539                    close(tid);
1540                tid = HDF5Constants.H5I_INVALID_HID;
1541            }
1542            finally {
1543                close(tmptid);
1544            }
1545
1546            break;
1547        case CLASS_BITFIELD:
1548            log.trace("createNative(): CLASS_BITFIELD size is {}", datatypeSize);
1549
1550            try {
1551                switch ((int)datatypeSize) {
1552                case 1:
1553                    log.trace("createNative(): CLASS_BITFIELD is H5T_NATIVE_B8");
1554                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B8);
1555                    break;
1556                case 2:
1557                    log.trace("createNative(): CLASS_BITFIELD is H5T_NATIVE_B16");
1558                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B16);
1559                    break;
1560                case 4:
1561                    log.trace("createNative(): CLASS_BITFIELD is H5T_NATIVE_B32");
1562                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B32);
1563                    break;
1564                case 8:
1565                    log.trace("createNative(): CLASS_BITFIELD is H5T_NATIVE_B64");
1566                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B64);
1567                    break;
1568                default:
1569                    if (datatypeSize == NATIVE) {
1570                        datatypeNATIVE = true;
1571                        datatypeSize   = 1;
1572                    }
1573                    else
1574                        datatypeNATIVE = false;
1575
1576                    /* Custom sized bitfield */
1577                    tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_B8);
1578                    H5.H5Tset_size(tid, datatypeSize);
1579                    H5.H5Tset_precision(tid, 8 * datatypeSize);
1580
1581                    break;
1582                }
1583
1584                if (datatypeOrder == Datatype.ORDER_BE) {
1585                    log.trace("createNative(): CLASS_BITFIELD order is H5T_ORDER_BE");
1586                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_BE);
1587                }
1588                else if (datatypeOrder == Datatype.ORDER_LE) {
1589                    log.trace("createNative(): CLASS_BITFIELD order is H5T_ORDER_LE");
1590                    H5.H5Tset_order(tid, HDF5Constants.H5T_ORDER_LE);
1591                }
1592            }
1593            catch (Exception ex) {
1594                log.debug("createNative(): native bitfield datatype creation failed: ", ex);
1595                if (tid >= 0)
1596                    close(tid);
1597                tid = HDF5Constants.H5I_INVALID_HID;
1598            }
1599
1600            break;
1601        case CLASS_OPAQUE:
1602            log.trace("createNative(): CLASS_OPAQUE is {}-byte H5T_OPAQUE", datatypeSize);
1603
1604            try {
1605                if (datatypeSize == NATIVE) {
1606                    datatypeNATIVE = true;
1607                    tid            = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_OPAQUE);
1608                    datatypeSize   = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_OPAQUE);
1609                }
1610                else {
1611                    datatypeNATIVE = false;
1612                    tid            = H5.H5Tcreate(HDF5Constants.H5T_OPAQUE, datatypeSize);
1613                }
1614
1615                if (opaqueTag != null) {
1616                    H5.H5Tset_tag(tid, opaqueTag);
1617                }
1618            }
1619            catch (Exception ex) {
1620                log.debug("createNative(): native opaque datatype creation failed: ", ex);
1621                if (tid >= 0)
1622                    close(tid);
1623                tid = HDF5Constants.H5I_INVALID_HID;
1624            }
1625
1626            break;
1627        default:
1628            log.debug("createNative(): Unknown class");
1629            break;
1630        } // (tclass)
1631
1632        // set up enum members
1633        if ((datatypeClass == CLASS_ENUM) && (enumMembers != null)) {
1634            log.trace("createNative(): set up enum members");
1635            try {
1636                String memstr;
1637                String memname;
1638                byte[] memval = null;
1639
1640                Iterator entries = enumMembers.entrySet().iterator();
1641                while (entries.hasNext()) {
1642                    Entry thisEntry = (Entry)entries.next();
1643                    memstr          = (String)thisEntry.getKey();
1644                    memname         = (String)thisEntry.getValue();
1645
1646                    if (datatypeSize == 1) {
1647                        log.trace("createNative(): CLASS_ENUM is H5T_NATIVE_INT8");
1648                        Byte tval = Byte.parseByte(memstr);
1649                        memval    = HDFNativeData.byteToByte(tval);
1650                    }
1651                    else if (datatypeSize == 2) {
1652                        log.trace("createNative(): CLASS_ENUM is H5T_NATIVE_INT16");
1653                        Short tval = Short.parseShort(memstr);
1654                        memval     = HDFNativeData.shortToByte(tval);
1655                    }
1656                    else if (datatypeSize == 4) {
1657                        log.trace("createNative(): CLASS_ENUM is H5T_NATIVE_INT32");
1658                        Integer tval = Integer.parseInt(memstr);
1659                        memval       = HDFNativeData.intToByte(tval);
1660                    }
1661                    else if (datatypeSize == 8) {
1662                        log.trace("createNative(): CLASS_INT-ENUM is H5T_NATIVE_INT64");
1663                        Long tval = Long.parseLong(memstr);
1664                        memval    = HDFNativeData.longToByte(tval);
1665                    }
1666                    else {
1667                        log.debug("createNative(): enum datatypeSize incorrect");
1668                    }
1669                    log.trace("createNative(): H5Tenum_insert {} {}", memname, memval);
1670                    H5.H5Tenum_insert(tid, memname, memval);
1671                }
1672            }
1673            catch (Exception ex) {
1674                log.debug("createNative(): set up enum members failure: ", ex);
1675            }
1676        } // (datatypeClass == CLASS_ENUM)
1677
1678        try {
1679            tmptid = tid;
1680            tid    = H5.H5Tget_native_type(tmptid);
1681        }
1682        catch (HDF5Exception ex) {
1683            log.debug("createNative(): H5Tget_native_type({}) failure: ", tmptid, ex);
1684        }
1685        finally {
1686            close(tmptid);
1687        }
1688
1689        return tid;
1690    }
1691
1692    /**
1693     * Allocates a one-dimensional array of byte, short, int, long, float, double, or String to store data in
1694     * memory. For example,
1695     *
1696     * <pre>
1697     * long tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_INT32);
1698     * int[] data = (int[]) H5Datatype.allocateArray(datatype, 100);
1699     * </pre>
1700     *
1701     * returns a 32-bit integer array of size 100.
1702     *
1703     * @param dtype
1704     *                  the type.
1705     * @param numPoints
1706     *                  the total number of data points of the array.
1707     * @return the array object if successful; otherwise, return null.
1708     * @throws OutOfMemoryError
1709     *                          If there is a failure.
1710     */
1711    public static final Object allocateArray(final H5Datatype dtype, int numPoints) throws OutOfMemoryError
1712    {
1713        log.trace("allocateArray(): start: numPoints={}", numPoints);
1714
1715        Object data         = null;
1716        H5Datatype baseType = (H5Datatype)dtype.getDatatypeBase();
1717        int typeClass       = dtype.getDatatypeClass();
1718        long typeSize       = dtype.getDatatypeSize();
1719
1720        if (numPoints < 0) {
1721            log.debug("allocateArray(): numPoints < 0");
1722            return null;
1723        }
1724
1725        // Scalar members have dimensionality zero, i.e. size =0
1726        // what can we do about it, set the size to 1
1727        if (numPoints == 0)
1728            numPoints = 1;
1729
1730        log.trace("allocateArray(): tclass={} : tsize={}", typeClass, typeSize);
1731
1732        if (dtype.isVarStr()) {
1733            log.trace("allocateArray(): is_variable_str={}", dtype.isVarStr());
1734
1735            data = new String[numPoints];
1736            for (int i = 0; i < numPoints; i++)
1737                ((String[])data)[i] = "";
1738        }
1739        else if (typeClass == HDF5Constants.H5T_INTEGER) {
1740            log.trace("allocateArray(): class H5T_INTEGER");
1741            if (typeSize == NATIVE)
1742                typeSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_INT);
1743
1744            switch ((int)typeSize) {
1745            case 1:
1746                data = new byte[numPoints];
1747                break;
1748            case 2:
1749                data = new short[numPoints];
1750                break;
1751            case 4:
1752                data = new int[numPoints];
1753                break;
1754            case 8:
1755                data = new long[numPoints];
1756                break;
1757            default:
1758                break;
1759            }
1760        }
1761        else if (typeClass == HDF5Constants.H5T_ENUM) {
1762            log.trace("allocateArray(): class H5T_ENUM");
1763
1764            if (baseType != null)
1765                data = H5Datatype.allocateArray(baseType, numPoints);
1766            else {
1767                if (typeSize == NATIVE)
1768                    typeSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_INT);
1769                data = new byte[(int)(numPoints * typeSize)];
1770            }
1771        }
1772        else if (typeClass == HDF5Constants.H5T_COMPOUND) {
1773            log.trace("allocateArray(): class H5T_COMPOUND");
1774
1775            data = new ArrayList<>(dtype.getCompoundMemberTypes().size());
1776        }
1777        else if (typeClass == HDF5Constants.H5T_FLOAT) {
1778            log.trace("allocateArray(): class H5T_FLOAT");
1779            if (typeSize == NATIVE)
1780                typeSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_FLOAT);
1781
1782            switch ((int)typeSize) {
1783            case 4:
1784                data = new float[numPoints];
1785                break;
1786            case 8:
1787                data = new double[numPoints];
1788                break;
1789            case 16:
1790                data = new byte[numPoints * 16];
1791                break;
1792            default:
1793                break;
1794            }
1795        }
1796        else if ((typeClass == HDF5Constants.H5T_STRING) || (typeClass == HDF5Constants.H5T_REFERENCE)) {
1797            log.trace("allocateArray(): class H5T_STRING || H5T_REFERENCE");
1798
1799            data = new byte[(int)(numPoints * typeSize)];
1800        }
1801        else if (dtype.isVLEN()) {
1802            log.trace("allocateArray(): isVLEN");
1803
1804            data = new ArrayList[numPoints];
1805            for (int j = 0; j < numPoints; j++)
1806                ((ArrayList[])data)[j] = new ArrayList<byte[]>();
1807            // if (baseType != null)
1808            // ((ArrayList<>)data).add(H5Datatype.allocateArray(baseType, numPoints));
1809        }
1810        else if (typeClass == HDF5Constants.H5T_ARRAY) {
1811            log.trace("allocateArray(): class H5T_ARRAY");
1812
1813            try {
1814                log.trace("allocateArray(): ArrayRank={}", dtype.getArrayDims().length);
1815
1816                // Use the base datatype to define the array
1817                long[] arrayDims = dtype.getArrayDims();
1818                int asize        = numPoints;
1819                for (int j = 0; j < arrayDims.length; j++) {
1820                    log.trace("allocateArray(): Array dims[{}]={}", j, arrayDims[j]);
1821
1822                    asize *= arrayDims[j];
1823                }
1824
1825                if (baseType != null)
1826                    data = H5Datatype.allocateArray(baseType, asize);
1827            }
1828            catch (Exception ex) {
1829                log.debug("allocateArray(): H5T_ARRAY class failure: ", ex);
1830            }
1831        }
1832        else if ((typeClass == HDF5Constants.H5T_OPAQUE) || (typeClass == HDF5Constants.H5T_BITFIELD)) {
1833            log.trace("allocateArray(): class H5T_OPAQUE || H5T_BITFIELD");
1834            if (typeSize == NATIVE)
1835                typeSize = H5.H5Tget_size(typeClass);
1836
1837            data = new byte[(int)(numPoints * typeSize)];
1838        }
1839        else {
1840            log.debug("allocateArray(): class ???? ({})", typeClass);
1841
1842            data = null;
1843        }
1844
1845        return data;
1846    }
1847
1848    /**
1849     * Returns the size (in bytes) of a given datatype identifier. It basically just calls H5Tget_size(tid).
1850     *
1851     * @param tid
1852     *            The datatype identifier.
1853     * @return The size of the datatype in bytes.
1854     * @see hdf.hdf5lib.H5#H5Tget_size(long)
1855     */
1856    public static final long getDatatypeSize(long tid)
1857    {
1858        // data type information
1859        long tsize = -1;
1860
1861        try {
1862            tsize = H5.H5Tget_size(tid);
1863        }
1864        catch (Exception ex) {
1865            tsize = -1;
1866        }
1867
1868        return tsize;
1869    }
1870
1871    /*
1872     * (non-Javadoc)
1873     * @see hdf.object.Datatype#getDescription()
1874     */
1875    @Override
1876    public String getDescription()
1877    {
1878        log.trace("getDescription(): start - isNamed={}", isNamed());
1879
1880        if (datatypeDescription != null)
1881            return datatypeDescription;
1882
1883        StringBuilder description = new StringBuilder();
1884        long tid                  = HDF5Constants.H5I_INVALID_HID;
1885
1886        switch (datatypeClass) {
1887        case CLASS_CHAR:
1888            log.trace("getDescription(): Char");
1889            description.append("8-bit ").append(isUnsigned() ? "unsigned " : "").append("integer");
1890            break;
1891        case CLASS_INTEGER:
1892            log.trace("getDescription(): Int [{}]", datatypeNATIVE);
1893            if (datatypeNATIVE)
1894                description.append("native ").append(isUnsigned() ? "unsigned " : "").append("integer");
1895            else
1896                description.append(String.valueOf(datatypeSize * 8))
1897                    .append("-bit ")
1898                    .append(isUnsigned() ? "unsigned " : "")
1899                    .append("integer");
1900            break;
1901        case CLASS_FLOAT:
1902            log.trace("getDescription(): Float");
1903            if (datatypeNATIVE)
1904                description.append("native floating-point");
1905            else
1906                description.append(String.valueOf(datatypeSize * 8)).append("-bit floating-point");
1907            break;
1908        case CLASS_STRING:
1909            log.trace("getDescription(): String");
1910            description.append("String, length = ").append(isVarStr() ? "variable" : datatypeSize);
1911
1912            try {
1913                tid = createNative();
1914                if (tid >= 0) {
1915                    String strPadType;
1916                    String strCSETType;
1917                    int strPad  = H5.H5Tget_strpad(tid);
1918                    int strCSET = H5.H5Tget_cset(tid);
1919
1920                    if (strPad == HDF5Constants.H5T_STR_NULLTERM)
1921                        strPadType = "H5T_STR_NULLTERM";
1922                    else if (strPad == HDF5Constants.H5T_STR_NULLPAD)
1923                        strPadType = "H5T_STR_NULLPAD";
1924                    else if (strPad == HDF5Constants.H5T_STR_SPACEPAD)
1925                        strPadType = "H5T_STR_SPACEPAD";
1926                    else
1927                        strPadType = null;
1928
1929                    if (strPadType != null)
1930                        description.append(", padding = ").append(strPadType);
1931
1932                    if (strCSET == HDF5Constants.H5T_CSET_ASCII)
1933                        strCSETType = "H5T_CSET_ASCII";
1934                    else if (strCSET == HDF5Constants.H5T_CSET_UTF8)
1935                        strCSETType = "H5T_CSET_UTF8";
1936                    else
1937                        strCSETType = null;
1938
1939                    if (strCSETType != null)
1940                        description.append(", cset = ").append(strCSETType);
1941                }
1942                else {
1943                    log.debug("createNative() failure");
1944                }
1945            }
1946            catch (Exception ex) {
1947                log.debug("H5Tget_strpad failure: ", ex);
1948            }
1949            finally {
1950                close(tid);
1951            }
1952            break;
1953        case CLASS_BITFIELD:
1954            log.trace("getDescription(): Bit");
1955            if (datatypeNATIVE)
1956                description.append("native bitfield");
1957            else
1958                description.append(String.valueOf(datatypeSize * 8)).append("-bit bitfield");
1959            break;
1960        case CLASS_OPAQUE:
1961            log.trace("getDescription(): Opaque");
1962            if (datatypeNATIVE)
1963                description.append("native Opaque");
1964            else
1965                description.append(String.valueOf(datatypeSize)).append("-byte Opaque");
1966
1967            if (opaqueTag != null) {
1968                description.append(", tag = ").append(opaqueTag);
1969            }
1970
1971            break;
1972        case CLASS_COMPOUND:
1973            log.trace("getDescription(): Compound");
1974            description.append("Compound");
1975
1976            if ((compoundMemberTypes != null) && !compoundMemberTypes.isEmpty()) {
1977                Iterator<String> memberNames   = null;
1978                Iterator<Datatype> memberTypes = compoundMemberTypes.iterator();
1979
1980                if (compoundMemberNames != null)
1981                    memberNames = compoundMemberNames.iterator();
1982
1983                description.append(" {");
1984
1985                while (memberTypes.hasNext()) {
1986                    if (memberNames != null && memberNames.hasNext()) {
1987                        description.append(memberNames.next()).append(" = ");
1988                    }
1989
1990                    description.append(memberTypes.next().getDescription());
1991
1992                    if (memberTypes.hasNext())
1993                        description.append(", ");
1994                }
1995
1996                description.append("}");
1997            }
1998
1999            break;
2000        case CLASS_REFERENCE:
2001            log.trace("getDescription(): Ref");
2002            description.append("Reference");
2003
2004            try {
2005                boolean isRegionType = false;
2006
2007                tid = createNative();
2008                if (tid >= 0) {
2009                    if (!H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF)) {
2010                        isRegionType = H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF_DSETREG);
2011
2012                        description.setLength(0);
2013                        if (isRegionType) {
2014                            description.append("Dataset region reference");
2015                        }
2016                        else {
2017                            description.append("Object reference");
2018                        }
2019                    }
2020                }
2021            }
2022            catch (Exception ex) {
2023                log.debug("H5.H5Tequal failure: ", ex);
2024            }
2025            finally {
2026                close(tid);
2027            }
2028
2029            break;
2030        case CLASS_ENUM:
2031            log.trace("getDescription(): Enum");
2032            if (datatypeNATIVE)
2033                description.append("native enum");
2034            else
2035                description.append(String.valueOf(datatypeSize * 8)).append("-bit enum");
2036
2037            String members = getEnumMembersAsString();
2038            if (members != null)
2039                description.append(" (").append(members).append(")");
2040
2041            break;
2042        case CLASS_VLEN:
2043            log.trace("getDescription(): Var Len");
2044            description.append("Variable-length");
2045
2046            if (baseType != null)
2047                description.append(" of ").append(baseType.getDescription());
2048
2049            break;
2050        case CLASS_ARRAY:
2051            log.trace("getDescription(): Array");
2052            description.append("Array");
2053
2054            if (arrayDims != null) {
2055                description.append(" [");
2056                for (int i = 0; i < arrayDims.length; i++) {
2057                    description.append(arrayDims[i]);
2058                    if (i < arrayDims.length - 1)
2059                        description.append(" x ");
2060                }
2061                description.append("]");
2062            }
2063
2064            if (baseType != null)
2065                description.append(" of ").append(baseType.getDescription());
2066
2067            break;
2068        default:
2069            description.append("Unknown");
2070            break;
2071        }
2072        if (isNamed())
2073            description.append("->").append(getFullName());
2074
2075        return description.toString();
2076    }
2077
2078    /**
2079     * Checks if a datatype specified by the identifier is an unsigned integer.
2080     *
2081     * @param tid
2082     *            the datatype ID to be checked.
2083     * @return true is the datatype is an unsigned integer; otherwise returns false.
2084     */
2085    public static final boolean isUnsigned(long tid)
2086    {
2087        boolean unsigned = false;
2088
2089        if (tid >= 0) {
2090            try {
2091                int tclass = H5.H5Tget_class(tid);
2092                log.trace("isUnsigned(): tclass = {}", tclass);
2093                if (tclass != HDF5Constants.H5T_FLOAT && tclass != HDF5Constants.H5T_STRING &&
2094                    tclass != HDF5Constants.H5T_REFERENCE && tclass != HDF5Constants.H5T_BITFIELD &&
2095                    tclass != HDF5Constants.H5T_OPAQUE && tclass != HDF5Constants.H5T_VLEN &&
2096                    tclass != HDF5Constants.H5T_COMPOUND && tclass != HDF5Constants.H5T_ARRAY) {
2097                    int tsign = H5.H5Tget_sign(tid);
2098                    if (tsign == HDF5Constants.H5T_SGN_NONE)
2099                        unsigned = true;
2100                    else
2101                        log.trace("isUnsigned(): not unsigned");
2102                }
2103                else {
2104                    log.trace("isUnsigned(): tclass not integer type");
2105                }
2106            }
2107            catch (Exception ex) {
2108                log.debug("isUnsigned(): Datatype {} failure", tid, ex);
2109                unsigned = false;
2110            }
2111        }
2112        else {
2113            log.trace("isUnsigned(): not a valid datatype");
2114        }
2115
2116        return unsigned;
2117    }
2118
2119    /**
2120     * Removes all of the elements from metadata list. The list should be empty after this call returns.
2121     */
2122    @Override
2123    public void clear()
2124    {
2125        super.clear();
2126        objMetadata.clear();
2127    }
2128
2129    /**
2130     * Retrieves the object's metadata, such as attributes, from the file. Metadata, such as attributes, is
2131     * stored in a List.
2132     *
2133     * @return the list of metadata objects.
2134     * @throws HDF5Exception
2135     *                       if the metadata can not be retrieved
2136     */
2137    @Override
2138    public List<Attribute> getMetadata() throws HDF5Exception
2139    {
2140        int gmIndexType  = 0;
2141        int gmIndexOrder = 0;
2142
2143        try {
2144            gmIndexType = fileFormat.getIndexType(null);
2145        }
2146        catch (Exception ex) {
2147            log.debug("getMetadata(): getIndexType failed: ", ex);
2148        }
2149        try {
2150            gmIndexOrder = fileFormat.getIndexOrder(null);
2151        }
2152        catch (Exception ex) {
2153            log.debug("getMetadata(): getIndexOrder failed: ", ex);
2154        }
2155        return this.getMetadata(gmIndexType, gmIndexOrder);
2156    }
2157
2158    /**
2159     * Retrieves the object's metadata, such as attributes, from the file. Metadata, such as attributes, is
2160     * stored in a List.
2161     *
2162     * @param attrPropList
2163     *                     the list of properties to get
2164     * @return the list of metadata objects.
2165     * @throws HDF5Exception
2166     *                       if the metadata can not be retrieved
2167     */
2168    public List<Attribute> getMetadata(int... attrPropList) throws HDF5Exception
2169    {
2170        try {
2171            this.linkTargetObjName = H5File.getLinkTargetName(this);
2172        }
2173        catch (Exception ex) {
2174            log.debug("getMetadata(): getLinkTargetName failed: ", ex);
2175        }
2176
2177        List<Attribute> attrlist = null;
2178        try {
2179            attrlist = objMetadata.getMetadata(attrPropList);
2180        }
2181        catch (Exception ex) {
2182            log.debug("getMetadata(): getMetadata failed: ", ex);
2183        }
2184        return attrlist;
2185    }
2186
2187    /**
2188     * Writes a specific piece of metadata (such as an attribute) into the file. If an HDF(4&amp;5) attribute
2189     * exists in the file, this method updates its value. If the attribute does not exist in the file, it
2190     * creates the attribute in the file and attaches it to the object. It will fail to write a new attribute
2191     * to the object where an attribute with the same name already exists. To update the value of an existing
2192     * attribute in the file, one needs to get the instance of the attribute by getMetadata(), change its
2193     * values, then use writeMetadata() to write the value.
2194     *
2195     * @param info
2196     *             the metadata to write.
2197     * @throws Exception
2198     *                   if the metadata can not be written
2199     */
2200    @Override
2201    public void writeMetadata(Object info) throws Exception
2202    {
2203        try {
2204            objMetadata.writeMetadata(info);
2205        }
2206        catch (Exception ex) {
2207            log.debug("writeMetadata(): Object not an Attribute");
2208        }
2209    }
2210
2211    /**
2212     * Deletes an existing piece of metadata from this object.
2213     *
2214     * @param info
2215     *             the metadata to delete.
2216     * @throws HDF5Exception
2217     *                       if the metadata can not be removed
2218     */
2219    @Override
2220    public void removeMetadata(Object info) throws HDF5Exception
2221    {
2222        try {
2223            objMetadata.removeMetadata(info);
2224        }
2225        catch (Exception ex) {
2226            log.debug("removeMetadata(): Object not an Attribute");
2227            return;
2228        }
2229
2230        Attribute attr = (Attribute)info;
2231        log.trace("removeMetadata(): {}", attr.getAttributeName());
2232        long tid = open();
2233        if (tid >= 0) {
2234            try {
2235                H5.H5Adelete(tid, attr.getAttributeName());
2236            }
2237            catch (Exception ex) {
2238                log.debug("removeMetadata(): ", ex);
2239            }
2240            finally {
2241                close(tid);
2242            }
2243        }
2244        else {
2245            log.debug("removeMetadata(): failed to open datatype");
2246        }
2247    }
2248
2249    /**
2250     * Updates an existing piece of metadata attached to this object.
2251     *
2252     * @param info
2253     *             the metadata to update.
2254     * @throws HDF5Exception
2255     *                       if the metadata can not be updated
2256     */
2257    @Override
2258    public void updateMetadata(Object info) throws HDF5Exception
2259    {
2260        try {
2261            objMetadata.updateMetadata(info);
2262        }
2263        catch (Exception ex) {
2264            log.debug("updateMetadata(): Object not an Attribute");
2265            return;
2266        }
2267    }
2268
2269    /*
2270     * (non-Javadoc)
2271     * @see hdf.object.HObject#setName(java.lang.String)
2272     */
2273    @Override
2274    public void setName(String newName) throws Exception
2275    {
2276        if (newName == null)
2277            throw new IllegalArgumentException("The new name is NULL");
2278
2279        H5File.renameObject(this, newName);
2280        super.setName(newName);
2281    }
2282
2283    @Override
2284    public void setFullname(String newPath, String newName) throws Exception
2285    {
2286        H5File.renameObject(this, newPath, newName);
2287        super.setFullname(newPath, newName);
2288    }
2289
2290    @Override
2291    public boolean isText()
2292    {
2293        return (datatypeClass == Datatype.CLASS_STRING);
2294    }
2295
2296    /**
2297     * Checks if this datatype is an object reference type.
2298     *
2299     * @return true if the datatype is an object reference; false otherwise
2300     */
2301    public boolean isRefObj() { return isRefObj; }
2302
2303    /**
2304     * Checks if this datatype is a region reference type.
2305     *
2306     * @return true if the datatype is a region reference; false otherwise
2307     */
2308    public boolean isRegRef() { return isRegRef; }
2309
2310    /**
2311     * Checks if this datatype is a standard reference type.
2312     *
2313     * @return true if the datatype is a standard reference; false otherwise
2314     */
2315    public boolean isStdRef() { return isStdRef; }
2316
2317    /*
2318     * (non-Javadoc)
2319     * @see hdf.object.Datatype#getReferenceType()
2320     */
2321    @Override
2322    public long getReferenceType() throws HDF5Exception
2323    {
2324        if (isRegRef)
2325            return HDF5Constants.H5T_STD_REF_DSETREG;
2326        if (isRefObj)
2327            return HDF5Constants.H5T_STD_REF_OBJ;
2328        if (isStdRef)
2329            return HDF5Constants.H5T_STD_REF;
2330        return -1;
2331    }
2332
2333    /**
2334     * Describes the dataset object description for a 1.10 reference.
2335     *
2336     * @param container
2337     *                  the dataset/attribute with the reference
2338     * @param refarr
2339     *                  the reference datatype data to be checked.
2340     *
2341     * @return the dataset reference object description.
2342     */
2343    public static String descReferenceObject(long container, byte[] refarr)
2344    {
2345        String region_desc = H5.H5Rget_name_string(container, HDF5Constants.H5R_OBJECT, refarr);
2346        region_desc += " H5O_TYPE_OBJ_REF";
2347        log.trace("descReferenceObject region_desc={}:", region_desc);
2348        return region_desc;
2349    }
2350
2351    /**
2352     * Describes the dataset region description for a 1.10 reference.
2353     *
2354     * @param container
2355     *                  the dataset/attribute with the reference
2356     * @param refarr
2357     *                  the reference datatype data to be checked.
2358     *
2359     * @return the dataset region description.
2360     */
2361    public static String descRegionDataset(long container, byte[] refarr)
2362    {
2363        String region_desc = H5.H5Rget_name_string(container, HDF5Constants.H5R_DATASET_REGION, refarr);
2364        log.trace("descRegionDataset region_desc={}:", region_desc);
2365        long new_obj_id = HDF5Constants.H5I_INVALID_HID;
2366        try {
2367            log.trace("descRegionDataset refarr2={}:", refarr);
2368            new_obj_id       = H5.H5Rdereference(container, HDF5Constants.H5P_DEFAULT,
2369                                                 HDF5Constants.H5R_DATASET_REGION, refarr);
2370            long new_obj_sid = HDF5Constants.H5I_INVALID_HID;
2371            try {
2372                log.trace("descRegionDataset refarr3={}:", refarr);
2373                new_obj_sid = H5.H5Rget_region(container, HDF5Constants.H5R_DATASET_REGION, refarr);
2374                try {
2375                    int region_type = H5.H5Sget_select_type(new_obj_sid);
2376                    log.debug("descRegionDataset Reference Region Type {}", region_type);
2377                    long reg_ndims   = H5.H5Sget_simple_extent_ndims(new_obj_sid);
2378                    StringBuilder sb = new StringBuilder();
2379                    if (HDF5Constants.H5S_SEL_POINTS == region_type) {
2380                        sb.append(" REGION_TYPE POINT ");
2381                        long reg_npoints = H5.H5Sget_select_elem_npoints(new_obj_sid);
2382                        long getcoord[]  = new long[(int)(reg_ndims * reg_npoints)];
2383                        try {
2384                            H5.H5Sget_select_elem_pointlist(new_obj_sid, 0, reg_npoints, getcoord);
2385                        }
2386                        catch (Exception ex5) {
2387                            log.debug("descRegionDataset H5.H5Sget_select_elem_pointlist: ", ex5);
2388                        }
2389                        sb.append("{ ");
2390                        for (int i = 0; i < (int)reg_npoints; i++) {
2391                            if (i > 0)
2392                                sb.append(" ");
2393                            sb.append("(");
2394                            for (int j = 0; j < (int)reg_ndims; j++) {
2395                                if (j > 0)
2396                                    sb.append(",");
2397                                sb.append(getcoord[i * (int)reg_ndims + j]);
2398                            }
2399                            sb.append(")");
2400                        }
2401                        sb.append(" }");
2402                        region_desc += sb.toString();
2403                    }
2404                    else if (HDF5Constants.H5S_SEL_HYPERSLABS == region_type) {
2405                        sb.append(" REGION_TYPE BLOCK ");
2406                        long reg_nblocks = H5.H5Sget_select_hyper_nblocks(new_obj_sid);
2407                        long getblocks[] = new long[(int)(reg_ndims * reg_nblocks) * 2];
2408                        try {
2409                            H5.H5Sget_select_hyper_blocklist(new_obj_sid, 0, reg_nblocks, getblocks);
2410                        }
2411                        catch (Exception ex5) {
2412                            log.debug("descRegionDataset H5.H5Sget_select_hyper_blocklist: ", ex5);
2413                        }
2414                        sb.append("{ ");
2415                        for (int i = 0; i < (int)reg_nblocks; i++) {
2416                            if (i > 0)
2417                                sb.append(" ");
2418                            sb.append("(");
2419                            for (int j = 0; j < (int)reg_ndims; j++) {
2420                                if (j > 0)
2421                                    sb.append(",");
2422                                sb.append(getblocks[i * 2 * (int)reg_ndims + j]);
2423                            }
2424                            sb.append(")-(");
2425                            for (int j = 0; j < (int)reg_ndims; j++) {
2426                                if (j > 0)
2427                                    sb.append(",");
2428                                sb.append(getblocks[i * 2 * (int)reg_ndims + (int)reg_ndims + j]);
2429                            }
2430                            sb.append(")");
2431                        }
2432                        sb.append(" }");
2433                        region_desc += sb.toString();
2434                    }
2435                    else
2436                        region_desc += " REGION_TYPE UNKNOWN";
2437                }
2438                catch (Exception ex4) {
2439                    log.debug("descRegionDataset Region Type", ex4);
2440                }
2441            }
2442            catch (Exception ex3) {
2443                log.debug("descRegionDataset Space Open", ex3);
2444            }
2445            finally {
2446                H5.H5Sclose(new_obj_sid);
2447            }
2448            log.trace("descRegionDataset finish");
2449        }
2450        catch (Exception ex2) {
2451            log.debug("descRegionDataset ", ex2);
2452        }
2453        finally {
2454            H5.H5Dclose(new_obj_id);
2455        }
2456        return region_desc;
2457    }
2458
2459    /**
2460     * Gets the dataset reference type for a 1.10 reference.
2461     *
2462     * @param container the dataset/attribute with the reference
2463     * @param obj_type  the dataset/attribute object type
2464     * @param refarr    the reference datatype data to be checked.
2465     *
2466     * @return the dataset reference type.
2467     */
2468    public static int typeObjectRef(long container, int obj_type, byte[] refarr)
2469    {
2470        int ref_type    = -1;
2471        long new_obj_id = HDF5Constants.H5I_INVALID_HID;
2472        try {
2473            log.trace("typeObjectRef refarr2={}:", refarr);
2474            new_obj_id = H5.H5Rdereference(container, HDF5Constants.H5P_DEFAULT, obj_type, refarr);
2475            if (HDF5Constants.H5R_DATASET_REGION == obj_type) {
2476                long new_obj_sid = HDF5Constants.H5I_INVALID_HID;
2477                try {
2478                    log.trace("typeObjectRef refarr3={}:", refarr);
2479                    new_obj_sid = H5.H5Rget_region(container, HDF5Constants.H5R_DATASET_REGION, refarr);
2480                    try {
2481                        ref_type = H5.H5Sget_select_type(new_obj_sid);
2482                        log.debug("typeObjectRef Reference Region Type {}", ref_type);
2483                    }
2484                    catch (Exception ex4) {
2485                        log.debug("typeObjectRef Region Type", ex4);
2486                    }
2487                }
2488                catch (Exception ex3) {
2489                    log.debug("typeObjectRef Space Open", ex3);
2490                }
2491                finally {
2492                    H5.H5Sclose(new_obj_sid);
2493                }
2494            }
2495            else {
2496                H5O_info_t objInfo;
2497
2498                objInfo  = H5.H5Oget_info(new_obj_id);
2499                ref_type = objInfo.type;
2500            }
2501            log.trace("typeObjectRef finish");
2502        }
2503        catch (Exception ex2) {
2504            log.debug("typeObjectRef ", ex2);
2505        }
2506        finally {
2507            H5.H5Dclose(new_obj_id);
2508        }
2509        return ref_type;
2510    }
2511
2512    /**
2513     * Checks if a reference datatype is all zero.
2514     *
2515     * @param refarr
2516     *               the reference datatype data to be checked.
2517     * @return true is the reference datatype data is all zero; otherwise returns false.
2518     */
2519    public static boolean zeroArrayCheck(final byte[] refarr)
2520    {
2521        for (byte b : refarr) {
2522            if (b != 0)
2523                return false;
2524        }
2525        return true;
2526    }
2527
2528    /**
2529     * Gets the string padding.
2530     *
2531     * @return the string padding value
2532     */
2533    public int getNativeStrPad() { return nativeStrPad; }
2534
2535    /**
2536     * Extracts compound information into flat structure. For example, compound datatype "nest" has {nest1{a,
2537     * b, c}, d, e} then extractCompoundInfo() will put the names of nested compound fields into a flat list
2538     * as
2539     *
2540     * <pre>
2541     * nest.nest1.a
2542     * nest.nest1.b
2543     * nest.nest1.c
2544     * nest.d
2545     * nest.e
2546     * </pre>
2547     *
2548     * @param dtype
2549     *                      the datatype to extract compound info from
2550     * @param name
2551     *                      the name of the compound datatype
2552     * @param names
2553     *                      the list to store the member names of the compound datatype
2554     * @param flatListTypes
2555     *                      the list to store the nested member names of the compound datatype
2556     */
2557    public static void extractCompoundInfo(final H5Datatype dtype, String name, List<String> names,
2558                                           List<Datatype> flatListTypes)
2559    {
2560        log.trace("extractCompoundInfo(): start: name={}", name);
2561
2562        if (dtype.isArray()) {
2563            log.trace("extractCompoundInfo(): array type - extracting compound info from base datatype");
2564            H5Datatype.extractCompoundInfo((H5Datatype)dtype.getDatatypeBase(), name, names, flatListTypes);
2565        }
2566        else if (dtype.isVLEN() && !dtype.isVarStr()) {
2567            log.trace(
2568                "extractCompoundInfo(): variable-length type - extracting compound info from base datatype");
2569            H5Datatype.extractCompoundInfo((H5Datatype)dtype.getDatatypeBase(), name, names, flatListTypes);
2570        }
2571        else if (dtype.isCompound()) {
2572            List<String> compoundMemberNames   = dtype.getCompoundMemberNames();
2573            List<Datatype> compoundMemberTypes = dtype.getCompoundMemberTypes();
2574            Datatype mtype                     = null;
2575            String mname                       = null;
2576
2577            if (compoundMemberNames == null) {
2578                log.debug("extractCompoundInfo(): compoundMemberNames is null");
2579                return;
2580            }
2581
2582            if (compoundMemberNames.isEmpty()) {
2583                log.debug("extractCompoundInfo(): compound datatype has no members");
2584                return;
2585            }
2586
2587            log.trace("extractCompoundInfo(): nMembers={}", compoundMemberNames.size());
2588
2589            for (int i = 0; i < compoundMemberNames.size(); i++) {
2590                log.trace("extractCompoundInfo(): member[{}]:", i);
2591
2592                mtype = compoundMemberTypes.get(i);
2593
2594                log.trace("extractCompoundInfo(): type={} with size={}", mtype.getDescription(),
2595                          mtype.getDatatypeSize());
2596
2597                if (names != null) {
2598                    mname = name + compoundMemberNames.get(i);
2599                    log.trace("extractCompoundInfo(): mname={}, name={}", mname, name);
2600                }
2601
2602                if (mtype.isCompound()) {
2603                    H5Datatype.extractCompoundInfo((H5Datatype)mtype, mname + CompoundDS.SEPARATOR, names,
2604                                                   flatListTypes);
2605                    log.trace("extractCompoundInfo(): continue after recursive compound");
2606                    continue;
2607                }
2608
2609                if (names != null) {
2610                    names.add(mname);
2611                }
2612
2613                flatListTypes.add(mtype);
2614
2615                /*
2616                 * For ARRAY of COMPOUND and VLEN of COMPOUND types, we first add the top-level array or vlen
2617                 * type to the list of datatypes, and then follow that with a listing of the datatypes inside
2618                 * the nested compound.
2619                 */
2620                /*
2621                 * TODO: Don't flatten variable-length types until true variable-length support is
2622                 * implemented.
2623                 */
2624                if (mtype.isArray() /* || (mtype.isVLEN() && !mtype.isVarStr()) */) {
2625                    H5Datatype.extractCompoundInfo((H5Datatype)mtype, mname + CompoundDS.SEPARATOR, names,
2626                                                   flatListTypes);
2627                }
2628            }
2629        }
2630    }
2631
2632    /**
2633     * Creates a datatype of a compound with one field. This function is needed to read/write data field by
2634     * field.
2635     *
2636     * @param memberName
2637     *                   The name of the datatype
2638     * @return the identifier of the compound datatype.
2639     * @throws HDF5Exception
2640     *                       If there is an error at the HDF5 library level.
2641     */
2642    public long createCompoundFieldType(String memberName) throws HDF5Exception
2643    {
2644        log.trace("createCompoundFieldType(): start member_name={}", memberName);
2645
2646        long topTID  = HDF5Constants.H5I_INVALID_HID;
2647        long tmpTID1 = HDF5Constants.H5I_INVALID_HID;
2648
2649        try {
2650            if (this.isArray()) {
2651                log.trace("createCompoundFieldType(): array datatype");
2652
2653                if (baseType != null) {
2654                    log.trace("createCompoundFieldType(): creating compound field type from base datatype");
2655                    tmpTID1 = ((H5Datatype)baseType).createCompoundFieldType(memberName);
2656                }
2657
2658                log.trace("createCompoundFieldType(): creating container array datatype");
2659                topTID = H5.H5Tarray_create(tmpTID1, arrayDims.length, arrayDims);
2660            }
2661            else if (this.isVLEN()) {
2662                log.trace("createCompoundFieldType(): variable-length datatype");
2663
2664                if (baseType != null) {
2665                    log.trace("createCompoundFieldType(): creating compound field type from base datatype");
2666                    tmpTID1 = ((H5Datatype)baseType).createCompoundFieldType(memberName);
2667                }
2668
2669                log.trace("createCompoundFieldType(): creating container variable-length datatype");
2670                topTID = H5.H5Tvlen_create(tmpTID1);
2671            }
2672            else if (this.isCompound()) {
2673                log.trace("createCompoundFieldType(): compound datatype");
2674
2675                String insertedName = memberName;
2676
2677                int sep = memberName.indexOf(CompoundDS.SEPARATOR);
2678                if (sep >= 0) {
2679                    /*
2680                     * If a compound separator character is present in the supplied string, then there is an
2681                     * additional level of compound nesting. We will create a compound type to hold the nested
2682                     * compound type.
2683                     */
2684                    insertedName = memberName.substring(0, sep);
2685
2686                    log.trace("createCompoundFieldType(): member with name {} is nested inside compound",
2687                              insertedName);
2688                }
2689
2690                /*
2691                 * Retrieve the index of the compound member by its name.
2692                 */
2693                int memberIndex = this.compoundMemberNames.indexOf(insertedName);
2694                if (memberIndex >= 0) {
2695                    H5Datatype memberType = (H5Datatype)this.compoundMemberTypes.get(memberIndex);
2696
2697                    log.trace("createCompoundFieldType(): Member {} is type {} of size={} with baseType={}",
2698                              insertedName, memberType.getDescription(), memberType.getDatatypeSize(),
2699                              memberType.getDatatypeBase());
2700
2701                    if (sep >= 0)
2702                        /*
2703                         * Additional compound nesting; create the nested compound type.
2704                         */
2705                        tmpTID1 = memberType.createCompoundFieldType(memberName.substring(sep + 1));
2706                    else
2707                        tmpTID1 = memberType.createNative();
2708
2709                    log.trace("createCompoundFieldType(): creating container compound datatype");
2710                    topTID = H5.H5Tcreate(HDF5Constants.H5T_COMPOUND, datatypeSize);
2711
2712                    log.trace("createCompoundFieldType(): inserting member {} into compound datatype",
2713                              insertedName);
2714                    H5.H5Tinsert(topTID, insertedName, 0, tmpTID1);
2715
2716                    /*
2717                     * WARNING!!! This step is crucial. Without it, the compound type created might be larger
2718                     * than the size of the single datatype field we are inserting. Performing a read with a
2719                     * compound datatype of an incorrect size will corrupt JVM memory and cause strange
2720                     * behavior and crashes.
2721                     */
2722                    H5.H5Tpack(topTID);
2723                }
2724                else {
2725                    log.debug(
2726                        "createCompoundFieldType(): member name {} not found in compound datatype's member name list",
2727                        memberName);
2728                }
2729            }
2730        }
2731        catch (Exception ex) {
2732            log.debug("createCompoundFieldType(): creation of compound field type failed: ", ex);
2733            topTID = HDF5Constants.H5I_INVALID_HID;
2734        }
2735        finally {
2736            close(tmpTID1);
2737        }
2738
2739        return topTID;
2740    }
2741
2742    private boolean datatypeIsComplex(long tid)
2743    {
2744        long tclass = HDF5Constants.H5T_NO_CLASS;
2745
2746        try {
2747            tclass = H5.H5Tget_class(tid);
2748            log.trace("datatypeIsComplex():{}", tclass);
2749        }
2750        catch (Exception ex) {
2751            log.debug("datatypeIsComplex():", ex);
2752        }
2753
2754        boolean retVal = (tclass == HDF5Constants.H5T_COMPOUND);
2755        retVal |= (tclass == HDF5Constants.H5T_ENUM);
2756        retVal |= (tclass == HDF5Constants.H5T_VLEN);
2757        retVal |= (tclass == HDF5Constants.H5T_ARRAY);
2758
2759        return retVal;
2760    }
2761
2762    private boolean datatypeIsReference(long tid)
2763    {
2764        long tclass = HDF5Constants.H5T_NO_CLASS;
2765
2766        try {
2767            tclass = H5.H5Tget_class(tid);
2768            log.trace("datatypeIsReference():{}", tclass);
2769        }
2770        catch (Exception ex) {
2771            log.debug("datatypeIsReference():", ex);
2772        }
2773
2774        return (tclass == HDF5Constants.H5T_REFERENCE);
2775    }
2776
2777    private boolean datatypeIsAtomic(long tid)
2778    {
2779        boolean retVal = !(datatypeIsComplex(tid) | datatypeIsReference(tid) | isRef());
2780        retVal |= isOpaque();
2781        retVal |= isBitField();
2782
2783        return retVal;
2784    }
2785
2786    private boolean datatypeClassIsComplex(long tclass)
2787    {
2788        boolean retVal = (tclass == HDF5Constants.H5T_COMPOUND);
2789        retVal |= (tclass == HDF5Constants.H5T_ENUM);
2790        retVal |= (tclass == HDF5Constants.H5T_VLEN);
2791        retVal |= (tclass == HDF5Constants.H5T_ARRAY);
2792
2793        return retVal;
2794    }
2795
2796    private boolean datatypeClassIsReference(long tclass) { return (tclass == HDF5Constants.H5T_REFERENCE); }
2797
2798    private boolean datatypeClassIsOpaque(long tclass) { return (tclass == Datatype.CLASS_OPAQUE); }
2799
2800    private boolean datatypeClassIsAtomic(long tclass)
2801    {
2802        boolean retVal = !(datatypeClassIsComplex(tclass) | datatypeClassIsReference(tclass));
2803        retVal |= (tclass == Datatype.CLASS_OPAQUE);
2804        retVal |= (tclass == Datatype.CLASS_BITFIELD);
2805
2806        return retVal;
2807    }
2808}