    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.io.IOException;
    018import java.util.ArrayList;
    019import java.util.Iterator;
    020import java.util.LinkedList;
    021import java.util.List;
    022import java.util.Queue;
    023import java.util.Vector;
    024
    025import org.slf4j.Logger;
    026import org.slf4j.LoggerFactory;
    027
    028import ucar.nc2.NetcdfFile;
    029import ucar.nc2.Variable;
    030import ucar.nc2.iosp.netcdf3.N3header;
    031
    032import hdf.object.Attribute;
    033import hdf.object.Dataset;
    034import hdf.object.Datatype;
    035import hdf.object.FileFormat;
    036import hdf.object.Group;
    037import hdf.object.HObject;
    038
    039/**
    040 * This class provides file level APIs. File access APIs include retrieving the
    041 * file hierarchy, opening and closing file, and writing file content to disk.
    042 *
    043 * @version 2.4 9/4/2007
    044 * @author Peter X. Cao
    045 */
    046public class NC2File extends FileFormat {
    047    private static final long serialVersionUID = 6941235662108358451L;
    048
    049    private static final Logger   log = LoggerFactory.getLogger(NC2File.class);
    050
    051    /**
    052     * The root object of this file.
    053     */
    054    private HObject                         rootObject;
    055
    056    /**
    057     * The list of unique (tag, ref) pairs. It is used to avoid duplicate
    058     * objects in memory.
    059     */
    060    @SuppressWarnings("rawtypes")
    061    private List                            objList;
    062
    063    /** the netcdf file */
    064    private NetcdfFile                      ncFile;
    065
    066    private static boolean isFileOpen;
    067
    068    /**
    069     * Constructs an empty NC2File with read-only access.
    070     */
    071    public NC2File() {
    072        this("");
    073    }
    074
    075    /**
    076     * Creates an NC2File object of given file name with read-only access.
    077     *
    078     * @param fileName
    079     *            A valid file name, with a relative or absolute path.
    080     */
    081    public NC2File(String fileName) {
    082        super(fileName);
    083
    084        isFileOpen = false;
    085        isReadOnly = true;
    086        objList = new Vector();
    087        ncFile = null;
    088
    089        this.fid = -1;
    090
    091        if ((fullFileName != null) && (fullFileName.length() > 0)) {
    092            try {
    093                log.trace("NetcdfFile:{}", fullFileName);
    094                ncFile = NetcdfFile.open(fullFileName);
    095                this.fid = 1;
    096            }
    097            catch (Exception ex) {
    098                log.trace("NC2File:{}", fullFileName, ex);
    099            }
    100        }
    101    }
    102
    103    /**
    104     * Checks if the given file format is a NetCDF3 file.
    105     *
    106     * @param fileformat
    107     *            the fileformat to be checked.
    108     *
    109     * @return true if the given file is a NetCDF3 file; otherwise returns false.
    110     */
    111    @Override
    112    public boolean isThisType(FileFormat fileformat) {
    113        return (fileformat instanceof NC2File);
    114    }
    115
    116    /**
    117     * Checks if the given file is a NetCDF file.
    118     *
    119     * @param filename
    120     *            the file to be checked.
    121     *
    122     * @return true if the given file is a NetCDF file; otherwise returns false.
    123     */
    124    @Override
    125    public boolean isThisType(String filename) {
    126        boolean isNetcdf = false;
    127        ucar.unidata.io.RandomAccessFile raf = null;
    128
    129        try {
    130            raf = new ucar.unidata.io.RandomAccessFile(filename, "r");
    131        }
    132        catch (Exception ex) {
    133            log.trace("raf null - exit", ex);
    134            raf = null;
    135        }
    136
    137        if (raf == null) {
    138            return false;
    139        }
    140
    141        try {
    142            isNetcdf = N3header.isValidFile(raf);
    143        }
    144        catch (IOException e) {
    145            log.trace("raf isValidFile - failure", e);
    146            return false;
    147        }
    148
    149        try {
    150            raf.close();
    151        }
    152        catch (Exception ex) {
    153            log.trace("raf close:", ex);
    154        }
    155
    156        log.trace("{} - isNetcdf:{}", filename, isNetcdf);
    157        return isNetcdf;
    158    }
    159
    160    /**
    161     * Creates a NC2File instance with specified file name and READ access.
    162     * Regardless of specified access, the NC2File implementation uses READ.
    163     *
    164     * @see hdf.object.FileFormat#createInstance(java.lang.String, int)
    165     */
    166    @Override
    167    public FileFormat createInstance(String filename, int access)
    168            throws Exception {
    169        return new NC2File(filename);
    170    }
    171
    172    // Implementing FileFormat
    173    @Override
    174    public long open() throws Exception {
    175        log.trace("open(): start isFileOpen={}", isFileOpen);
    176
    177        if (!isFileOpen) {
    178            isFileOpen = true;
    179            rootObject = loadTree();
    180        }
    181
    182        return 0;
    183    }
    184
    185    private HObject loadTree() {
    186        long[] oid = { 0 };
    187        // root object does not have a parent path or a parent node
    188        NC2Group rootGroup = new NC2Group(this, "/", null, null, oid);
    189
    190        if (ncFile == null) {
    191            return rootGroup;
    192        }
    193
    194        log.trace("loadTree(): iterate members");
    195        Iterator it = ncFile.getVariables().iterator();
    196        Variable ncDataset = null;
    197        NC2Dataset d = null;
    198        while (it.hasNext()) {
    199            ncDataset = (Variable) it.next();
    200            oid[0] = ncDataset.hashCode();
    201            d = new NC2Dataset(this, ncDataset, oid);
    202            rootGroup.addToMemberList(d);
    203        }
    204
    205        return rootGroup;
    206    }
    207
    208    // Implementing FileFormat
    209    @Override
    210    public void close() throws IOException {
    211        if (ncFile != null) {
    212            ncFile.close();
    213        }
    214
    215        isFileOpen = false;
    216        fid = -1;
    217        objList = null;
    218    }
    219
    220    // Implementing FileFormat
    221    @Override
    222    public HObject getRootObject() {
    223        return rootObject;
    224    }
    225
    226    /**
    227     * @return the NetCDF file.
    228     */
    229    public NetcdfFile getNetcdfFile() {
    230        return ncFile;
    231    }
    232
    233    @Override
    234    public Group createGroup(String name, Group pgroup) throws Exception {
    235        throw new UnsupportedOperationException("Unsupported operation - create group.");
    236    }
    237
    238    @Override
    239    public Datatype createDatatype(int tclass, int tsize, int torder, int tsign)
    240            throws Exception {
    241        throw new UnsupportedOperationException("Unsupported operation - create datatype.");
    242    }
    243
    244    @Override
    245    public Datatype createDatatype(int tclass, int tsize, int torder,
    246            int tsign, Datatype tbase) throws Exception {
    247        throw new UnsupportedOperationException("Unsupported operation - create datatype.");
    248    }
    249
    250    @Override
    251    public Datatype createNamedDatatype(Datatype tnative, String name) throws Exception {
    252        throw new UnsupportedOperationException("netcdf3 does not support named datatype.");
    253    }
    254
    255    @Override
    256    public Dataset createScalarDS(String name, Group pgroup, Datatype type,
    257            long[] dims, long[] maxdims, long[] chunks,
    258            int gzip, Object fillValue, Object data) throws Exception {
    259        throw new UnsupportedOperationException("Unsupported operation create dataset.");
    260    }
    261
    262    @Override
    263    public Dataset createImage(String name, Group pgroup, Datatype type,
    264            long[] dims, long[] maxdims, long[] chunks,
    265            int gzip, int ncomp, int intelace, Object data) throws Exception {
    266        throw new UnsupportedOperationException("Unsupported operation create image.");
    267    }
    268
    269    @Override
    270    public void delete(HObject obj) throws Exception {
    271        throw new UnsupportedOperationException("Unsupported operation.");
    272    }
    273
    274    @Override
    275    public HObject copy(HObject srcObj, Group dstGroup, String dstName)
    276            throws Exception {
    277        throw new UnsupportedOperationException("Unsupported operation - copy.");
    278    }
    279
    280    @Override
    281    public void writeAttribute(HObject obj, hdf.object.Attribute attr, boolean attrExisted) throws Exception {
    282        throw new UnsupportedOperationException("Unsupported operation - write attribute.");
    283    }
    284
    285    private HObject copyGroup(NC2Group srcGroup, NC2Group pgroup)
    286            throws Exception {
    287        throw new UnsupportedOperationException("Unsupported operation - copy group.");
    288    }
    289
    290    private void copyDataset(Dataset srcDataset, NC2Group pgroup)
    291            throws Exception {
    292        throw new UnsupportedOperationException("Unsupported operation - copy dataset.");
    293    }
    294
    295    /**
    296     * Copies the attributes of one object to another object.
    297     *
    298     * NC3 does not support attribute copy
    299     *
    300     * @param src
    301     *            The source object.
    302     * @param dst
    303     *            The destination object.
    304     */
    305    public void copyAttributes(HObject src, HObject dst) {
    306        throw new UnsupportedOperationException("Unsupported operation copy attributes with HObject.");
    307    }
    308
    309    /**
    310     * Copies the attributes of one object to another object.
    311     *
    312     * NC3 does not support attribute copy
    313     *
    314     * @param srcID
    315     *            The source identifier.
    316     * @param dstID
    317     *            The destination identifier.
    318     */
    319    public void copyAttributes(int srcID, int dstID) {
    320        throw new UnsupportedOperationException("Unsupported operation - copy attributes.");
    321    }
    322
    323    /**
    324     * converts a ucar.nc2.Attribute into an hdf.object.nc2.NC2Attribute
    325     *
    326     * @param parent
    327     *            the parent object.
    328     * @param netcdfAttr
    329     *            the ucar.nc2.Attribute object.
    330     *
    331     * @return the hdf.object.nc2.NC2Attribute if successful
    332     */
    333    public static hdf.object.nc2.NC2Attribute convertAttribute(HObject parent, ucar.nc2.Attribute netcdfAttr) {
    334        hdf.object.nc2.NC2Attribute ncsaAttr = null;
    335
    336        if (netcdfAttr == null) {
    337            return null;
    338        }
    339
    340        String attrName = netcdfAttr.getShortName();
    341        long[] attrDims = { netcdfAttr.getLength() };
    342        log.trace("convertAttribute(): attrName={} len={}", attrName, netcdfAttr.getLength());
    343        Datatype attrType = null;
    344        try {
    345            attrType = new NC2Datatype(netcdfAttr.getDataType());
    346        }
    347        catch (Exception ex) {
    348            attrType = null;
    349        }
    350        ncsaAttr = new hdf.object.nc2.NC2Attribute(parent, attrName, attrType, attrDims);
    351        Object[] attrValues = { netcdfAttr.getValue(0) };
    352        ncsaAttr.setData(attrValues);
    353
    354        log.trace("convertAttribute(): finish data={}", netcdfAttr.getValue(0));
    355        return ncsaAttr;
    356    }
    357
    358    /**
    359     * Retrieves the file structure from disk and returns the root object.
    360     *
    361     * First gets the top level objects or objects that do not belong to any
    362     * groups. If a top level object is a group, call the depth_first() to
    363     * retrieve the sub-tree of that group, recursively.
    364     */
    365    private void loadIntoMemory() {
    366        if (fid < 0) {
    367            log.debug("loadIntoMemory(): Invalid File Id");
    368            return;
    369        }
    370    }
    371
    372    /**
    373     * Retrieves the tree structure of the file by depth-first order. The
    374     * current implementation only retrieves groups and datasets.
    375     *
    376     * @param parentObject
    377     *            the parent object.
    378     */
    379    private void depth_first(HObject parentObj) {
    380        log.trace("depth_first(pobj = {})", parentObj);
    381
    382        if (parentObj == null) {
    383            log.debug("depth_first(): Parent object is null");
    384            return;
    385        }
    386    } // private depth_first()
    387
    388    /**
    389     * Returns a list of all the members of this NetCDF3 in a
    390     * breadth-first ordering that are rooted at the specified
    391     * object.
    392     */
    393    private static List<HObject> getMembersBreadthFirst(HObject obj) {
    394        List<HObject> allMembers = new ArrayList<>();
    395        Queue<HObject> queue = new LinkedList<>();
    396        HObject currentObject = obj;
    397
    398        queue.add(currentObject);
    399
    400        while(!queue.isEmpty()) {
    401            currentObject = queue.remove();
    402            allMembers.add(currentObject);
    403
    404            if(currentObject instanceof Group) {
    405                queue.addAll(((Group) currentObject).getMemberList());
    406            }
    407        }
    408
    409        return allMembers;
    410    }
    411
    412    /**
    413     * Returns the version of the library.
    414     */
    415    @Override
    416    public String getLibversion() {
    417        return "NetCDF Java (version 4.3)";
    418    }
    419
    420    // implementing FileFormat
    421    @Override
    422    public HObject get(String path) throws Exception {
    423        throw new UnsupportedOperationException("get() is not supported");
    424    }
    425}