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