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