    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}