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