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 COPYING file, which can be found  *
009 * at the root of the source code distribution tree,                         *
010 * or in https://www.hdfgroup.org/licenses.                                  *
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.h4;
016
017import java.util.List;
018import java.util.Vector;
019
020import hdf.hdflib.HDFConstants;
021import hdf.hdflib.HDFException;
022import hdf.hdflib.HDFLibrary;
023import hdf.object.Attribute;
024import hdf.object.CompoundDS;
025import hdf.object.Dataset;
026import hdf.object.Datatype;
027import hdf.object.FileFormat;
028import hdf.object.Group;
029import hdf.object.MetaDataContainer;
030import hdf.object.h4.H4CompoundAttribute;
031
032/**
033 * H4Vdata describes a multi-dimension array of HDF4 vdata, inheriting CompoundDS.
034 *
035 * A vdata is like a table that consists of a collection of records whose values
036 * are stored in fixed-length fields. All records have the same structure and
037 * all values in each field have the same data type. Vdatas are uniquely
038 * identified by a name, a class, and a series of individual field names.
039 *
040 * <b>How to Select a Subset</b>
041 *
042 * Dataset defines APIs for reading, writing and subsetting a dataset. No function is
043 * defined to select a subset of a data array. The selection is done in an implicit way.
044 * Function calls to dimension information such as getSelectedDims() return an array
045 * of dimension values, which is a reference to the array in the dataset object.
046 * Changes of the array outside the dataset object directly change the values of
047 * the array in the dataset object. It is like pointers in C.
048 *
049 * The following is an example of how to make a subset. In the example, the dataset
050 * is a 4-dimension with size of [200][100][50][10], i.e.
051 * dims[0]=200; dims[1]=100; dims[2]=50; dims[3]=10; <br>
052 * We want to select every other data point in dims[1] and dims[2]
053 * <pre>
054     int rank = dataset.getRank();   // number of dimensions of the dataset
055     long[] dims = dataset.getDims(); // the dimension sizes of the dataset
056     long[] selected = dataset.getSelectedDims(); // the selected size of the dataet
057     long[] start = dataset.getStartDims(); // the offset of the selection
058     long[] stride = dataset.getStride(); // the stride of the dataset
059     int[]  selectedIndex = dataset.getSelectedIndex(); // the selected dimensions for display
060
061     // select dim1 and dim2 as 2D data for display, and slice through dim0
062     selectedIndex[0] = 1;
063     selectedIndex[1] = 2;
064     selectedIndex[1] = 0;
065
066     // reset the selection arrays
067     for (int i=0; i&lt;rank; i++) {
068         start[i] = 0;
069         selected[i] = 1;
070         stride[i] = 1;
071    }
072
073    // set stride to 2 on dim1 and dim2 so that every other data point is selected.
074    stride[1] = 2;
075    stride[2] = 2;
076
077    // set the selection size of dim1 and dim2
078    selected[1] = dims[1]/stride[1];
079    selected[2] = dims[1]/stride[2];
080
081    // when dataset.read() is called, the selection above will be used since
082    // the dimension arrays is passed by reference. Changes of these arrays
083    // outside the dataset object directly change the values of these array
084    // in the dataset object.
085
086 * </pre>
087 *
088 * @version 1.1 9/4/2007
089 * @author Peter X. Cao
090 */
091public class H4Vdata extends CompoundDS implements MetaDataContainer {
092    private static final long serialVersionUID = -5978700886955419959L;
093
094    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(H4Vdata.class);
095
096    /**
097     * The list of attributes of this data object. Members of the list are
098     * instance of H4CompoundAttribute.
099     */
100    @SuppressWarnings("rawtypes")
101    private List attributeList;
102
103    /**
104     * Number of records of this Vdata table.
105     */
106    private int numberOfRecords;
107
108    /**
109     * The data types of the members of the compound dataset.
110     */
111    private long[] memberTIDs;
112
113    /** the number of attributes */
114    private int nAttributes = -1;
115
116    /**
117     * Creates an H4Vdata object with specific name and path.
118     *
119     * @param theFile the HDF file.
120     * @param name the name of this H4Vdata.
121     * @param path the full path of this H4Vdata.
122     */
123    public H4Vdata(FileFormat theFile, String name, String path) { this(theFile, name, path, null); }
124
125    /**
126     * Creates an H4Vdata object with specific name, path and oid.
127     *
128     * @param theFile the HDF file.
129     * @param name the name of this H4Vdata.
130     * @param path the full path of this H4Vdata.
131     * @param oid the unique identifier of this data object.
132     */
133    @SuppressWarnings("deprecation")
134    public H4Vdata(FileFormat theFile, String name, String path, long[] oid)
135    {
136        super(theFile, name, path, oid);
137        numberOfRecords = 0;
138        numberOfMembers = 0;
139        memberOrders    = null;
140    }
141
142    /*
143     * (non-Javadoc)
144     * @see hdf.object.DataFormat#hasAttribute()
145     */
146    @Override
147    public boolean hasAttribute()
148    {
149        if (nAttributes < 0) {
150            long id = open();
151
152            if (id >= 0) {
153                try {
154                    nAttributes = HDFLibrary.VSnattrs(id);
155                }
156                catch (Exception ex) {
157                    log.debug("hasAttribute() failure: ", ex);
158                    nAttributes = 0;
159                }
160
161                log.trace("hasAttribute(): nAttributes={}", nAttributes);
162
163                close(id);
164            }
165        }
166
167        return (nAttributes > 0);
168    }
169
170    // implementing Dataset
171    /**
172     * Returns the datatype of the data object.
173     *
174     * @return the datatype of the data object.
175     */
176    @Override
177    public Datatype getDatatype()
178    {
179        if (!inited)
180            init();
181
182        if (datatype == null) {
183            try {
184                datatype = new H4Datatype(-1);
185            }
186            catch (Exception ex) {
187                log.debug("getDatatype(): failed to create datatype: ", ex);
188                datatype = null;
189            }
190        }
191
192        return datatype;
193    }
194
195    /**
196     * Returns the fill values for the data object.
197     *
198     * @return the fill values for the data object.
199     */
200    @Override
201    public Object getFillValue()
202    {
203        return null;
204    }
205
206    // Implementing Dataset
207    @Override
208    public byte[] readBytes() throws HDFException
209    {
210        byte[] theData = null;
211
212        if (!isInited())
213            init();
214
215        if (numberOfMembers <= 0) {
216            log.debug("readBytes(): VData contains no members");
217            return null; // this Vdata does not have any filed
218        }
219
220        long id = open();
221        if (id < 0) {
222            log.debug("readBytes(): Invalid VData ID");
223            return null;
224        }
225
226        String allNames = memberNames[0];
227        for (int i = 0; i < numberOfMembers; i++)
228            allNames += "," + memberNames[i];
229
230        try {
231            // moves the access pointer to the start position
232            HDFLibrary.VSseek(id, (int)startDims[0]);
233            // Specify the fields to be accessed
234            HDFLibrary.VSsetfields(id, allNames);
235            int[] recordSize = {0};
236            HDFLibrary.VSQueryvsize(id, recordSize);
237            int size = recordSize[0] * (int)selectedDims[0];
238            theData  = new byte[size];
239            HDFLibrary.VSread(id, theData, (int)selectedDims[0], HDFConstants.FULL_INTERLACE);
240        }
241        catch (Exception ex) {
242            log.debug("readBytes(): failure: ", ex);
243        }
244        finally {
245            close(id);
246        }
247
248        return theData;
249    }
250
251    // Implementing DataFormat
252    /**
253     * Reads the data from file.
254     *
255     * read() reads the data from file to a memory buffer and returns the memory
256     * buffer. The dataset object does not hold the memory buffer. To store the
257     * memory buffer in the dataset object, one must call getData().
258     *
259     * By default, the whole dataset is read into memory. Users can also select
260     * a subset to read. Subsetting is done in an implicit way.
261     *
262     * @return the data read from file.
263     *
264     * @see #getData()
265     *
266     * @throws HDFException
267     *             if object can not be read
268     * @throws OutOfMemoryError
269     *             if memory is exhausted
270     */
271    @SuppressWarnings({"rawtypes", "unchecked", "deprecation"})
272    @Override
273    public Object read() throws HDFException
274    {
275        List list = null;
276
277        if (!isInited())
278            init();
279
280        if (numberOfMembers <= 0) {
281            log.debug("read(): VData contains no members");
282            return null; // this Vdata does not have any filed
283        }
284
285        long id = open();
286        if (id < 0) {
287            log.debug("read(): Invalid VData ID");
288            return null;
289        }
290
291        list = new Vector();
292
293        // assume external data files are located in the same directory as the main file.
294        HDFLibrary.HXsetdir(getFileFormat().getParent());
295
296        Object member_data = null;
297        for (int i = 0; i < numberOfMembers; i++) {
298            if (!isMemberSelected[i])
299                continue;
300
301            try {
302                // moves the access pointer to the start position
303                HDFLibrary.VSseek(id, (int)startDims[0]);
304                // Specify the fields to be accessed
305                HDFLibrary.VSsetfields(id, memberNames[i]);
306            }
307            catch (HDFException ex) {
308                log.debug("read(): failure: ", ex);
309                isMemberSelected[i] = false;
310                continue;
311            }
312
313            int n = memberOrders[i] * (int)selectedDims[0];
314
315            member_data = H4Datatype.allocateArray(memberTIDs[i], n);
316
317            log.trace("read(): index={} isMemberSelected[i]={} memberOrders[i]={} array size={}", i,
318                      isMemberSelected[i], memberOrders[i], n);
319            if (member_data == null) {
320                String[] nullValues = new String[n];
321                for (int j = 0; j < n; j++)
322                    nullValues[j] = "*ERROR*";
323                list.add(nullValues);
324                continue;
325            }
326
327            try {
328                HDFLibrary.VSread(id, member_data, (int)selectedDims[0], HDFConstants.FULL_INTERLACE);
329                if ((memberTIDs[i] == HDFConstants.DFNT_CHAR) ||
330                    (memberTIDs[i] == HDFConstants.DFNT_UCHAR8)) {
331                    // convert characters to string
332                    log.trace("read(): convert characters to string");
333                    member_data = Dataset.byteToString((byte[])member_data, memberOrders[i]);
334                    try {
335                        memberTypes[i] = new H4Datatype(Datatype.CLASS_STRING, memberOrders[i],
336                                                        Datatype.NATIVE, Datatype.NATIVE);
337                    }
338                    catch (Exception ex) {
339                        log.debug("read(): failed to create datatype for member[{}]: ", i, ex);
340                        memberTypes[i] = null;
341                    }
342                    memberOrders[i] = 1; // one String
343                }
344                else if (H4Datatype.isUnsigned(memberTIDs[i])) {
345                    // convert unsigned integer to appropriate Java integer
346                    log.trace("read(): convert unsigned integer to appropriate Java integer");
347                    member_data = Dataset.convertFromUnsignedC(member_data);
348                }
349            }
350            catch (HDFException ex) {
351                String[] nullValues = new String[n];
352                for (int j = 0; j < n; j++)
353                    nullValues[j] = "*ERROR*";
354                list.add(nullValues);
355                continue;
356            }
357
358            list.add(member_data);
359        } //  (int i=0; i<numberOfMembers; i++)
360
361        close(id);
362
363        return list;
364    }
365
366    // Implementing DataFormat
367    /**
368     * Writes a memory buffer to the object in the file.
369     *
370     * @param buf
371     *            the data to write
372     *
373     * @throws HDFException
374     *             if data can not be written
375     */
376    @Override
377    public void write(Object buf) throws HDFException
378    {
379        // For writing to a vdata, VSsetfields can only be called once, to set
380        // up the fields in a vdata. Once the vdata fields are set, they may
381        // not be changed. Thus, to update some fields of a record after the
382        // first write, the user must read all the fields to a buffer, update
383        // the buffer, then write the entire record back to the vdata.
384        log.trace("write(): disabled");
385        /*
386        if (buf == null || numberOfMembers <= 0 || !(buf instanceof List))
387            return; // no data to write
388
389        List list = (List)buf;
390        Object member_data = null;
391        String member_name = null;
392
393        int vid = open();
394        if (vid < 0) return;
395
396        int idx = 0;
397        for (int i=0; i<numberOfMembers; i++) {
398            if (!isMemberSelected[i])
399                continue;
400
401            HDFLibrary.VSsetfields(vid, memberNames[i]);
402
403            try {
404                // Specify the fields to be accessed
405
406                // moves the access pointer to the start position
407                HDFLibrary.VSseek(vid, (int)startDims[0]);
408            }
409            catch (HDFException ex) {
410                continue;
411            }
412
413            member_data = list.get(idx++);
414            if (member_data == null)
415                continue;
416
417            if (memberTIDs[i] == HDFConstants.DFNT_CHAR ||
418                memberTIDs[i] ==  HDFConstants.DFNT_UCHAR8) {
419                member_data = Dataset.stringToByte((String[])member_data, memberOrders[i]);
420            }
421            else if (H4Datatype.isUnsigned(memberTIDs[i])) {
422                // convert unsigned integer to appropriate Java integer
423                member_data = Dataset.convertToUnsignedC(member_data);
424            }
425
426
427            int interlace = HDFConstants.NO_INTERLACE;
428            try {
429                int write_num = HDFLibrary.VSwrite(
430                    vid, member_data, (int)selectedDims[0], interlace);
431            }
432            catch (HDFException ex) {
433                log.debug("write():", ex);
434            }
435        } //  (int i=0; i<numberOfMembers; i++)
436
437        close(vid);
438         */
439    }
440
441    /**
442     * Converts the data values of this data object to appropriate Java integers if
443     * they are unsigned integers.
444     *
445     * @see hdf.object.Dataset#convertToUnsignedC(Object)
446     * @see hdf.object.Dataset#convertFromUnsignedC(Object, Object)
447     *
448     * @return the converted data buffer.
449     */
450    @Override
451    public Object convertFromUnsignedC()
452    {
453        throw new UnsupportedOperationException("H4Vdata:convertFromUnsignedC Unsupported operation.");
454    }
455
456    /**
457     * Converts Java integer data values of this data object back to unsigned C-type
458     * integer data if they are unsigned integers.
459     *
460     * @see hdf.object.Dataset#convertToUnsignedC(Object)
461     * @see hdf.object.Dataset#convertToUnsignedC(Object, Object)
462     *
463     * @return the converted data buffer.
464     */
465    @Override
466    public Object convertToUnsignedC()
467    {
468        throw new UnsupportedOperationException("H4Vdata:convertToUnsignedC Unsupported operation.");
469    }
470
471    // Implementing DataFormat
472    /**
473     * Retrieves the object's metadata, such as attributes, from the file.
474     *
475     * Metadata, such as attributes, is stored in a List.
476     *
477     * @return the list of metadata objects.
478     *
479     * @throws HDFException
480     *             if the metadata can not be retrieved
481     */
482    @Override
483    @SuppressWarnings({"rawtypes", "unchecked"})
484    public List getMetadata() throws HDFException
485    {
486        if (attributeList != null) {
487            log.trace("getMetdata(): attributeList != null");
488            return attributeList;
489        }
490
491        long id = open();
492
493        if (id < 0) {
494            log.debug("getMetadata(): Invalid VData ID");
495            return attributeList;
496        }
497
498        int n = 0;
499        try {
500            n = HDFLibrary.VSnattrs(id);
501
502            if (n <= 0) {
503                log.debug("getMetadata(): VData number of attributes <= 0");
504                return attributeList;
505            }
506
507            attributeList     = new Vector(n, 5);
508            boolean b         = false;
509            String[] attrName = new String[1];
510            int[] attrInfo    = new int[5];
511
512            // _HDF_VDATA (or -1) to specify the vdata attribute
513            int nleft = n;
514            for (int j = -1; j < numberOfMembers; j++) {
515                for (int i = 0; i < nleft; i++) {
516                    attrName[0] = "";
517
518                    try {
519                        b = HDFLibrary.VSattrinfo(id, j, i, attrName, attrInfo);
520                        // mask off the litend bit
521                        attrInfo[0] = attrInfo[0] & (~HDFConstants.DFNT_LITEND);
522                    }
523                    catch (HDFException ex) {
524                        log.debug("getMetadata(): attribute[{}] VSattrinfo failure: ", i, ex);
525                        b = false;
526                        ex.printStackTrace();
527                    }
528
529                    if (!b || attrName[0].length() <= 0)
530                        continue;
531
532                    long[] attrDims = {attrInfo[1]};
533                    H4CompoundAttribute attr =
534                        new H4CompoundAttribute(this, attrName[0], new H4Datatype(attrInfo[0]), attrDims);
535                    if (j >= 0)
536                        attr.setProperty("field", memberNames[j]);
537                    attributeList.add(attr);
538
539                    Object buf = null;
540                    try {
541                        buf = H4Datatype.allocateArray(attrInfo[0], attrInfo[1]);
542                    }
543                    catch (OutOfMemoryError e) {
544                        log.debug("getMetadata(): out of memory: ", e);
545                        buf = null;
546                    }
547
548                    try {
549                        HDFLibrary.VSgetattr(id, j, i, buf);
550                    }
551                    catch (HDFException ex) {
552                        log.debug("getMetadata(): attribute[{}] VSgetattr failure: ", i, ex);
553                        buf = null;
554                    }
555
556                    if (buf != null) {
557                        if ((attrInfo[0] == HDFConstants.DFNT_CHAR) ||
558                            (attrInfo[0] == HDFConstants.DFNT_UCHAR8)) {
559                            buf = Dataset.byteToString((byte[])buf, attrInfo[1]);
560                        }
561
562                        attr.setAttributeData(buf);
563                        nleft--;
564                    }
565                } //  (int i=0; i<n; i++)
566            }     //  (int j=-1; j<numberOfMembers; j++)
567        }
568        catch (Exception ex) {
569            log.debug("getMetadata(): failure: ", ex);
570        }
571        finally {
572            close(id);
573        }
574
575        // todo: We shall also load attributes of fields
576
577        return attributeList;
578    }
579
580    // To do: Implementing DataFormat
581    /**
582     * Writes a specific piece of metadata (such as an attribute) into the file.
583     *
584     * If an HDF(4&amp;5) attribute exists in the file, this method updates its
585     * value. If the attribute does not exist in the file, it creates the
586     * attribute in the file and attaches it to the object. It will fail to
587     * write a new attribute to the object where an attribute with the same name
588     * already exists. To update the value of an existing attribute in the file,
589     * one needs to get the instance of the attribute by getMetadata(), change
590     * its values, then use writeMetadata() to write the value.
591     *
592     * @param info
593     *            the metadata to write.
594     *
595     * @throws Exception
596     *             if the metadata can not be written
597     */
598    @Override
599    @SuppressWarnings({"rawtypes", "unchecked"})
600    public void writeMetadata(Object info) throws Exception
601    {
602        // only attribute metadata is supported.
603        if (!(info instanceof Attribute)) {
604            log.debug("writeMetadata(): Object not an H4Attribute");
605            return;
606        }
607
608        try {
609            getFileFormat().writeAttribute(this, (H4ScalarAttribute)info, true);
610
611            if (attributeList == null)
612                attributeList = new Vector();
613
614            attributeList.add(info);
615            nAttributes = attributeList.size();
616        }
617        catch (Exception ex) {
618            log.trace("writeMetadata(): failure: ", ex);
619        }
620    }
621
622    /**
623     * Deletes an existing piece of metadata from this object.
624     *
625     * @param info
626     *            the metadata to delete.
627     *
628     * @throws HDFException
629     *             if the metadata can not be removed
630     */
631    @Override
632    public void removeMetadata(Object info) throws HDFException
633    {
634        log.trace("removeMetadata(): disabled");
635    }
636
637    /**
638     * Updates an existing piece of metadata attached to this object.
639     *
640     * @param info
641     *            the metadata to update.
642     *
643     * @throws Exception
644     *             if the metadata can not be updated
645     */
646    @Override
647    public void updateMetadata(Object info) throws Exception
648    {
649        log.trace("updateMetadata(): disabled");
650    }
651
652    // Implementing DataFormat
653    @Override
654    public long open()
655    {
656        // try to open with write permission
657        long vsid = -1;
658        try {
659            vsid = HDFLibrary.VSattach(getFID(), (int)oid[1], "w");
660        }
661        catch (HDFException ex) {
662            log.debug("open(w): VSattach failure: ", ex);
663            vsid = -1;
664        }
665
666        // try to open with read-only permission
667        if (vsid < 0) {
668            try {
669                vsid = HDFLibrary.VSattach(getFID(), (int)oid[1], "r");
670            }
671            catch (HDFException ex) {
672                log.debug("open(r): VSattach failure: ", ex);
673                vsid = -1;
674            }
675            log.debug("open(r): VSattach vsid: {}", vsid);
676        }
677
678        return vsid;
679    }
680
681    // Implementing DataFormat
682    @Override
683    public void close(long vsid)
684    {
685        try {
686            HDFLibrary.VSdetach(vsid);
687        }
688        catch (Exception ex) {
689            log.debug("close(): VSdetach failure: ", ex);
690        }
691    }
692
693    /**
694     * Initializes the H4Vdata such as dimension sizes of this dataset.
695     */
696    @Override
697    public void init()
698    {
699        if (inited) {
700            log.trace("init(): Already initialized");
701            return; // already called. Initialize only once
702        }
703
704        long id = open();
705        if (id < 0) {
706            log.debug("init(): Invalid VData ID");
707            return;
708        }
709
710        try {
711            numberOfMembers = HDFLibrary.VFnfields(id);
712            numberOfRecords = HDFLibrary.VSelts(id);
713        }
714        catch (HDFException ex) {
715            numberOfMembers = 0;
716            numberOfRecords = 0;
717        }
718
719        //        Still need to get information if there is no record, see bug 1738
720        //        if ((numberOfMembers <=0) || (numberOfRecords <= 0)) {
721        //            // no table field is defined or no records
722        //            close(id);
723        //            return;
724        //        }
725
726        // a Vdata table is an one dimension array of records.
727        // each record has the same fields
728        rank             = 1;
729        dims             = new long[1];
730        dims[0]          = numberOfRecords;
731        selectedDims     = new long[1];
732        selectedDims[0]  = numberOfRecords;
733        selectedIndex[0] = 0;
734        startDims        = new long[1];
735        startDims[0]     = 0;
736
737        memberNames      = new String[numberOfMembers];
738        memberTIDs       = new long[numberOfMembers];
739        memberTypes      = new Datatype[numberOfMembers];
740        memberOrders     = new int[numberOfMembers];
741        isMemberSelected = new boolean[numberOfMembers];
742
743        try {
744            datatype = new H4Datatype(Datatype.CLASS_COMPOUND, -1, Datatype.NATIVE, Datatype.NATIVE);
745        }
746        catch (Exception ex) {
747            log.debug("init(): failed to create compound datatype for VData");
748            datatype = null;
749        }
750
751        for (int i = 0; i < numberOfMembers; i++) {
752            isMemberSelected[i] = true;
753            try {
754                memberNames[i] = HDFLibrary.VFfieldname(id, i);
755                memberTIDs[i]  = HDFLibrary.VFfieldtype(id, i);
756                try {
757                    memberTypes[i] = new H4Datatype(memberTIDs[i]);
758                }
759                catch (Exception ex) {
760                    log.debug("init(): failed to create datatype for member[{}]: ", i, ex);
761                    memberTypes[i] = null;
762                }
763                // mask off the litend bit
764                memberTIDs[i]   = memberTIDs[i] & (~HDFConstants.DFNT_LITEND);
765                memberOrders[i] = HDFLibrary.VFfieldorder(id, i);
766                log.trace(
767                    "init():{}> isMemberSelected[i]={} memberNames[i]={} memberTIDs[i]={} memberOrders[i]={}",
768                    i, isMemberSelected[i], memberNames[i], memberTIDs[i], memberOrders[i]);
769
770                /*
771                 * NOTE: An ugly workaround to get HDF4 "compound" datatypes to work correctly.
772                 */
773                if (datatype != null) {
774                    datatype.getCompoundMemberNames().add(memberNames[i]);
775                    datatype.getCompoundMemberTypes().add(memberTypes[i]);
776                }
777            }
778            catch (HDFException ex) {
779                log.debug("init(): member[{}]: ", i, ex);
780                continue;
781            }
782        } //  (int i=0; i<numberOfMembers; i++)
783
784        inited = true;
785
786        close(id);
787    }
788
789    /**
790     * Returns the number of records.
791     *
792     * @return the number of records
793     */
794    public int getRecordCount() { return numberOfRecords; }
795
796    /**
797     * Returns the number of fields.
798     *
799     * @return the number of fields
800     */
801    public int getFieldCount() { return numberOfMembers; }
802
803    /**
804     * Returns the orders of fields
805     *
806     * @return the orders of fields
807     */
808    public int[] getFieldOrders() { return memberOrders; }
809
810    // Implementing DataFormat
811    /**
812     * Retrieves the object's metadata, such as attributes, from the file.
813     *
814     * Metadata, such as attributes, is stored in a List.
815     *
816     * @param attrPropList
817     *             the list of properties to get
818     *
819     * @return the list of metadata objects.
820     *
821     * @throws Exception
822     *             if the metadata can not be retrieved
823     */
824    @SuppressWarnings("rawtypes")
825    public List getMetadata(int... attrPropList) throws Exception
826    {
827        throw new UnsupportedOperationException("getMetadata(int... attrPropList) is not supported");
828    }
829
830    @Override
831    public Dataset copy(Group pgroup, String name, long[] dims, Object data) throws Exception
832    {
833        throw new UnsupportedOperationException("Writing a vdata to a new dataset is not implemented.");
834    }
835}