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