    001/*****************************************************************************
    002 * Copyright by The HDF Group.                                               *
    003 * Copyright by the Board of Trustees of the University of Illinois.         *
    004 * All rights reserved.                                                      *
    005 *                                                                           *
    006 * This file is part of the HDF Java Products distribution.                  *
    007 * The full copyright notice, including terms governing use, modification,   *
    008 * and redistribution, is contained in the files COPYING and Copyright.html. *
    009 * COPYING can be found at the root of the source code distribution tree.    *
    010 * Or, see https://support.hdfgroup.org/products/licenses.html               *
    011 * If you do not have access to either file, you may request a copy from     *
    012 * help@hdfgroup.org.                                                        *
    013 ****************************************************************************/
    014
    015package hdf.object;
    016
    017/**
    018 * A CompoundDS is a dataset with compound datatype.
    019 * <p>
    020 * A compound datatype is an aggregation of one or more datatypes. Each member
    021 * of a compound type has a name which is unique within that type, and a
    022 * datatype of that member in a compound datum. Compound datatypes can be nested,
    023 * i.e. members of a compound datatype can be some other compound datatype.
    024 * <p>
    025 * For more details on compound datatypes,
    026 * see <b> <a href="https://support.hdfgroup.org/HDF5/doc/UG/HDF5_Users_Guide-Responsive%20HTML5/index.html">HDF5 User's Guide</a> </b>
    027 * <p>
    028 * Since Java cannot handle C-structured compound data, data in a compound dataset
    029 * is loaded in to an Java List. Each element of the list is a data array that
    030 * corresponds to a compound field. The data is read/written by compound field.
    031 * <p>
    032 * For example, if compound dataset "comp" has the following nested structure,
    033 * and member datatypes
    034 *
    035 * <pre>
    036 * comp --&gt; m01 (int)
    037 * comp --&gt; m02 (float)
    038 * comp --&gt; nest1 --&gt; m11 (char)
    039 * comp --&gt; nest1 --&gt; m12 (String)
    040 * comp --&gt; nest1 --&gt; nest2 --&gt; m21 (long)
    041 * comp --&gt; nest1 --&gt; nest2 --&gt; m22 (double)
    042 * </pre>
    043 *
    044 * The data object is a Java list of six arrays: {int[], float[], char[],
    045 * Stirng[], long[] and double[]}.
    046 *
    047 *
    048 * @version 1.1 9/4/2007
    049 * @author Peter X. Cao
    050 */
    051public abstract class CompoundDS extends Dataset implements CompoundDataFormat {
    052    private static final long serialVersionUID = -4880399929644095662L;
    053
    054    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CompoundDS.class);
    055
    056    /**
    057     * A single character to separate the names of nested compound fields. An
    058     * extended ASCII character, 0x95, is used to avoid common characters in
    059     * compound names.
    060     */
    061    public static final String SEPARATOR = "\u0095";
    062
    063    /**
    064     * The number of members of the compound dataset.
    065     */
    066    protected int numberOfMembers;
    067
    068    /**
    069     * The names of members of the compound dataset.
    070     */
    071    protected String[] memberNames;
    072
    073    /**
    074     * Returns array containing the total number of elements of the members of
    075     * this compound dataset.
    076     * <p>
    077     * For example, a compound dataset COMP has members of A, B and C as
    078     *
    079     * <pre>
    080     *     COMP {
    081     *         int A;
    082     *         float B[5];
    083     *         double C[2][3];
    084     *     }
    085     * </pre>
    086     *
    087     * memberOrders is an integer array of {1, 5, 6} to indicate that member A
    088     * has one element, member B has 5 elements, and member C has 6 elements.
    089     */
    090    protected int[] memberOrders;
    091
    092    /**
    093     * The dimension sizes of each member.
    094     * <p>
    095     * The i-th element of the Object[] is an integer array (int[]) that
    096     * contains the dimension sizes of the i-th member.
    097     */
    098    protected transient Object[] memberDims;
    099
    100    /**
    101     * The datatypes of compound members.
    102     */
    103    protected Datatype[] memberTypes;
    104
    105    /**
    106     * The array to store flags to indicate if a member of this compound
    107     * dataset is selected for read/write.
    108     * <p>
    109     * If a member is selected, the read/write will perform on the member.
    110     * Applications such as HDFView will only display the selected members of
    111     * the compound dataset.
    112     *
    113     * <pre>
    114     * For example, if a compound dataset has four members
    115     *     String[] memberNames = {"X", "Y", "Z", "TIME"};
    116     * and
    117     *     boolean[] isMemberSelected = {true, false, false, true};
    118     * members "X" and "TIME" are selected for read and write.
    119     * </pre>
    120     */
    121    protected boolean[] isMemberSelected;
    122
    123    /**
    124     * Constructs a CompoundDS object with the given file, dataset name and path.
    125     * <p>
    126     * The dataset object represents an existing dataset in the file. For
    127     * example, new H5CompoundDS(file, "dset1", "/g0/") constructs a dataset
    128     * object that corresponds to the dataset, "dset1", at group "/g0/".
    129     * <p>
    130     * This object is usually constructed at FileFormat.open(), which loads the
    131     * file structure and object information into memory. It is rarely used
    132     * elsewhere.
    133     *
    134     * @param theFile
    135     *            the file that contains the dataset.
    136     * @param dsName
    137     *            the name of the CompoundDS, e.g. "compDS".
    138     * @param dsPath
    139     *            the full path of the CompoundDS, e.g. "/g1".
    140     */
    141    public CompoundDS(FileFormat theFile, String dsName, String dsPath) {
    142        this(theFile, dsName, dsPath, null);
    143    }
    144
    145    /**
    146     * @deprecated Not for public use in the future.<br>
    147     *             Using {@link #CompoundDS(FileFormat, String, String)}
    148     *
    149     * @param theFile
    150     *            the file that contains the dataset.
    151     * @param dsName
    152     *            the name of the CompoundDS, e.g. "compDS".
    153     * @param dsPath
    154     *            the full path of the CompoundDS, e.g. "/g1".
    155     * @param oid
    156     *            the oid of the CompoundDS.
    157     */
    158    @Deprecated
    159    public CompoundDS(FileFormat theFile, String dsName, String dsPath, long[] oid) {
    160        super(theFile, dsName, dsPath, oid);
    161
    162        numberOfMembers = 0;
    163        memberNames = null;
    164        isMemberSelected = null;
    165        memberTypes = null;
    166    }
    167
    168    /**
    169     * Returns the number of members of the compound dataset.
    170     *
    171     * @return the number of members of the compound dataset.
    172     */
    173    @Override
    174    public final int getMemberCount() {
    175        return numberOfMembers;
    176    }
    177
    178    /**
    179     * Returns the number of selected members of the compound dataset.
    180     *
    181     * Selected members are the compound fields which are selected for
    182     * read/write.
    183     * <p>
    184     * For example, in a compound datatype of {int A, float B, char[] C},
    185     * users can choose to retrieve only {A, C} from the dataset. In this
    186     * case, getSelectedMemberCount() returns two.
    187     *
    188     * @return the number of selected members.
    189     */
    190    @Override
    191    public final int getSelectedMemberCount() {
    192        int count = 0;
    193
    194        if (isMemberSelected != null) {
    195            for (int i = 0; i < isMemberSelected.length; i++) {
    196                if (isMemberSelected[i]) {
    197                    count++;
    198                }
    199            }
    200        }
    201        log.trace("count of selected members={}", count);
    202
    203        return count;
    204    }
    205
    206    /**
    207     * Returns the names of the members of the compound dataset. The names of
    208     * compound members are stored in an array of Strings.
    209     * <p>
    210     * For example, for a compound datatype of {int A, float B, char[] C}
    211     * getMemberNames() returns ["A", "B", "C"}.
    212     *
    213     * @return the names of compound members.
    214     */
    215    @Override
    216    public final String[] getMemberNames() {
    217        return memberNames;
    218    }
    219
    220    /**
    221     * Returns an array of the names of the selected members of the compound dataset.
    222     *
    223     * @return an array of the names of the selected members of the compound dataset.
    224     */
    225    public final String[] getSelectedMemberNames() {
    226        if (isMemberSelected == null) {
    227            log.debug("getSelectedMemberNames(): isMemberSelected array is null");
    228            return memberNames;
    229        }
    230
    231        int idx = 0;
    232        String[] names = new String[getSelectedMemberCount()];
    233        for (int i = 0; i < isMemberSelected.length; i++) {
    234            if (isMemberSelected[i]) {
    235                names[idx++] = memberNames[i];
    236            }
    237        }
    238
    239        return names;
    240    }
    241
    242    /**
    243     * Checks if a member of the compound dataset is selected for read/write.
    244     *
    245     * @param idx
    246     *            the index of compound member.
    247     *
    248     * @return true if the i-th memeber is selected; otherwise returns false.
    249     */
    250    @Override
    251    public final boolean isMemberSelected(int idx) {
    252        if ((isMemberSelected != null) && (isMemberSelected.length > idx)) {
    253            return isMemberSelected[idx];
    254        }
    255        else {
    256            return false;
    257        }
    258    }
    259
    260    /**
    261     * Selects the i-th member for read/write.
    262     *
    263     * @param idx
    264     *            the index of compound member.
    265     */
    266    @Override
    267    public final void selectMember(int idx) {
    268        if ((isMemberSelected != null) && (isMemberSelected.length > idx)) {
    269            isMemberSelected[idx] = true;
    270        }
    271    }
    272
    273    /**
    274     * Selects/deselects all members.
    275     *
    276     * @param selectAll
    277     *            The indicator to select or deselect all members. If true, all
    278     *            members are selected for read/write. If false, no member is
    279     *            selected for read/write.
    280     */
    281    @Override
    282    public final void setAllMemberSelection(boolean selectAll) {
    283        if (isMemberSelected == null) {
    284            return;
    285        }
    286
    287        for (int i = 0; i < isMemberSelected.length; i++) {
    288            isMemberSelected[i] = selectAll;
    289        }
    290    }
    291
    292    /**
    293     * Returns array containing the total number of elements of the members of
    294     * the compound dataset.
    295     * <p>
    296     * For example, a compound dataset COMP has members of A, B and C as
    297     *
    298     * <pre>
    299     *     COMP {
    300     *         int A;
    301     *         float B[5];
    302     *         double C[2][3];
    303     *     }
    304     * </pre>
    305     *
    306     * getMemberOrders() will return an integer array of {1, 5, 6} to indicate
    307     * that member A has one element, member B has 5 elements, and member C has
    308     * 6 elements.
    309     *
    310     * @return the array containing the total number of elements of the members
    311     *         of compound.
    312     */
    313    @Override
    314    public final int[] getMemberOrders() {
    315        return memberOrders;
    316    }
    317
    318    /**
    319     * Returns array containing the total number of elements of the selected
    320     * members of the compound dataset.
    321     *
    322     * <p>
    323     * For example, a compound dataset COMP has members of A, B and C as
    324     *
    325     * <pre>
    326     *     COMP {
    327     *         int A;
    328     *         float B[5];
    329     *         double C[2][3];
    330     *     }
    331     * </pre>
    332     *
    333     * If A and B are selected, getSelectedMemberOrders() returns an array of
    334     * {1, 5}
    335     *
    336     * @return array containing the total number of elements of the selected
    337     *         members of compound.
    338     */
    339    @Override
    340    public final int[] getSelectedMemberOrders() {
    341        if (isMemberSelected == null) {
    342            log.debug("getSelectedMemberOrders(): isMemberSelected array is null");
    343            return memberOrders;
    344        }
    345
    346        int idx = 0;
    347        int[] orders = new int[getSelectedMemberCount()];
    348        for (int i = 0; i < isMemberSelected.length; i++) {
    349            if (isMemberSelected[i]) {
    350                orders[idx++] = memberOrders[i];
    351            }
    352        }
    353
    354        return orders;
    355    }
    356
    357    /**
    358     * Returns the dimension sizes of the i-th member.
    359     * <p>
    360     * For example, a compound dataset COMP has members of A, B and C as
    361     *
    362     * <pre>
    363     *     COMP {
    364     *         int A;
    365     *         float B[5];
    366     *         double C[2][3];
    367     *     }
    368     * </pre>
    369     *
    370     * getMemberDims(2) returns an array of {2, 3}, while getMemberDims(1)
    371     * returns an array of {5}, and getMemberDims(0) returns null.
    372     *
    373     * @param i  the i-th member
    374     *
    375     * @return the dimension sizes of the i-th member, null if the compound
    376     *         member is not an array.
    377     */
    378    @Override
    379    public final int[] getMemberDims(int i) {
    380        if (memberDims == null) {
    381            return null;
    382        }
    383        return (int[]) memberDims[i];
    384    }
    385
    386    /**
    387     * Returns an array of datatype objects of compound members.
    388     * <p>
    389     * Each member of a compound dataset has its own datatype. The datatype of a
    390     * member can be atomic or other compound datatype (nested compound).
    391     * Sub-classes set up the datatype objects at init().
    392     * <p>
    393     *
    394     * @return the array of datatype objects of the compound members.
    395     */
    396    @Override
    397    public final Datatype[] getMemberTypes() {
    398        return memberTypes;
    399    }
    400
    401    /**
    402     * Returns an array of datatype objects of selected compound members.
    403     *
    404     * @return an array of datatype objects of selected compound members.
    405     */
    406    @Override
    407    public final Datatype[] getSelectedMemberTypes() {
    408        if (isMemberSelected == null) {
    409            log.debug("getSelectedMemberTypes(): isMemberSelected array is null");
    410            return memberTypes;
    411        }
    412
    413        int idx = 0;
    414        Datatype[] types = new Datatype[getSelectedMemberCount()];
    415        for (int i = 0; i < isMemberSelected.length; i++) {
    416            if (isMemberSelected[i]) {
    417                types[idx++] = memberTypes[i];
    418            }
    419        }
    420
    421        return types;
    422    }
    423
    424    /**
    425     * @deprecated Not implemented for compound dataset.
    426     */
    427    @Deprecated
    428    @Override
    429    public Dataset copy(Group pgroup, String name, long[] dims, Object data)
    430            throws Exception {
    431        throw new UnsupportedOperationException(
    432                "Writing a subset of a compound dataset to a new dataset is not implemented.");
    433    }
    434}