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