    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.HDFChunkInfo;
    021import hdf.hdflib.HDFCompInfo;
    022import hdf.hdflib.HDFConstants;
    023import hdf.hdflib.HDFDeflateCompInfo;
    024import hdf.hdflib.HDFException;
    025import hdf.hdflib.HDFJPEGCompInfo;
    026import hdf.hdflib.HDFLibrary;
    027import hdf.hdflib.HDFNBITCompInfo;
    028import hdf.hdflib.HDFSKPHUFFCompInfo;
    029import hdf.hdflib.HDFSZIPCompInfo;
    030import hdf.object.Attribute;
    031import hdf.object.Dataset;
    032import hdf.object.Datatype;
    033import hdf.object.FileFormat;
    034import hdf.object.Group;
    035import hdf.object.HObject;
    036import hdf.object.ScalarDS;
    037
    038/**
    039 * H4SDS describes HDF4 Scientific Data Sets (SDS) and operations performed on
    040 * the SDS. A SDS is a group of data structures used to store and describe
    041 * multidimensional arrays of scientific data.
    042 * <p>
    043 * The data contained in an SDS array has a data type associated with it. The
    044 * standard data types supported by the SD interface include 32- and 64-bit
    045 * floating-point numbers, 8-, 16- and 32-bit signed integers, 8-, 16- and
    046 * 32-bit unsigned integers, and 8-bit characters.
    047 * <p>
    048 * <b>How to Select a Subset</b>
    049 * <p>
    050 * Dataset defines APIs for reading, writing and subsetting a dataset. No function
    051 * is defined to select a subset of a data array. The selection is done in an implicit
    052 * way. Function calls to dimension information such as getSelectedDims() return an array
    053 * of dimension values, which is a reference to the array in the dataset object.
    054 * Changes of the array outside the dataset object directly change the values of
    055 * the array in the dataset object. It is like pointers in C.
    056 * <p>
    057 *
    058 * The following is an example of how to make a subset. In the example, the dataset
    059 * is a 4-dimension with size of [200][100][50][10], i.e.
    060 * dims[0]=200; dims[1]=100; dims[2]=50; dims[3]=10; <br>
    061 * We want to select every other data point in dims[1] and dims[2]
    062 * <pre>
    063     int rank = dataset.getRank();   // number of dimensions of the dataset
    064     long[] dims = dataset.getDims(); // the dimension sizes of the dataset
    065     long[] selected = dataset.getSelectedDims(); // the selected size of the dataet
    066     long[] start = dataset.getStartDims(); // the offset of the selection
    067     long[] stride = dataset.getStride(); // the stride of the dataset
    068     int[]  selectedIndex = dataset.getSelectedIndex(); // the selected dimensions for display
    069
    070     // select dim1 and dim2 as 2D data for display,and slice through dim0
    071     selectedIndex[0] = 1;
    072     selectedIndex[1] = 2;
    073     selectedIndex[1] = 0;
    074
    075     // reset the selection arrays
    076     for (int i=0; i&lt;rank; i++) {
    077         start[i] = 0;
    078         selected[i] = 1;
    079         stride[i] = 1;
    080    }
    081
    082    // set stride to 2 on dim1 and dim2 so that every other data point is selected.
    083    stride[1] = 2;
    084    stride[2] = 2;
    085
    086    // set the selection size of dim1 and dim2
    087    selected[1] = dims[1]/stride[1];
    088    selected[2] = dims[1]/stride[2];
    089
    090    // when dataset.read() is called, the slection above will be used since
    091    // the dimension arrays is passed by reference. Changes of these arrays
    092    // outside the dataset object directly change the values of these array
    093    // in the dataset object.
    094
    095 * </pre>
    096 *
    097 * <p>
    098 * @version 1.1 9/4/2007
    099 * @author Peter X. Cao
    100 */
    101public class H4SDS extends ScalarDS
    102{
    103    private static final long serialVersionUID = 2557157923292438696L;
    104
    105    private static final org.slf4j.Logger   log = org.slf4j.LoggerFactory.getLogger(H4SDS.class);
    106
    107    /** tag for netCDF datasets.
    108     *  HDF4 library supports netCDF version 2.3.2. It only supports SDS APIs.
    109     */
    110    // magic number for netCDF: "C(67) D(68) F(70) '\001'"
    111    public static final int                 DFTAG_NDG_NETCDF = 67687001;
    112
    113    /**
    114     * The list of attributes of this data object. Members of the list are
    115     * instance of Attribute.
    116     */
    117    @SuppressWarnings("rawtypes")
    118    private List                            attributeList;
    119
    120    /**
    121     * The SDS interface identifier obtained from SDstart(filename, access)
    122     */
    123    private long                            sdid;
    124
    125    /** the datatype identifier */
    126    private long                            datatypeID = -1;
    127
    128    private int                             nAttributes = -1;
    129
    130
    131    public H4SDS(FileFormat theFile, String name, String path)
    132    {
    133        this(theFile, name, path, null);
    134    }
    135
    136    /**
    137     * Creates an H4SDS object with specific name and path.
    138     *
    139     * @param theFile
    140     *            the HDF file.
    141     * @param name
    142     *            the name of this H4SDS.
    143     * @param path
    144     *            the full path of this H4SDS.
    145     * @param oid
    146     *            the unique identifier of this data object.
    147     */
    148    @SuppressWarnings("deprecation")
    149    public H4SDS(FileFormat theFile, String name, String path, long[] oid)
    150    {
    151        super(theFile, name, path, oid);
    152        unsignedConverted = false;
    153        sdid = ((H4File)getFileFormat()).getSDAccessID();
    154    }
    155
    156    /*
    157     * (non-Javadoc)
    158     * @see hdf.object.DataFormat#hasAttribute()
    159     */
    160    @Override
    161    public boolean hasAttribute ()
    162    {
    163        if (nAttributes < 0) {
    164            sdid = ((H4File)getFileFormat()).getSDAccessID();
    165
    166            long id = open();
    167
    168            if (id >= 0) {
    169                try { // retireve attributes of the dataset
    170                    String[] objName = {""};
    171                    int[] sdInfo = {0, 0, 0};
    172                    int[] tmpDim = new int[HDFConstants.MAX_VAR_DIMS];
    173                    HDFLibrary.SDgetinfo(id, objName, tmpDim, sdInfo);
    174                    nAttributes = sdInfo[2];
    175                }
    176                catch (Exception ex) {
    177                    log.debug("hasAttribute(): failure: ", ex);
    178                    nAttributes=0;
    179                }
    180
    181                log.trace("hasAttribute(): nAttributes={}", nAttributes);
    182
    183                close(id);
    184            }
    185        }
    186
    187        return (nAttributes>0);
    188    }
    189
    190    // ***** need to implement from ScalarDS *****
    191    @Override
    192    public byte[][] readPalette(int idx) { return null;}
    193
    194    // ***** need to implement from ScalarDS *****
    195    @Override
    196    public byte[] getPaletteRefs() { return null;}
    197
    198    // implementing Dataset
    199    @Override
    200    public Datatype getDatatype()
    201    {
    202        if (!inited)
    203            init();
    204
    205        if (datatype == null) {
    206            try {
    207                datatype = new H4Datatype(datatypeID);
    208            }
    209            catch (Exception ex) {
    210                log.debug("getDatatype(): failed to create datatype: ", ex);
    211                datatype = null;
    212            }
    213        }
    214
    215        return datatype;
    216    }
    217
    218    // To do: Implementing Dataset
    219    @Override
    220    public Dataset copy(Group pgroup, String dname, long[] dims, Object buff)
    221            throws Exception
    222    {
    223        log.trace("copy(): start: parentGroup={} datasetName={}", pgroup, dname);
    224
    225        Dataset dataset = null;
    226        long srcdid = -1;
    227        long dstdid = -1;
    228        long tid = -1;
    229        int size = 1;
    230        int theRank = 2;
    231        String path = null;
    232        int[] count = null;
    233        int[] start = null;
    234
    235        if (pgroup == null) {
    236            log.debug("copy(): Parent group is null");
    237            return null;
    238        }
    239
    240        if (dname == null) {
    241            dname = getName();
    242        }
    243
    244        if (pgroup.isRoot()) {
    245            path = HObject.SEPARATOR;
    246        }
    247        else {
    248            path = pgroup.getPath()+pgroup.getName()+HObject.SEPARATOR;
    249        }
    250        log.trace("copy(): path={}", path);
    251
    252        srcdid = open();
    253        if (srcdid < 0) {
    254            log.debug("copy(): Invalid source SDID");
    255            return null;
    256        }
    257
    258        if (dims == null) {
    259            if (!isInited())
    260                init();
    261
    262            theRank = getRank();
    263
    264            dims = getDims();
    265        }
    266        else {
    267            theRank = dims.length;
    268        }
    269
    270        start = new int[theRank];
    271        count = new int[theRank];
    272        for (int i=0; i<theRank; i++) {
    273            start[i] = 0;
    274            count[i] = (int)dims[i];
    275            size *= count[i];
    276        }
    277        log.trace("copy(): theRank={} with size={}", theRank, size);
    278
    279        // create the new dataset and attach it to the parent group
    280        tid = datatypeID;
    281        dstdid = HDFLibrary.SDcreate(
    282                ((H4File)pgroup.getFileFormat()).getSDAccessID(),
    283                dname, tid, theRank, count);
    284        if (dstdid < 0) {
    285            log.debug("copy(): Invalid dest SDID");
    286            return null;
    287        }
    288
    289        int ref = HDFLibrary.SDidtoref(dstdid);
    290        if (!pgroup.isRoot()) {
    291            long vgid = pgroup.open();
    292            HDFLibrary.Vaddtagref(vgid, HDFConstants.DFTAG_NDG, ref);
    293            pgroup.close(vgid);
    294        }
    295
    296        // copy attributes from one object to the new object
    297        log.trace("copy(): copy attributes");
    298        copyAttribute(srcdid, dstdid);
    299
    300        // read data from the source dataset
    301        log.trace("copy(): read data from the source dataset");
    302        if (buff == null) {
    303            buff = new byte[size * HDFLibrary.DFKNTsize(tid)];
    304            HDFLibrary.SDreaddata(srcdid, start, null, count, buff);
    305        }
    306
    307        // write the data into the destination dataset
    308        log.trace("copy(): write the data into the destination dataset");
    309        HDFLibrary.SDwritedata(dstdid, start, null, count, buff);
    310
    311        long[] oid = {HDFConstants.DFTAG_NDG, ref};
    312        dataset = new H4SDS(pgroup.getFileFormat(), dname, path, oid);
    313
    314        pgroup.addToMemberList(dataset);
    315
    316        close(srcdid);
    317
    318        try {
    319            HDFLibrary.SDendaccess(dstdid);
    320        }
    321        catch (HDFException ex) {
    322            log.debug("copy(): SDendaccess failure: ", ex);
    323        }
    324
    325        return dataset;
    326    }
    327
    328    // Implementing Dataset
    329    @Override
    330    public byte[] readBytes() throws HDFException
    331    {
    332        byte[] theData = null;
    333
    334        if (!isInited())
    335            init();
    336
    337        long id = open();
    338        if (id < 0) {
    339            log.debug("readBytes(): Invalid SDID");
    340            return null;
    341        }
    342
    343        int datasize = 1;
    344        int[] select = new int[rank];
    345        int[] start = new int[rank];
    346        for (int i=0; i<rank; i++) {
    347            datasize *= (int)selectedDims[i];
    348            select[i] = (int)selectedDims[i];
    349            start[i] = (int)startDims[i];
    350        }
    351
    352        int[] stride = null;
    353        if (selectedStride != null) {
    354            stride = new int[rank];
    355            for (int i=0; i<rank; i++) {
    356                stride[i] = (int)selectedStride[i];
    357            }
    358        }
    359
    360        try {
    361            int size = HDFLibrary.DFKNTsize(datatypeID)*datasize;
    362            theData = new byte[size];
    363            HDFLibrary.SDreaddata(id, start, stride, select, theData);
    364        }
    365        catch (Exception ex) {
    366            log.debug("readBytes(): failure: ", ex);
    367        }
    368        finally {
    369            close(id);
    370        }
    371
    372        return theData;
    373    }
    374
    375    // Implementing DataFormat
    376    @Override
    377    public Object read() throws HDFException, OutOfMemoryError
    378    {
    379        Object theData = null;
    380
    381        if (!isInited())
    382            init();
    383
    384        long id = open();
    385        if (id < 0) {
    386            log.debug("read(): Invalid SDID");
    387            return null;
    388        }
    389
    390        int datasize = 1;
    391        int[] select = new int[rank];
    392        int[] start = new int[rank];
    393        for (int i=0; i<rank; i++) {
    394            datasize *= (int)selectedDims[i];
    395            select[i] = (int)selectedDims[i];
    396            start[i] = (int)startDims[i];
    397        }
    398
    399        int[] stride = null;
    400        if (selectedStride != null) {
    401            stride = new int[rank];
    402            for (int i=0; i<rank; i++) {
    403                stride[i] = (int)selectedStride[i];
    404            }
    405        }
    406
    407        try {
    408            theData = H4Datatype.allocateArray(datatypeID, datasize);
    409
    410            if (theData != null) {
    411                // assume external data files are located in the same directory as the main file.
    412                HDFLibrary.HXsetdir(getFileFormat().getParent());
    413
    414                HDFLibrary.SDreaddata(id, start, stride, select, theData);
    415
    416                if (isText) {
    417                    theData = byteToString((byte[])theData, select[0]);
    418                }
    419            }
    420        }
    421        catch (Exception ex) {
    422            log.debug("read(): failure: ", ex);
    423        }
    424        finally {
    425            close(id);
    426        }
    427
    428        if (fillValue==null && isImageDisplay) {
    429            try {
    430                getMetadata();
    431            } // need to set fillValue for images
    432            catch (Exception ex) {
    433                log.debug("read(): getMetadata failure: ", ex);
    434            }
    435        }
    436
    437        if ((rank > 1) && (selectedIndex[0] > selectedIndex[1]))
    438            isDefaultImageOrder = false;
    439        else
    440            isDefaultImageOrder = true;
    441
    442        log.trace("read(): isDefaultImageOrder={}", isDefaultImageOrder);
    443        return theData;
    444    }
    445
    446    // Implementing DataFormat
    447    @SuppressWarnings("deprecation")
    448    @Override
    449    public void write(Object buf) throws HDFException
    450    {
    451        if (buf == null) {
    452            log.debug("write(): Object is null");
    453            return;
    454        }
    455
    456        long id = open();
    457        if (id < 0) {
    458            log.debug("write(): Invalid SDID");
    459            return;
    460        }
    461
    462        int[] select = new int[rank];
    463        int[] start = new int[rank];
    464        for (int i=0; i<rank; i++) {
    465            select[i] = (int)selectedDims[i];
    466            start[i] = (int)startDims[i];
    467        }
    468
    469        int[] stride = null;
    470        if (selectedStride != null) {
    471            stride = new int[rank];
    472            for (int i=0; i<rank; i++) {
    473                stride[i] = (int)selectedStride[i];
    474            }
    475        }
    476
    477        Object tmpData = buf;
    478        try {
    479            if (getDatatype().isUnsigned() && unsignedConverted) {
    480                tmpData = convertToUnsignedC(buf);
    481            }
    482            // assume external data files are located in the same directory as the main file.
    483            HDFLibrary.HXsetdir(getFileFormat().getParent());
    484
    485            HDFLibrary.SDwritedata(id, start, stride, select, tmpData);
    486        }
    487        catch (Exception ex) {
    488            log.debug("write(): failure: ", ex);
    489        }
    490        finally {
    491            tmpData = null;
    492            close(id);
    493        }
    494    }
    495
    496    // Implementing DataFormat
    497    @Override
    498    @SuppressWarnings({"rawtypes", "unchecked"})
    499    public List getMetadata() throws HDFException
    500    {
    501        if (attributeList != null) {
    502            log.trace("getMetdata(): attributeList != null");
    503            return attributeList;
    504        }
    505
    506        long id = open();
    507        String[] objName = {""};
    508        int[] sdInfo = {0, 0, 0};
    509        try {
    510            // retrieve attributes of the dataset
    511            int[] tmpDim = new int[HDFConstants.MAX_VAR_DIMS];
    512            HDFLibrary.SDgetinfo(id, objName, tmpDim, sdInfo);
    513            int n = sdInfo[2];
    514
    515            if ((attributeList == null) && (n>0)) {
    516                attributeList = new Vector(n, 5);
    517            }
    518
    519            boolean b = false;
    520            String[] attrName = new String[1];
    521            int[] attrInfo = {0, 0};
    522            for (int i=0; i<n; i++) {
    523                attrName[0] = "";
    524                try {
    525                    b = HDFLibrary.SDattrinfo(id, i, attrName, attrInfo);
    526                    // mask off the litend bit
    527                    attrInfo[0] = attrInfo[0] & (~HDFConstants.DFNT_LITEND);
    528                }
    529                catch (HDFException ex) {
    530                    log.debug("getMetadata(): attribute[{}] SDattrinfo failure: ", i, ex);
    531                    b = false;
    532                }
    533
    534                if (!b) {
    535                    continue;
    536                }
    537
    538                long[] attrDims = {attrInfo[1]};
    539                Attribute attr = new Attribute(this, attrName[0], new H4Datatype(attrInfo[0]), attrDims);
    540                attributeList.add(attr);
    541
    542                Object buf = null;
    543                try {
    544                    buf = H4Datatype.allocateArray(attrInfo[0], attrInfo[1]);
    545                }
    546                catch (OutOfMemoryError e) {
    547                    log.debug("getMetadata(): out of memory: ", e);
    548                    buf = null;
    549                }
    550
    551                try {
    552                    HDFLibrary.SDreadattr(id, i, buf);
    553                }
    554                catch (HDFException ex) {
    555                    log.debug("getMetadata(): attribute[{}] SDreadattr failure: ", i, ex);
    556                    buf = null;
    557                }
    558
    559                if (buf != null) {
    560                    if ((attrInfo[0] == HDFConstants.DFNT_CHAR) ||
    561                        (attrInfo[0] ==  HDFConstants.DFNT_UCHAR8)) {
    562                        buf = Dataset.byteToString((byte[])buf, attrInfo[1]);
    563                    }
    564                    else if (attrName[0].equalsIgnoreCase("fillValue") ||
    565                            attrName[0].equalsIgnoreCase("_fillValue")) {
    566                        fillValue = buf;
    567                    }
    568
    569                    attr.setData(buf);
    570                }
    571
    572            } // (int i=0; i<n; i++)
    573
    574            // retrieve attribute of dimension
    575            // BUG !! HDFLibrary.SDgetdimstrs(dimID, argv, 80) does not return anything
    576            /**
    577             * for (int i=0; i< rank; i++) { int dimID = HDFLibrary.SDgetdimid(id, i); String[] argv = {" ", "
    578             * ", " "}; HDFLibrary.SDgetdimstrs(dimID, argv, 80); }
    579             */
    580        }
    581        catch (Exception ex) {
    582            log.debug("getMetadata(): failure: ", ex);
    583        }
    584        finally {
    585            close(id);
    586        }
    587
    588        return attributeList;
    589    }
    590
    591    // To do: implementing DataFormat
    592    @Override
    593    @SuppressWarnings({"rawtypes", "unchecked"})
    594    public void writeMetadata(Object info) throws Exception
    595    {
    596        // only attribute metadata is supported.
    597        if (!(info instanceof Attribute)) {
    598            log.debug("writeMetadata(): Object not an Attribute");
    599            return;
    600        }
    601
    602        try {
    603            getFileFormat().writeAttribute(this, (Attribute)info, true);
    604
    605            if (attributeList == null) {
    606                attributeList = new Vector();
    607            }
    608
    609            attributeList.add(info);
    610            nAttributes = attributeList.size();
    611        }
    612        catch (Exception ex) {
    613            log.trace("writeMetadata(): failure: ", ex);
    614        }
    615    }
    616
    617    // To do: implementing DataFormat
    618    @Override
    619    public void removeMetadata(Object info) throws HDFException {
    620        log.trace("removeMetadata(): disabled");
    621    }
    622
    623    // implementing DataFormat
    624    @Override
    625    public void updateMetadata(Object info) throws Exception {
    626        log.trace("updateMetadata(): disabled");
    627    }
    628
    629    // Implementing HObject
    630    @Override
    631    public long open()
    632    {
    633        long id=-1;
    634
    635        try {
    636            int index = 0;
    637            int tag = (int)oid[0];
    638
    639            log.trace("open(): tag={}", tag);
    640            if (tag == H4SDS.DFTAG_NDG_NETCDF) {
    641                index = (int)oid[1]; //HDFLibrary.SDidtoref(id) fails for netCDF
    642            }
    643            else {
    644                index = HDFLibrary.SDreftoindex(sdid, (int)oid[1]);
    645            }
    646
    647            id = HDFLibrary.SDselect(sdid,index);
    648        }
    649        catch (HDFException ex) {
    650            log.debug("open(): failure: ", ex);
    651            id = -1;
    652        }
    653
    654        return id;
    655    }
    656
    657    // Implementing HObject
    658    @Override
    659    public void close(long id)
    660    {
    661        try { HDFLibrary.SDendaccess(id); }
    662        catch (HDFException ex) { log.debug("close(): failure: ", ex); }
    663    }
    664
    665    /**
    666     * Initializes the H4SDS such as dimension size of this dataset.
    667     */
    668    @SuppressWarnings("deprecation")
    669    @Override
    670    public void init()
    671    {
    672        if (inited) {
    673            log.trace("init(): Already initialized");
    674            return; // already called. Initialize only once
    675        }
    676
    677        long id = open();
    678        String[] objName = {""};
    679        String[] dimName = {""};
    680        int[] dimInfo = {0, 0, 0};
    681        int[] sdInfo = {0, 0, 0};
    682        boolean isUnlimited = false;
    683
    684        int[] idims = new int[HDFConstants.MAX_VAR_DIMS];
    685        try {
    686            HDFLibrary.SDgetinfo(id, objName, idims, sdInfo);
    687            // mask off the litend bit
    688            sdInfo[1] = sdInfo[1] & (~HDFConstants.DFNT_LITEND);
    689            nAttributes = sdInfo[2];
    690            rank = sdInfo[0];
    691
    692            if (rank <= 0) {
    693                rank = 1;
    694                idims[0] = 1;
    695            }
    696
    697            isUnlimited = HDFLibrary.SDisrecord(id);
    698            log.trace("init(): isUnlimited={}", isUnlimited);
    699
    700            datatypeID = sdInfo[1];
    701            isText = ((datatypeID == HDFConstants.DFNT_CHAR) || (datatypeID == HDFConstants.DFNT_UCHAR8));
    702
    703            // get the dimension names
    704            try {
    705                dimNames = new String[rank];
    706                for (int i=0; i<rank; i++) {
    707                    long dimid = HDFLibrary.SDgetdimid(id, i);
    708                    HDFLibrary.SDdiminfo(dimid, dimName, dimInfo);
    709                    dimNames[i] = dimName[0];
    710                }
    711            }
    712            catch (Exception ex) {
    713                log.debug("init(): get the dimension names: ", ex);
    714            }
    715
    716            // get compression information
    717            try {
    718                HDFCompInfo compInfo = new HDFCompInfo();
    719                HDFLibrary.SDgetcompinfo(id, compInfo);
    720
    721                compression.setLength(0);
    722
    723                if (compInfo.ctype == HDFConstants.COMP_CODE_DEFLATE) {
    724                    HDFDeflateCompInfo comp = new HDFDeflateCompInfo();
    725                    HDFLibrary.SDgetcompinfo(id, comp);
    726                    compression.append("GZIP(level=").append(comp.level).append(")");
    727                }
    728                else if (compInfo.ctype == HDFConstants.COMP_CODE_SZIP) {
    729                    HDFSZIPCompInfo comp = new HDFSZIPCompInfo();
    730                    HDFLibrary.SDgetcompinfo(id, comp);
    731                    compression.append("SZIP(bits_per_pixel=").append(comp.bits_per_pixel).append(",options_mask=")
    732                            .append(comp.options_mask).append(",pixels=").append(comp.pixels).append(",pixels_per_block=")
    733                            .append(comp.pixels_per_block).append(",pixels_per_scanline=").append(comp.pixels_per_scanline).append(")");
    734                }
    735                else if (compInfo.ctype == HDFConstants.COMP_CODE_JPEG) {
    736                    HDFJPEGCompInfo comp = new HDFJPEGCompInfo();
    737                    HDFLibrary.SDgetcompinfo(id, comp);
    738                    compression.append("JPEG(quality=").append(comp.quality).append(",options_mask=")
    739                            .append(",force_baseline=").append(comp.force_baseline).append(")");
    740                }
    741                else if (compInfo.ctype == HDFConstants.COMP_CODE_SKPHUFF) {
    742                    HDFSKPHUFFCompInfo comp = new HDFSKPHUFFCompInfo();
    743                    HDFLibrary.SDgetcompinfo(id, comp);
    744                    compression.append("SKPHUFF(skp_size=").append(comp.skp_size).append(")");
    745                }
    746                else if (compInfo.ctype == HDFConstants.COMP_CODE_RLE) {
    747                    compression.append("RLE");
    748                }
    749                else if (compInfo.ctype == HDFConstants.COMP_CODE_NBIT) {
    750                    HDFNBITCompInfo comp = new HDFNBITCompInfo();
    751                    HDFLibrary.SDgetcompinfo(id, comp);
    752                    compression.append("NBIT(nt=").append(comp.nt).append(",bit_len=").append(comp.bit_len)
    753                            .append(",ctype=").append(comp.ctype).append(",fill_one=").append(comp.fill_one)
    754                            .append(",sign_ext=").append(comp.sign_ext).append(",start_bit=").append(comp.start_bit).append(")");
    755                }
    756
    757                if (compression.length() == 0)
    758                    compression.append("NONE");
    759            }
    760            catch (Exception ex) {
    761                log.debug("init(): get compression information failure: ", ex);
    762            }
    763
    764            // get chunk information
    765            try {
    766                HDFChunkInfo chunkInfo = new HDFChunkInfo();
    767                int[] cflag = {HDFConstants.HDF_NONE};
    768
    769                try {
    770                    HDFLibrary.SDgetchunkinfo(id, chunkInfo, cflag);
    771                }
    772                catch (Exception ex) {
    773                    ex.printStackTrace();
    774                }
    775
    776                storageLayout.setLength(0);
    777
    778                if (cflag[0] == HDFConstants.HDF_NONE) {
    779                    chunkSize = null;
    780                    storageLayout.append("NONE");
    781                }
    782                else {
    783                    chunkSize = new long[rank];
    784                    for (int i=0; i<rank; i++) {
    785                        chunkSize[i] = chunkInfo.chunk_lengths[i];
    786                    }
    787                    storageLayout.append("CHUNKED: ").append(chunkSize[0]);
    788                    for (int i = 1; i < rank; i++) {
    789                        storageLayout.append(" X ").append(chunkSize[i]);
    790                    }
    791                }
    792            }
    793            catch (Exception ex) {
    794                log.debug("init(): get chunk information failure: ", ex);
    795            }
    796
    797            inited = true;
    798        }
    799        catch (HDFException ex) {
    800            log.debug("init(): failure: ", ex);
    801        }
    802        finally {
    803            close(id);
    804        }
    805
    806        dims = new long[rank];
    807        maxDims = new long[rank];
    808        startDims = new long[rank];
    809        selectedDims = new long[rank];
    810
    811        for (int i=0; i<rank; i++) {
    812            startDims[i] = 0;
    813            selectedDims[i] = 1;
    814            dims[i] = maxDims[i] = idims[i];
    815        }
    816
    817        if (isUnlimited)
    818            maxDims[0] = -1;
    819
    820        selectedIndex[0] = 0;
    821        selectedIndex[1] = 1;
    822        selectedIndex[2] = 2;
    823
    824        // select only two dimension a time,
    825        if (rank == 1) {
    826            selectedDims[0] = dims[0];
    827        }
    828
    829        if (rank > 1) {
    830            selectedDims[0] = dims[0];
    831            if (isText) {
    832                selectedDims[1] = 1;
    833            }
    834            else {
    835                selectedDims[1] = dims[1];
    836            }
    837        }
    838    }
    839
    840    // Implementing ScalarDS
    841    @Override
    842    public byte[][] getPalette()
    843    {
    844        return palette;
    845    }
    846
    847    /**
    848     * Creates a new dataset.
    849     *
    850     * @param name the name of the dataset to create.
    851     * @param pgroup the parent group of the new dataset.
    852     * @param type the datatype of the dataset.
    853     * @param dims the dimension size of the dataset.
    854     * @param maxdims the max dimension size of the dataset.
    855     * @param chunks the chunk size of the dataset.
    856     * @param gzip the level of the gzip compression.
    857     * @param fillValue the default value.
    858     * @param data the array of data values.
    859     *
    860     * @return the new dataset if successful. Otherwise returns null.
    861     *
    862     * @throws Exception if the dataset can not be created
    863     */
    864    public static H4SDS create(
    865        String name,
    866        Group pgroup,
    867        Datatype type,
    868        long[] dims,
    869        long[] maxdims,
    870        long[] chunks,
    871        int gzip,
    872        Object fillValue,
    873        Object data) throws Exception
    874    {
    875        H4SDS dataset = null;
    876        if ((pgroup == null) ||
    877            (name == null)||
    878            (dims == null)) {
    879            log.trace("create(): Parent group, name or dims is null");
    880            return null;
    881        }
    882
    883        H4File file = (H4File)pgroup.getFileFormat();
    884
    885        if (file == null) {
    886            log.trace("create(): Parent group FileFormat is null");
    887            return null;
    888        }
    889
    890        String path = HObject.SEPARATOR;
    891        if (!pgroup.isRoot()) {
    892            path = pgroup.getPath()+pgroup.getName()+HObject.SEPARATOR;
    893        }
    894        // prepare the dataspace
    895        int rank = dims.length;
    896        int[] idims = new int[rank];
    897        int[] start = new int[rank];
    898        for (int i=0; i<rank; i++) {
    899            idims[i] = (int)dims[i];
    900            start[i] = 0;
    901        }
    902
    903        // only the first element of the SDcreate parameter dim_sizes (i.e.,
    904        // the dimension of the lowest rank or the slowest-changing dimension)
    905        // can be assigned the value SD_UNLIMITED (or 0) to make the first
    906        // dimension unlimited.
    907        if ((maxdims != null) && (maxdims[0]<=0)) {
    908            idims[0] = 0; // set to unlimited dimension.
    909        }
    910
    911        int[] ichunks = null;
    912        if (chunks != null) {
    913            ichunks = new int[rank];
    914            for (int i=0; i<rank; i++) {
    915                ichunks[i] = (int)chunks[i];
    916            }
    917        }
    918
    919        // unlimited cannot be used with chunking or compression for HDF 4.2.6 or earlier.
    920        if (idims[0] == 0 && (ichunks != null || gzip>0)) {
    921            log.debug("create(): Unlimited cannot be used with chunking or compression");
    922            throw new HDFException("Unlimited cannot be used with chunking or compression");
    923        }
    924
    925        long sdid = (file).getSDAccessID();
    926        long sdsid = -1;
    927        long vgid = -1;
    928        long tid = type.createNative();
    929
    930        if(tid >= 0) {
    931            try {
    932                sdsid = HDFLibrary.SDcreate(sdid, name, tid, rank, idims);
    933                // set fill value to zero.
    934                int vsize = HDFLibrary.DFKNTsize(tid);
    935                byte[] fill = new byte[vsize];
    936                for (int i=0; i<vsize; i++) {
    937                    fill[i] = 0;
    938                }
    939                HDFLibrary.SDsetfillvalue(sdsid, fill);
    940
    941                // when we create a new dataset with unlimited dimension,
    942                // we have to write some data into the dataset or otherwise
    943                // the current dataset has zero dimensin size.
    944
    945                // comment out the following lines because SDwritedata fails when
    946                // try to write data into a zero dimension array. 05/25/05
    947                // don't know why the code was first put here ????
    948                /**
    949                if (idims[0] == 0 && data == null)
    950                {
    951                    idims[0] = (int)dims[0];
    952                    data = new byte[tsize*vsize];
    953                }
    954                 */
    955
    956            }
    957            catch (Exception ex) {
    958                log.debug("create(): failure: ", ex);
    959                throw (ex);
    960            }
    961        }
    962
    963        if (sdsid < 0) {
    964            log.debug("create(): Dataset creation failed");
    965            throw (new HDFException("Unable to create the new dataset."));
    966        }
    967
    968        HDFDeflateCompInfo compInfo = null;
    969        if (gzip > 0) {
    970            // set compression
    971            compInfo = new HDFDeflateCompInfo();
    972            compInfo.level = gzip;
    973            if (chunks == null)
    974                HDFLibrary.SDsetcompress(sdsid, HDFConstants.COMP_CODE_DEFLATE, compInfo);
    975        }
    976
    977        if (chunks != null) {
    978            // set chunk
    979            HDFChunkInfo chunkInfo = new HDFChunkInfo(ichunks);
    980            int flag = HDFConstants.HDF_CHUNK;
    981
    982            if (gzip > 0) {
    983                flag = HDFConstants.HDF_CHUNK | HDFConstants.HDF_COMP;
    984                chunkInfo = new HDFChunkInfo(ichunks, HDFConstants.COMP_CODE_DEFLATE, compInfo);
    985            }
    986
    987            try  {
    988                HDFLibrary.SDsetchunk (sdsid, chunkInfo, flag);
    989            }
    990            catch (Exception err) {
    991                log.debug("create(): SDsetchunk failure: ", err);
    992                err.printStackTrace();
    993                throw new HDFException("SDsetchunk failed.");
    994            }
    995        }
    996
    997        if ((sdsid > 0) && (data != null)) {
    998            HDFLibrary.SDwritedata(sdsid, start, null, idims, data);
    999        }
    1000
    1001        int ref = HDFLibrary.SDidtoref(sdsid);
    1002
    1003        if (!pgroup.isRoot()) {
    1004            // add the dataset to the parent group
    1005            vgid = pgroup.open();
    1006            if (vgid < 0) {
    1007                if (sdsid > 0) {
    1008                    HDFLibrary.SDendaccess(sdsid);
    1009                }
    1010                log.debug("create(): Invalid Parent Group ID");
    1011                throw (new HDFException("Unable to open the parent group."));
    1012            }
    1013
    1014            HDFLibrary.Vaddtagref(vgid, HDFConstants.DFTAG_NDG, ref);
    1015
    1016            pgroup.close(vgid);
    1017        }
    1018
    1019        try {
    1020            if (sdsid > 0) {
    1021                HDFLibrary.SDendaccess(sdsid);
    1022            }
    1023        }
    1024        catch (Exception ex) {
    1025            log.debug("create(): SDendaccess failure: ", ex);
    1026        }
    1027
    1028        long[] oid = {HDFConstants.DFTAG_NDG, ref};
    1029        dataset = new H4SDS(file, name, path, oid);
    1030
    1031        if (dataset != null) {
    1032            pgroup.addToMemberList(dataset);
    1033        }
    1034
    1035        return dataset;
    1036    }
    1037
    1038    public static H4SDS create(
    1039            String name,
    1040            Group pgroup,
    1041            Datatype type,
    1042            long[] dims,
    1043            long[] maxdims,
    1044            long[] chunks,
    1045            int gzip,
    1046            Object data) throws Exception
    1047    {
    1048        return create(name, pgroup, type, dims, maxdims, chunks, gzip, null, data);
    1049    }
    1050
    1051    /**
    1052     * copy attributes from one SDS to another SDS
    1053     */
    1054    private void copyAttribute(long srcdid, long dstdid)
    1055    {
    1056        log.trace("copyAttribute(): start: srcdid={} dstdid={}", srcdid, dstdid);
    1057        try {
    1058            String[] objName = {""};
    1059            int[] sdInfo = {0, 0, 0};
    1060            int[] tmpDim = new int[HDFConstants.MAX_VAR_DIMS];
    1061            HDFLibrary.SDgetinfo(srcdid, objName, tmpDim, sdInfo);
    1062            int numberOfAttributes = sdInfo[2];
    1063            log.trace("copyAttribute(): numberOfAttributes={}", numberOfAttributes);
    1064
    1065            boolean b = false;
    1066            String[] attrName = new String[1];
    1067            int[] attrInfo = {0, 0};
    1068            for (int i=0; i<numberOfAttributes; i++) {
    1069                attrName[0] = "";
    1070                try {
    1071                    b = HDFLibrary.SDattrinfo(srcdid, i, attrName, attrInfo);
    1072                }
    1073                catch (HDFException ex) {
    1074                    log.debug("copyAttribute(): attribute[{}] SDattrinfo failure: ", i, ex);
    1075                    b = false;
    1076                }
    1077
    1078                if (!b) {
    1079                    continue;
    1080                }
    1081
    1082                // read attribute data from source dataset
    1083                byte[] attrBuff = new byte[attrInfo[1] * HDFLibrary.DFKNTsize(attrInfo[0])];
    1084                try {
    1085                    HDFLibrary.SDreadattr(srcdid, i, attrBuff);
    1086                }
    1087                catch (HDFException ex) {
    1088                    log.debug("copyAttribute(): attribute[{}] SDreadattr failure: ", i, ex);
    1089                    attrBuff = null;
    1090                }
    1091
    1092                if (attrBuff == null) {
    1093                    log.debug("copyAttribute(): attrBuff[{}] is null", i);
    1094                    continue;
    1095                }
    1096
    1097                // attach attribute to the destination dataset
    1098                HDFLibrary.SDsetattr(dstdid, attrName[0], attrInfo[0], attrInfo[1], attrBuff);
    1099            } // (int i=0; i<numberOfAttributes; i++)
    1100        }
    1101        catch (Exception ex) {
    1102            log.debug("copyAttribute(): failure: ", ex);
    1103        }
    1104    }
    1105
    1106    //Implementing DataFormat
    1107    @SuppressWarnings("rawtypes")
    1108    public List getMetadata(int... attrPropList) throws Exception {
    1109        throw new UnsupportedOperationException("getMetadata(int... attrPropList) is not supported");
    1110    }
    1111}