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}