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