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.nc2;
016
017import java.util.List;
018import java.util.Vector;
019
020import hdf.object.Dataset;
021import hdf.object.Datatype;
022import hdf.object.FileFormat;
023import hdf.object.Group;
024import hdf.object.HObject;
025import hdf.object.MetaDataContainer;
026import hdf.object.ScalarDS;
027
028import ucar.ma2.DataType;
029import ucar.nc2.Variable;
030
031/**
032 * NC2Dataset describes an multi-dimension array of HDF5 scalar or atomic data
033 * types, such as byte, int, short, long, float, double and string, and
034 * operations performed on the scalar dataset
035 *
036 * The library predefines a modest number of datatypes. For details, read <a
037 * href="https://support.hdfgroup.org/HDF5/doc/UG/HDF5_Users_Guide-Responsive%20HTML5/HDF5_Users_Guide/Datatypes/HDF5_Datatypes.htm">HDF5 Datatypes</a>
038 *
039 * @version 1.1 9/4/2007
040 * @author Peter X. Cao
041 */
042public class NC2Dataset extends ScalarDS implements MetaDataContainer {
043    private static final long serialVersionUID = -6031051694304457461L;
044
045    private static final org.slf4j.Logger   log = org.slf4j.LoggerFactory.getLogger(NC2Dataset.class);
046
047    /** tag for netCDF datasets.
048     *  HDF4 library supports netCDF version 2.3.2. It only supports SDS APIs.
049     */
050    // magic number for netCDF: "C(67) D(68) F(70) '\001'"
051    public static final int                 DFTAG_NDG_NETCDF = 67687001;
052
053    /**
054     * The list of attributes of this data object. Members of the list are
055     * instance of Attribute.
056     */
057    @SuppressWarnings("rawtypes")
058    private List                            attributeList;
059
060    /** the native dataset */
061    private Variable nativeDataset;
062
063    /**
064     * Constructs an NC2Dataset object with specific netcdf variable.
065     *
066     * @param fileFormat
067     *            the netcdf file.
068     * @param ncDataset
069     *            the netcdf variable.
070     * @param oid
071     *            the unique identifier of this data object.
072     */
073    public NC2Dataset(FileFormat fileFormat, Variable ncDataset, long[] oid) {
074        super(fileFormat, ncDataset.getName(), HObject.SEPARATOR, oid);
075        unsignedConverted = false;
076        nativeDataset = ncDataset;
077    }
078
079    /**
080     * Check if the object has any attributes attached.
081     *
082     * @return true if it has any attributes, false otherwise.
083     */
084    @Override
085    public boolean hasAttribute() {
086        return false;
087    }
088
089    // Implementing Dataset
090    @Override
091    public Dataset copy(Group pgroup, String dstName, long[] dims, Object buff) throws Exception {
092        // not supported
093        throw new UnsupportedOperationException("Unsupported operation for NetCDF.");
094    }
095
096    // implementing Dataset
097    @Override
098    public byte[] readBytes() throws Exception {
099        // not supported
100        throw new UnsupportedOperationException("Unsupported operation for NetCDF.");
101    }
102
103    // Implementing DataFormat
104    /**
105     * Reads the data from file.
106     *
107     * read() reads the data from file to a memory buffer and returns the memory
108     * buffer. The dataset object does not hold the memory buffer. To store the
109     * memory buffer in the dataset object, one must call getData().
110     *
111     * By default, the whole dataset is read into memory. Users can also select
112     * a subset to read. Subsetting is done in an implicit way.
113     *
114     * @return the data read from file.
115     *
116     * @see #getData()
117     *
118     * @throws Exception
119     *             if object can not be read
120     * @throws OutOfMemoryError
121     *             if memory is exhausted
122     */
123    @Override
124    public Object read() throws Exception {
125        Object theData = null;
126
127        if (nativeDataset == null)
128            return null;
129
130        int[] origin = new int[rank];
131        int[] shape = new int[rank];
132
133        for (int i = 0; i < rank; i++) {
134            origin[i] = (int) startDims[i];
135            shape[i] = (int) selectedDims[i];
136            log.trace("read(): origin-shape [{}]={}-{}", i, origin[i], shape[i]);
137        }
138
139        ucar.ma2.Array ncArray = null;
140
141        try {
142            ncArray = nativeDataset.read(origin, shape);
143        }
144        catch (Exception ex) {
145            ncArray = nativeDataset.read();
146        }
147        Object oneD = ncArray.copyTo1DJavaArray();
148
149        if (oneD == null)
150            return null;
151
152        if (oneD.getClass().getName().startsWith("[C")) {
153            char[] charA = (char[]) oneD;
154            int nCols = (int) getWidth();
155            int nRows = (int) getHeight();
156
157            String[] strA = new String[nRows];
158            String allStr = new String(charA);
159
160            int indx0 = 0;
161            for (int i = 0; i < nRows; i++) {
162                indx0 = i * nCols;
163                strA[i] = allStr.substring(indx0, indx0 + nCols);
164            }
165            theData = strA;
166        }
167        else {
168            theData = oneD;
169        }
170
171        return theData;
172    }
173
174    // Implementing DataFormat
175    /**
176     * Writes a memory buffer to the object in the file.
177     *
178     * @param buf
179     *            the data to write
180     *
181     * @throws Exception
182     *             if data can not be written
183     */
184    @Override
185    public void write(Object buf) throws Exception {
186        // not supported
187        throw new UnsupportedOperationException("Unsupported operation for NetCDF.");
188    }
189
190    // Implementing DataFormat
191    /**
192     * Retrieves the object's metadata, such as attributes, from the file.
193     *
194     * Metadata, such as attributes, is stored in a List.
195     *
196     * @return the list of metadata objects.
197     *
198     * @throws Exception
199     *             if the metadata can not be retrieved
200     */
201    @Override
202    public List getMetadata() throws Exception {
203        if (attributeList != null)
204            return attributeList;
205
206        if (nativeDataset == null)
207            return (attributeList = null);
208
209        List ncAttrList = nativeDataset.getAttributes();
210        if (ncAttrList == null)
211            return (attributeList = null);
212
213        int n = ncAttrList.size();
214        attributeList = new Vector(n);
215        ucar.nc2.Attribute ncAttr = null;
216        for (int i = 0; i < n; i++) {
217            ncAttr = (ucar.nc2.Attribute) ncAttrList.get(i);
218            log.trace("getMetadata(): Attribute[{}]:{}", i, ncAttr.toString());
219            attributeList.add(NC2File.convertAttribute(this, ncAttr));
220        }
221
222        return attributeList;
223    }
224
225    // implementing DataFormat
226    /**
227     * Writes a specific piece of metadata (such as an attribute) into the file.
228     *
229     * If an HDF(4&amp;5) attribute exists in the file, this method updates its
230     * value. If the attribute does not exist in the file, it creates the
231     * attribute in the file and attaches it to the object. It will fail to
232     * write a new attribute to the object where an attribute with the same name
233     * already exists. To update the value of an existing attribute in the file,
234     * one needs to get the instance of the attribute by getMetadata(), change
235     * its values, then use writeMetadata() to write the value.
236     *
237     * @param info
238     *            the metadata to write.
239     *
240     * @throws Exception
241     *             if the metadata can not be written
242     */
243   @Override
244    public void writeMetadata(Object info) throws Exception {
245        // not supported
246        throw new UnsupportedOperationException("Unsupported operation for NetCDF.");
247    }
248
249    // implementing DataFormat
250   /**
251    * Deletes an existing piece of metadata from this object.
252    *
253    * @param info
254    *            the metadata to delete.
255    *
256    * @throws Exception
257    *             if the metadata can not be removed
258    */
259    @Override
260    public void removeMetadata(Object info) throws Exception {
261        // not supported
262        throw new UnsupportedOperationException("Unsupported operation for NetCDF.");
263    }
264
265    /**
266     * Updates an existing piece of metadata attached to this object.
267     *
268     * @param info
269     *            the metadata to update.
270     *
271     * @throws Exception
272     *             if the metadata can not be updated
273     */
274    @Override
275    public void updateMetadata(Object info) throws Exception {
276        // not supported
277        throw new UnsupportedOperationException("Unsupported operation for NetCDF.");
278    }
279
280    // Implementing HObject
281    @Override
282    public long open() {
283        return -1;
284    }
285
286    // Implementing HObject
287    @Override
288    public void close(long did) {
289    }
290
291    /**
292     * Retrieve and initialize dimensions and member information.
293     */
294    @Override
295    public void init() {
296        if (nativeDataset == null)
297            return;
298
299        if (inited)
300            return; // already called. Initialize only once
301
302        isText = nativeDataset.getDataType().equals(DataType.STRING);
303        boolean isChar = nativeDataset.getDataType().equals(DataType.CHAR);
304
305        rank = nativeDataset.getRank();
306        log.trace("init(): rank:{}", rank);
307
308        if (rank == 0) {
309            // a scalar data point
310            isScalar = true;
311            rank = 1;
312            dims = new long[] { 1 };
313        }
314        else {
315            isScalar = false;
316            dims = new long[rank];
317            for (int i = 0; i < rank; i++)
318                dims[i] = (nativeDataset.getDimension(i).getLength());
319        }
320
321        startDims = new long[rank];
322        selectedDims = new long[rank];
323        for (int i = 0; i < rank; i++) {
324            startDims[i] = 0;
325            selectedDims[i] = 1;
326        }
327
328        if (rank == 1) {
329            selectedIndex[0] = 0;
330            selectedDims[0] = dims[0];
331        }
332        else if (rank == 2) {
333            selectedIndex[0] = 0;
334            selectedIndex[1] = 1;
335            selectedDims[0] = dims[0];
336            selectedDims[1] = dims[1];
337        }
338        else if (rank > 2) {
339            selectedIndex[0] = 0;
340            selectedIndex[1] = 1;
341            selectedIndex[2] = 2;
342            selectedDims[0] = dims[0];
343            selectedDims[1] = dims[1];
344        }
345
346        if ((rank > 1) && isText)
347            selectedDims[1] = 1;
348
349        inited = true;
350    }
351
352    /**
353     * Creates a new dataset.
354     *
355     * @param name
356     *            the name of the dataset to create.
357     * @param pgroup
358     *            the parent group of the new dataset.
359     * @param type
360     *            the datatype of the dataset.
361     * @param dims
362     *            the dimension size of the dataset.
363     * @param maxdims
364     *            the max dimension size of the dataset.
365     * @param chunks
366     *            the chunk size of the dataset.
367     * @param gzip
368     *            the level of the gzip compression.
369     * @param data
370     *            the array of data values.
371     *
372     * @return the new dataset if successful. Otherwise returns null.
373     *
374     * @throws Exception
375     *            if there is an error
376    */
377    public static NC2Dataset create(String name, Group pgroup, Datatype type,
378            long[] dims, long[] maxdims, long[] chunks, int gzip, Object data) throws Exception {
379        // not supported
380        throw new UnsupportedOperationException("Unsupported operation for NetCDF.");
381    }
382
383    // implementing ScalarDS
384    /**
385     * Returns the datatype of the data object.
386     *
387     * @return the datatype of the data object.
388     */
389   @Override
390    public Datatype getDatatype() {
391        if (datatype == null) {
392            try {
393                datatype = new NC2Datatype(nativeDataset.getDataType());
394            }
395            catch (Exception ex) {
396                datatype = null;
397            }
398        }
399
400        return datatype;
401    }
402
403    /**
404     * Sets the name of the data object.
405     *
406     * @param newName
407     *            the new name of the object.
408     */
409    @Override
410    public void setName(String newName) throws Exception {
411        // not supported
412        throw new UnsupportedOperationException("Unsupported operation for NetCDF.");
413    }
414
415    //Implementing DataFormat
416    /**
417     * Retrieves the object's metadata, such as attributes, from the file.
418     *
419     * Metadata, such as attributes, is stored in a List.
420     *
421     * @param attrPropList
422     *             the list of properties to get
423     *
424     * @return the list of metadata objects.
425     *
426     * @throws Exception
427     *             if the metadata can not be retrieved
428     */
429    public List getMetadata(int... attrPropList) throws Exception {
430        throw new UnsupportedOperationException("getMetadata(int... attrPropList) is not supported");
431    }
432}