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