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