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