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