    001/*****************************************************************************
    002 * Copyright by The HDF Group.                                               *
    003 * Copyright by the Board of Trustees of the University of Illinois.         *
    004 * All rights reserved.                                                      *
    005 *                                                                           *
    006 * This file is part of the HDF Java Products distribution.                  *
    007 * The full copyright notice, including terms governing use, modification,   *
    008 * and redistribution, is contained in the files COPYING and Copyright.html. *
    009 * COPYING can be found at the root of the source code distribution tree.    *
    010 * Or, see https://support.hdfgroup.org/products/licenses.html               *
    011 * If you do not have access to either file, you may request a copy from     *
    012 * help@hdfgroup.org.                                                        *
    013 ****************************************************************************/
    014
    015package hdf.object.h5;
    016
    017import java.lang.reflect.Array;
    018import java.text.DecimalFormat;
    019import java.util.ArrayList;
    020import java.util.Arrays;
    021import java.util.Iterator;
    022import java.util.List;
    023import java.util.Vector;
    024
    025import hdf.hdf5lib.H5;
    026import hdf.hdf5lib.HDF5Constants;
    027import hdf.hdf5lib.HDFNativeData;
    028import hdf.hdf5lib.exceptions.HDF5DataFiltersException;
    029import hdf.hdf5lib.exceptions.HDF5Exception;
    030import hdf.hdf5lib.structs.H5O_info_t;
    031import hdf.object.Attribute;
    032import hdf.object.CompoundDS;
    033import hdf.object.Dataset;
    034import hdf.object.Datatype;
    035import hdf.object.FileFormat;
    036import hdf.object.Group;
    037import hdf.object.HObject;
    038import hdf.object.Utils;
    039
    040/**
    041 * The H5CompoundDS class defines an HDF5 dataset of compound datatypes.
    042 * <p>
    043 * An HDF5 dataset is an object composed of a collection of data elements, or raw data, and metadata
    044 * that stores a description of the data elements, data layout, and all other information necessary
    045 * to write, read, and interpret the stored data.
    046 * <p>
    047 * A HDF5 compound datatype is similar to a struct in C or a common block in Fortran: it is a
    048 * collection of one or more atomic types or small arrays of such types. Each member of a compound
    049 * type has a name which is unique within that type, and a byte offset that determines the first
    050 * byte (smallest byte address) of that member in a compound datum.
    051 * <p>
    052 * For more information on HDF5 datasets and datatypes, read the <a href=
    053 * "https://support.hdfgroup.org/HDF5/doc/UG/HDF5_Users_Guide-Responsive%20HTML5/index.html">HDF5
    054 * User's Guide</a>.
    055 * <p>
    056 * There are two basic types of compound datasets: simple compound data and nested compound data.
    057 * Members of a simple compound dataset have atomic datatypes. Members of a nested compound dataset
    058 * are compound or array of compound data.
    059 * <p>
    060 * Since Java does not understand C structures, we cannot directly read/write compound data values
    061 * as in the following C example.
    062 *
    063 * <pre>
    064 * typedef struct s1_t {
    065 *         int    a;
    066 *         float  b;
    067 *         double c;
    068 *         } s1_t;
    069 *     s1_t       s1[LENGTH];
    070 *     ...
    071 *     H5Dwrite(..., s1);
    072 *     H5Dread(..., s1);
    073 * </pre>
    074 *
    075 * Values of compound data fields are stored in java.util.Vector object. We read and write compound
    076 * data by fields instead of compound structure. As for the example above, the java.util.Vector
    077 * object has three elements: int[LENGTH], float[LENGTH] and double[LENGTH]. Since Java understands
    078 * the primitive datatypes of int, float and double, we will be able to read/write the compound data
    079 * by field.
    080 *
    081 * @version 1.1 9/4/2007
    082 * @author Peter X. Cao
    083 */
    084public class H5CompoundDS extends CompoundDS {
    085    private static final long serialVersionUID = -5968625125574032736L;
    086
    087    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(H5CompoundDS.class);
    088
    089    /**
    090     * The list of attributes of this data object. Members of the list are instance of Attribute.
    091     */
    092    private List<Attribute> attributeList;
    093
    094    private int nAttributes = -1;
    095
    096    private H5O_info_t objInfo;
    097
    098    /**
    099     * A list of names of all fields including nested fields.
    100     * <p>
    101     * The nested names are separated by CompoundDS.SEPARATOR. For example, if compound dataset "A" has
    102     * the following nested structure,
    103     *
    104     * <pre>
    105     * A --&gt; m01
    106     * A --&gt; m02
    107     * A --&gt; nest1 --&gt; m11
    108     * A --&gt; nest1 --&gt; m12
    109     * A --&gt; nest1 --&gt; nest2 --&gt; m21
    110     * A --&gt; nest1 --&gt; nest2 --&gt; m22
    111     * i.e.
    112     * A = { m01, m02, nest1{m11, m12, nest2{ m21, m22}}}
    113     * </pre>
    114     *
    115     * The flatNameList of compound dataset "A" will be {m01, m02, nest1[m11, nest1[m12,
    116     * nest1[nest2[m21, nest1[nest2[m22}
    117     *
    118     */
    119    private List<String> flatNameList;
    120
    121    /**
    122     * A list of datatypes of all fields including nested fields.
    123     */
    124    private List<Datatype> flatTypeList;
    125
    126    /** flag to indicate if the dataset is an external dataset */
    127    private boolean isExternal = false;
    128
    129    /** flag to indicate if the dataset is a virtual dataset */
    130    private boolean isVirtual = false;
    131    private List<String> virtualNameList;
    132
    133    /*
    134     * Enum to indicate the type of I/O to perform inside of the common I/O
    135     * function.
    136     */
    137    protected static enum IO_TYPE {
    138        READ, WRITE
    139    };
    140
    141    /**
    142     * Constructs an instance of a HDF5 compound dataset with given file, dataset name and path.
    143     * <p>
    144     * The dataset object represents an existing dataset in the file. For example, new
    145     * H5CompoundDS(file, "dset1", "/g0/") constructs a dataset object that corresponds to the
    146     * dataset,"dset1", at group "/g0/".
    147     * <p>
    148     * This object is usually constructed at FileFormat.open(), which loads the file structure and
    149     * object information into memory. It is rarely used elsewhere.
    150     *
    151     * @param theFile
    152     *            the file that contains the data object.
    153     * @param theName
    154     *            the name of the data object, e.g. "dset".
    155     * @param thePath
    156     *            the full path of the data object, e.g. "/arrays/".
    157     */
    158    public H5CompoundDS(FileFormat theFile, String theName, String thePath) {
    159        this(theFile, theName, thePath, null);
    160    }
    161
    162    /**
    163     * @deprecated Not for public use in the future.<br>
    164     *             Using {@link #H5CompoundDS(FileFormat, String, String)}
    165     *
    166     * @param theFile
    167     *            the file that contains the data object.
    168     * @param theName
    169     *            the name of the data object, e.g. "dset".
    170     * @param thePath
    171     *            the full path of the data object, e.g. "/arrays/".
    172     * @param oid
    173     *            the oid of the data object.
    174     */
    175    @Deprecated
    176    public H5CompoundDS(FileFormat theFile, String theName, String thePath, long[] oid) {
    177        super(theFile, theName, thePath, oid);
    178        objInfo = new H5O_info_t(-1L, -1L, 0, 0, -1L, 0L, 0L, 0L, 0L, null, null, null);
    179
    180        if ((oid == null) && (theFile != null)) {
    181            // retrieve the object ID
    182            try {
    183                byte[] refBuf = H5.H5Rcreate(theFile.getFID(), this.getFullName(), HDF5Constants.H5R_OBJECT, -1);
    184                this.oid = new long[1];
    185                this.oid[0] = HDFNativeData.byteToLong(refBuf, 0);
    186            }
    187            catch (Exception ex) {
    188                log.debug("constructor ID {} for {} failed H5Rcreate", theFile.getFID(), this.getFullName());
    189            }
    190        }
    191    }
    192
    193    /*
    194     * (non-Javadoc)
    195     *
    196     * @see hdf.object.HObject#open()
    197     */
    198    @Override
    199    public long open() {
    200        long did = -1;
    201
    202        try {
    203            did = H5.H5Dopen(getFID(), getPath() + getName(), HDF5Constants.H5P_DEFAULT);
    204            log.trace("open(): did={}", did);
    205        }
    206        catch (HDF5Exception ex) {
    207            log.debug("open(): Failed to open dataset {}: ", getPath() + getName(), ex);
    208            did = -1;
    209        }
    210
    211        return did;
    212    }
    213
    214    /*
    215     * (non-Javadoc)
    216     *
    217     * @see hdf.object.HObject#close(int)
    218     */
    219    @Override
    220    public void close(long did) {
    221        if (did >= 0) {
    222            try {
    223                H5.H5Fflush(did, HDF5Constants.H5F_SCOPE_LOCAL);
    224            }
    225            catch (Exception ex) {
    226                log.debug("close(): H5Fflush(did {}) failure: ", did, ex);
    227            }
    228            try {
    229                H5.H5Dclose(did);
    230            }
    231            catch (HDF5Exception ex) {
    232                log.debug("close(): H5Dclose(did {}) failure: ", did, ex);
    233            }
    234        }
    235    }
    236
    237    /**
    238     * Retrieves datatype and dataspace information from file and sets the dataset
    239     * in memory.
    240     * <p>
    241     * The init() is designed to support lazy operation in a dataset object. When a
    242     * data object is retrieved from file, the datatype, dataspace and raw data are
    243     * not loaded into memory. When it is asked to read the raw data from file,
    244     * init() is first called to get the datatype and dataspace information, then
    245     * load the raw data from file.
    246     * <p>
    247     * init() is also used to reset the selection of a dataset (start, stride and
    248     * count) to the default, which is the entire dataset for 1D or 2D datasets. In
    249     * the following example, init() at step 1) retrieves datatype and dataspace
    250     * information from file. getData() at step 3) reads only one data point. init()
    251     * at step 4) resets the selection to the whole dataset. getData() at step 4)
    252     * reads the values of whole dataset into memory.
    253     *
    254     * <pre>
    255     * dset = (Dataset) file.get(NAME_DATASET);
    256     *
    257     * // 1) get datatype and dataspace information from file
    258     * dset.init();
    259     * rank = dset.getRank(); // rank = 2, a 2D dataset
    260     * count = dset.getSelectedDims();
    261     * start = dset.getStartDims();
    262     * dims = dset.getDims();
    263     *
    264     * // 2) select only one data point
    265     * for (int i = 0; i &lt; rank; i++) {
    266     *     start[0] = 0;
    267     *     count[i] = 1;
    268     * }
    269     *
    270     * // 3) read one data point
    271     * data = dset.getData();
    272     *
    273     * // 4) reset selection to the whole dataset
    274     * dset.init();
    275     *
    276     * // 5) clean the memory data buffer
    277     * dset.clearData();
    278     *
    279     * // 6) Read the whole dataset
    280     * data = dset.getData();
    281     * </pre>
    282     */
    283    @Override
    284    public void init() {
    285        if (inited) {
    286            resetSelection();
    287            log.trace("init(): Dataset already initialized");
    288            return; // already called. Initialize only once
    289        }
    290
    291        long did = -1;
    292        long tid = -1;
    293        long sid = -1;
    294        flatNameList = new Vector<>();
    295        flatTypeList = new Vector<>();
    296
    297        did = open();
    298        if (did >= 0) {
    299            // check if it is an external or virtual dataset
    300            long pid = -1;
    301            try {
    302                pid = H5.H5Dget_create_plist(did);
    303                try {
    304                    int nfiles = H5.H5Pget_external_count(pid);
    305                    isExternal = (nfiles > 0);
    306                    int layoutType = H5.H5Pget_layout(pid);
    307                    if (isVirtual = (layoutType == HDF5Constants.H5D_VIRTUAL)) {
    308                        try {
    309                            long vmaps = H5.H5Pget_virtual_count(pid);
    310                            if (vmaps > 0) {
    311                                virtualNameList = new Vector<>();
    312                                for (long next = 0; next < vmaps; next++) {
    313                                    try {
    314                                        String fname = H5.H5Pget_virtual_filename(pid, next);
    315                                        virtualNameList.add(fname);
    316                                        log.trace("init(): virtualNameList[{}]={}", next, fname);
    317                                    }
    318                                    catch (Exception err) {
    319                                        log.trace("init(): vds[{}] continue", next);
    320                                    }
    321                                }
    322                            }
    323                        }
    324                        catch (Exception err) {
    325                            log.debug("init(): vds count error: ", err);
    326                        }
    327                    }
    328                    log.trace("init(): pid={} nfiles={} isExternal={} isVirtual={}", pid, nfiles, isExternal, isVirtual);
    329                }
    330                catch (Exception ex) {
    331                    log.debug("init(): check if it is an external or virtual dataset:", ex);
    332                }
    333            }
    334            catch (Exception ex) {
    335                log.debug("init(): H5Dget_create_plist() failure: ", ex);
    336            }
    337            finally {
    338                try {
    339                    H5.H5Pclose(pid);
    340                }
    341                catch (Exception ex) {
    342                    log.debug("init(): H5Pclose(pid {}) failure: ", pid, ex);
    343                }
    344            }
    345
    346            try {
    347                sid = H5.H5Dget_space(did);
    348                rank = H5.H5Sget_simple_extent_ndims(sid);
    349                tid = H5.H5Dget_type(did);
    350                log.trace("init(): tid={} sid={} rank={}", tid, sid, rank);
    351
    352                if (rank == 0) {
    353                    // a scalar data point
    354                    rank = 1;
    355                    dims = new long[1];
    356                    dims[0] = 1;
    357                    log.trace("init(): rank is a scalar data point");
    358                }
    359                else {
    360                    dims = new long[rank];
    361                    maxDims = new long[rank];
    362                    H5.H5Sget_simple_extent_dims(sid, dims, maxDims);
    363                    log.trace("init(): rank={}, dims={}, maxDims={}", rank, dims, maxDims);
    364                }
    365
    366                startDims = new long[rank];
    367                selectedDims = new long[rank];
    368
    369                try {
    370                    datatype = new H5Datatype(getFileFormat(), tid);
    371
    372                    log.trace("init(): tid={} has isText={} : isVLEN={} : isEnum={} : isUnsigned={} : isRegRef={}", tid,
    373                            datatype.isText(), datatype.isVLEN(), ((H5Datatype) datatype).isEnum(), datatype.isUnsigned(), ((H5Datatype) datatype).isRegRef());
    374
    375                    H5Datatype.extractCompoundInfo((H5Datatype) datatype, "", flatNameList, flatTypeList);
    376                }
    377                catch (Exception ex) {
    378                    log.debug("init(): failed to create datatype for dataset: ", ex);
    379                    datatype = null;
    380                }
    381
    382                // initialize member information
    383                numberOfMembers = flatNameList.size();
    384                log.trace("init(): numberOfMembers={}", numberOfMembers);
    385
    386                memberNames = new String[numberOfMembers];
    387                memberTypes = new Datatype[numberOfMembers];
    388                memberOrders = new int[numberOfMembers];
    389                isMemberSelected = new boolean[numberOfMembers];
    390                memberDims = new Object[numberOfMembers];
    391
    392                for (int i = 0; i < numberOfMembers; i++) {
    393                    isMemberSelected[i] = true;
    394                    memberOrders[i] = 1;
    395                    memberDims[i] = null;
    396
    397                    try {
    398                        memberTypes[i] = flatTypeList.get(i);
    399                        log.trace("init()[{}]: memberTypes[{}]={}", i, i, memberTypes[i].getDescription());
    400
    401                        if (memberTypes[i].isArray()) {
    402                            long mdim[] = memberTypes[i].getArrayDims();
    403                            int idim[] = new int[mdim.length];
    404                            int arrayNpoints = 1;
    405
    406                            for (int j = 0; j < idim.length; j++) {
    407                                idim[j] = (int) mdim[j];
    408                                arrayNpoints *= idim[j];
    409                            }
    410
    411                            memberDims[i] = idim;
    412                            memberOrders[i] = arrayNpoints;
    413                        }
    414                    }
    415                    catch (Exception ex) {
    416                        log.debug("init()[{}]: memberTypes[{}] get failure: ", i, i, ex);
    417                        memberTypes[i] = null;
    418                    }
    419
    420                    try {
    421                        memberNames[i] = flatNameList.get(i);
    422                        log.trace("init()[{}]: memberNames[{}]={}", i, i, memberNames[i]);
    423                    }
    424                    catch (Exception ex) {
    425                        log.debug("init()[{}]: memberNames[{}] get failure: ", i, i, ex);
    426                        memberNames[i] = "null";
    427                    }
    428                } //  (int i=0; i<numberOfMembers; i++)
    429
    430                inited = true;
    431            }
    432            catch (HDF5Exception ex) {
    433                numberOfMembers = 0;
    434                memberNames = null;
    435                memberTypes = null;
    436                memberOrders = null;
    437                log.debug("init(): ", ex);
    438            }
    439            finally {
    440                if (datatype != null)
    441                    datatype.close(tid);
    442
    443                try {
    444                    H5.H5Sclose(sid);
    445                }
    446                catch (HDF5Exception ex2) {
    447                    log.debug("init(): H5Sclose(sid {}) failure: ", sid, ex2);
    448                }
    449            }
    450
    451            close(did);
    452        }
    453        else {
    454            log.debug("init(): failed to open dataset");
    455        }
    456
    457        resetSelection();
    458    }
    459
    460    /*
    461     * (non-Javadoc)
    462     *
    463     * @see hdf.object.DataFormat#hasAttribute()
    464     */
    465    @Override
    466    public boolean hasAttribute() {
    467        objInfo.num_attrs = nAttributes;
    468
    469        if (objInfo.num_attrs < 0) {
    470            long did = open();
    471            if (did >= 0) {
    472                try {
    473                    objInfo = H5.H5Oget_info(did);
    474                    nAttributes = (int) objInfo.num_attrs;
    475                }
    476                catch (Exception ex) {
    477                    objInfo.num_attrs = 0;
    478                    log.debug("hasAttribute(): get object info failure: ", ex);
    479                }
    480                close(did);
    481            }
    482            else {
    483                log.debug("hasAttribute(): could not open dataset");
    484            }
    485        }
    486
    487        log.trace("hasAttribute(): nAttributes={}", objInfo.num_attrs);
    488        return (objInfo.num_attrs > 0);
    489    }
    490
    491    /*
    492     * (non-Javadoc)
    493     *
    494     * @see hdf.object.Dataset#getDatatype()
    495     */
    496    @Override
    497    public Datatype getDatatype() {
    498        if (!inited)
    499            init();
    500
    501        if (datatype == null) {
    502            long did = -1;
    503            long tid = -1;
    504
    505            did = open();
    506            if (did >= 0) {
    507                try {
    508                    tid = H5.H5Dget_type(did);
    509                    datatype = new H5Datatype(getFileFormat(), tid);
    510                }
    511                catch (Exception ex) {
    512                    log.debug("getDatatype(): ", ex);
    513                }
    514                finally {
    515                    try {
    516                        H5.H5Tclose(tid);
    517                    }
    518                    catch (HDF5Exception ex) {
    519                        log.debug("getDatatype(): H5Tclose(tid {}) failure: ", tid, ex);
    520                    }
    521                    try {
    522                        H5.H5Dclose(did);
    523                    }
    524                    catch (HDF5Exception ex) {
    525                        log.debug("getDatatype(): H5Dclose(did {}) failure: ", did, ex);
    526                    }
    527                }
    528            }
    529        }
    530
    531        if (isExternal) {
    532            String pdir = this.getFileFormat().getAbsoluteFile().getParent();
    533
    534            if (pdir == null) {
    535                pdir = ".";
    536            }
    537            System.setProperty("user.dir", pdir);
    538            log.trace("getDatatype(): External dataset: user.dir={}", pdir);
    539        }
    540
    541        return datatype;
    542    }
    543
    544    @Override
    545    public Object getFillValue() {
    546        return null;
    547    }
    548
    549    /*
    550     * (non-Javadoc)
    551     *
    552     * @see hdf.object.Dataset#clear()
    553     */
    554    @Override
    555    public void clear() {
    556        super.clear();
    557
    558        if (attributeList != null) {
    559            ((Vector<Attribute>) attributeList).setSize(0);
    560        }
    561    }
    562
    563    /*
    564     * (non-Javadoc)
    565     *
    566     * @see hdf.object.Dataset#readBytes()
    567     */
    568    @Override
    569    public byte[] readBytes() throws HDF5Exception {
    570        byte[] theData = null;
    571
    572        if (!isInited())
    573            init();
    574
    575        long did = open();
    576        if (did >= 0) {
    577            long fspace = -1;
    578            long mspace = -1;
    579            long tid = -1;
    580
    581            try {
    582                long[] lsize = { 1 };
    583                for (int j = 0; j < selectedDims.length; j++) {
    584                    lsize[0] *= selectedDims[j];
    585                }
    586
    587                fspace = H5.H5Dget_space(did);
    588                mspace = H5.H5Screate_simple(rank, selectedDims, null);
    589
    590                // set the rectangle selection
    591                // HDF5 bug: for scalar dataset, H5Sselect_hyperslab gives core dump
    592                if (rank * dims[0] > 1) {
    593                    H5.H5Sselect_hyperslab(fspace, HDF5Constants.H5S_SELECT_SET, startDims, selectedStride,
    594                            selectedDims, null); // set block to 1
    595                }
    596
    597                tid = H5.H5Dget_type(did);
    598                long size = H5.H5Tget_size(tid) * lsize[0];
    599                log.trace("readBytes(): size = {}", size);
    600
    601                if (size < Integer.MIN_VALUE || size > Integer.MAX_VALUE) throw new Exception("Invalid int size");
    602
    603                theData = new byte[(int) size];
    604
    605                log.trace("readBytes(): H5Dread: did={} tid={} fspace={} mspace={}", did, tid, fspace, mspace);
    606                H5.H5Dread(did, tid, mspace, fspace, HDF5Constants.H5P_DEFAULT, theData);
    607            }
    608            catch (Exception ex) {
    609                log.debug("readBytes(): failed to read data: ", ex);
    610            }
    611            finally {
    612                try {
    613                    H5.H5Sclose(fspace);
    614                }
    615                catch (Exception ex2) {
    616                    log.debug("readBytes(): H5Sclose(fspace {}) failure: ", fspace, ex2);
    617                }
    618                try {
    619                    H5.H5Sclose(mspace);
    620                }
    621                catch (Exception ex2) {
    622                    log.debug("readBytes(): H5Sclose(mspace {}) failure: ", mspace, ex2);
    623                }
    624                try {
    625                    H5.H5Tclose(tid);
    626                }
    627                catch (HDF5Exception ex2) {
    628                    log.debug("readBytes(): H5Tclose(tid {}) failure: ", tid, ex2);
    629                }
    630                close(did);
    631            }
    632        }
    633
    634        return theData;
    635    }
    636
    637    /*
    638     * (non-Javadoc)
    639     *
    640     * @see hdf.object.Dataset#read()
    641     */
    642    @Override
    643    public Object read() throws Exception {
    644        Object readData = null;
    645
    646        if (!isInited())
    647            init();
    648
    649        try {
    650            readData = compoundDatasetCommonIO(IO_TYPE.READ, null);
    651        }
    652        catch (Exception ex) {
    653            log.debug("read(): failed to read compound dataset: ", ex);
    654            throw new Exception("failed to read compound dataset: " + ex.getMessage(), ex);
    655        }
    656
    657        return readData;
    658    }
    659
    660    /**
    661     * Writes the given data buffer into this dataset in a file.
    662     * <p>
    663     * The data buffer is a vector that contains the data values of compound fields. The data is written
    664     * into file field by field.
    665     *
    666     * @param buf
    667     *            The vector that contains the data values of compound fields.
    668     *
    669     * @throws Exception
    670     *             If there is an error at the HDF5 library level.
    671     */
    672    @Override
    673    public void write(Object buf) throws Exception {
    674        if (this.getFileFormat().isReadOnly())
    675            throw new Exception("cannot write to compound dataset in file opened as read-only");
    676
    677        if (!isInited())
    678            init();
    679
    680        try {
    681            compoundDatasetCommonIO(IO_TYPE.WRITE, buf);
    682        }
    683        catch (Exception ex) {
    684            log.debug("write(): failed to write compound dataset: ", ex);
    685            throw new Exception("failed to write compound dataset: " + ex.getMessage(), ex);
    686        }
    687    }
    688
    689    private Object compoundDatasetCommonIO(IO_TYPE ioType, Object writeBuf) throws Exception {
    690        H5Datatype dsDatatype = (H5Datatype) getDatatype();
    691        Object data = null;
    692
    693        if (numberOfMembers <= 0) {
    694            log.debug("compoundDatasetCommonIO(): Dataset contains no members");
    695            throw new Exception("dataset contains no members");
    696        }
    697
    698        /*
    699         * I/O type-specific pre-initialization.
    700         */
    701        if (ioType == IO_TYPE.WRITE) {
    702            if ((writeBuf == null) || !(writeBuf instanceof List)) {
    703                log.debug("compoundDatasetCommonIO(): writeBuf is null or invalid");
    704                throw new Exception("write buffer is null or invalid");
    705            }
    706
    707            /*
    708             * Check for any unsupported datatypes and fail early before
    709             * attempting to write to the dataset.
    710             */
    711            if (dsDatatype.isArray() && dsDatatype.getDatatypeBase().isCompound()) {
    712                log.debug("compoundDatasetCommonIO(): cannot write dataset of type ARRAY of COMPOUND");
    713                throw new HDF5Exception("Unsupported dataset of type ARRAY of COMPOUND");
    714            }
    715
    716            if (dsDatatype.isVLEN() && dsDatatype.getDatatypeBase().isCompound()) {
    717                log.debug("compoundDatasetCommonIO(): cannot write dataset of type VLEN of COMPOUND");
    718                throw new HDF5Exception("Unsupported dataset of type VLEN of COMPOUND");
    719            }
    720        }
    721
    722        long did = open();
    723        if (did >= 0) {
    724            long[] spaceIDs = { -1, -1 }; // spaceIDs[0]=mspace, spaceIDs[1]=fspace
    725
    726            try {
    727                /*
    728                 * NOTE: this call sets up a hyperslab selection in the file according to the
    729                 * current selection in the dataset object.
    730                 */
    731                long totalSelectedSpacePoints = H5Utils.getTotalSelectedSpacePoints(did, dims, startDims,
    732                        selectedStride, selectedDims, spaceIDs);
    733
    734                data = compoundTypeIO(ioType, did, spaceIDs, (int) totalSelectedSpacePoints, dsDatatype, writeBuf, new int[]{0});
    735            }
    736            finally {
    737                if (HDF5Constants.H5S_ALL != spaceIDs[0]) {
    738                    try {
    739                        H5.H5Sclose(spaceIDs[0]);
    740                    }
    741                    catch (Exception ex) {
    742                        log.debug("compoundDatasetCommonIO(): H5Sclose(spaceIDs[0] {}) failure: ", spaceIDs[0], ex);
    743                    }
    744                }
    745
    746                if (HDF5Constants.H5S_ALL != spaceIDs[1]) {
    747                    try {
    748                        H5.H5Sclose(spaceIDs[1]);
    749                    }
    750                    catch (Exception ex) {
    751                        log.debug("compoundDatasetCommonIO(): H5Sclose(spaceIDs[1] {}) failure: ", spaceIDs[1], ex);
    752                    }
    753                }
    754
    755                close(did);
    756            }
    757        }
    758        else
    759            log.debug("compoundDatasetCommonIO(): failed to open dataset");
    760
    761        return data;
    762    }
    763
    764    /*
    765     * Private recursive routine to read/write an entire compound datatype field by
    766     * field. This routine is called recursively for ARRAY of COMPOUND and VLEN of
    767     * COMPOUND datatypes.
    768     *
    769     * NOTE: the globalMemberIndex hack is ugly, but we need to keep track of a
    770     * running counter so that we can index properly into the flattened name list
    771     * generated from H5Datatype.extractCompoundInfo() at dataset init time.
    772     */
    773    private Object compoundTypeIO(IO_TYPE ioType, long did, long[] spaceIDs, int nSelPoints, final H5Datatype cmpdType,
    774            Object writeBuf, int[] globalMemberIndex) {
    775        Object theData = null;
    776
    777        if (cmpdType.isArray()) {
    778            log.trace("compoundTypeIO(): ARRAY type");
    779
    780            long[] arrayDims = cmpdType.getArrayDims();
    781            int arrSize = nSelPoints;
    782            for (int i = 0; i < arrayDims.length; i++) {
    783                arrSize *= arrayDims[i];
    784            }
    785
    786            theData = compoundTypeIO(ioType, did, spaceIDs, arrSize, (H5Datatype) cmpdType.getDatatypeBase(), writeBuf, globalMemberIndex);
    787        }
    788        else if (cmpdType.isVLEN() && !cmpdType.isVarStr()) {
    789            /*
    790             * TODO: true variable-length support.
    791             */
    792            String[] errVal = new String[nSelPoints];
    793            String errStr = "*UNSUPPORTED*";
    794
    795            for (int j = 0; j < nSelPoints; j++)
    796                errVal[j] = errStr;
    797
    798            /*
    799             * Setup a fake data list.
    800             */
    801            Datatype baseType = cmpdType.getDatatypeBase();
    802            while (baseType != null && !baseType.isCompound()) {
    803                baseType = baseType.getDatatypeBase();
    804            }
    805
    806            List<Object> fakeVlenData = (List<Object>) H5Datatype.allocateArray((H5Datatype) baseType, nSelPoints);
    807            fakeVlenData.add(errVal);
    808
    809            theData = fakeVlenData;
    810        }
    811        else if (cmpdType.isCompound()) {
    812            List<Object> memberDataList = null;
    813            List<Datatype> typeList = cmpdType.getCompoundMemberTypes();
    814
    815            log.trace("compoundTypeIO(): {} {} members:", (ioType == IO_TYPE.READ) ? "read" : "write",
    816                    typeList.size());
    817
    818            if (ioType == IO_TYPE.READ) {
    819                memberDataList = (List<Object>) H5Datatype.allocateArray(cmpdType, nSelPoints);
    820            }
    821
    822            try {
    823                for (int i = 0, writeListIndex = 0; i < typeList.size(); i++) {
    824                    H5Datatype memberType = null;
    825                    String memberName = null;
    826                    Object memberData = null;
    827
    828                    try {
    829                        memberType = (H5Datatype) typeList.get(i);
    830                    }
    831                    catch (Exception ex) {
    832                        log.debug("compoundTypeIO(): get member {} failure: ", i, ex);
    833                        globalMemberIndex[0]++;
    834                        continue;
    835                    }
    836
    837                    /*
    838                     * Since the type list used here is not a flattened structure, we need to skip
    839                     * the member selection check for compound types, as otherwise having a single
    840                     * member not selected would skip the reading/writing for the entire compound
    841                     * type. The member selection check will be deferred to the recursive compound
    842                     * read/write below.
    843                     */
    844                    if (!memberType.isCompound()) {
    845                        if (!isMemberSelected[globalMemberIndex[0] % this.getMemberCount()]) {
    846                            log.debug("compoundTypeIO(): member[{}] is not selected", i);
    847                            globalMemberIndex[0]++;
    848                            continue; // the field is not selected
    849                        }
    850                    }
    851
    852                    if (!memberType.isCompound()) {
    853                        try {
    854                            memberName = new String(flatNameList.get(globalMemberIndex[0]));
    855                        }
    856                        catch (Exception ex) {
    857                            log.debug("compoundTypeIO(): get member {} name failure: ", i, ex);
    858                            memberName = "null";
    859                        }
    860                    }
    861
    862                    log.trace("compoundTypeIO(): member[{}]({}) is type {}", i, memberName, memberType.getDescription());
    863
    864                    if (ioType == IO_TYPE.READ) {
    865                        try {
    866                            if (memberType.isCompound())
    867                                memberData = compoundTypeIO(ioType, did, spaceIDs, nSelPoints, memberType, writeBuf, globalMemberIndex);
    868                            else if (memberType.isArray() /* || (memberType.isVLEN() && !memberType.isVarStr()) */) {
    869                                /*
    870                                 * Recursively detect any nested array/vlen of compound types.
    871                                 */
    872                                boolean compoundFound = false;
    873
    874                                Datatype base = memberType.getDatatypeBase();
    875                                while (base != null) {
    876                                    if (base.isCompound())
    877                                        compoundFound = true;
    878
    879                                    base = base.getDatatypeBase();
    880                                }
    881
    882                                if (compoundFound) {
    883                                    /*
    884                                     * Skip the top-level array/vlen type.
    885                                     */
    886                                    globalMemberIndex[0]++;
    887
    888                                    memberData = compoundTypeIO(ioType, did, spaceIDs, nSelPoints, memberType, writeBuf, globalMemberIndex);
    889                                }
    890                                else {
    891                                    memberData = readSingleCompoundMember(did, spaceIDs, nSelPoints, memberType, memberName);
    892                                    globalMemberIndex[0]++;
    893                                }
    894                            }
    895                            else {
    896                                memberData = readSingleCompoundMember(did, spaceIDs, nSelPoints, memberType, memberName);
    897                                globalMemberIndex[0]++;
    898                            }
    899                        }
    900                        catch (Exception ex) {
    901                            log.debug("compoundTypeIO(): failed to read member {}: ", i, ex);
    902                            globalMemberIndex[0]++;
    903                            memberData = null;
    904                        }
    905
    906                        if (memberData == null) {
    907                            String[] errVal = new String[nSelPoints];
    908                            String errStr = "*ERROR*";
    909
    910                            for (int j = 0; j < nSelPoints; j++)
    911                                errVal[j] = errStr;
    912
    913                            memberData = errVal;
    914                        }
    915
    916                        memberDataList.add(memberData);
    917                    }
    918                    else {
    919                        try {
    920                            /*
    921                             * TODO: currently doesn't correctly handle non-selected compound members.
    922                             */
    923                            memberData = ((List<?>) writeBuf).get(writeListIndex++);
    924                        }
    925                        catch (Exception ex) {
    926                            log.debug("compoundTypeIO(): get member[{}] data failure: ", i, ex);
    927                            globalMemberIndex[0]++;
    928                            continue;
    929                        }
    930
    931                        if (memberData == null) {
    932                            log.debug("compoundTypeIO(): member[{}] data is null", i);
    933                            globalMemberIndex[0]++;
    934                            continue;
    935                        }
    936
    937                        try {
    938                            if (memberType.isCompound()) {
    939                                List<?> nestedList = (List<?>) ((List<?>) writeBuf).get(writeListIndex++);
    940                                compoundTypeIO(ioType, did, spaceIDs, nSelPoints, memberType, nestedList, globalMemberIndex);
    941                            }
    942                            else {
    943                                writeSingleCompoundMember(did, spaceIDs, nSelPoints, memberType, memberName, memberData);
    944                                globalMemberIndex[0]++;
    945                            }
    946                        }
    947                        catch (Exception ex) {
    948                            log.debug("compoundTypeIO(): failed to write member[{}]: ", i, ex);
    949                            globalMemberIndex[0]++;
    950                        }
    951                    }
    952                } //  (i = 0, writeListIndex = 0; i < atomicTypeList.size(); i++)
    953            }
    954            catch (Exception ex) {
    955                log.debug("compoundTypeIO(): failure: ", ex);
    956                memberDataList = null;
    957            }
    958
    959            theData = memberDataList;
    960        }
    961
    962        return theData;
    963    }
    964
    965    /*
    966     * Private routine to read a single field of a compound datatype by creating a
    967     * compound datatype and inserting the single field into that datatype.
    968     */
    969    private Object readSingleCompoundMember(long dsetID, long[] spaceIDs, int nSelPoints, final H5Datatype memberType,
    970            String memberName) throws Exception {
    971        H5Datatype dsDatatype = (H5Datatype) this.getDatatype();
    972        Object memberData = null;
    973
    974        try {
    975            memberData = H5Datatype.allocateArray(memberType, nSelPoints);
    976            log.trace("readSingleCompoundMember(): allocateArray {} points ", nSelPoints);
    977        }
    978        catch (OutOfMemoryError err) {
    979            memberData = null;
    980            throw new Exception("Out of memory");
    981        }
    982        catch (Exception ex) {
    983            log.debug("readSingleCompoundMember(): ", ex);
    984            memberData = null;
    985        }
    986
    987        if (memberData != null) {
    988            /*
    989             * Create a compound datatype containing just a single field (the one which we
    990             * want to read).
    991             */
    992            long compTid = -1;
    993            try {
    994                compTid = dsDatatype.createCompoundFieldType(memberName);
    995            }
    996            catch (HDF5Exception ex) {
    997                log.debug("readSingleCompoundMember(): unable to create compound field type for member of type {}: ",
    998                        memberType.getDescription(), ex);
    999                memberData = null;
    1000            }
    1001
    1002            /*
    1003             * Actually read the data for this member now that everything has been setup.
    1004             */
    1005            try {
    1006                if (memberType.isVLEN() || (memberType.isArray() && memberType.getDatatypeBase().isVLEN())) {
    1007                    log.trace("readSingleCompoundMember(): H5DreadVL did={} compTid={} spaceIDs[0]={} spaceIDs[1]={}",
    1008                            dsetID, compTid, (spaceIDs[0] == HDF5Constants.H5P_DEFAULT) ? "H5P_DEFAULT" : spaceIDs[0],
    1009                            (spaceIDs[1] == HDF5Constants.H5P_DEFAULT) ? "H5P_DEFAULT" : spaceIDs[1]);
    1010
    1011                    H5.H5DreadVL(dsetID, compTid, spaceIDs[0], spaceIDs[1], HDF5Constants.H5P_DEFAULT, (Object[]) memberData);
    1012                }
    1013                else {
    1014                    log.trace("readSingleCompoundMember(): H5Dread did={} compTid={} spaceIDs[0]={} spaceIDs[1]={}",
    1015                            dsetID, compTid, (spaceIDs[0] == HDF5Constants.H5P_DEFAULT) ? "H5P_DEFAULT" : spaceIDs[0],
    1016                            (spaceIDs[1] == HDF5Constants.H5P_DEFAULT) ? "H5P_DEFAULT" : spaceIDs[1]);
    1017
    1018                    H5.H5Dread(dsetID, compTid, spaceIDs[0], spaceIDs[1], HDF5Constants.H5P_DEFAULT, memberData);
    1019                }
    1020            }
    1021            catch (HDF5DataFiltersException exfltr) {
    1022                log.debug("readSingleCompoundMember(): read failure: ", exfltr);
    1023                throw new Exception("Filter not available exception: " + exfltr.getMessage(), exfltr);
    1024            }
    1025            catch (Exception ex) {
    1026                log.debug("readSingleCompoundMember(): read failure: ", ex);
    1027                throw new Exception("failed to read compound member: " + ex.getMessage(), ex);
    1028            }
    1029            finally {
    1030                dsDatatype.close(compTid);
    1031            }
    1032
    1033            /*
    1034             * Perform any necessary data conversions.
    1035             */
    1036            if (memberType.isUnsigned()) {
    1037                log.trace("readSingleCompoundMember(): converting from unsigned C-type integers");
    1038                memberData = Dataset.convertFromUnsignedC(memberData, null);
    1039            }
    1040            else if (Utils.getJavaObjectRuntimeClass(memberData) == 'B') {
    1041                log.trace("readSingleCompoundMember(): converting byte array member into Object");
    1042
    1043                /*
    1044                 * For all other types that get read into memory as a byte[] (such as nested
    1045                 * compounds and arrays of compounds), we must manually convert the byte[] into
    1046                 * something usable.
    1047                 */
    1048                memberData = convertByteMember(memberType, (byte[]) memberData);
    1049            }
    1050        }
    1051
    1052        return memberData;
    1053    }
    1054
    1055    /*
    1056     * Private routine to write a single field of a compound datatype by creating a
    1057     * compound datatype and inserting the single field into that datatype.
    1058     */
    1059    private void writeSingleCompoundMember(long dsetID, long[] spaceIDs, int nSelPoints, final H5Datatype memberType,
    1060            String memberName, Object theData) throws Exception {
    1061        H5Datatype dsDatatype = (H5Datatype) this.getDatatype();
    1062
    1063        /*
    1064         * Check for any unsupported datatypes before attempting to write this compound
    1065         * member.
    1066         */
    1067        if (memberType.isVLEN() && !memberType.isVarStr()) {
    1068            log.debug("writeSingleCompoundMember(): writing of VL non-strings is not currently supported");
    1069            throw new Exception("writing of VL non-strings is not currently supported");
    1070        }
    1071
    1072        /*
    1073         * Perform any necessary data conversions before writing the data.
    1074         */
    1075        Object tmpData = theData;
    1076        try {
    1077            if (memberType.isUnsigned()) {
    1078                // Check if we need to convert unsigned integer data from Java-style
    1079                // to C-style integers
    1080                long tsize = memberType.getDatatypeSize();
    1081                String cname = theData.getClass().getName();
    1082                char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    1083                boolean doIntConversion = (((tsize == 1) && (dname == 'S'))
    1084                        || ((tsize == 2) && (dname == 'I')) || ((tsize == 4) && (dname == 'J')));
    1085
    1086                if (doIntConversion) {
    1087                    log.trace("writeSingleCompoundMember(): converting integer data to unsigned C-type integers");
    1088                    tmpData = convertToUnsignedC(theData, null);
    1089                }
    1090            }
    1091            else if (memberType.isString() && (Array.get(theData, 0) instanceof String)) {
    1092                log.trace("writeSingleCompoundMember(): converting string array to byte array");
    1093                tmpData = stringToByte((String[]) theData, (int) memberType.getDatatypeSize());
    1094            }
    1095            else if (memberType.isEnum() && (Array.get(theData, 0) instanceof String)) {
    1096                log.trace("writeSingleCompoundMember(): converting enum names to values");
    1097                tmpData = memberType.convertEnumNameToValue((String[]) theData);
    1098            }
    1099        }
    1100        catch (Exception ex) {
    1101            log.debug("writeSingleCompoundMember(): data conversion failure: ", ex);
    1102            tmpData = null;
    1103        }
    1104
    1105        if (tmpData == null) {
    1106            log.debug("writeSingleCompoundMember(): data is null");
    1107            return;
    1108        }
    1109
    1110        /*
    1111         * Create a compound datatype containing just a single field (the one which we
    1112         * want to write).
    1113         */
    1114        long compTid = -1;
    1115        try {
    1116            compTid = dsDatatype.createCompoundFieldType(memberName);
    1117        }
    1118        catch (HDF5Exception ex) {
    1119            log.debug("writeSingleCompoundMember(): unable to create compound field type for member of type {}: ",
    1120                    memberType.getDescription(), ex);
    1121        }
    1122
    1123        /*
    1124         * Actually write the data now that everything has been setup.
    1125         */
    1126        try {
    1127            if (memberType.isVarStr()) {
    1128                log.trace("writeSingleCompoundMember(): H5Dwrite_string did={} compTid={} spaceIDs[0]={} spaceIDs[1]={}",
    1129                        dsetID, compTid, (spaceIDs[0] == HDF5Constants.H5P_DEFAULT) ? "H5P_DEFAULT" : spaceIDs[0],
    1130                        (spaceIDs[1] == HDF5Constants.H5P_DEFAULT) ? "H5P_DEFAULT" : spaceIDs[1]);
    1131
    1132                H5.H5Dwrite_string(dsetID, compTid, spaceIDs[0], spaceIDs[1], HDF5Constants.H5P_DEFAULT, (String[]) tmpData);
    1133            }
    1134            else {
    1135                log.trace("writeSingleCompoundMember(): H5Dwrite did={} compTid={} spaceIDs[0]={} spaceIDs[1]={}",
    1136                        dsetID, compTid, (spaceIDs[0] == HDF5Constants.H5P_DEFAULT) ? "H5P_DEFAULT" : spaceIDs[0],
    1137                        (spaceIDs[1] == HDF5Constants.H5P_DEFAULT) ? "H5P_DEFAULT" : spaceIDs[1]);
    1138
    1139                // BUG!!! does not write nested compound data and no
    1140                // exception was caught. Need to check if it is a java
    1141                // error or C library error.
    1142                H5.H5Dwrite(dsetID, compTid, spaceIDs[0], spaceIDs[1], HDF5Constants.H5P_DEFAULT, tmpData);
    1143            }
    1144        }
    1145        catch (Exception ex) {
    1146            log.debug("writeSingleCompoundMember(): write failure: ", ex);
    1147            throw new Exception("failed to write compound member: " + ex.getMessage(), ex);
    1148        }
    1149        finally {
    1150            dsDatatype.close(compTid);
    1151        }
    1152    }
    1153
    1154    /*
    1155     * Private routine to convert datatypes that are read in as byte arrays to
    1156     * regular types.
    1157     */
    1158    private Object convertByteMember(final H5Datatype dtype, byte[] byteData) {
    1159        Object theObj = null;
    1160
    1161        if (dtype.getDatatypeSize() == 1) {
    1162            /*
    1163             * Normal byte[] type, such as an integer datatype of size 1.
    1164             */
    1165            theObj = byteData;
    1166        }
    1167        else if (dtype.isString() && !dtype.isVarStr() && convertByteToString) {
    1168            log.trace("convertByteMember(): converting byte array to string array");
    1169
    1170            theObj = byteToString(byteData, (int) dtype.getDatatypeSize());
    1171        }
    1172        else if (dtype.isInteger()) {
    1173            log.trace("convertByteMember(): converting byte array to integer array");
    1174
    1175            theObj = HDFNativeData.byteToInt(byteData);
    1176        }
    1177        else if (dtype.isFloat()) {
    1178            log.trace("convertByteMember(): converting byte array to float array");
    1179
    1180            if (dtype.getDatatypeSize() == 16)
    1181                theObj = dtype.byteToBigDecimal(byteData, 0);
    1182            else
    1183                theObj = HDFNativeData.byteToFloat(byteData);
    1184        }
    1185        else if (dtype.isRef()) {
    1186            log.trace("convertByteMember(): reference type - converting byte array to long array");
    1187
    1188            theObj = HDFNativeData.byteToLong(byteData);
    1189        }
    1190        else if (dtype.isArray()) {
    1191            H5Datatype baseType = (H5Datatype) dtype.getDatatypeBase();
    1192
    1193            /*
    1194             * Retrieve the real base datatype in the case of ARRAY of ARRAY datatypes.
    1195             */
    1196            while (baseType.isArray()) baseType = (H5Datatype) baseType.getDatatypeBase();
    1197
    1198            /*
    1199             * Optimize for the common cases of Arrays.
    1200             */
    1201            switch (baseType.getDatatypeClass()) {
    1202                case Datatype.CLASS_INTEGER:
    1203                case Datatype.CLASS_FLOAT:
    1204                case Datatype.CLASS_CHAR:
    1205                case Datatype.CLASS_STRING:
    1206                case Datatype.CLASS_BITFIELD:
    1207                case Datatype.CLASS_OPAQUE:
    1208                case Datatype.CLASS_COMPOUND:
    1209                case Datatype.CLASS_REFERENCE:
    1210                case Datatype.CLASS_ENUM:
    1211                case Datatype.CLASS_VLEN:
    1212                case Datatype.CLASS_TIME:
    1213                    theObj = convertByteMember(baseType, byteData);
    1214                    break;
    1215
    1216                case Datatype.CLASS_ARRAY:
    1217                {
    1218                    H5Datatype arrayType = (H5Datatype) dtype.getDatatypeBase();
    1219
    1220                    long[] arrayDims = dtype.getArrayDims();
    1221                    int arrSize = 1;
    1222                    for (int i = 0; i < arrayDims.length; i++) {
    1223                        arrSize *= arrayDims[i];
    1224                    }
    1225
    1226                    theObj = new Object[arrSize];
    1227
    1228                    for (int i = 0; i < arrSize; i++) {
    1229                        byte[] indexedBytes = Arrays.copyOfRange(byteData, (int) (i * arrayType.getDatatypeSize()),
    1230                                (int) ((i + 1) * arrayType.getDatatypeSize()));
    1231                        ((Object[]) theObj)[i] = convertByteMember(arrayType, indexedBytes);
    1232                    }
    1233
    1234                    break;
    1235                }
    1236
    1237                case Datatype.CLASS_NO_CLASS:
    1238                default:
    1239                    log.debug("convertByteMember(): invalid datatype class");
    1240                    theObj = new String("*ERROR*");
    1241            }
    1242        }
    1243        else if (dtype.isCompound()) {
    1244            /*
    1245             * TODO: still valid after reading change?
    1246             */
    1247            theObj = convertCompoundByteMembers(dtype, byteData);
    1248        }
    1249        else {
    1250            theObj = byteData;
    1251        }
    1252
    1253        return theObj;
    1254    }
    1255
    1256    /**
    1257     * Given an array of bytes representing a compound Datatype, converts each of
    1258     * its members into Objects and returns the results.
    1259     *
    1260     * @param dtype
    1261     *            The compound datatype to convert
    1262     * @param data
    1263     *            The byte array representing the data of the compound Datatype
    1264     * @return The converted types of the bytes
    1265     */
    1266    private Object convertCompoundByteMembers(final H5Datatype dtype, byte[] data) {
    1267        List<Object> theData = null;
    1268
    1269        List<Datatype> allSelectedTypes = Arrays.asList(this.getSelectedMemberTypes());
    1270        List<Datatype> localTypes = new ArrayList<>(dtype.getCompoundMemberTypes());
    1271        Iterator<Datatype> localIt = localTypes.iterator();
    1272        while (localIt.hasNext()) {
    1273            Datatype curType = localIt.next();
    1274
    1275            if (curType.isCompound())
    1276                continue;
    1277
    1278            if (!allSelectedTypes.contains(curType))
    1279                localIt.remove();
    1280        }
    1281
    1282        theData = new ArrayList<>(localTypes.size());
    1283        for (int i = 0, index = 0; i < localTypes.size(); i++) {
    1284            Datatype curType = localTypes.get(i);
    1285
    1286            if (curType.isCompound())
    1287                theData.add(convertCompoundByteMembers((H5Datatype) curType,
    1288                        Arrays.copyOfRange(data, index, index + (int) curType.getDatatypeSize())));
    1289            else
    1290                theData.add(convertByteMember((H5Datatype) curType,
    1291                        Arrays.copyOfRange(data, index, index + (int) curType.getDatatypeSize())));
    1292
    1293            index += curType.getDatatypeSize();
    1294        }
    1295
    1296        return theData;
    1297    }
    1298
    1299    @Override
    1300    public Object convertFromUnsignedC() {
    1301        throw new UnsupportedOperationException("H5CompoundDS:convertFromUnsignedC Unsupported operation.");
    1302    }
    1303
    1304    @Override
    1305    public Object convertToUnsignedC() {
    1306        throw new UnsupportedOperationException("H5CompoundDS:convertToUnsignedC Unsupported operation.");
    1307    }
    1308
    1309    /*
    1310     * (non-Javadoc)
    1311     *
    1312     * @see hdf.object.DataFormat#getMetadata()
    1313     */
    1314    @Override
    1315    public List<Attribute> getMetadata() throws HDF5Exception {
    1316        return this.getMetadata(fileFormat.getIndexType(null), fileFormat.getIndexOrder(null));
    1317    }
    1318
    1319    /*
    1320     * (non-Javadoc)
    1321     *
    1322     * @see hdf.object.DataFormat#getMetadata(int...)
    1323     */
    1324    public List<Attribute> getMetadata(int... attrPropList) throws HDF5Exception {
    1325
    1326        if (!isInited()) {
    1327            init();
    1328        }
    1329
    1330        try {
    1331            this.linkTargetObjName = H5File.getLinkTargetName(this);
    1332        }
    1333        catch (Exception ex) {
    1334            log.debug("getMetadata(): getLinkTargetName failed: ", ex);
    1335        }
    1336
    1337        if (attributeList != null) {
    1338            log.trace("getMetadata(): attributeList != null");
    1339            return attributeList;
    1340        }
    1341
    1342        long did = -1;
    1343        long pcid = -1;
    1344        long paid = -1;
    1345        int indxType = fileFormat.getIndexType(null);
    1346        int order = fileFormat.getIndexOrder(null);
    1347
    1348        // load attributes first
    1349        if (attrPropList.length > 0) {
    1350            indxType = attrPropList[0];
    1351            if (attrPropList.length > 1) {
    1352                order = attrPropList[1];
    1353            }
    1354        }
    1355
    1356        attributeList = H5File.getAttribute(this, indxType, order);
    1357
    1358        did = open();
    1359        if (did >= 0) {
    1360            try {
    1361                // get the compression and chunk information
    1362                pcid = H5.H5Dget_create_plist(did);
    1363                paid = H5.H5Dget_access_plist(did);
    1364                long storageSize = H5.H5Dget_storage_size(did);
    1365                int nfilt = H5.H5Pget_nfilters(pcid);
    1366                int layoutType = H5.H5Pget_layout(pcid);
    1367
    1368                storageLayout.setLength(0);
    1369                compression.setLength(0);
    1370
    1371                if (layoutType == HDF5Constants.H5D_CHUNKED) {
    1372                    chunkSize = new long[rank];
    1373                    H5.H5Pget_chunk(pcid, rank, chunkSize);
    1374                    int n = chunkSize.length;
    1375
    1376                    storageLayout.append("CHUNKED: ").append(chunkSize[0]);
    1377                    for (int i = 1; i < n; i++) {
    1378                        storageLayout.append(" X ").append(chunkSize[i]);
    1379                    }
    1380
    1381                    if (nfilt > 0) {
    1382                        long nelmts = 1;
    1383                        long uncompSize;
    1384                        long datumSize = getDatatype().getDatatypeSize();
    1385                        if (datumSize < 0) {
    1386                            long tmptid = -1;
    1387                            try {
    1388                                tmptid = H5.H5Dget_type(did);
    1389                                datumSize = H5.H5Tget_size(tmptid);
    1390                            }
    1391                            finally {
    1392                                try {
    1393                                    H5.H5Tclose(tmptid);
    1394                                }
    1395                                catch (Exception ex2) {
    1396                                    log.debug("getMetadata(): H5Tclose(tmptid {}) failure: ", tmptid, ex2);
    1397                                }
    1398                            }
    1399                        }
    1400
    1401                        for (int i = 0; i < rank; i++) {
    1402                            nelmts *= dims[i];
    1403                        }
    1404                        uncompSize = nelmts * datumSize;
    1405
    1406                        /* compression ratio = uncompressed size / compressed size */
    1407
    1408                        if (storageSize != 0) {
    1409                            double ratio = (double) uncompSize / (double) storageSize;
    1410                            DecimalFormat df = new DecimalFormat();
    1411                            df.setMinimumFractionDigits(3);
    1412                            df.setMaximumFractionDigits(3);
    1413                            compression.append(df.format(ratio)).append(":1");
    1414                        }
    1415                    }
    1416                }
    1417                else if (layoutType == HDF5Constants.H5D_COMPACT) {
    1418                    storageLayout.append("COMPACT");
    1419                }
    1420                else if (layoutType == HDF5Constants.H5D_CONTIGUOUS) {
    1421                    storageLayout.append("CONTIGUOUS");
    1422                    if (H5.H5Pget_external_count(pcid) > 0)
    1423                        storageLayout.append(" - EXTERNAL ");
    1424                }
    1425                else if (layoutType == HDF5Constants.H5D_VIRTUAL) {
    1426                    storageLayout.append("VIRTUAL - ");
    1427                    try {
    1428                        long vmaps = H5.H5Pget_virtual_count(pcid);
    1429                        try {
    1430                            int virtView = H5.H5Pget_virtual_view(paid);
    1431                            long virtGap = H5.H5Pget_virtual_printf_gap(paid);
    1432                            if (virtView == HDF5Constants.H5D_VDS_FIRST_MISSING)
    1433                                storageLayout.append("First Missing");
    1434                            else
    1435                                storageLayout.append("Last Available");
    1436                            storageLayout.append("\nGAP : ").append(virtGap);
    1437                        }
    1438                        catch (Exception err) {
    1439                            log.debug("getMetadata(): vds error: ", err);
    1440                            storageLayout.append("ERROR");
    1441                        }
    1442                        storageLayout.append("\nMAPS : ").append(vmaps);
    1443                        if (vmaps > 0) {
    1444                            for (long next = 0; next < vmaps; next++) {
    1445                                try {
    1446                                    H5.H5Pget_virtual_vspace(pcid, next);
    1447                                    H5.H5Pget_virtual_srcspace(pcid, next);
    1448                                    String fname = H5.H5Pget_virtual_filename(pcid, next);
    1449                                    String dsetname = H5.H5Pget_virtual_dsetname(pcid, next);
    1450                                    storageLayout.append("\n").append(fname).append(" : ").append(dsetname);
    1451                                }
    1452                                catch (Exception err) {
    1453                                    log.debug("getMetadata(): vds space[{}] error: ", next, err);
    1454                                    storageLayout.append("ERROR");
    1455                                }
    1456                            }
    1457                        }
    1458                    }
    1459                    catch (Exception err) {
    1460                        log.debug("getMetadata(): vds count error: ", err);
    1461                        storageLayout.append("ERROR");
    1462                    }
    1463                }
    1464                else {
    1465                    chunkSize = null;
    1466                    storageLayout.append("NONE");
    1467                }
    1468
    1469                int[] flags = { 0, 0 };
    1470                long[] cdNelmts = { 20 };
    1471                int[] cdValues = new int[(int) cdNelmts[0]];
    1472                String[] cdName = { "", "" };
    1473                log.trace("getMetadata(): {} filters in pipeline", nfilt);
    1474                int filter = -1;
    1475                int[] filterConfig = { 1 };
    1476
    1477                filters.setLength(0);
    1478
    1479                if (nfilt == 0) {
    1480                    filters.append("NONE");
    1481                }
    1482                else {
    1483                    for (int i = 0, k = 0; i < nfilt; i++) {
    1484                        log.trace("getMetadata(): filter[{}]", i);
    1485                        if (i > 0) {
    1486                            filters.append(", ");
    1487                        }
    1488                        if (k > 0) {
    1489                            compression.append(", ");
    1490                        }
    1491
    1492                        try {
    1493                            cdNelmts[0] = 20;
    1494                            cdValues = new int[(int) cdNelmts[0]];
    1495                            cdValues = new int[(int) cdNelmts[0]];
    1496                            filter = H5.H5Pget_filter(pcid, i, flags, cdNelmts, cdValues, 120, cdName, filterConfig);
    1497                            log.trace("getMetadata(): filter[{}] is {} has {} elements ", i, cdName[0], cdNelmts[0]);
    1498                            for (int j = 0; j < cdNelmts[0]; j++) {
    1499                                log.trace("getMetadata(): filter[{}] element {} = {}", i, j, cdValues[j]);
    1500                            }
    1501                        }
    1502                        catch (Exception err) {
    1503                            log.debug("getMetadata(): filter[{}] error: ", i, err);
    1504                            filters.append("ERROR");
    1505                            continue;
    1506                        }
    1507
    1508                        if (filter == HDF5Constants.H5Z_FILTER_NONE) {
    1509                            filters.append("NONE");
    1510                        }
    1511                        else if (filter == HDF5Constants.H5Z_FILTER_DEFLATE) {
    1512                            filters.append("GZIP");
    1513                            compression.append(COMPRESSION_GZIP_TXT).append(cdValues[0]);
    1514                            k++;
    1515                        }
    1516                        else if (filter == HDF5Constants.H5Z_FILTER_FLETCHER32) {
    1517                            filters.append("Error detection filter");
    1518                        }
    1519                        else if (filter == HDF5Constants.H5Z_FILTER_SHUFFLE) {
    1520                            filters.append("SHUFFLE: Nbytes = ").append(cdValues[0]);
    1521                        }
    1522                        else if (filter == HDF5Constants.H5Z_FILTER_NBIT) {
    1523                            filters.append("NBIT");
    1524                        }
    1525                        else if (filter == HDF5Constants.H5Z_FILTER_SCALEOFFSET) {
    1526                            filters.append("SCALEOFFSET: MIN BITS = ").append(cdValues[0]);
    1527                        }
    1528                        else if (filter == HDF5Constants.H5Z_FILTER_SZIP) {
    1529                            filters.append("SZIP");
    1530                            compression.append("SZIP: Pixels per block = ").append(cdValues[1]);
    1531                            k++;
    1532                            int flag = -1;
    1533                            try {
    1534                                flag = H5.H5Zget_filter_info(filter);
    1535                            }
    1536                            catch (Exception ex) {
    1537                                log.debug("getMetadata(): H5Zget_filter_info failure: ", ex);
    1538                                flag = -1;
    1539                            }
    1540                            if (flag == HDF5Constants.H5Z_FILTER_CONFIG_DECODE_ENABLED) {
    1541                                compression.append(": H5Z_FILTER_CONFIG_DECODE_ENABLED");
    1542                            }
    1543                            else if ((flag == HDF5Constants.H5Z_FILTER_CONFIG_ENCODE_ENABLED)
    1544                                    || (flag >= (HDF5Constants.H5Z_FILTER_CONFIG_ENCODE_ENABLED
    1545                                            + HDF5Constants.H5Z_FILTER_CONFIG_DECODE_ENABLED))) {
    1546                                compression.append(": H5Z_FILTER_CONFIG_ENCODE_ENABLED");
    1547                            }
    1548                        }
    1549                        else {
    1550                            filters.append("USERDEFINED ").append(cdName[0]).append("(").append(filter).append("): ");
    1551                            for (int j = 0; j < cdNelmts[0]; j++) {
    1552                                if (j > 0)
    1553                                    filters.append(", ");
    1554                                filters.append(cdValues[j]);
    1555                            }
    1556                            log.debug("getMetadata(): filter[{}] is user defined compression", i);
    1557                        }
    1558                    } //  (int i=0; i<nfilt; i++)
    1559                }
    1560
    1561                if (compression.length() == 0) {
    1562                    compression.append("NONE");
    1563                }
    1564                log.trace("getMetadata(): filter compression={}", compression);
    1565
    1566                log.trace("getMetadata(): filter information={}", filters);
    1567
    1568                storage.setLength(0);
    1569                storage.append("SIZE: ").append(storageSize);
    1570
    1571                try {
    1572                    int[] at = { 0 };
    1573                    H5.H5Pget_alloc_time(pcid, at);
    1574                    storage.append(", allocation time: ");
    1575                    if (at[0] == HDF5Constants.H5D_ALLOC_TIME_EARLY) {
    1576                        storage.append("Early");
    1577                    }
    1578                    else if (at[0] == HDF5Constants.H5D_ALLOC_TIME_INCR) {
    1579                        storage.append("Incremental");
    1580                    }
    1581                    else if (at[0] == HDF5Constants.H5D_ALLOC_TIME_LATE) {
    1582                        storage.append("Late");
    1583                    }
    1584                    else
    1585                        storage.append("Default");
    1586                }
    1587                catch (Exception ex) {
    1588                    log.debug("getMetadata(): Storage allocation time:", ex);
    1589                }
    1590                log.trace("getMetadata(): storage={}", storage);
    1591            }
    1592            finally {
    1593                try {
    1594                    H5.H5Pclose(paid);
    1595                }
    1596                catch (Exception ex) {
    1597                    log.debug("getMetadata(): H5Pclose(paid {}) failure: ", paid, ex);
    1598                }
    1599                try {
    1600                    H5.H5Pclose(pcid);
    1601                }
    1602                catch (Exception ex) {
    1603                    log.debug("getMetadata(): H5Pclose(pcid {}) failure: ", pcid, ex);
    1604                }
    1605                close(did);
    1606            }
    1607        }
    1608
    1609        return attributeList;
    1610    }
    1611
    1612    /*
    1613     * (non-Javadoc)
    1614     *
    1615     * @see hdf.object.DataFormat#writeMetadata(java.lang.Object)
    1616     */
    1617    @Override
    1618    public void writeMetadata(Object info) throws Exception {
    1619        // only attribute metadata is supported.
    1620        if (!(info instanceof Attribute)) {
    1621            log.debug("writeMetadata(): Object not an Attribute");
    1622            return;
    1623        }
    1624
    1625        boolean attrExisted = false;
    1626        Attribute attr = (Attribute) info;
    1627        log.trace("writeMetadata(): {}", attr.getName());
    1628
    1629        if (attributeList == null) {
    1630            this.getMetadata();
    1631        }
    1632
    1633        if (attributeList != null)
    1634            attrExisted = attributeList.contains(attr);
    1635
    1636        getFileFormat().writeAttribute(this, attr, attrExisted);
    1637        // add the new attribute into attribute list
    1638        if (!attrExisted) {
    1639            attributeList.add(attr);
    1640            nAttributes = attributeList.size();
    1641        }
    1642    }
    1643
    1644    /*
    1645     * (non-Javadoc)
    1646     *
    1647     * @see hdf.object.DataFormat#removeMetadata(java.lang.Object)
    1648     */
    1649    @Override
    1650    public void removeMetadata(Object info) throws HDF5Exception {
    1651        // only attribute metadata is supported.
    1652        if (!(info instanceof Attribute)) {
    1653            log.debug("removeMetadata(): Object not an Attribute");
    1654            return;
    1655        }
    1656
    1657        Attribute attr = (Attribute) info;
    1658        log.trace("removeMetadata(): {}", attr.getName());
    1659        long did = open();
    1660        if (did >= 0) {
    1661            try {
    1662                H5.H5Adelete(did, attr.getName());
    1663                List<Attribute> attrList = getMetadata();
    1664                attrList.remove(attr);
    1665                nAttributes = attrList.size();
    1666            }
    1667            finally {
    1668                close(did);
    1669            }
    1670        }
    1671    }
    1672
    1673    /*
    1674     * (non-Javadoc)
    1675     *
    1676     * @see hdf.object.DataFormat#updateMetadata(java.lang.Object)
    1677     */
    1678    @Override
    1679    public void updateMetadata(Object info) throws HDF5Exception {
    1680        // only attribute metadata is supported.
    1681        if (!(info instanceof Attribute)) {
    1682            log.debug("updateMetadata(): Object not an Attribute");
    1683            return;
    1684        }
    1685
    1686        nAttributes = -1;
    1687    }
    1688
    1689    /*
    1690     * (non-Javadoc)
    1691     *
    1692     * @see hdf.object.HObject#setName(java.lang.String)
    1693     */
    1694    @Override
    1695    public void setName(String newName) throws Exception {
    1696        if (newName == null)
    1697            throw new IllegalArgumentException("The new name is NULL");
    1698
    1699        H5File.renameObject(this, newName);
    1700        super.setName(newName);
    1701    }
    1702
    1703    /**
    1704     * Resets selection of dataspace
    1705     */
    1706    private void resetSelection() {
    1707        for (int i = 0; i < rank; i++) {
    1708            startDims[i] = 0;
    1709            selectedDims[i] = 1;
    1710            if (selectedStride != null) {
    1711                selectedStride[i] = 1;
    1712            }
    1713        }
    1714
    1715        if (rank == 1) {
    1716            selectedIndex[0] = 0;
    1717            selectedDims[0] = dims[0];
    1718        }
    1719        else if (rank == 2) {
    1720            selectedIndex[0] = 0;
    1721            selectedIndex[1] = 1;
    1722            selectedDims[0] = dims[0];
    1723            selectedDims[1] = dims[1];
    1724        }
    1725        else if (rank > 2) {
    1726            // selectedIndex[0] = rank - 2; // columns
    1727            // selectedIndex[1] = rank - 1; // rows
    1728            // selectedIndex[2] = rank - 3;
    1729            selectedIndex[0] = 0; // width, the fastest dimension
    1730            selectedIndex[1] = 1; // height
    1731            selectedIndex[2] = 2; // frames
    1732            // selectedDims[rank - 1] = dims[rank - 1];
    1733            // selectedDims[rank - 2] = dims[rank - 2];
    1734            selectedDims[selectedIndex[0]] = dims[selectedIndex[0]];
    1735            selectedDims[selectedIndex[1]] = dims[selectedIndex[1]];
    1736        }
    1737
    1738        isDataLoaded = false;
    1739        setAllMemberSelection(true);
    1740    }
    1741
    1742    /**
    1743     * @deprecated Not for public use in the future. <br>
    1744     *             Using
    1745     *             {@link #create(String, Group, long[], long[], long[], int, String[], Datatype[], int[], long[][], Object)}
    1746     *
    1747     * @param name
    1748     *            the name of the dataset to create.
    1749     * @param pgroup
    1750     *            parent group where the new dataset is created.
    1751     * @param dims
    1752     *            the dimension size of the dataset.
    1753     * @param memberNames
    1754     *            the names of compound datatype
    1755     * @param memberDatatypes
    1756     *            the datatypes of the compound datatype
    1757     * @param memberSizes
    1758     *            the dim sizes of the members
    1759     * @param data
    1760     *            list of data arrays written to the new dataset, null if no data is written to the new
    1761     *            dataset.
    1762     *
    1763     * @return the new compound dataset if successful; otherwise returns null.
    1764     *
    1765     * @throws Exception
    1766     *             if there is a failure.
    1767     */
    1768    @Deprecated
    1769    public static Dataset create(String name, Group pgroup, long[] dims, String[] memberNames,
    1770            Datatype[] memberDatatypes, int[] memberSizes, Object data) throws Exception {
    1771        if ((pgroup == null) || (name == null) || (dims == null) || (memberNames == null) || (memberDatatypes == null)
    1772                || (memberSizes == null)) {
    1773            return null;
    1774        }
    1775
    1776        int nMembers = memberNames.length;
    1777        int memberRanks[] = new int[nMembers];
    1778        long memberDims[][] = new long[nMembers][1];
    1779        for (int i = 0; i < nMembers; i++) {
    1780            memberRanks[i] = 1;
    1781            memberDims[i][0] = memberSizes[i];
    1782        }
    1783
    1784        return H5CompoundDS.create(name, pgroup, dims, memberNames, memberDatatypes, memberRanks, memberDims, data);
    1785    }
    1786
    1787    /**
    1788     * @deprecated Not for public use in the future. <br>
    1789     *             Using
    1790     *             {@link #create(String, Group, long[], long[], long[], int, String[], Datatype[], int[], long[][], Object)}
    1791     *
    1792     * @param name
    1793     *            the name of the dataset to create.
    1794     * @param pgroup
    1795     *            parent group where the new dataset is created.
    1796     * @param dims
    1797     *            the dimension size of the dataset.
    1798     * @param memberNames
    1799     *            the names of compound datatype
    1800     * @param memberDatatypes
    1801     *            the datatypes of the compound datatype
    1802     * @param memberRanks
    1803     *            the ranks of the members
    1804     * @param memberDims
    1805     *            the dim sizes of the members
    1806     * @param data
    1807     *            list of data arrays written to the new dataset, null if no data is written to the new
    1808     *            dataset.
    1809     *
    1810     * @return the new compound dataset if successful; otherwise returns null.
    1811     *
    1812     * @throws Exception
    1813     *             if the dataset can not be created.
    1814     */
    1815    @Deprecated
    1816    public static Dataset create(String name, Group pgroup, long[] dims, String[] memberNames,
    1817            Datatype[] memberDatatypes, int[] memberRanks, long[][] memberDims, Object data) throws Exception {
    1818        return H5CompoundDS.create(name, pgroup, dims, null, null, -1, memberNames, memberDatatypes, memberRanks,
    1819                memberDims, data);
    1820    }
    1821
    1822    /**
    1823     * Creates a simple compound dataset in a file with/without chunking and compression.
    1824     * <p>
    1825     * This function provides an easy way to create a simple compound dataset in file by hiding tedious
    1826     * details of creating a compound dataset from users.
    1827     * <p>
    1828     * This function calls H5.H5Dcreate() to create a simple compound dataset in file. Nested compound
    1829     * dataset is not supported. The required information to create a compound dataset includes the
    1830     * name, the parent group and data space of the dataset, the names, datatypes and data spaces of the
    1831     * compound fields. Other information such as chunks, compression and the data buffer is optional.
    1832     * <p>
    1833     * The following example shows how to use this function to create a compound dataset in file.
    1834     *
    1835     * <pre>
    1836     * H5File file = null;
    1837     * String message = &quot;&quot;;
    1838     * Group pgroup = null;
    1839     * int[] DATA_INT = new int[DIM_SIZE];
    1840     * float[] DATA_FLOAT = new float[DIM_SIZE];
    1841     * String[] DATA_STR = new String[DIM_SIZE];
    1842     * long[] DIMs = { 50, 10 };
    1843     * long[] CHUNKs = { 25, 5 };
    1844     *
    1845     * try {
    1846     *     file = (H5File) H5FILE.open(fname, H5File.CREATE);
    1847     *     file.open();
    1848     *     pgroup = (Group) file.get(&quot;/&quot;);
    1849     * }
    1850     * catch (Exception ex) {
    1851     * }
    1852     *
    1853     * Vector data = new Vector();
    1854     * data.add(0, DATA_INT);
    1855     * data.add(1, DATA_FLOAT);
    1856     * data.add(2, DATA_STR);
    1857     *
    1858     * // create groups
    1859     * Datatype[] mdtypes = new H5Datatype[3];
    1860     * String[] mnames = { &quot;int&quot;, &quot;float&quot;, &quot;string&quot; };
    1861     * Dataset dset = null;
    1862     * try {
    1863     *     mdtypes[0] = new H5Datatype(Datatype.CLASS_INTEGER, 4, Datatype.NATIVE, Datatype.NATIVE);
    1864     *     mdtypes[1] = new H5Datatype(Datatype.CLASS_FLOAT, 4, Datatype.NATIVE, Datatype.NATIVE);
    1865     *     mdtypes[2] = new H5Datatype(Datatype.CLASS_STRING, STR_LEN, Datatype.NATIVE, Datatype.NATIVE);
    1866     *     dset = file.createCompoundDS(&quot;/CompoundDS&quot;, pgroup, DIMs, null, CHUNKs, 9, mnames, mdtypes, null, data);
    1867     * }
    1868     * catch (Exception ex) {
    1869     *     failed(message, ex, file);
    1870     *     return 1;
    1871     * }
    1872     * </pre>
    1873     *
    1874     * @param name
    1875     *            the name of the dataset to create.
    1876     * @param pgroup
    1877     *            parent group where the new dataset is created.
    1878     * @param dims
    1879     *            the dimension size of the dataset.
    1880     * @param maxdims
    1881     *            the max dimension size of the dataset. maxdims is set to dims if maxdims = null.
    1882     * @param chunks
    1883     *            the chunk size of the dataset. No chunking if chunk = null.
    1884     * @param gzip
    1885     *            GZIP compression level (1 to 9). 0 or negative values if no compression.
    1886     * @param memberNames
    1887     *            the names of compound datatype
    1888     * @param memberDatatypes
    1889     *            the datatypes of the compound datatype
    1890     * @param memberRanks
    1891     *            the ranks of the members
    1892     * @param memberDims
    1893     *            the dim sizes of the members
    1894     * @param data
    1895     *            list of data arrays written to the new dataset, null if no data is written to the new
    1896     *            dataset.
    1897     *
    1898     * @return the new compound dataset if successful; otherwise returns null.
    1899     *
    1900     * @throws Exception
    1901     *             if there is a failure.
    1902     */
    1903    public static Dataset create(String name, Group pgroup, long[] dims, long[] maxdims, long[] chunks, int gzip,
    1904            String[] memberNames, Datatype[] memberDatatypes, int[] memberRanks, long[][] memberDims, Object data)
    1905                    throws Exception {
    1906        H5CompoundDS dataset = null;
    1907        String fullPath = null;
    1908        long did = -1;
    1909        long tid = -1;
    1910        long plist = -1;
    1911        long sid = -1;
    1912
    1913        if ((pgroup == null) || (name == null) || (dims == null) || ((gzip > 0) && (chunks == null))
    1914                || (memberNames == null) || (memberDatatypes == null) || (memberRanks == null)
    1915                || (memberDims == null)) {
    1916            log.debug("create(): one or more parameters are null");
    1917            return null;
    1918        }
    1919
    1920        H5File file = (H5File) pgroup.getFileFormat();
    1921        if (file == null) {
    1922            log.debug("create(): parent group FileFormat is null");
    1923            return null;
    1924        }
    1925
    1926        String path = HObject.SEPARATOR;
    1927        if (!pgroup.isRoot()) {
    1928            path = pgroup.getPath() + pgroup.getName() + HObject.SEPARATOR;
    1929            if (name.endsWith("/")) {
    1930                name = name.substring(0, name.length() - 1);
    1931            }
    1932            int idx = name.lastIndexOf('/');
    1933            if (idx >= 0) {
    1934                name = name.substring(idx + 1);
    1935            }
    1936        }
    1937
    1938        fullPath = path + name;
    1939
    1940        int typeSize = 0;
    1941        int nMembers = memberNames.length;
    1942        long[] mTypes = new long[nMembers];
    1943        int memberSize = 1;
    1944        for (int i = 0; i < nMembers; i++) {
    1945            memberSize = 1;
    1946            for (int j = 0; j < memberRanks[i]; j++) {
    1947                memberSize *= memberDims[i][j];
    1948            }
    1949
    1950            mTypes[i] = -1;
    1951            // the member is an array
    1952            if ((memberSize > 1) && (!memberDatatypes[i].isString())) {
    1953                long tmptid = -1;
    1954                if ((tmptid = memberDatatypes[i].createNative()) >= 0) {
    1955                    try {
    1956                        mTypes[i] = H5.H5Tarray_create(tmptid, memberRanks[i], memberDims[i]);
    1957                    }
    1958                    finally {
    1959                        try {
    1960                            H5.H5Tclose(tmptid);
    1961                        }
    1962                        catch (Exception ex) {
    1963                            log.debug("create(): H5Tclose(tmptid {}) failure: ", tmptid, ex);
    1964                        }
    1965                    }
    1966                }
    1967            }
    1968            else {
    1969                mTypes[i] = memberDatatypes[i].createNative();
    1970            }
    1971            try {
    1972                typeSize += H5.H5Tget_size(mTypes[i]);
    1973            }
    1974            catch (Exception ex) {
    1975                log.debug("create(): array create H5Tget_size:", ex);
    1976
    1977                while (i > 0) {
    1978                    try {
    1979                        H5.H5Tclose(mTypes[i]);
    1980                    }
    1981                    catch (HDF5Exception ex2) {
    1982                        log.debug("create(): H5Tclose(mTypes[{}] {}) failure: ", i, mTypes[i], ex2);
    1983                    }
    1984                    i--;
    1985                }
    1986                throw ex;
    1987            }
    1988        } //  (int i = 0; i < nMembers; i++) {
    1989
    1990        // setup chunking and compression
    1991        boolean isExtentable = false;
    1992        if (maxdims != null) {
    1993            for (int i = 0; i < maxdims.length; i++) {
    1994                if (maxdims[i] == 0) {
    1995                    maxdims[i] = dims[i];
    1996                }
    1997                else if (maxdims[i] < 0) {
    1998                    maxdims[i] = HDF5Constants.H5S_UNLIMITED;
    1999                }
    2000
    2001                if (maxdims[i] != dims[i]) {
    2002                    isExtentable = true;
    2003                }
    2004            }
    2005        }
    2006
    2007        // HDF5 requires you to use chunking in order to define extendible
    2008        // datasets. Chunking makes it possible to extend datasets efficiently,
    2009        // without having to reorganize storage excessively. Using default size
    2010        // of 64x...which has good performance
    2011        if ((chunks == null) && isExtentable) {
    2012            chunks = new long[dims.length];
    2013            for (int i = 0; i < dims.length; i++)
    2014                chunks[i] = Math.min(dims[i], 64);
    2015        }
    2016
    2017        // prepare the dataspace and datatype
    2018        int rank = dims.length;
    2019
    2020        try {
    2021            sid = H5.H5Screate_simple(rank, dims, maxdims);
    2022
    2023            // figure out creation properties
    2024            plist = HDF5Constants.H5P_DEFAULT;
    2025
    2026            tid = H5.H5Tcreate(HDF5Constants.H5T_COMPOUND, typeSize);
    2027            int offset = 0;
    2028            for (int i = 0; i < nMembers; i++) {
    2029                H5.H5Tinsert(tid, memberNames[i], offset, mTypes[i]);
    2030                offset += H5.H5Tget_size(mTypes[i]);
    2031            }
    2032
    2033            if (chunks != null) {
    2034                plist = H5.H5Pcreate(HDF5Constants.H5P_DATASET_CREATE);
    2035
    2036                H5.H5Pset_layout(plist, HDF5Constants.H5D_CHUNKED);
    2037                H5.H5Pset_chunk(plist, rank, chunks);
    2038
    2039                // compression requires chunking
    2040                if (gzip > 0) {
    2041                    H5.H5Pset_deflate(plist, gzip);
    2042                }
    2043            }
    2044
    2045            long fid = file.getFID();
    2046
    2047            did = H5.H5Dcreate(fid, fullPath, tid, sid, HDF5Constants.H5P_DEFAULT, plist, HDF5Constants.H5P_DEFAULT);
    2048            dataset = new H5CompoundDS(file, name, path);
    2049        }
    2050        finally {
    2051            try {
    2052                H5.H5Pclose(plist);
    2053            }
    2054            catch (HDF5Exception ex) {
    2055                log.debug("create(): H5Pclose(plist {}) failure: ", plist, ex);
    2056            }
    2057            try {
    2058                H5.H5Sclose(sid);
    2059            }
    2060            catch (HDF5Exception ex) {
    2061                log.debug("create(): H5Sclose(sid {}) failure: ", sid, ex);
    2062            }
    2063            try {
    2064                H5.H5Tclose(tid);
    2065            }
    2066            catch (HDF5Exception ex) {
    2067                log.debug("create(): H5Tclose(tid {}) failure: ", tid, ex);
    2068            }
    2069            try {
    2070                H5.H5Dclose(did);
    2071            }
    2072            catch (HDF5Exception ex) {
    2073                log.debug("create(): H5Dclose(did {}) failure: ", did, ex);
    2074            }
    2075
    2076            for (int i = 0; i < nMembers; i++) {
    2077                try {
    2078                    H5.H5Tclose(mTypes[i]);
    2079                }
    2080                catch (HDF5Exception ex) {
    2081                    log.debug("create(): H5Tclose(mTypes[{}] {}) failure: ", i, mTypes[i], ex);
    2082                }
    2083            }
    2084        }
    2085
    2086        if (dataset != null) {
    2087            pgroup.addToMemberList(dataset);
    2088            if (data != null) {
    2089                dataset.init();
    2090                long selected[] = dataset.getSelectedDims();
    2091                for (int i = 0; i < rank; i++) {
    2092                    selected[i] = dims[i];
    2093                }
    2094                dataset.write(data);
    2095            }
    2096        }
    2097
    2098        return dataset;
    2099    }
    2100
    2101    /*
    2102     * (non-Javadoc)
    2103     *
    2104     * @see hdf.object.Dataset#isString(long)
    2105     */
    2106    @Override
    2107    public boolean isString(long tid) {
    2108        boolean b = false;
    2109        try {
    2110            b = (HDF5Constants.H5T_STRING == H5.H5Tget_class(tid));
    2111        }
    2112        catch (Exception ex) {
    2113            b = false;
    2114        }
    2115
    2116        return b;
    2117    }
    2118
    2119    /*
    2120     * (non-Javadoc)
    2121     *
    2122     * @see hdf.object.Dataset#getSize(long)
    2123     */
    2124    @Override
    2125    public long getSize(long tid) {
    2126        long tsize = -1;
    2127
    2128        try {
    2129            tsize = H5.H5Tget_size(tid);
    2130        }
    2131        catch (Exception ex) {
    2132            tsize = -1;
    2133        }
    2134
    2135        return tsize;
    2136    }
    2137
    2138    /*
    2139     * (non-Javadoc)
    2140     *
    2141     * @see hdf.object.Dataset#isVirtual()
    2142     */
    2143    @Override
    2144    public boolean isVirtual() {
    2145        return isVirtual;
    2146    }
    2147
    2148    /*
    2149     * (non-Javadoc)
    2150     *
    2151     * @see hdf.object.Dataset#getVirtualFilename(int)
    2152     */
    2153    @Override
    2154    public String getVirtualFilename(int index) {
    2155        return (isVirtual) ? virtualNameList.get(index) : null;
    2156    }
    2157
    2158    /*
    2159     * (non-Javadoc)
    2160     *
    2161     * @see hdf.object.Dataset#getVirtualMaps()
    2162     */
    2163    @Override
    2164    public int getVirtualMaps() {
    2165        return (isVirtual) ? virtualNameList.size() : -1;
    2166    }
    2167
    2168}