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, see <b> {@link <a
026 * href="http://hdfgroup.org/HDF5/doc/UG/index.html">HDF5 User's Guide</a>}
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 --> m01 (int)
037 * comp --> m02 (float)
038 * comp --> nest1 --> m11 (char)
039 * comp --> nest1 --> m12 (String)
040 * comp --> nest1 --> nest2 --> m21 (long)
041 * comp --> nest1 --> nest2 --> 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 * <p>
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     * <p>
137     * 
138     * @param theFile
139     *            the file that contains the dataset.
140     * @param name
141     *            the name of the CompoundDS, e.g. "compDS".
142     * @param path
143     *            the path of the CompoundDS, e.g. "/g1".
144     */
145    public CompoundDS(FileFormat theFile, String name, String path) {
146        this(theFile, name, path, null);
147    }
148
149    /**
150     * @deprecated Not for public use in the future.<br>
151     *             Using {@link #CompoundDS(FileFormat, String, String)}
152     */
153    @Deprecated
154    public CompoundDS(FileFormat theFile, String name, String path, long[] oid) {
155        super(theFile, name, path, oid);
156
157        numberOfMembers = 0;
158        memberNames = null;
159        isMemberSelected = null;
160        memberTypes = null;
161    }
162
163    /**
164     * Returns the number of members of the compound dataset.
165     * 
166     * @return the number of members of the compound dataset.
167     */
168    public final int getMemberCount() {
169        return numberOfMembers;
170    }
171
172    /**
173     * Returns the number of selected members of the compound dataset.
174     * 
175     * Selected members are the compound fields which are selected for
176     * read/write.
177     * <p>
178     * For example, in a compound datatype of {int A, float B, char[] C}, users
179     * can choose to retrieve only {A, C} from dataset. In this case,
180     * getSelectedMemberCount() returns two.
181     * 
182     * @return the number of selected members.
183     */
184    public final int getSelectedMemberCount() {
185        int count = 0;
186
187        if (isMemberSelected != null) {
188            for (int i = 0; i < isMemberSelected.length; i++) {
189                if (isMemberSelected[i]) {
190                    count++;
191                }
192            }
193        }
194        log.trace("count of selected members={}", count);
195
196        return count;
197    }
198
199    /**
200     * Returns the names of the members of the compound dataset. The names of
201     * compound members are stored in an array of Strings.
202     * <p>
203     * For example, for a compound datatype of {int A, float B, char[] C}
204     * getMemberNames() returns ["A", "B", "C"}.
205     * 
206     * @return the names of compound members.
207     */
208    public final String[] getMemberNames() {
209        return memberNames;
210    }
211
212    /**
213     * Checks if a member of compound is selected for read/write.
214     * 
215     * @param idx
216     *            the index of compound member.
217     * 
218     * @return true if the i-th memeber is selected; otherwise returns false.
219     */
220    public final boolean isMemberSelected(int idx) {
221        if ((isMemberSelected != null) && (isMemberSelected.length > idx)) {
222            return isMemberSelected[idx];
223        }
224        else {
225            return false;
226        }
227    }
228
229    /**
230     * Selects the i-th member for read/write.
231     * 
232     * @param idx
233     *            the index of compound member.
234     */
235    public final void selectMember(int idx) {
236        if ((isMemberSelected != null) && (isMemberSelected.length > idx)) {
237            isMemberSelected[idx] = true;
238        }
239    }
240
241    /**
242     * Selects/deselects all members.
243     * 
244     * @param isSelected
245     *            The indicator to select or deselect all members. If true, all
246     *            members are selected for read/write. If false, no member is
247     *            selected for read/write.
248     */
249    public final void setMemberSelection(boolean isSelected) {
250        if (isMemberSelected == null) {
251            return;
252        }
253
254        for (int i = 0; i < isMemberSelected.length; i++) {
255            isMemberSelected[i] = isSelected;
256        }
257    }
258
259    /**
260     * Returns array containing the total number of elements of the members of
261     * compound.
262     * <p>
263     * For example, a compound dataset COMP has members of A, B and C as
264     * 
265     * <pre>
266     *     COMP {
267     *         int A;
268     *         float B[5];
269     *         double C[2][3];
270     *     }
271     * </pre>
272     * 
273     * getMemberOrders() will return an integer array of {1, 5, 6} to indicate
274     * that member A has one element, member B has 5 elements, and member C has
275     * 6 elements.
276     * 
277     * @return the array containing the total number of elements of the members
278     *         of compound.
279     */
280    public final int[] getMemberOrders() {
281        return memberOrders;
282    }
283
284    /**
285     * Returns array containing the total number of elements of the elected
286     * members of compound.
287     * 
288     * <p>
289     * For example, a compound dataset COMP has members of A, B and C as
290     * 
291     * <pre>
292     *     COMP {
293     *         int A;
294     *         float B[5];
295     *         double C[2][3];
296     *     }
297     * </pre>
298     * 
299     * If A and B are selected, getSelectedMemberOrders() returns an array of
300     * {1, 5}
301     * 
302     * @return array containing the total number of elements of the selected
303     *         members of compound.
304     */
305    public final int[] getSelectedMemberOrders() {
306        if (isMemberSelected == null) {
307            return memberOrders;
308        }
309
310        int idx = 0;
311        int[] orders = new int[getSelectedMemberCount()];
312        for (int i = 0; i < isMemberSelected.length; i++) {
313            if (isMemberSelected[i]) {
314                orders[idx++] = memberOrders[i];
315            }
316        }
317
318        return orders;
319    }
320
321    /**
322     * Returns the dimension sizes of of the i-th member.
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     * getMemberDims(2) returns an array of {2, 3}, while getMemberDims(1)
335     * returns an array of {5}, getMemberDims(0) returns null.
336     * 
337     * @return the dimension sizes of of the i-th member, null if the compound
338     *         member is not an array.
339     */
340    public final int[] getMemberDims(int i) {
341        if (memberDims == null) {
342            return null;
343        }
344        return (int[]) memberDims[i];
345    }
346
347    /**
348     * Returns an array of datatype objects of compound members.
349     * <p>
350     * Each member of a compound dataset has its own datatype. The datatype of a
351     * member can be atomic or other compound datatype (nested compound).
352     * Sub-classes set up the datatype objects at init().
353     * <p>
354     * 
355     * @return the array of datatype objects of the compound members.
356     */
357    public final Datatype[] getMemberTypes() {
358        return memberTypes;
359    }
360
361    /**
362     * Returns an array of datatype objects of selected compound members.
363     * 
364     * @return an array of datatype objects of selected compound members.
365     */
366    public final Datatype[] getSelectedMemberTypes() {
367        if (isMemberSelected == null) {
368            return memberTypes;
369        }
370
371        int idx = 0;
372        Datatype[] types = new Datatype[getSelectedMemberCount()];
373        for (int i = 0; i < isMemberSelected.length; i++) {
374            if (isMemberSelected[i]) {
375                types[idx++] = memberTypes[i];
376            }
377        }
378
379        return types;
380    }
381
382    /**
383     * @deprecated Not implemented for compound dataset.
384     */
385    @Deprecated
386    @Override
387    public Dataset copy(Group pgroup, String name, long[] dims, Object data)
388            throws Exception {
389        throw new UnsupportedOperationException(
390                "Writing a subset of a compound dataset to a new dataset is not implemented.");
391    }
392
393}