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;
023import hdf.object.Attribute;
024import hdf.object.CompoundDS;
025import hdf.object.Dataset;
026import hdf.object.Datatype;
027import hdf.object.FileFormat;
028import hdf.object.Group;
029
030/**
031 * H4Vdata describes a multi-dimension array of HDF4 vdata, inheriting CompoundDS.
032 * <p>
033 * A vdata is like a table that consists of a collection of records whose values
034 * are stored in fixed-length fields. All records have the same structure and
035 * all values in each field have the same data type. Vdatas are uniquely
036 * identified by a name, a class, and a series of individual field names.
037 * <p>
038 * <b>How to Select a Subset</b>
039 * <p>
040 * Dataset defines APIs for reading, writing and subsetting a dataset. No function is
041 * defined to select a subset of a data array. The selection is done in an implicit way.
042 * Function calls to dimension information such as getSelectedDims() return an array
043 * of dimension values, which is a reference to the array in the dataset object.
044 * Changes of the array outside the dataset object directly change the values of
045 * the array in the dataset object. It is like pointers in C.
046 * <p>
047 *
048 * The following is an example of how to make a subset. In the example, the dataset
049 * is a 4-dimension with size of [200][100][50][10], i.e.
050 * dims[0]=200; dims[1]=100; dims[2]=50; dims[3]=10; <br>
051 * We want to select every other data point in dims[1] and dims[2]
052 * <pre>
053     int rank = dataset.getRank();   // number of dimensions of the dataset
054     long[] dims = dataset.getDims(); // the dimension sizes of the dataset
055     long[] selected = dataset.getSelectedDims(); // the selected size of the dataet
056     long[] start = dataset.getStartDims(); // the offset of the selection
057     long[] stride = dataset.getStride(); // the stride of the dataset
058     int[]  selectedIndex = dataset.getSelectedIndex(); // the selected dimensions for display
059
060     // select dim1 and dim2 as 2D data for display, and slice through dim0
061     selectedIndex[0] = 1;
062     selectedIndex[1] = 2;
063     selectedIndex[1] = 0;
064
065     // reset the selection arrays
066     for (int i=0; i&lt;rank; i++) {
067         start[i] = 0;
068         selected[i] = 1;
069         stride[i] = 1;
070    }
071
072    // set stride to 2 on dim1 and dim2 so that every other data point is selected.
073    stride[1] = 2;
074    stride[2] = 2;
075
076    // set the selection size of dim1 and dim2
077    selected[1] = dims[1]/stride[1];
078    selected[2] = dims[1]/stride[2];
079
080    // when dataset.read() is called, the selection above will be used since
081    // the dimension arrays is passed by reference. Changes of these arrays
082    // outside the dataset object directly change the values of these array
083    // in the dataset object.
084
085 * </pre>
086 *
087 * @version 1.1 9/4/2007
088 * @author Peter X. Cao
089 */
090public class H4Vdata extends CompoundDS
091{
092    private static final long serialVersionUID = -5978700886955419959L;
093
094    private final static 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 Attribute.
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    private int                                 nAttributes = -1;
114
115
116    public H4Vdata(FileFormat theFile, String name, String path)
117    {
118        this(theFile, name, path, null);
119    }
120
121    /**
122     * Creates an H4Vdata object with specific name and path.
123     *
124     * @param theFile the HDF file.
125     * @param name the name of this H4Vdata.
126     * @param path the full path of this H4Vdata.
127     * @param oid the unique identifier of this data object.
128     */
129    @SuppressWarnings("deprecation")
130    public H4Vdata(
131            FileFormat theFile,
132            String name,
133            String path,
134            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    @Override
172    public Datatype getDatatype()
173    {
174        if (datatype == null) {
175            datatype = new H4Datatype(-1);
176        }
177
178        return datatype;
179    }
180
181    @Override
182    public Object getFillValue() {
183        return null;
184    }
185
186    // Implementing Dataset
187    @Override
188    public byte[] readBytes() throws HDFException
189    {
190        log.trace("readBytes(): start");
191
192        byte[] theData = null;
193
194        if (!isInited())
195            init();
196
197        if (numberOfMembers <= 0) {
198            log.debug("readBytes(): VData contains no members");
199            log.trace("readBytes(): finish");
200            return null; // this Vdata does not have any filed
201        }
202
203        long id = open();
204        if (id < 0) {
205            log.debug("readBytes(): Invalid VData ID");
206            log.trace("readBytes(): finish");
207            return null;
208        }
209
210        String allNames = memberNames[0];
211        for (int i=0; i<numberOfMembers; i++) {
212            allNames += ","+memberNames[i];
213        }
214
215        try {
216            // moves the access pointer to the start position
217            HDFLibrary.VSseek(id, (int)startDims[0]);
218            // Specify the fields to be accessed
219            HDFLibrary.VSsetfields(id, allNames);
220            int[] recordSize = {0};
221            HDFLibrary.VSQueryvsize(id, recordSize);
222            int size =recordSize[0] * (int)selectedDims[0];
223            theData = new byte[size];
224            HDFLibrary.VSread(
225                    id,
226                    theData,
227                    (int)selectedDims[0],
228                    HDFConstants.FULL_INTERLACE);
229        }
230        catch (Exception ex) {
231            log.debug("readBytes(): failure: ", ex);
232        }
233        finally {
234            close(id);
235        }
236
237        log.trace("readBytes(): finish");
238        return theData;
239    }
240
241    // Implementing DataFormat
242    @SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
243    @Override
244    public Object read() throws HDFException
245    {
246        log.trace("read(): start");
247
248        List list = null;
249
250        if (!isInited())
251            init();
252
253        if (numberOfMembers <= 0) {
254            log.debug("read(): VData contains no members");
255            log.trace("read(): finish");
256            return null; // this Vdata does not have any filed
257        }
258
259        long id = open();
260        if (id < 0) {
261            log.debug("read(): Invalid VData ID");
262            log.trace("read(): finish");
263            return null;
264        }
265
266        list = new Vector();
267
268        // assume external data files are located in the same directory as the main file.
269        HDFLibrary.HXsetdir(getFileFormat().getParent());
270
271        Object member_data = null;
272        for (int i=0; i<numberOfMembers; i++) {
273            if (!isMemberSelected[i]) {
274                continue;
275            }
276
277            try {
278                // moves the access pointer to the start position
279                HDFLibrary.VSseek(id, (int)startDims[0]);
280                // Specify the fields to be accessed
281                HDFLibrary.VSsetfields(id, memberNames[i]);
282            }
283            catch (HDFException ex) {
284                log.debug("read(): failure: ", ex);
285                isMemberSelected[i] = false;
286                continue;
287            }
288
289            int n = memberOrders[i]*(int)selectedDims[0];
290
291            member_data = H4Datatype.allocateArray(memberTIDs[i], n);
292
293            log.trace("read(): index={} isMemberSelected[i]={} memberOrders[i]={} array size={}", i, isMemberSelected[i], memberOrders[i], n);
294            if (member_data == null) {
295                String[] nullValues = new String[n];
296                for (int j=0; j<n; j++) {
297                    nullValues[j] = "*error*";
298                }
299                list.add(nullValues);
300                continue;
301            }
302
303            try {
304                HDFLibrary.VSread(
305                        id,
306                        member_data,
307                        (int)selectedDims[0],
308                        HDFConstants.FULL_INTERLACE);
309                if ((memberTIDs[i] == HDFConstants.DFNT_CHAR) ||
310                        (memberTIDs[i] ==  HDFConstants.DFNT_UCHAR8)) {
311                    // convert characters to string
312                    log.trace("read(): convert characters to string");
313                    member_data = Dataset.byteToString((byte[])member_data, memberOrders[i]);
314                    memberTypes[i] = new H4Datatype(Datatype.CLASS_STRING, memberOrders[i], -1, -1);
315                    memberOrders[i] = 1; //one String
316                }
317                else if (H4Datatype.isUnsigned(memberTIDs[i])) {
318                    // convert unsigned integer to appropriate Java integer
319                    log.trace("read(): convert unsigned integer to appropriate Java integer");
320                    member_data = Dataset.convertFromUnsignedC(member_data);
321                }
322            }
323            catch (HDFException ex) {
324                String[] nullValues = new String[n];
325                for (int j=0; j<n; j++) {
326                    nullValues[j] = "*error*";
327                }
328                list.add(nullValues);
329                continue;
330            }
331
332            list.add(member_data);
333        } // for (int i=0; i<numberOfMembers; i++)
334
335        close(id);
336
337        log.trace("read(): finish");
338        return list;
339    }
340
341    // Implementing DataFormat
342    @Override
343    public void write(Object buf) throws HDFException
344    {
345        //For writing to a vdata, VSsetfields can only be called once, to set
346        //up the fields in a vdata. Once the vdata fields are set, they may
347        //not be changed. Thus, to update some fields of a record after the
348        //first write, the user must read all the fields to a buffer, update
349        //the buffer, then write the entire record back to the vdata.
350        log.trace("write(): disabled");
351        /*
352        if (buf == null || numberOfMembers <= 0 || !(buf instanceof List))
353            return; // no data to write
354
355        List list = (List)buf;
356        Object member_data = null;
357        String member_name = null;
358
359        int vid = open();
360        if (vid < 0) return;
361
362        int idx = 0;
363        for (int i=0; i<numberOfMembers; i++) {
364            if (!isMemberSelected[i])
365                continue;
366
367            HDFLibrary.VSsetfields(vid, memberNames[i]);
368
369            try {
370                // Specify the fields to be accessed
371
372                // moves the access pointer to the start position
373                HDFLibrary.VSseek(vid, (int)startDims[0]);
374            }
375            catch (HDFException ex) {
376                continue;
377            }
378
379            member_data = list.get(idx++);
380            if (member_data == null)
381                continue;
382
383            if (memberTIDs[i] == HDFConstants.DFNT_CHAR ||
384                memberTIDs[i] ==  HDFConstants.DFNT_UCHAR8) {
385                member_data = Dataset.stringToByte((String[])member_data, memberOrders[i]);
386            }
387            else if (H4Datatype.isUnsigned(memberTIDs[i])) {
388                // convert unsigned integer to appropriate Java integer
389                member_data = Dataset.convertToUnsignedC(member_data);
390            }
391
392
393            int interlace = HDFConstants.NO_INTERLACE;
394            try {
395                int write_num = HDFLibrary.VSwrite(
396                    vid, member_data, (int)selectedDims[0], interlace);
397            }
398            catch (HDFException ex) {
399                log.debug("write():", ex);
400            }
401        } // for (int i=0; i<numberOfMembers; i++)
402
403        close(vid);
404         */
405    }
406
407    @Override
408    public Object convertFromUnsignedC() {
409        throw new UnsupportedOperationException("H4Vdata:convertFromUnsignedC Unsupported operation.");
410    }
411
412    @Override
413    public Object convertToUnsignedC() {
414        throw new UnsupportedOperationException("H4Vdata:convertToUnsignedC Unsupported operation.");
415    }
416
417    // Implementing DataFormat
418    @Override
419    @SuppressWarnings({"rawtypes", "unchecked"})
420    public List getMetadata() throws HDFException
421    {
422        log.trace("getMetadata(): start");
423
424        if (attributeList != null) {
425            log.trace("getMetdata(): attributeList != null");
426            log.trace("getMetadata(): finish");
427            return attributeList;
428        }
429
430        long id = open();
431
432        if (id < 0) {
433            log.debug("getMetadata(): Invalid VData ID");
434            log.trace("getMetadata(): finish");
435            return attributeList;
436        }
437
438        int n=0;
439        try {
440            n = HDFLibrary.VSnattrs(id);
441
442            if (n <= 0) {
443                log.debug("getMetadata(): VData number of attributes <= 0");
444                log.trace("getMetadata(): finish");
445                return attributeList;
446            }
447
448            attributeList = new Vector(n, 5);
449            boolean b = false;
450            String[] attrName = new String[1];
451            int[] attrInfo = new int[5];
452
453            // _HDF_VDATA (or -1) to specify the vdata attribute
454            int nleft = n;
455            for (int j=-1; j<numberOfMembers; j++) {
456                for (int i=0; i<nleft; i++) {
457                    attrName[0] = "";
458
459                    try {
460                        b = HDFLibrary.VSattrinfo(id, j, i, attrName, attrInfo);
461                        // mask off the litend bit
462                        attrInfo[0] = attrInfo[0] & (~HDFConstants.DFNT_LITEND);
463                    }
464                    catch (HDFException ex) {
465                        log.debug("getMetadata(): attribute[{}] VSattrinfo failure: ", i, ex);
466                        b = false;
467                        ex.printStackTrace();
468                    }
469
470                    if (!b || attrName[0].length()<=0) {
471                        continue;
472                    }
473
474                    long[] attrDims = {attrInfo[1]};
475                    Attribute attr = new Attribute(this, attrName[0], new H4Datatype(attrInfo[0]), attrDims);
476                    if (j>=0)
477                        attr.setProperty("field", memberNames[j]);
478                    attributeList.add(attr);
479
480                    Object buf = null;
481                    try {
482                        buf = H4Datatype.allocateArray(attrInfo[0], attrInfo[1]);
483                    }
484                    catch (OutOfMemoryError e) {
485                        log.debug("getMetadata(): out of memory: ", e);
486                        buf = null;
487                    }
488
489                    try {
490                        HDFLibrary.VSgetattr(id, j, i, buf);
491                    }
492                    catch (HDFException ex) {
493                        log.debug("getMetadata(): attribute[{}] VSgetattr failure: ", i, ex);
494                        buf = null;
495                    }
496
497                    if (buf != null) {
498                        if ((attrInfo[0] == HDFConstants.DFNT_CHAR) ||
499                                (attrInfo[0] ==  HDFConstants.DFNT_UCHAR8)) {
500                            buf = Dataset.byteToString((byte[])buf, attrInfo[1]);
501                        }
502
503                        attr.setData(buf);
504                        nleft--;
505                    }
506                } // for (int i=0; i<n; i++)
507            } // for (int j=-1; j<numberOfMembers; j++)
508        }
509        catch (Exception ex) {
510            log.debug("getMetadata(): failure: ", ex);
511        }
512        finally {
513            close(id);
514        }
515
516        // todo: We shall also load attributes of fields
517
518        log.trace("getMetadata(): finish");
519        return attributeList;
520    }
521
522    // To do: Implementing DataFormat
523    @Override
524    @SuppressWarnings({"rawtypes", "unchecked"})
525    public void writeMetadata(Object info) throws Exception
526    {
527        log.trace("writeMetadata(): start");
528
529        // only attribute metadata is supported.
530        if (!(info instanceof Attribute)) {
531            log.debug("writeMetadata(): Object not an Attribute");
532            log.trace("writeMetadata(): finish");
533            return;
534        }
535
536        try {
537            getFileFormat().writeAttribute(this, (Attribute)info, true);
538
539            if (attributeList == null) {
540                attributeList = new Vector();
541            }
542
543            attributeList.add(info);
544            nAttributes = attributeList.size();
545        }
546        catch (Exception ex) {
547            log.trace("writeMetadata(): failure: ", ex);
548        }
549
550        log.trace("writeMetadata(): finish");
551    }
552
553    // To do: Implementing DataFormat
554    @Override
555    public void removeMetadata(Object info) throws HDFException
556    {
557        log.trace("removeMetadata(): disabled");
558    }
559
560    // implementing DataFormat
561    @Override
562    public void updateMetadata(Object info) throws Exception {
563        log.trace("updateMetadata(): disabled");
564    }
565
566    // Implementing DataFormat
567    @Override
568    public long open()
569    {
570        log.trace("open(): start");
571
572        // try to open with write permission
573        long vsid = -1;
574        try {
575            vsid = HDFLibrary.VSattach(getFID(), (int)oid[1], "w");
576        }
577        catch (HDFException ex) {
578            log.debug("open(): VSattach failure: ", ex);
579            vsid = -1;
580        }
581
582        // try to open with read-only permission
583        if (vsid < 0) {
584            try {
585                vsid = HDFLibrary.VSattach(getFID(), (int)oid[1], "r");
586            }
587            catch (HDFException ex) {
588                log.debug("open(): VSattach failure: ", ex);
589                vsid = -1;
590            }
591        }
592
593        log.trace("open(): finish");
594        return vsid;
595    }
596
597    // Implementing DataFormat
598    @Override
599    public void close(long vsid)
600    {
601        try {
602            HDFLibrary.VSdetach(vsid);
603        }
604        catch (Exception ex) {
605            log.debug("close(): VSdetach failure: ", ex);
606        }
607    }
608
609    /**
610     * Initializes the H4Vdata such as dimension sizes of this dataset.
611     */
612    @Override
613    public void init()
614    {
615        log.trace("init(): start");
616
617        if (inited) {
618            log.trace("init(): Already initialized");
619            log.trace("init(): finish");
620            return; // already called. Initialize only once
621        }
622
623        long id = open();
624        if (id < 0) {
625            log.debug("init(): Invalid VData ID");
626            log.trace("init(): finish");
627            return;
628        }
629
630        try {
631            numberOfMembers = HDFLibrary.VFnfields(id);
632            numberOfRecords = HDFLibrary.VSelts(id);
633        }
634        catch (HDFException ex) {
635            numberOfMembers = 0;
636            numberOfRecords = 0;
637        }
638
639        //        Still need to get information if there is no record, see bug 1738
640        //        if ((numberOfMembers <=0) || (numberOfRecords <= 0)) {
641        //            // no table field is defined or no records
642        //            close(id);
643        //            return;
644        //        }
645
646        // a Vdata table is an one dimension array of records.
647        // each record has the same fields
648        rank = 1;
649        dims = new long[1];
650        dims[0] = numberOfRecords;
651        selectedDims = new long[1];
652        selectedDims[0] = numberOfRecords;
653        selectedIndex[0] = 0;
654        startDims = new long[1];
655        startDims[0] = 0;
656
657        memberNames = new String[numberOfMembers];
658        memberTIDs = new long[numberOfMembers];
659        memberTypes = new Datatype[numberOfMembers];
660        memberOrders = new int[numberOfMembers];
661        isMemberSelected = new boolean[numberOfMembers];
662
663        for (int i=0; i<numberOfMembers; i++) {
664            isMemberSelected[i] = true;
665            try {
666                memberNames[i] = HDFLibrary.VFfieldname(id, i);
667                memberTIDs[i] = HDFLibrary.VFfieldtype(id, i);
668                memberTypes[i] = new H4Datatype(memberTIDs[i]);
669                // mask off the litend bit
670                memberTIDs[i] = memberTIDs[i] & (~HDFConstants.DFNT_LITEND);
671                memberOrders[i] = HDFLibrary.VFfieldorder(id, i);
672                log.trace("init():{}> isMemberSelected[i]={} memberNames[i]={} memberTIDs[i]={} memberOrders[i]={}", i, isMemberSelected[i], memberNames[i], memberTIDs[i], memberOrders[i]);
673            }
674            catch (HDFException ex) {
675                log.debug("init(): member[{}]: ", i, ex);
676                log.trace("init(): continue");
677                continue;
678            }
679        } // for (int i=0; i<numberOfMembers; i++)
680
681        inited = true;
682
683        close(id);
684        log.trace("init(): finish");
685    }
686
687    /**
688     * Returns the number of records.
689     *
690     * @return the number of records
691     */
692    public int getRecordCount()
693    {
694        return numberOfRecords;
695    }
696
697    /**
698     * Returns the number of fields.
699     *
700     * @return the number of fields
701     */
702    public int getFieldCount()
703    {
704        return numberOfMembers;
705    }
706
707    /**
708     * Returns the orders of fields
709     *
710     * @return the orders of fields
711     */
712    public int[] getFieldOrders()
713    {
714        return memberOrders;
715    }
716
717    //Implementing DataFormat
718    @SuppressWarnings("rawtypes")
719    public List getMetadata(int... attrPropList) throws Exception {
720        throw new UnsupportedOperationException("getMetadata(int... attrPropList) is not supported");
721    }
722
723    @Override
724    public Dataset copy(Group pgroup, String name, long[] dims, Object data)
725            throws Exception {
726        throw new UnsupportedOperationException(
727                "Writing a vdata to a new dataset is not implemented.");
728    }
729}