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            log.trace("getSelectedMemberNames(): finish");
229            return memberNames;
230        }
231
232        int idx = 0;
233        String[] names = new String[getSelectedMemberCount()];
234        for (int i = 0; i < isMemberSelected.length; i++) {
235            if (isMemberSelected[i]) {
236                names[idx++] = memberNames[i];
237            }
238        }
239
240        return names;
241    }
242
243    /**
244     * Checks if a member of the compound dataset is selected for read/write.
245     *
246     * @param idx
247     *            the index of compound member.
248     *
249     * @return true if the i-th memeber is selected; otherwise returns false.
250     */
251    @Override
252    public final boolean isMemberSelected(int idx) {
253        if ((isMemberSelected != null) && (isMemberSelected.length > idx)) {
254            return isMemberSelected[idx];
255        }
256        else {
257            return false;
258        }
259    }
260
261    /**
262     * Selects the i-th member for read/write.
263     *
264     * @param idx
265     *            the index of compound member.
266     */
267    @Override
268    public final void selectMember(int idx) {
269        if ((isMemberSelected != null) && (isMemberSelected.length > idx)) {
270            isMemberSelected[idx] = true;
271        }
272    }
273
274    /**
275     * Selects/deselects all members.
276     *
277     * @param selectAll
278     *            The indicator to select or deselect all members. If true, all
279     *            members are selected for read/write. If false, no member is
280     *            selected for read/write.
281     */
282    @Override
283    public final void setAllMemberSelection(boolean selectAll) {
284        if (isMemberSelected == null) {
285            return;
286        }
287
288        for (int i = 0; i < isMemberSelected.length; i++) {
289            isMemberSelected[i] = selectAll;
290        }
291    }
292
293    /**
294     * Returns array containing the total number of elements of the members of
295     * the compound dataset.
296     * <p>
297     * For example, a compound dataset COMP has members of A, B and C as
298     *
299     * <pre>
300     *     COMP {
301     *         int A;
302     *         float B[5];
303     *         double C[2][3];
304     *     }
305     * </pre>
306     *
307     * getMemberOrders() will return an integer array of {1, 5, 6} to indicate
308     * that member A has one element, member B has 5 elements, and member C has
309     * 6 elements.
310     *
311     * @return the array containing the total number of elements of the members
312     *         of compound.
313     */
314    @Override
315    public final int[] getMemberOrders() {
316        return memberOrders;
317    }
318
319    /**
320     * Returns array containing the total number of elements of the selected
321     * members of the compound dataset.
322     *
323     * <p>
324     * For example, a compound dataset COMP has members of A, B and C as
325     *
326     * <pre>
327     *     COMP {
328     *         int A;
329     *         float B[5];
330     *         double C[2][3];
331     *     }
332     * </pre>
333     *
334     * If A and B are selected, getSelectedMemberOrders() returns an array of
335     * {1, 5}
336     *
337     * @return array containing the total number of elements of the selected
338     *         members of compound.
339     */
340    @Override
341    public final int[] getSelectedMemberOrders() {
342        log.trace("getSelectedMemberOrders(): start");
343
344        if (isMemberSelected == null) {
345            log.debug("getSelectedMemberOrders(): isMemberSelected array is null");
346            log.trace("getSelectedMemberOrders(): finish");
347            return memberOrders;
348        }
349
350        int idx = 0;
351        int[] orders = new int[getSelectedMemberCount()];
352        for (int i = 0; i < isMemberSelected.length; i++) {
353            if (isMemberSelected[i]) {
354                orders[idx++] = memberOrders[i];
355            }
356        }
357
358        log.trace("getSelectedMemberOrders(): finish");
359
360        return orders;
361    }
362
363    /**
364     * Returns the dimension sizes of the i-th member.
365     * <p>
366     * For example, a compound dataset COMP has members of A, B and C as
367     *
368     * <pre>
369     *     COMP {
370     *         int A;
371     *         float B[5];
372     *         double C[2][3];
373     *     }
374     * </pre>
375     *
376     * getMemberDims(2) returns an array of {2, 3}, while getMemberDims(1)
377     * returns an array of {5}, and getMemberDims(0) returns null.
378     *
379     * @param i  the i-th member
380     *
381     * @return the dimension sizes of the i-th member, null if the compound
382     *         member is not an array.
383     */
384    @Override
385    public final int[] getMemberDims(int i) {
386        if (memberDims == null) {
387            return null;
388        }
389        return (int[]) memberDims[i];
390    }
391
392    /**
393     * Returns an array of datatype objects of compound members.
394     * <p>
395     * Each member of a compound dataset has its own datatype. The datatype of a
396     * member can be atomic or other compound datatype (nested compound).
397     * Sub-classes set up the datatype objects at init().
398     * <p>
399     *
400     * @return the array of datatype objects of the compound members.
401     */
402    @Override
403    public final Datatype[] getMemberTypes() {
404        return memberTypes;
405    }
406
407    /**
408     * Returns an array of datatype objects of selected compound members.
409     *
410     * @return an array of datatype objects of selected compound members.
411     */
412    @Override
413    public final Datatype[] getSelectedMemberTypes() {
414        log.trace("getSelectedMemberTypes(): start");
415
416        if (isMemberSelected == null) {
417            log.debug("getSelectedMemberTypes(): isMemberSelected array is null");
418            log.trace("getSelectedMemberTypes(): finish");
419            return memberTypes;
420        }
421
422        int idx = 0;
423        Datatype[] types = new Datatype[getSelectedMemberCount()];
424        for (int i = 0; i < isMemberSelected.length; i++) {
425            if (isMemberSelected[i]) {
426                types[idx++] = memberTypes[i];
427            }
428        }
429
430        log.trace("getSelectedMemberTypes(): finish");
431
432        return types;
433    }
434
435    /**
436     * @deprecated Not implemented for compound dataset.
437     */
438    @Deprecated
439    @Override
440    public Dataset copy(Group pgroup, String name, long[] dims, Object data)
441            throws Exception {
442        throw new UnsupportedOperationException(
443                "Writing a subset of a compound dataset to a new dataset is not implemented.");
444    }
445}