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