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