001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the COPYING file, which can be found  *
009 * at the root of the source code distribution tree,                         *
010 * or in https://www.hdfgroup.org/licenses.                                  *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.object;
016
017import java.util.ArrayList;
018import java.util.HashMap;
019import java.util.Iterator;
020import java.util.List;
021import java.util.Map;
022import java.util.Map.Entry;
023
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027/**
028 * Datatype is an abstract class that defines datatype characteristics and APIs for a data type.
029 *
030 * A datatype has four basic characteristics: class, size, byte order and sign. These characteristics are defined in the
031 * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
032 *
033 * These characteristics apply to all the sub-classes. The sub-classes may have different ways to describe a datatype.
034 * We here define the <strong> native datatype</strong> to the datatype used by the sub-class. For example, H5Datatype
035 * uses a datatype identifier (hid_t) to specify a datatype. NC2Datatype uses ucar.nc2.DataType object to describe its
036 * datatype. "Native" here is different from the "native" definition in the HDF5 library.
037 *
038 * Two functions, createNative() and fromNative(), are defined to convert the general characteristics to/from the native
039 * datatype. Sub-classes must implement these functions so that the conversion will be done correctly. The values of the
040 * CLASS member are not identical to HDF5 values for a datatype class.
041 *
042 * @version 1.1 9/4/2007
043 * @author Peter X. Cao
044 */
045public abstract class Datatype extends HObject implements MetaDataContainer
046{
047    private static final long serialVersionUID = -581324710549963177L;
048
049    private static final Logger log = LoggerFactory.getLogger(Datatype.class);
050
051    /**
052     * The default definition for datatype size, order, and sign.
053     */
054    public static final int NATIVE = -1;
055
056    /**
057     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
058     */
059    public static final int CLASS_NO_CLASS = -1;
060
061    /**
062     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
063     */
064    public static final int CLASS_INTEGER = 0;
065
066    /**
067     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
068     */
069    public static final int CLASS_FLOAT = 1;
070
071    /**
072     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
073     */
074    public static final int CLASS_CHAR = 2;
075
076    /**
077     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
078     */
079    public static final int CLASS_STRING = 3;
080
081    /**
082     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
083     */
084    public static final int CLASS_BITFIELD = 4;
085
086    /**
087     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
088     */
089    public static final int CLASS_OPAQUE = 5;
090
091    /**
092     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
093     */
094    public static final int CLASS_COMPOUND = 6;
095
096    /**
097     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
098     */
099    public static final int CLASS_REFERENCE = 7;
100
101    /**
102     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
103     */
104    public static final int CLASS_ENUM = 8;
105
106    /**
107     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
108     */
109    public static final int CLASS_VLEN = 9;
110
111    /**
112     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
113     */
114    public static final int CLASS_ARRAY = 10;
115
116    /**
117     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
118     */
119    public static final int CLASS_TIME = 11;
120
121    /**
122     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
123     */
124    public static final int ORDER_LE = 0;
125
126    /**
127     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
128     */
129    public static final int ORDER_BE = 1;
130
131    /**
132     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
133     */
134    public static final int ORDER_VAX = 2;
135
136    /**
137     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
138     */
139    public static final int ORDER_NONE = 3;
140
141    /**
142     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
143     */
144    public static final int SIGN_NONE = 0;
145
146    /**
147     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
148     */
149    public static final int SIGN_2 = 1;
150
151    /**
152     * See <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User Guide</a>
153     */
154    public static final int NSGN = 2;
155
156    /**
157     * The description of the datatype.
158     */
159    protected String datatypeDescription = null;
160
161    /**
162     * The description of the datatype.
163     */
164    protected boolean datatypeNATIVE = false;
165
166    /**
167     * The class of the datatype.
168     */
169    protected int datatypeClass;
170
171    /**
172     * The size (in bytes) of the datatype.
173     */
174    protected long datatypeSize;
175
176    /**
177     * The byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, and
178     * ORDER_VAX.
179     */
180    protected int datatypeOrder;
181
182    /**
183     * The sign of the datatype.
184     */
185    protected int datatypeSign;
186
187    /**
188     * The base datatype of this datatype (null if this datatype is atomic).
189     */
190    protected Datatype baseType;
191
192    /**
193     * Determines whether this datatype is a named datatype
194     */
195    protected boolean isNamed = false;
196
197    /**
198     * The dimensions of the ARRAY element of an ARRAY datatype.
199     */
200    protected long[] arrayDims;
201
202    /**
203     * Determines whether this datatype is a variable-length type.
204     */
205    protected boolean isVLEN = false;
206
207    /**
208     * Determines whether this datatype is a variable-length string type.
209     */
210    protected boolean isVariableStr = false;
211
212    /**
213     * The (name, value) pairs of enum members.
214     */
215    protected Map<String, String> enumMembers;
216
217    /**
218     * The list of names of members of a compound Datatype.
219     */
220    protected List<String> compoundMemberNames;
221
222    /**
223     * The list of types of members of a compound Datatype.
224     */
225    protected List<Datatype> compoundMemberTypes;
226
227    /**
228     * The list of offsets of members of a compound Datatype.
229     */
230    protected List<Long> compoundMemberOffsets;
231
232    /**
233     * Constructs a named datatype with a given file, name and path.
234     *
235     * @param theFile
236     *            the HDF file.
237     * @param typeName
238     *            the name of the datatype, e.g "12-bit Integer".
239     * @param typePath
240     *            the full group path of the datatype, e.g. "/datatypes/".
241     */
242    public Datatype(FileFormat theFile, String typeName, String typePath) {
243        this(theFile, typeName, typePath, null);
244    }
245
246    /**
247     * @deprecated Not for public use in the future.<br>
248     *             Using {@link #Datatype(FileFormat, String, String)}
249     *
250     * @param theFile
251     *            the HDF file.
252     * @param typeName
253     *            the name of the datatype, e.g "12-bit Integer".
254     * @param typePath
255     *            the full group path of the datatype, e.g. "/datatypes/".
256     * @param oid
257     *            the oidof the datatype.
258     */
259    @Deprecated
260    public Datatype(FileFormat theFile, String typeName, String typePath, long[] oid) {
261        super(theFile, typeName, typePath, oid);
262    }
263
264    /**
265     * Constructs a Datatype with specified class, size, byte order and sign.
266     *
267     * The following is a list of a few examples of Datatype.
268     * <ol>
269     * <li>to create unsigned native integer<br>
270     * Datatype type = new Dataype(Datatype.CLASS_INTEGER, Datatype.NATIVE, Datatype.NATIVE, Datatype.SIGN_NONE);
271     * <li>to create 16-bit signed integer with big endian<br>
272     * Datatype type = new Dataype(Datatype.CLASS_INTEGER, 2, Datatype.ORDER_BE, Datatype.NATIVE);
273     * <li>to create native float<br>
274     * Datatype type = new Dataype(Datatype.CLASS_FLOAT, Datatype.NATIVE, Datatype.NATIVE, Datatype.NATIVE);
275     * <li>to create 64-bit double<br>
276     * Datatype type = new Dataype(Datatype.CLASS_FLOAT, 8, Datatype.NATIVE, Datatype.NATIVE);
277     * </ol>
278     *
279     * @param tclass
280     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
281     * @param tsize
282     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4.
283     *            Valid values are NATIVE or a positive value.
284     * @param torder
285     *            the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX,
286     *            ORDER_NONE and NATIVE.
287     * @param tsign
288     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and NATIVE.
289     *
290     * @throws Exception
291     *            if there is an error
292     */
293    public Datatype(int tclass, int tsize, int torder, int tsign) throws Exception {
294        this(tclass, tsize, torder, tsign, null);
295    }
296
297    /**
298     * Constructs a Datatype with specified class, size, byte order and sign.
299     *
300     * The following is a list of a few examples of Datatype.
301     * <ol>
302     * <li>to create unsigned native integer<br>
303     * Datatype type = new Dataype(Datatype.CLASS_INTEGER, Datatype.NATIVE, Datatype.NATIVE, Datatype.SIGN_NONE);
304     * <li>to create 16-bit signed integer with big endian<br>
305     * Datatype type = new Dataype(Datatype.CLASS_INTEGER, 2, Datatype.ORDER_BE, Datatype.NATIVE);
306     * <li>to create native float<br>
307     * Datatype type = new Dataype(Datatype.CLASS_FLOAT, Datatype.NATIVE, Datatype.NATIVE, Datatype.NATIVE);
308     * <li>to create 64-bit double<br>
309     * Datatype type = new Dataype(Datatype.CLASS_FLOAT, 8, Datatype.NATIVE, Datatype.NATIVE);
310     * </ol>
311     *
312     * @param tclass
313     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and
314     *            etc.
315     * @param tsize
316     *            the size of the datatype in bytes, e.g. for a 32-bit integer,
317     *            the size is 4.
318     *            Valid values are NATIVE or a positive value.
319     * @param torder
320     *            the byte order of the datatype. Valid values are ORDER_LE,
321     *            ORDER_BE, ORDER_VAX, ORDER_NONE and NATIVE.
322     * @param tsign
323     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and NATIVE.
324     * @param tbase
325     *            the base datatype of the new datatype
326     *
327     * @throws Exception
328     *            if there is an error
329     */
330    public Datatype(int tclass, int tsize, int torder, int tsign, Datatype tbase) throws Exception {
331        this(null, tclass, tsize, torder, tsign, tbase, null);
332    }
333
334    /**
335     * Constructs a Datatype with specified class, size, byte order and sign.
336     *
337     * The following is a list of a few examples of Datatype.
338     * <ol>
339     * <li>to create unsigned native integer<br>
340     * Datatype type = new Dataype(Datatype.CLASS_INTEGER, Datatype.NATIVE, Datatype.NATIVE, Datatype.SIGN_NONE);
341     * <li>to create 16-bit signed integer with big endian<br>
342     * Datatype type = new Dataype(Datatype.CLASS_INTEGER, 2, Datatype.ORDER_BE, Datatype.NATIVE);
343     * <li>to create native float<br>
344     * Datatype type = new Dataype(Datatype.CLASS_FLOAT, Datatype.NATIVE, Datatype.NATIVE, Datatype.NATIVE);
345     * <li>to create 64-bit double<br>
346     * Datatype type = new Dataype(Datatype.CLASS_FLOAT, 8, Datatype.NATIVE, Datatype.NATIVE);
347     * </ol>
348     *
349     * @param theFile
350     *            the HDF file.
351     * @param tclass
352     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
353     * @param tsize
354     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4.
355     *            Valid values are NATIVE or a positive value.
356     * @param torder
357     *            the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX,
358     *            ORDER_NONE and NATIVE.
359     * @param tsign
360     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and NATIVE.
361     * @param tbase
362     *            the base datatype of the new datatype
363     * @param pbase
364     *            the parent datatype of the new datatype
365     *
366     * @throws Exception
367     *            if there is an error
368     */
369    public Datatype(FileFormat theFile, int tclass, int tsize, int torder, int tsign, Datatype tbase, Datatype pbase) throws Exception {
370        super(theFile, null, null, null);
371        if ((tsize == 0) || (tsize < 0 && tsize != Datatype.NATIVE))
372            throw new Exception("invalid datatype size - " + tsize);
373        if ((torder != Datatype.ORDER_LE) && (torder != Datatype.ORDER_BE) && (torder != Datatype.ORDER_VAX)
374                && (torder != Datatype.ORDER_NONE) && (torder != Datatype.NATIVE))
375            throw new Exception("invalid datatype order - " + torder);
376        if ((tsign != Datatype.SIGN_NONE) && (tsign != Datatype.SIGN_2) && (tsign != Datatype.NATIVE))
377            throw new Exception("invalid datatype sign - " + tsign);
378
379        datatypeClass = tclass;
380        datatypeSize = tsize;
381        if (datatypeSize == NATIVE)
382            datatypeNATIVE = true;
383        else
384            datatypeNATIVE = false;
385        datatypeOrder = torder;
386        datatypeSign = tsign;
387        enumMembers = null;
388        baseType = tbase;
389        arrayDims = null;
390        isVariableStr = (datatypeClass == Datatype.CLASS_STRING) && (tsize < 0);
391        isVLEN = (datatypeClass == Datatype.CLASS_VLEN) || isVariableStr;
392
393        compoundMemberNames = new ArrayList<>();
394        compoundMemberTypes = new ArrayList<>();
395        compoundMemberOffsets = new ArrayList<>();
396
397        log.trace("datatypeClass={} datatypeSize={} datatypeOrder={} datatypeSign={} baseType={}",
398                datatypeClass, datatypeSize, datatypeOrder, datatypeSign, baseType);
399    }
400
401    /**
402     * Constructs a Datatype with specified class, size, byte order and sign.
403     *
404     * The following is a list of a few examples of Datatype.
405     * <ol>
406     * <li>to create unsigned native integer<br>
407     * Datatype type = new Dataype(Datatype.CLASS_INTEGER, Datatype.NATIVE, Datatype.NATIVE, Datatype.SIGN_NONE);
408     * <li>to create 16-bit signed integer with big endian<br>
409     * Datatype type = new Dataype(Datatype.CLASS_INTEGER, 2, Datatype.ORDER_BE, Datatype.NATIVE);
410     * <li>to create native float<br>
411     * Datatype type = new Dataype(Datatype.CLASS_FLOAT, Datatype.NATIVE, Datatype.NATIVE, Datatype.NATIVE);
412     * <li>to create 64-bit double<br>
413     * Datatype type = new Dataype(Datatype.CLASS_FLOAT, 8, Datatype.NATIVE, Datatype.NATIVE);
414     * </ol>
415     *
416     * @param tclass
417     *            the class of the datatype, e.g. CLASS_INTEGER, CLASS_FLOAT and etc.
418     * @param tsize
419     *            the size of the datatype in bytes, e.g. for a 32-bit integer, the size is 4.
420     *            Valid values are NATIVE or a positive value.
421     * @param torder
422     *            the byte order of the datatype. Valid values are ORDER_LE, ORDER_BE, ORDER_VAX,
423     *            ORDER_NONE and NATIVE.
424     * @param tsign
425     *            the sign of the datatype. Valid values are SIGN_NONE, SIGN_2 and NATIVE.
426     * @param tbase
427     *            the base datatype of the new datatype
428     * @param pbase
429     *            the parent datatype of the new datatype
430     *
431     * @throws Exception
432     *            if there is an error
433     */
434    public Datatype(int tclass, int tsize, int torder, int tsign, Datatype tbase, Datatype pbase) throws Exception {
435        this(null, tclass, tsize, torder, tsign, tbase, pbase);
436    }
437
438    /**
439     * Constructs a Datatype with a given native datatype identifier.
440     *
441     * For example, if the datatype identifier is a 32-bit unsigned integer created from HDF5,
442     *
443     * <pre>
444     * long tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_UNINT32);
445     * Datatype dtype = new Datatype(tid);
446     * </pre>
447     *
448     * will construct a datatype equivalent to new Datatype(CLASS_INTEGER, 4, NATIVE, SIGN_NONE);
449     *
450     * @see #fromNative(long tid)
451     * @param theFile
452     *            the HDF file.
453     * @param tid
454     *            the native datatype identifier.
455     *
456     * @throws Exception
457     *            if there is an error
458     */
459    public Datatype(FileFormat theFile, long tid) throws Exception {
460        this(theFile, tid, null);
461    }
462
463    /**
464     * Constructs a Datatype with a given native datatype identifier.
465     *
466     * For example, if the datatype identifier is a 32-bit unsigned integer created from HDF5,
467     *
468     * <pre>
469     * long tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_UNINT32);
470     * Datatype dtype = new Datatype(tid);
471     * </pre>
472     *
473     * will construct a datatype equivalent to new Datatype(CLASS_INTEGER, 4, NATIVE, SIGN_NONE);
474     *
475     * @see #fromNative(long tid)
476     * @param theFile
477     *            the HDF file.
478     * @param tid
479     *            the native datatype identifier.
480     * @param pbase
481     *            the parent datatype of the new datatype
482     *
483     * @throws Exception
484     *            if there is an error
485     */
486    public Datatype(FileFormat theFile, long tid, Datatype pbase) throws Exception {
487        this(theFile, CLASS_NO_CLASS, NATIVE, NATIVE, NATIVE, null, pbase);
488    }
489
490    /**
491     * Opens access to this named datatype. Sub-classes must replace this default implementation. For
492     * example, in H5Datatype, open() function H5.H5Topen(loc_id, name) to get the datatype identifier.
493     *
494     * @return the datatype identifier if successful; otherwise returns negative value.
495     */
496    @Override
497    public long open() {
498        return -1;
499    }
500
501    /**
502     * Closes a datatype identifier.
503     *
504     * Sub-classes must replace this default implementation.
505     *
506     * @param id
507     *            the datatype identifier to close.
508     */
509    @Override
510    public abstract void close(long id);
511
512    /**
513     * Returns the class of the datatype. Valid values are:
514     * <ul>
515     * <li>CLASS_NO_CLASS
516     * <li>CLASS_INTEGER
517     * <li>CLASS_FLOAT
518     * <li>CLASS_CHAR
519     * <li>CLASS_STRING
520     * <li>CLASS_BITFIELD
521     * <li>CLASS_OPAQUE
522     * <li>CLASS_COMPOUND
523     * <li>CLASS_REFERENCE
524     * <li>CLASS_ENUM
525     * <li>CLASS_VLEN
526     * <li>CLASS_ARRAY
527     * </ul>
528     *
529     * @return the class of the datatype.
530     */
531    public int getDatatypeClass() {
532        return datatypeClass;
533    }
534
535    /**
536     * Returns the size of the datatype in bytes. For example, for a 32-bit
537     * integer, the size is 4 (bytes).
538     *
539     * @return the size of the datatype.
540     */
541    public long getDatatypeSize() {
542        return datatypeSize;
543    }
544
545    /**
546     * Returns the byte order of the datatype. Valid values are
547     * <ul>
548     * <li>ORDER_LE
549     * <li>ORDER_BE
550     * <li>ORDER_VAX
551     * <li>ORDER_NONE
552     * </ul>
553     *
554     * @return the byte order of the datatype.
555     */
556    public int getDatatypeOrder() {
557        return datatypeOrder;
558    }
559
560    /**
561     * Returns the sign (SIGN_NONE, SIGN_2) of an integer datatype.
562     *
563     * @return the sign of the datatype.
564     */
565    public int getDatatypeSign() {
566        return datatypeSign;
567    }
568
569    /**
570     * Returns the base datatype for this datatype.
571     *
572     * For example, in a dataset of type ARRAY of integer, the datatype of the dataset is ARRAY. The
573     * datatype of the base type is integer.
574     *
575     * @return the datatype of the contained basetype.
576     */
577    public Datatype getDatatypeBase() {
578        return baseType;
579    }
580
581    /**
582     * Sets the (key, value) pairs of enum members for enum datatype.
583     *
584     * For Example,
585     * <dl>
586     * <dt>setEnumMembers("-40=lowTemp, 90=highTemp")</dt>
587     * <dd>sets the key of enum member lowTemp to -40 and highTemp to 90.</dd>
588     * <dt>setEnumMembers("lowTemp, highTemp")</dt>
589     * <dd>sets enum members to defaults, i.e. 0=lowTemp and 1=highTemp</dd>
590     * <dt>setEnumMembers("10=lowTemp, highTemp")</dt>
591     * <dd>sets enum member lowTemp to 10 and highTemp to 11.</dd>
592     * </dl>
593     *
594     * @param enumStr
595     *            the (key, value) pairs of enum members
596     */
597    public final void setEnumMembers(String enumStr) {
598        log.trace("setEnumMembers: start enum_members={}", enumStr);
599        if (enumStr != null) {
600            enumMembers = new HashMap<>();
601            String[] entries = enumStr.split(",");
602            for (String entry : entries) {
603                String[] keyValue = entry.split("=");
604                enumMembers.put(keyValue[0].trim(), keyValue[1].trim());
605                if (log.isTraceEnabled())
606                    log.trace("setEnumMembers: value={} name={}", keyValue[0].trim(), keyValue[1].trim());
607            }
608        }
609        datatypeDescription = null; //reset description
610        log.trace("setEnumMembers: finish enum size={}", enumMembers.size());
611    }
612
613    /**
614     * Returns the Map&lt;String,String&gt; pairs of enum members for enum datatype.
615     *
616     * @return enumStr Map&lt;String,String%gt; pairs of enum members
617     */
618    public final Map<String, String> getEnumMembers() {
619        if (enumMembers == null) {
620            log.trace("getEnumMembers: null");
621            enumMembers = new HashMap<>();
622        }
623
624        return enumMembers;
625    }
626
627    /**
628     * Returns the HashMap pairs of enum members for enum datatype.
629     *
630     * For Example,
631     * <dl>
632     * <dt>getEnumMembersAsString()</dt>
633     * <dd>returns "10=lowTemp, 40=highTemp"</dd>
634     * </dl>
635     *
636     * @return enumStr the (key, value) pairs of enum members
637     */
638    @SuppressWarnings("rawtypes")
639    public final String getEnumMembersAsString() {
640        StringBuilder enumStr = new StringBuilder();
641        if (getEnumMembers() != null) {
642            Iterator<Entry<String, String>> entries = enumMembers.entrySet().iterator();
643            int i = enumMembers.size();
644            log.trace("getEnumMembersAsString: enum size={}", i);
645            while (entries.hasNext()) {
646                Entry thisEntry = entries.next();
647                enumStr.append((String) thisEntry.getKey())
648                       .append("=")
649                       .append((String) thisEntry.getValue());
650
651                i--;
652                if (i > 0)
653                    enumStr.append(", ");
654            }
655        }
656        log.trace("getEnumMembersAsString: finish {}", enumStr);
657        return enumStr.toString();
658    }
659
660    /**
661     * Returns the dimensions of an Array Datatype.
662     *
663     * @return dims the dimensions of the Array Datatype
664     */
665    public final long[] getArrayDims() {
666        return arrayDims;
667    }
668
669
670    /**
671     * Returns the member names of a Compound Datatype.
672     *
673     * @return member names of a Compound Datatype
674     */
675    public final List<String> getCompoundMemberNames() {
676        return compoundMemberNames;
677    }
678
679
680    /**
681     * Returns member types of a Compound Datatype.
682     *
683     * @return member types of a Compound Datatype
684     */
685    public final List<Datatype> getCompoundMemberTypes() {
686        return compoundMemberTypes;
687    }
688
689
690    /**
691     * Returns the member offsets of a Compound Datatype.
692     *
693     * @return member offsets of a Compound Datatype
694     */
695    public final List<Long> getCompoundMemberOffsets() {
696        return compoundMemberOffsets;
697    }
698
699    /**
700     * Converts the datatype object to a native datatype.
701     *
702     * Subclasses must implement it so that this datatype will be converted accordingly. Use close() to
703     * close the native identifier; otherwise, the datatype will be left open.
704     *
705     * For example, a HDF5 datatype created from<br>
706     *
707     * <pre>
708     * H5Dataype dtype = new H5Datatype(CLASS_INTEGER, 4, NATIVE, SIGN_NONE);
709     * int tid = dtype.createNative();
710     * </pre>
711     *
712     * The "tid" will be the HDF5 datatype id of a 64-bit unsigned integer, which is equivalent to
713     *
714     * <pre>
715     * int tid = H5.H5Tcopy(HDF5Constants.H5T_NATIVE_UNINT32);
716     * </pre>
717     *
718     * @return the identifier of the native datatype.
719     */
720    public abstract long createNative();
721
722    /**
723     * Set datatype characteristics (class, size, byte order and sign) from a given datatype identifier.
724     *
725     * Sub-classes must implement it so that this datatype will be converted accordingly.
726     *
727     * For example, if the type identifier is a 64-bit unsigned integer created from HDF5,
728     *
729     * <pre>
730     * H5Datatype dtype = new H5Datatype();
731     * dtype.fromNative(HDF5Constants.H5T_NATIVE_UNINT32);
732     * </pre>
733     *
734     * Where dtype is equivalent to <br>
735     * new H5Datatype(CLASS_INTEGER, 4, NATIVE, SIGN_NONE);
736     *
737     * @param nativeID
738     *            the datatype identifier.
739     */
740    public abstract void fromNative(long nativeID);
741
742    /**
743     * If the datatype is a reference, then return the type.
744     *
745     * @return the datatype reference type if successful; otherwise returns negative value.
746     */
747    public long getReferenceType() {
748        return -1;
749    }
750
751    /**
752     * Returns a short text description of this datatype.
753     *
754     * @return a short text description of this datatype
755     */
756    public String getDescription() {
757        if (datatypeDescription != null)
758            return datatypeDescription;
759
760        StringBuilder description = new StringBuilder();
761
762        switch (datatypeClass) {
763        case CLASS_CHAR:
764            description.append("8-bit ").append((isUnsigned() ? "unsigned " : "")).append("integer");
765            break;
766        case CLASS_INTEGER:
767            log.trace("getDescription(): Int [{}]", datatypeNATIVE);
768            if (datatypeNATIVE)
769                description.append("native ").append((isUnsigned() ? "unsigned " : "")).append("integer");
770            else
771                description.append(String.valueOf(datatypeSize * 8)).append("-bit ")
772                .append((isUnsigned() ? "unsigned " : "")).append("integer");
773            break;
774        case CLASS_FLOAT:
775            if (datatypeNATIVE)
776                description.append("native floating-point");
777            else
778                description.append(String.valueOf(datatypeSize * 8)).append("-bit floating-point");
779            break;
780        case CLASS_STRING:
781            description.append("String");
782            break;
783        case CLASS_REFERENCE:
784            description.append("Object reference");
785            break;
786        case CLASS_OPAQUE:
787            if (datatypeNATIVE)
788                description.append("native opaque");
789            else
790                description.append(String.valueOf(datatypeSize * 8)).append("-bit opaque");
791            break;
792        case CLASS_BITFIELD:
793            if (datatypeNATIVE)
794                description.append("native bitfield");
795            else
796                description.append(String.valueOf(datatypeSize * 8)).append("-bit bitfield");
797            break;
798        case CLASS_ENUM:
799            if (datatypeNATIVE)
800                description.append("native enum");
801            else
802                description.append(String.valueOf(datatypeSize * 8)).append("-bit enum");
803            break;
804        case CLASS_ARRAY:
805            description.append("Array");
806
807            if (arrayDims != null) {
808                description.append(" [");
809                for (int i = 0; i < arrayDims.length; i++) {
810                    description.append(arrayDims[i]);
811                    if (i < arrayDims.length - 1)
812                        description.append(" x ");
813                }
814                description.append("]");
815            }
816
817            break;
818        case CLASS_COMPOUND:
819            description.append("Compound");
820            break;
821        case CLASS_VLEN:
822            description.append("Variable-length");
823            break;
824        default:
825            description.append("Unknown");
826            break;
827        }
828
829        if (baseType != null)
830            description.append(" of " + baseType.getDescription());
831
832        return description.toString();
833    }
834
835    /**
836     * Checks if this datatype is unsigned.
837     *
838     * @return true if the datatype is unsigned;
839     *         otherwise, returns false.
840     */
841    public boolean isUnsigned() {
842        if (baseType != null)
843            return baseType.isUnsigned();
844        else {
845            if (isCompound()) {
846                if ((compoundMemberTypes != null) && !compoundMemberTypes.isEmpty()) {
847                    boolean allMembersUnsigned = true;
848
849                    Iterator<Datatype> cmpdTypeListIT = compoundMemberTypes.iterator();
850                    while (cmpdTypeListIT.hasNext()) {
851                        Datatype next = cmpdTypeListIT.next();
852
853                        allMembersUnsigned = allMembersUnsigned && next.isUnsigned();
854                    }
855
856                    return allMembersUnsigned;
857                }
858                else {
859                    log.debug("isUnsigned(): compoundMemberTypes is null");
860                    return false;
861                }
862            }
863            else {
864                return (datatypeSign == Datatype.SIGN_NONE);
865            }
866        }
867    }
868
869    /**
870     * Checks if this datatype is a boolean type.
871     *
872     * @return true if the datatype is boolean; false otherwise
873     */
874    public abstract boolean isText();
875
876    /**
877     * Checks if this datatype is an integer type.
878     *
879     * @return true if the datatype is integer; false otherwise
880     */
881    public boolean isInteger() {
882        return (datatypeClass == Datatype.CLASS_INTEGER);
883    }
884
885    /**
886     * Checks if this datatype is a floating-point type.
887     *
888     * @return true if the datatype is floating-point; false otherwise
889     */
890    public boolean isFloat() {
891        return (datatypeClass == Datatype.CLASS_FLOAT);
892    }
893
894    /**
895     * Checks if this datatype is a named type.
896     *
897     * @return true if the datatype is named; false otherwise
898     */
899    public boolean isNamed() {
900        return isNamed;
901    }
902
903    /**
904     * Checks if this datatype is a variable-length string type.
905     *
906     * @return true if the datatype is variable-length string; false otherwise
907     */
908    public boolean isVarStr() {
909        return isVariableStr;
910    }
911
912    /**
913     * Checks if this datatype is a variable-length type.
914     *
915     * @return true if the datatype is variable-length; false otherwise
916     */
917    public boolean isVLEN() {
918        return isVLEN;
919    }
920
921    /**
922     * Checks if this datatype is an compound type.
923     *
924     * @return true if the datatype is compound; false otherwise
925     */
926    public boolean isCompound() {
927        return (datatypeClass == Datatype.CLASS_COMPOUND);
928    }
929
930    /**
931     * Checks if this datatype is an array type.
932     *
933     * @return true if the datatype is array; false otherwise
934     */
935    public boolean isArray() {
936        return (datatypeClass == Datatype.CLASS_ARRAY);
937    }
938
939    /**
940     * Checks if this datatype is a string type.
941     *
942     * @return true if the datatype is string; false otherwise
943     */
944    public boolean isString() {
945        return (datatypeClass == Datatype.CLASS_STRING);
946    }
947
948    /**
949     * Checks if this datatype is a character type.
950     *
951     * @return true if the datatype is character; false otherwise
952     */
953    public boolean isChar() {
954        return (datatypeClass == Datatype.CLASS_CHAR);
955    }
956
957    /**
958     * Checks if this datatype is a reference type.
959     *
960     * @return true if the datatype is reference; false otherwise
961     */
962    public boolean isRef() {
963        return (datatypeClass == Datatype.CLASS_REFERENCE);
964    }
965
966    /**
967     * Checks if this datatype is a enum type.
968     *
969     * @return true if the datatype is enum; false otherwise
970     */
971    public boolean isEnum() {
972        return (datatypeClass == Datatype.CLASS_ENUM);
973    }
974
975    /**
976     * Checks if this datatype is a opaque type.
977     *
978     * @return true if the datatype is opaque; false otherwise
979     */
980    public boolean isOpaque() {
981        return (datatypeClass == Datatype.CLASS_OPAQUE);
982    }
983
984    /**
985     * Checks if this datatype is a bitfield type.
986     *
987     * @return true if the datatype is bitfield; false otherwise
988     */
989    public boolean isBitField() {
990        return (datatypeClass == Datatype.CLASS_BITFIELD);
991    }
992
993    /* Implement interface MetaDataContainer */
994
995    /**
996     * Removes all of the elements from metadata list.
997     * The list should be empty after this call returns.
998     */
999    @Override
1000    public void clear() {
1001    }
1002
1003    /**
1004     * Retrieves the object's metadata, such as attributes, from the file.
1005     *
1006     * Metadata, such as attributes, is stored in a List.
1007     *
1008     * @return the list of metadata objects.
1009     *
1010     * @throws Exception
1011     *             if the metadata can not be retrieved
1012     */
1013    @Override
1014    @SuppressWarnings("rawtypes")
1015    public List getMetadata() throws Exception {
1016        return null;
1017    }
1018
1019    /**
1020     * Writes a specific piece of metadata (such as an attribute) into the file.
1021     *
1022     * If an HDF(4&amp;5) attribute exists in the file, this method updates its
1023     * value. If the attribute does not exist in the file, it creates the
1024     * attribute in the file and attaches it to the object. It will fail to
1025     * write a new attribute to the object where an attribute with the same name
1026     * already exists. To update the value of an existing attribute in the file,
1027     * one needs to get the instance of the attribute by getMetadata(), change
1028     * its values, then use writeMetadata() to write the value.
1029     *
1030     * @param info
1031     *            the metadata to write.
1032     *
1033     * @throws Exception
1034     *             if the metadata can not be written
1035     */
1036    @Override
1037    public void writeMetadata(Object info) throws Exception {
1038        throw new UnsupportedOperationException("Unsupported operation. Subclasses must implement Datatype:writeMetadata.");
1039    }
1040
1041    /**
1042     * Deletes an existing piece of metadata from this object.
1043     *
1044     * @param info
1045     *            the metadata to delete.
1046     *
1047     * @throws Exception
1048     *             if the metadata can not be removed
1049     */
1050    @Override
1051    public void removeMetadata(Object info) throws Exception {
1052        throw new UnsupportedOperationException("Unsupported operation. Subclasses must implement Datatype:removeMetadata.");
1053    }
1054
1055    /**
1056     * Updates an existing piece of metadata attached to this object.
1057     *
1058     * @param info
1059     *            the metadata to update.
1060     *
1061     * @throws Exception
1062     *             if the metadata can not be updated
1063     */
1064    @Override
1065    public void updateMetadata(Object info) throws Exception {
1066        throw new UnsupportedOperationException("Unsupported operation. Subclasses must implement Datatype:updateMetadata.");
1067    }
1068
1069    @Override
1070    public String toString() {
1071        return getDescription();
1072    }
1073}