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