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.h4;
016
017import java.io.File;
018import java.lang.reflect.Array;
019import java.util.ArrayList;
020import java.util.Iterator;
021import java.util.LinkedList;
022import java.util.List;
023import java.util.Queue;
024import java.util.Vector;
025
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029import hdf.hdflib.HDFConstants;
030import hdf.hdflib.HDFException;
031import hdf.hdflib.HDFLibrary;
032
033import hdf.object.Attribute;
034import hdf.object.Dataset;
035import hdf.object.Datatype;
036import hdf.object.FileFormat;
037import hdf.object.Group;
038import hdf.object.HObject;
039
040import hdf.object.h4.H4ScalarAttribute;
041
042/**
043 * This class provides file level APIs. File access APIs include retrieving the
044 * file hierarchy, opening and closing file, and writing file content to disk.
045 *
046 * @version 2.4 9/4/2007
047 * @author Peter X. Cao
048 */
049public class H4File extends FileFormat {
050    private static final long serialVersionUID = 8985533001471224030L;
051
052    private static final Logger   log = LoggerFactory.getLogger(H4File.class);
053
054    /**
055     * the file access flag.
056     */
057    private int                             flag;
058
059    /**
060     * The root object of this file.
061     */
062    private HObject                         rootObject;
063
064    /**
065     * The list of unique (tag, ref) pairs. It is used to avoid duplicate
066     * objects in memory.
067     */
068    @SuppressWarnings("rawtypes")
069    private List                            objList;
070
071    /**
072     * The GR interface identifier. The identifier is returned by GRstart(fid),
073     * which initializes the GR interface for the file specified by the
074     * parameter. GRstart(fid) is an expensive call. It should be called only
075     * once. Calling GRstart(fid) in a loop should be avoided.
076     */
077    private long                            grid;
078
079    /** if this is a netcdf file */
080    private boolean                         isNetCDF = false;
081
082    /**
083     * The SDS interface identifier. The identifier is returned by
084     * SDstart(fname, flag), which initializes the SD interface for the file
085     * specified by the parameter. SDstart(fname, flag) is an expensive call. It
086     * should be called only once. Calling SDstart(fname, flag) in a loop should
087     * be avoided.
088     */
089    private long                            sdid;
090
091    /**
092     * secret flag: show CDF0.0, etc., to help debug
093     */
094    private boolean                         showAll = false;
095
096    /**
097     * Creates an H4File with read write access.
098     */
099    public H4File() {
100        this("", WRITE);
101    }
102
103    /**
104     * Creates an H4File with read write access.
105     *
106     * @param pathname
107     *        The file path string.
108     */
109    public H4File(String pathname) {
110        this(pathname, WRITE);
111    }
112
113    /**
114     * Creates an H4File instance with specified file name and access.
115     *
116     * The access parameter values and corresponding behaviors:
117     * <ul>
118     * <li>READ: Read-only access; open() will fail if file doesn't exist.
119     * <li>WRITE: Read/Write access; if file doesn't exist, open() will create
120     * it; open() will fail if read/write access not allowed.
121     * <li>CREATE: Read/Write access; create a new file or truncate an existing
122     * one; open() will fail if file can't be created or if file exists but
123     * can't be opened read/write.
124     * </ul>
125     *
126     * This constructor does not open the file for access, nor does it confirm
127     * that the file can later be opened read/write or created.
128     *
129     * The flag returned by {@link #isReadOnly()} is set to true if the access
130     * parameter value is READ, even though the file isn't yet open.
131     *
132     * @param fileName
133     *            A valid file name, with a relative or absolute path.
134     * @param access
135     *            The file access flag, which determines behavior when file is
136     *            opened. Acceptable values are <code> READ, WRITE, </code> and
137     *            <code>CREATE</code>.
138     *
139     * @throws NullPointerException
140     *             If the <code>fileName</code> argument is <code>null</code>.
141     */
142    @SuppressWarnings("rawtypes")
143    public H4File(String fileName, int access) {
144        super(fileName);
145
146        isReadOnly = (access == READ);
147        objList = new Vector();
148
149        this.fid = -1;
150
151        if ((access & FILE_CREATE_OPEN) == FILE_CREATE_OPEN) {
152            File f = new File(fileName);
153            if (f.exists())
154                access = WRITE;
155            else
156                access = CREATE;
157        }
158
159        if (access == READ)
160            flag = HDFConstants.DFACC_READ;
161        else if (access == WRITE)
162            flag = HDFConstants.DFACC_WRITE;
163        else if (access == CREATE)
164            flag = HDFConstants.DFACC_CREATE;
165        else
166            flag = access;
167
168        log.trace("File: {} isReadOnly={} accessType={}", fileName, isReadOnly, flag);
169
170        String shwAll = System.getProperty("h4showall");
171        if (shwAll != null) {
172            showAll = true;
173            log.debug("show all is on");
174        }
175        else {
176            log.debug("show all is off");
177        }
178    }
179
180    /**
181     * Checks if the given file format is an HDF4 file.
182     *
183     * @param fileformat
184     *            the fileformat to be checked.
185     *
186     * @return true if the given file is an HDF4 file; otherwise returns false.
187     */
188    @Override
189    public boolean isThisType(FileFormat fileformat) {
190        return (fileformat instanceof H4File);
191    }
192
193    /**
194     * Checks if the given file is an HDF4 file or netCDF. HDF4 library supports
195     * netCDF version 2.3.2. It only supports SDS APIs.
196     *
197     * @param filename
198     *            the file to be checked.
199     *
200     * @return true if the given file is an HDF4 file; otherwise returns false.
201     */
202    @Override
203    public boolean isThisType(String filename) {
204        boolean isH4 = false;
205
206        try {
207            isH4 = HDFLibrary.Hishdf(filename);
208        }
209        catch (HDFException ex) {
210            isH4 = false;
211        }
212
213        if (!isH4)
214            isH4 = isNetCDF(filename);
215
216        log.trace("isThisType(): isH4={}", isH4);
217        return isH4;
218    }
219
220    /**
221     * Creates an HDF4 file with the specified name and returns a new H4File
222     * instance associated with the file.
223     *
224     * @throws HDFException
225     *             If the file cannot be created or if createFlag has unexpected value.
226     *
227     * @see hdf.object.FileFormat#createFile(java.lang.String, int)
228     * @see #H4File(String, int)
229     */
230    @Override
231    public FileFormat createFile(String filename, int createFlag) throws Exception {
232        // Flag if we need to create or truncate the file.
233        Boolean doCreateFile = true;
234
235        // Won't create or truncate if CREATE_OPEN specified and file exists
236        if (createFlag == FILE_CREATE_OPEN) {
237            File f = new File(filename);
238            if (f.exists()) {
239                doCreateFile = false;
240            }
241        }
242
243        log.trace("createFile(): doCreateFile={}", doCreateFile);
244
245        if (doCreateFile) {
246            long fileid = HDFLibrary.Hopen(filename, HDFConstants.DFACC_CREATE);
247            try {
248                HDFLibrary.Hclose(fileid);
249            }
250            catch (HDFException ex) {
251                log.debug("Hclose failure: ", ex);
252            }
253        }
254
255        return new H4File(filename, WRITE);
256    }
257
258    /**
259     * Creates an H4File instance with specified file name and access.
260     *
261     * @see hdf.object.FileFormat#createInstance(java.lang.String, int)
262     * @see #H4File(String, int)
263     */
264    @Override
265    public FileFormat createInstance(String filename, int access) throws Exception {
266        return new H4File(filename, access);
267    }
268
269    // Implementing FileFormat
270    @Override
271    public long open() throws Exception {
272        if (fid >= 0) {
273            log.trace("open(): File {} already open", fid);
274            return fid; // file is opened already
275        }
276
277        // check for valid file access permission
278        if (flag < 0) { // invalid access id
279            throw new HDFException("Invalid access identifer -- " + flag);
280        }
281        else if (flag == HDFConstants.DFACC_READ) {
282            if (!exists()) {
283                log.debug("File {} does not exist", fullFileName);
284                throw new HDFException("File does not exist -- " + fullFileName);
285            }
286            else if (exists() && !canRead()) {
287                log.debug("Cannot read file {}", fullFileName);
288                throw new HDFException("Cannot read file -- " + fullFileName);
289            }
290        }
291        else if ((flag == HDFConstants.DFACC_WRITE) || (flag == HDFConstants.DFACC_CREATE)) {
292            if (exists() && !canWrite()) {
293                log.debug("Cannot write file {}, try opening as read-only", fullFileName);
294                throw new HDFException("Cannot write file, try opening as read-only -- " + fullFileName);
295            }
296        }
297
298        // Only check for NetCDF if the file exists, else isNetCDF() throws an exception
299        if (exists())
300            isNetCDF = isNetCDF(fullFileName);
301        if (isNetCDF)
302            isReadOnly = true; // read only for netCDF
303        log.trace("open(): isNetCDF={}", isNetCDF);
304
305        // only support SDS APIs for netCDF
306        if (isNetCDF) {
307            fid = 0;
308        }
309        else {
310            log.trace("HDFLibrary - open({},{})", fullFileName, flag);
311            fid = HDFLibrary.Hopen(fullFileName, flag);
312            HDFLibrary.Vstart(fid);
313            grid = HDFLibrary.GRstart(fid);
314            log.trace("open(): fid:{} grid:{}", fid, grid);
315        }
316
317        try {
318            sdid = HDFLibrary.SDstart(fullFileName, flag & ~HDFConstants.DFACC_CREATE);
319
320            if (sdid < 0)
321                log.debug("open(): SDstart failed!");
322        }
323        catch (HDFException ex) {
324            log.debug("open(): SDstart failure: ", ex);
325        }
326
327        log.trace("open(): sdid:{}", sdid);
328
329        // load the file hierarchy
330        loadIntoMemory();
331
332        return fid;
333    }
334
335    // Implementing FileFormat
336    @Override
337    public void close() throws HDFException {
338        // clean unused objects
339        if (rootObject != null) {
340            HObject theObj = null;
341            Iterator<HObject> it = getMembersBreadthFirst(rootObject).iterator();
342            while (it.hasNext()) {
343                theObj = it.next();
344
345                if (theObj instanceof Dataset)
346                    ((Dataset) theObj).clearData();
347                else if (theObj instanceof Group)
348                    ((Group) theObj).clear();
349            }
350        }
351
352        try {
353            HDFLibrary.GRend(grid);
354        }
355        catch (HDFException ex) {
356            log.debug("close(): GRend failure: ", ex);
357        }
358        try {
359            HDFLibrary.SDend(sdid);
360        }
361        catch (HDFException ex) {
362            log.debug("close(): SDend failure: ", ex);
363        }
364        try {
365            HDFLibrary.Vend(fid);
366        }
367        catch (HDFException ex) {
368            log.debug("close(): Vend failure: ", ex);
369        }
370
371        HDFLibrary.Hclose(fid);
372
373        fid = -1;
374        objList = null;
375    }
376
377    // Implementing FileFormat
378    @Override
379    public HObject getRootObject() {
380        return rootObject;
381    }
382
383    @Override
384    public Group createGroup(String name, Group pgroup) throws Exception {
385        return H4Group.create(name, pgroup);
386    }
387
388    @Override
389    public Datatype createDatatype(int tclass, int tsize, int torder, int tsign) throws Exception {
390        return new H4Datatype(tclass, tsize, torder, tsign);
391    }
392
393    @Override
394    public Datatype createDatatype(int tclass, int tsize, int torder, int tsign, Datatype tbase) throws Exception {
395        return new H4Datatype(tclass, tsize, torder, tsign);
396    }
397
398    @Override
399    public Datatype createNamedDatatype(Datatype tnative, String name) throws Exception {
400        throw new UnsupportedOperationException("HDF4 does not support named datatype.");
401    }
402
403    @Override
404    public Dataset createScalarDS(String name, Group pgroup, Datatype type,
405            long[] dims, long[] maxdims, long[] chunks,
406            int gzip, Object fillValue, Object data) throws Exception {
407        return H4SDS.create(name, pgroup, type, dims, maxdims, chunks, gzip, fillValue, data);
408    }
409
410    @Override
411    public Dataset createImage(String name, Group pgroup, Datatype type,
412            long[] dims, long[] maxdims, long[] chunks,
413            int gzip, int ncomp, int interlace, Object data) throws Exception {
414        return H4GRImage.create(name, pgroup, type, dims, maxdims, chunks, gzip, ncomp, interlace, data);
415    }
416
417    /**
418     * Delete an object from the file.
419     *
420     * @param obj
421     *            the data object to delete.
422     *
423     * @throws Exception if the object can not be deleted
424     */
425    @Override
426    public void delete(HObject obj) throws Exception {
427        throw new UnsupportedOperationException("Cannot delete HDF4 object.");
428    }
429
430    /**
431     * Copy an object to a group.
432     *
433     * @param srcObj
434     *            the object to copy.
435     * @param dstGroup
436     *            the destination group.
437     *
438     * @return the destination group, if the copy was successful, or
439     *            null otherwise.
440     *
441     * @throws Exception if the object can not be copied
442     */
443    @Override
444    public HObject copy(HObject srcObj, Group dstGroup, String dstName) throws Exception {
445        log.trace("copy(): start: srcObj={} dstGroup={} dstName={}", srcObj, dstGroup, dstName);
446
447        if ((srcObj == null) || (dstGroup == null)) {
448            log.debug("copy(): source or destination is null");
449            return null;
450        }
451
452        if (dstName == null) {
453            dstName = srcObj.getName();
454            log.trace("copy(): dstName is null, using dstName={}", dstName);
455        }
456
457        HObject newObj = null;
458        if (srcObj instanceof H4SDS) {
459            log.trace("copy(): srcObj instanceof H4SDS");
460            newObj = ((H4SDS) srcObj).copy(dstGroup, dstName, null, null);
461        }
462        else if (srcObj instanceof H4GRImage) {
463            log.trace("copy(): srcObj instanceof H4GRImage");
464            newObj = ((H4GRImage) srcObj).copy(dstGroup, dstName, null, null);
465        }
466        else if (srcObj instanceof H4Vdata) {
467            log.trace("copy(): srcObj instanceof H4Vdata");
468            newObj = ((H4Vdata) srcObj).copy(dstGroup, dstName, null, null);
469        }
470        else if (srcObj instanceof H4Group) {
471            log.trace("copy(): srcObj instanceof H4Group");
472            newObj = copyGroup((H4Group) srcObj, (H4Group) dstGroup);
473        }
474
475        return newObj;
476    }
477
478    /**
479     * Creates a new attribute and attaches it to the object if the
480     * attribute does not exist. Otherwise, just update the value of
481     * the attribute.
482     *
483     * @param obj
484     *            the object which the attribute is to be attached to.
485     * @param attr
486     *            the attribute to attach.
487     * @param isSDglobalAttr
488     *            The indicator if the given attribute exists.
489     *
490     * @throws HDFException if the attribute can not be written
491     */
492    @Override
493    public void writeAttribute(HObject obj, Attribute attr, boolean isSDglobalAttr) throws HDFException {
494        log.trace("writeAttribute(): start: obj={} attribute={} isSDglobalAttr={}", obj, attr, isSDglobalAttr);
495
496        String attrName = attr.getAttributeName();
497        long attrType = attr.getAttributeDatatype().createNative();
498        long[] dims = attr.getAttributeDims();
499        int count = 1;
500        if (dims != null) {
501            for (int i = 0; i < dims.length; i++)
502                count *= (int) dims[i];
503        }
504
505        log.trace("writeAttribute(): count={}", count);
506
507        Object attrValue;
508        try {
509            attrValue = attr.getAttributeData();
510        }
511        catch (Exception ex) {
512            attrValue = null;
513            log.trace("writeAttribute(): getData() failure:", ex);
514        }
515
516        if (Array.get(attrValue, 0) instanceof String) {
517            String strValue = (String) Array.get(attrValue, 0);
518
519            if (strValue.length() > count) {
520                // truncate the extra characters
521                strValue = strValue.substring(0, count);
522                Array.set(attrValue, 0, strValue);
523            }
524            else {
525                // pad space to the unused space
526                for (int i = strValue.length(); i < count; i++) {
527                    strValue += " ";
528                }
529            }
530
531            byte[] bval = strValue.getBytes();
532            // add null to the end to get rid of the junks
533            bval[(strValue.length() - 1)] = 0;
534            attrValue = bval;
535        }
536
537        if ((obj instanceof H4Group) && ((H4Group) obj).isRoot()) {
538            if (isSDglobalAttr)
539                HDFLibrary.SDsetattr(sdid, attrName, attrType, count, attrValue);
540            else
541                HDFLibrary.GRsetattr(grid, attrName, attrType, count, attrValue);
542            log.trace("writeAttribute(): wrote attribute to root H4Group");
543            return;
544        }
545
546        long id = obj.open();
547
548        if (id >= 0) {
549            if (obj instanceof H4Group) {
550                HDFLibrary.Vsetattr(id, attrName, attrType, count, attrValue);
551                log.trace("writeAttribute(): wrote attribute to H4Group");
552            }
553            else if (obj instanceof H4SDS) {
554                HDFLibrary.SDsetattr(id, attrName, attrType, count, attrValue);
555                log.trace("writeAttribute(): wrote attribute to H4SDS");
556            }
557            else if (obj instanceof H4GRImage) {
558                HDFLibrary.GRsetattr(id, attrName, attrType, count, attrValue);
559                log.trace("writeAttribute(): wrote attribute to H4GRImage");
560            }
561            else if (obj instanceof H4Vdata) {
562                HDFLibrary.VSsetattr(id, -1, attrName, attrType, count, attrValue);
563                log.trace("writeAttribute(): wrote attribute to H4Vdata");
564            }
565
566            obj.close(id);
567        }
568    }
569
570    private HObject copyGroup(H4Group srcGroup, H4Group pgroup) throws Exception {
571        log.trace("copyGroup(): start: srcGroup={} parentGroup={}", srcGroup, pgroup);
572
573        H4Group group = null;
574        long srcgid = -1;
575        long dstgid = -1;
576        String gname = null;
577        String path = null;
578
579        dstgid = HDFLibrary.Vattach(fid, -1, "w");
580        if (dstgid < 0) {
581            log.trace("copyGroup(): Invalid dst Group Id");
582            return null;
583        }
584
585        gname = srcGroup.getName();
586        srcgid = srcGroup.open();
587
588        HDFLibrary.Vsetname(dstgid, gname);
589        int ref = HDFLibrary.VQueryref(dstgid);
590        int tag = HDFLibrary.VQuerytag(dstgid);
591
592        if (pgroup.isRoot()) {
593            path = HObject.SEPARATOR;
594        }
595        else {
596            // add the dataset to the parent group
597            path = pgroup.getPath() + pgroup.getName() + HObject.SEPARATOR;
598            long pid = pgroup.open();
599            HDFLibrary.Vinsert(pid, dstgid);
600            pgroup.close(pid);
601        }
602
603        // copy attributes
604        int numberOfAttributes = 0;
605        try {
606            numberOfAttributes = HDFLibrary.Vnattrs(srcgid);
607        }
608        catch (Exception ex) {
609            log.debug("copyGroup(): Vnattrs failure: ", ex);
610            numberOfAttributes = 0;
611        }
612
613        String[] attrName = new String[1];
614        byte[] attrBuff = null;
615        int[] attrInfo = new int[3]; // data_type, count, size
616        for (int i = 0; i < numberOfAttributes; i++) {
617            try {
618                attrName[0] = "";
619                HDFLibrary.Vattrinfo(srcgid, i, attrName, attrInfo);
620                attrBuff = new byte[attrInfo[2]];
621                HDFLibrary.Vgetattr(srcgid, i, attrBuff);
622                HDFLibrary.Vsetattr(dstgid, attrName[0], attrInfo[0],
623                        attrInfo[2], attrBuff);
624            }
625            catch (Exception ex) {
626                log.trace("copyGroup(): failure: ", ex);
627            }
628        }
629
630        long[] oid = { tag, ref };
631        group = new H4Group(this, gname, path, pgroup, oid);
632
633        pgroup.addToMemberList(group);
634
635        // copy members of the source group to the new group
636        List<HObject> members = srcGroup.getMemberList();
637        if ((members != null) && !members.isEmpty()) {
638            Iterator<HObject> iterator = members.iterator();
639            while (iterator.hasNext()) {
640                HObject mObj = iterator.next();
641                try {
642                    copy(mObj, group, mObj.getName());
643                }
644                catch (Exception ex) {
645                    log.debug("copy(): failure: ", ex);
646                }
647            }
648        }
649
650        srcGroup.close(srcgid);
651
652        if (dstgid >= 0) {
653            try {
654                HDFLibrary.Vdetach(dstgid);
655            }
656            catch (Exception ex) {
657                log.debug("copyGroup(): Vdetach failure: ", ex);
658            }
659        }
660
661        return group;
662    }
663
664    /**
665     * Retrieves the file structure from disk and returns the root object.
666     *
667     * First gets the top level objects or objects that do not belong to any
668     * groups. If a top level object is a group, call the depth_first() to
669     * retrieve the sub-tree of that group, recursively.
670     */
671    private void loadIntoMemory() {
672        if (fid < 0) {
673            log.debug("loadIntoMemory(): Invalid File Id");
674            return;
675        }
676
677        int n = 0;
678        int ref = -1;
679        int[] argv = null;
680
681        // get top level VGroup
682        int[] tmpN = new int[1];
683        int[] refs = null;
684
685        try {
686            // first call to get the number of lone Vgroups
687            log.trace("loadIntoMemory(): first call to Vlone: get number of lone Vgroups");
688            n = HDFLibrary.Vlone(fid, tmpN, 0);
689            log.trace("loadIntoMemory(): number of lone Vgroups={}", n);
690            refs = new int[n];
691
692            // second call to get the references of all lone Vgroups
693            log.trace("loadIntoMemory(): second call to Vlone: get references of lone Vgroups");
694            n = HDFLibrary.Vlone(fid, refs, n);
695        }
696        catch (HDFException ex) {
697            log.trace("loadIntoMemory(): get Vlone failure: ", ex);
698            n = 0;
699        }
700
701        /*
702         * TODO: Root group's name should be changed to 'this.getName()' and all
703         * previous accesses of this field should now use getPath() instead of getName()
704         * to get the root group. The root group actually does have a path of "/". The
705         * depth_first method will have to be changed to setup other object paths
706         * appropriately, as it currently assumes the root path to be null.
707         */
708        long[] oid = { 0, 0 };
709        // root object does not have a parent path or a parent object
710        rootObject = new H4Group(this, "/", null, null, oid);
711
712        int i0 = Math.max(0, getStartMembers());
713        int i1 = getMaxMembers();
714        if (i1 >= n) {
715            i1 = n;
716            i0 = 0; // load all members
717        }
718        i1 += i0;
719        i1 = Math.min(i1, n);
720
721        // Iterate through the file to see members of the group
722        log.trace("loadIntoMemory(): start={} to last={}", i0, i1);
723        for (int i = i0; i < i1; i++) {
724            ref = refs[i];
725            log.trace("loadIntoMemory(): Iterate[{}] members of the group ref={}",i,ref);
726            H4Group g = getVGroup(HDFConstants.DFTAG_VG, ref, HObject.SEPARATOR, (H4Group) rootObject, false);
727
728            if (g != null) {
729                ((H4Group) rootObject).addToMemberList(g);
730
731                // recursively get the sub-tree
732                depth_first(g);
733            }
734        }
735
736        // get the top level GR images
737        argv = new int[2];
738        boolean b = false;
739        try {
740            b = HDFLibrary.GRfileinfo(grid, argv);
741        }
742        catch (HDFException ex) {
743            log.debug("loadIntoMemory(): GRfileinfo failure: ",ex);
744            b = false;
745        }
746
747        if (b) {
748            n = argv[0];
749
750            for (int i = 0; i < n; i++) {
751                // no duplicate object at top level
752                H4GRImage gr = getGRImage(HDFConstants.DFTAG_RIG, i, HObject.SEPARATOR, false);
753                if (gr != null) {
754                    ((H4Group) rootObject).addToMemberList(gr);
755                }
756            } //  (int i=0; i<n; i++)
757        } //  ( grid!=HDFConstants.FAIL && HDFLibrary.GRfileinfo(grid,argv) )
758
759        // get top level SDS
760        try {
761            b = HDFLibrary.SDfileinfo(sdid, argv);
762        }
763        catch (HDFException ex) {
764            log.debug("loadIntoMemory(): SDfileinfo failure: ",ex);
765            b = false;
766        }
767
768        if (b) {
769            n = argv[0];
770            for (int i = 0; i < n; i++) {
771                // no duplicate object at top level
772                H4SDS sds = getSDS(HDFConstants.DFTAG_NDG, i, HObject.SEPARATOR, false);
773                if (sds != null)
774                    ((H4Group) rootObject).addToMemberList(sds);
775            }
776        } // (HDFLibrary.SDfileinfo(sdid, argv))
777
778        // get top level VData
779        try {
780            n = HDFLibrary.VSlone(fid, tmpN, 0);
781            log.trace("loadIntoMemory(): number of lone Vdatas={}", n);
782            refs = new int[n];
783            n = HDFLibrary.VSlone(fid, refs, n);
784        }
785        catch (HDFException ex) {
786            log.debug("loadIntoMemory(): VSlone failure: ",ex);
787            n = 0;
788        }
789
790        for (int i = 0; i < n; i++) {
791            ref = refs[i];
792            log.trace("loadIntoMemory(): references of Vdata[{}]={}", i, ref);
793
794            // no duplicate object at top level
795            H4Vdata vdata = getVdata(HDFConstants.DFTAG_VS, ref, HObject.SEPARATOR, false);
796
797            if (vdata != null)
798                ((H4Group) rootObject).addToMemberList(vdata);
799        }
800
801        if (rootObject != null) {
802            // retrieve file annotation, GR and SDS global attributes
803            @SuppressWarnings("rawtypes")
804            List attributeList = null;
805            try {
806                attributeList = ((H4Group) rootObject).getMetadata();
807            }
808            catch (HDFException ex) {
809                log.debug("loadIntoMemory(): getMetadata failure: ", ex);
810            }
811
812            try {
813                getFileAnnotation(fid, attributeList);
814            }
815            catch (HDFException ex) {
816                log.debug("loadIntoMemory(): getFileAnnotation failure: ", ex);
817            }
818            try {
819                getGRglobalAttribute(grid, attributeList);
820            }
821            catch (HDFException ex) {
822                log.debug("loadIntoMemory(): getGRglobalAttribute failure: ", ex);
823            }
824            try {
825                getSDSglobalAttribute(sdid, attributeList);
826            }
827            catch (HDFException ex) {
828                log.debug("loadIntoMemory(): getSDglobalAttribute failure: ", ex);
829            }
830        }
831
832    }
833
834    /**
835     * Retrieves the tree structure of the file by depth-first order. The
836     * current implementation only retrieves groups and datasets. It does not
837     * include named datatypes and soft links.
838     *
839     * @param parentObject
840     *            the parent object.
841     */
842    private void depth_first(HObject parentObj) {
843        log.trace("depth_first(pobj = {})", parentObj);
844
845        if (parentObj == null) {
846            log.debug("depth_first(): Parent object is null");
847            return;
848        }
849
850        int nelems = 0;
851        int ref = -1;
852        int tag = -1;
853        int index = -1;
854        int[] tags = null;
855        int[] refs = null;
856
857        H4Group parentGroup = (H4Group) parentObj;
858
859        String fullPath = parentGroup.getPath() + parentGroup.getName() + HObject.SEPARATOR;
860        long gid = parentGroup.open();
861        if (gid == HDFConstants.FAIL) {
862            log.debug("depth_first(): Invalid Parent group ID");
863            return;
864        }
865
866        try {
867            nelems = HDFLibrary.Vntagrefs(gid);
868            tags = new int[nelems];
869            refs = new int[nelems];
870            nelems = HDFLibrary.Vgettagrefs(gid, tags, refs, nelems);
871        }
872        catch (HDFException ex) {
873            log.debug("depth_first(): failure: ", ex);
874            nelems = 0;
875        }
876        finally {
877            parentGroup.close(gid);
878        }
879
880        int i0 = Math.max(0, getStartMembers());
881        int i1 = getMaxMembers();
882        if (i1 >= nelems) {
883            i1 = nelems;
884            i0 = 0; // load all members
885        }
886        i1 += i0;
887        i1 = Math.min(i1, nelems);
888
889        // Iterate through the file to see members of the group
890        for (int i = i0; i < i1; i++) {
891            tag = tags[i];
892            ref = refs[i];
893
894            switch (tag) {
895                case HDFConstants.DFTAG_RIG:
896                case HDFConstants.DFTAG_RI:
897                case HDFConstants.DFTAG_RI8:
898                    try {
899                        index = HDFLibrary.GRreftoindex(grid, (short) ref);
900                    }
901                    catch (HDFException ex) {
902                        index = HDFConstants.FAIL;
903                    }
904                    if (index != HDFConstants.FAIL) {
905                        H4GRImage gr = getGRImage(tag, index, fullPath, true);
906                        parentGroup.addToMemberList(gr);
907                    }
908                    break;
909                case HDFConstants.DFTAG_SD:
910                case HDFConstants.DFTAG_SDG:
911                case HDFConstants.DFTAG_NDG:
912                    try {
913                        index = HDFLibrary.SDreftoindex(sdid, ref);
914                    }
915                    catch (HDFException ex) {
916                        index = HDFConstants.FAIL;
917                    }
918                    if (index != HDFConstants.FAIL) {
919                        H4SDS sds = getSDS(tag, index, fullPath, true);
920                        parentGroup.addToMemberList(sds);
921                    }
922                    break;
923                case HDFConstants.DFTAG_VH:
924                case HDFConstants.DFTAG_VS:
925                    H4Vdata vdata = getVdata(tag, ref, fullPath, true);
926                    parentGroup.addToMemberList(vdata);
927                    break;
928                case HDFConstants.DFTAG_VG:
929                    H4Group vgroup = getVGroup(tag, ref, fullPath, parentGroup, true);
930                    parentGroup.addToMemberList(vgroup);
931                    if ((vgroup != null) && (parentGroup != null)) {
932                        // check for loops
933                        boolean looped = false;
934                        H4Group theGroup = parentGroup;
935                        while ((theGroup != null) && !looped) {
936                            long[] oid = { tag, ref };
937                            if (theGroup.equalsOID(oid)) {
938                                looped = true;
939                            }
940                            else {
941                                theGroup = (H4Group) theGroup.getParent();
942                            }
943                        }
944                        if (!looped) {
945                            depth_first(vgroup);
946                        }
947                    }
948                    break;
949                default:
950                    break;
951            } // switch
952
953        } // (int i=0; i<nelms; i++)
954    } // private depth_first()
955
956    /**
957     * Returns a list of all the members of this H4File in a
958     * breadth-first ordering that are rooted at the specified
959     * object.
960     */
961    private static List<HObject> getMembersBreadthFirst(HObject obj) {
962        List<HObject> allMembers = new ArrayList<>();
963        Queue<HObject> queue = new LinkedList<>();
964        HObject currentObject = obj;
965
966        queue.add(currentObject);
967
968        while(!queue.isEmpty()) {
969            currentObject = queue.remove();
970            allMembers.add(currentObject);
971
972            if(currentObject instanceof Group)
973                queue.addAll(((Group) currentObject).getMemberList());
974        }
975
976        return allMembers;
977    }
978
979    /**
980     * Retrieve a GR image for the given GR image identifier and index.
981     *
982     * @param tag
983     *            the reference tag of the GR image.
984     * @param index
985     *            the index of the image.
986     * @param path
987     *            the path of the image.
988     * @param copyAllowed
989     *            The indicator if multiple copies of an object is allowed.
990     *
991     * @return the new H5GRImage if successful; otherwise returns null.
992     */
993    @SuppressWarnings("unchecked")
994    private final H4GRImage getGRImage(int tag, int index, String path, boolean copyAllowed) {
995        log.trace("getGRImage(): start: tag={} index={} path={} copyAllowed={}", tag, index, path, copyAllowed);
996
997        long id = -1;
998        int ref = -1;
999        H4GRImage gr = null;
1000        String[] objName = { "" };
1001        int[] imgInfo = new int[4];
1002        int[] dimSizes = { 0, 0 };
1003
1004        try {
1005            id = HDFLibrary.GRselect(grid, index);
1006            ref = HDFLibrary.GRidtoref(id);
1007            log.trace("getGRImage(): GRselect:{} GRidtoref:{}",id,ref);
1008            HDFLibrary.GRgetiminfo(id, objName, imgInfo, dimSizes);
1009        }
1010        catch (HDFException ex) {
1011            log.debug("getGRImage(): failure: ", ex);
1012            id = HDFConstants.FAIL;
1013        }
1014        finally {
1015            if (id >= 0) {
1016                try {
1017                    HDFLibrary.GRendaccess(id);
1018                }
1019                catch (HDFException ex) {
1020                    log.debug("getGRImage(): GRendaccess failure: ", ex);
1021                }
1022            }
1023        }
1024
1025        if (id != HDFConstants.FAIL) {
1026            long[] oid = { tag, ref };
1027
1028            if (copyAllowed) {
1029                objList.add(oid);
1030            }
1031            else if (find(oid)) {
1032                log.trace("getGRImage(): Image found in memory with OID:({}, {})", oid[0], oid[1]);
1033                return null;
1034            }
1035
1036            gr = new H4GRImage(this, objName[0], path, oid);
1037        }
1038
1039        return gr;
1040    }
1041
1042    /**
1043     * Retrieve a SDS for the given sds identifier and index.
1044     *
1045     * @param tag
1046     *            the reference tag of the group (DFTAG_SD, DFTAG_SDG, DFTAG_NDG).
1047     * @param index
1048     *            the index of the SDS.
1049     * @param path
1050     *            the path of the SDS.
1051     * @param copyAllowed
1052     *            The indicator if multiple copies of an object is allowed.
1053     *
1054     * @return the new H4SDS if successful; otherwise returns null.
1055     */
1056    @SuppressWarnings("unchecked")
1057    private final H4SDS getSDS(int tag, int index, String path, boolean copyAllowed) {
1058        log.trace("getSDS(): start: tag={} index={} path={} copyAllowed={}", tag, index, path, copyAllowed);
1059
1060        long id = -1;
1061        int ref = -1;
1062        H4SDS sds = null;
1063        String[] objName = { "" };
1064        int[] tmpInfo = new int[HDFConstants.MAX_VAR_DIMS];
1065        int[] sdInfo = { 0, 0, 0 };
1066
1067        boolean isCoordvar = false;
1068        try {
1069            id = HDFLibrary.SDselect(sdid, index);
1070            if (isNetCDF) {
1071                ref = index; // HDFLibrary.SDidtoref(id) fails for netCDF
1072                tag = H4SDS.DFTAG_NDG_NETCDF;
1073            }
1074            else {
1075                ref = HDFLibrary.SDidtoref(id);
1076            }
1077            log.trace("getSDS(): SDselect id={} with ref={} isNetCDF={}", id, ref, isNetCDF);
1078
1079            HDFLibrary.SDgetinfo(id, objName, tmpInfo, sdInfo);
1080            log.trace("getSDS(): SDselect id={} with objName={}: rank={}, numberType={}, nAttributes={}", id, objName, sdInfo[0], sdInfo[1], sdInfo[2]);
1081
1082            try {
1083                isCoordvar = HDFLibrary.SDiscoordvar(id);
1084            }
1085            catch (Exception ex) {
1086                log.debug("getSDS(): SDiscoordvar failure: ", ex);
1087            }
1088        }
1089        catch (HDFException ex) {
1090            log.debug("getSDS(): failure: ", ex);
1091            id = HDFConstants.FAIL;
1092        }
1093        finally {
1094            if (id >= 0) {
1095                try {
1096                    HDFLibrary.SDendaccess(id);
1097                }
1098                catch (HDFException ex) {
1099                    log.debug("getSDS(): SDendaccess failure: ", ex);
1100                }
1101            }
1102        }
1103
1104        // check if the given SDS has dimension metadata
1105        // Coordinate variables are not displayed. They are created to store
1106        // metadata associated with dimensions. To ensure compatibility with
1107        // netCDF, coordinate variables are implemented as data sets
1108
1109        if (isCoordvar)
1110            objName[0] += " (dimension)";
1111
1112        if (id != HDFConstants.FAIL) { // && !isCoordvar)
1113            long[] oid = { tag, ref };
1114
1115            if (copyAllowed) {
1116                objList.add(oid);
1117            }
1118            else if (find(oid)) {
1119                log.trace("getSDS(): SDS found in memory with OID:({}, {})", oid[0], oid[1]);
1120                return null;
1121            }
1122
1123            sds = new H4SDS(this, objName[0], path, oid);
1124        }
1125
1126        return sds;
1127    }
1128
1129    /**
1130     * Retrieve a Vdata for the given Vdata identifier and index.
1131     *
1132     * @param tag
1133     *            the reference tag of the Vdata.
1134     * @param ref
1135     *            the reference identifier of the Vdata.
1136     * @param path
1137     *            the path of the Vdata.
1138     * @param copyAllowed
1139     *            The indicator if multiple copies of an object is allowed.
1140     *
1141     * @return the new H4Vdata if successful; otherwise returns null.
1142     */
1143    @SuppressWarnings("unchecked")
1144    private final H4Vdata getVdata(int tag, int ref, String path, boolean copyAllowed) {
1145        log.trace("getVdata(): start: tag={} ref={} path={} copyAllowed={}", tag, ref, path, copyAllowed);
1146
1147        long id = -1;
1148        H4Vdata vdata = null;
1149        String[] objName = { "" };
1150        String[] vClass = { "" };
1151        long[] oid = { tag, ref };
1152
1153        if (copyAllowed) {
1154            objList.add(oid);
1155        }
1156        else if (find(oid)) {
1157            log.trace("getVdata(): VData found in memory with OID:({}, {})", oid[0], oid[1]);
1158            return null;
1159        }
1160
1161        try {
1162            id = HDFLibrary.VSattach(fid, ref, "r");
1163            HDFLibrary.VSgetclass(id, vClass);
1164            vClass[0] = vClass[0].trim();
1165            HDFLibrary.VSgetname(id, objName);
1166        }
1167        catch (HDFException ex) {
1168            log.trace("getVData(): failure: ", ex);
1169            id = HDFConstants.FAIL;
1170        }
1171        finally {
1172            if (id >= 0) {
1173                try {
1174                    HDFLibrary.VSdetach(id);
1175                }
1176                catch (HDFException ex) {
1177                    log.debug("getVData(): VSdetach failure: ", ex);
1178                }
1179            }
1180        }
1181
1182        if (showAll ||
1183                ((id != HDFConstants.FAIL)
1184                        && !vClass[0].equalsIgnoreCase(HDFConstants.HDF_ATTRIBUTE) // do not display Vdata named "Attr0.0" // commented out for bug 1737
1185                        && !vClass[0].startsWith(HDFConstants.HDF_CHK_TBL)         // do not display internal Vdata, "_HDF_CHK_TBL_"
1186                        && !vClass[0].startsWith(HDFConstants.HDF_SDSVAR)          // do not display attributes
1187                        && !vClass[0].startsWith(HDFConstants.HDF_CRDVAR)
1188                        && !vClass[0].startsWith(HDFConstants.DIM_VALS)
1189                        && !vClass[0].startsWith(HDFConstants.DIM_VALS01)
1190                        && !vClass[0].startsWith(HDFConstants.RIGATTRCLASS)
1191                        && !vClass[0].startsWith(HDFConstants.RIGATTRNAME)
1192                        && !vClass[0].equalsIgnoreCase(HDFConstants.HDF_CDF)))     // do not display internal vdata for CDF, "CDF0.0"
1193        {
1194            vdata = new H4Vdata(this, objName[0], path, oid);
1195        }
1196
1197        return vdata;
1198    }
1199
1200    /**
1201     * Retrieve a VGroup for the given VGroup identifier and index.
1202     *
1203     * @param tag
1204     *            the reference tag of the VGroup.
1205     * @param ref
1206     *            the reference identifier of the VGroup.
1207     * @param path
1208     *            the path of the VGroup.
1209     * @param pgroup
1210     *            the parent group.
1211     * @param copyAllowed
1212     *            The indicator if multiple copies of an object is allowed.
1213     *
1214     * @return the new H4VGroup if successful; otherwise returns null.
1215     */
1216    @SuppressWarnings("unchecked")
1217    private final H4Group getVGroup(int tag, int ref, String path, H4Group pgroup, boolean copyAllowed) {
1218        log.trace("getVGroup(): start: tag={}, ref={} path={} pgroup={} copyAllowed={}", tag, ref, path, pgroup, copyAllowed);
1219
1220        long id = -1;
1221        H4Group vgroup = null;
1222        String[] objName = { "" };
1223        String[] vClass = { "" };
1224        long[] oid = { tag, ref };
1225
1226        if (ref <= 0) {
1227            log.trace("getVGroup(): Skipping dummy root group with ref={}", ref);
1228            return null;
1229        }
1230
1231        if (copyAllowed) {
1232            objList.add(oid);
1233        }
1234        else if (find(oid)) {
1235            log.trace("getVGroup(): VGroup found in memory with OID:({}, {})", oid[0], oid[1]);
1236            return null;
1237        }
1238
1239        try {
1240            id = HDFLibrary.Vattach(fid, ref, "r");
1241            log.trace("getVGroup(): Vattach fid={} id={}", fid, id);
1242            HDFLibrary.Vgetclass(id, vClass);
1243            vClass[0] = vClass[0].trim();
1244            HDFLibrary.Vgetname(id, objName);
1245        }
1246        catch (HDFException ex) {
1247            log.debug("getVGroup(): failure: ",ex);
1248            id = HDFConstants.FAIL;
1249        }
1250        finally {
1251            if (id >= 0) {
1252                try {
1253                    HDFLibrary.Vdetach(id);
1254                }
1255                catch (HDFException ex) {
1256                    log.debug("getVGroup(): Vdetach failure: ", ex);
1257                }
1258            }
1259        }
1260
1261        // ignore the Vgroups created by the GR interface
1262        if (showAll || ((id != HDFConstants.FAIL)
1263                && !vClass[0].equalsIgnoreCase(HDFConstants.GR_NAME) // do not display Vdata named "Attr0.0"
1264                && !vClass[0].equalsIgnoreCase(HDFConstants.RI_NAME)
1265                && !vClass[0].equalsIgnoreCase(HDFConstants.RIGATTRNAME)
1266                && !vClass[0].equalsIgnoreCase(HDFConstants.RIGATTRCLASS)
1267                && !vClass[0].equalsIgnoreCase(HDFConstants.HDF_CDF)))
1268        {
1269            vgroup = new H4Group(this, objName[0], path, pgroup, oid);
1270        }
1271
1272        return vgroup;
1273    }
1274
1275    /**
1276     * Check if object already exists in memory by matching the (tag, ref) pairs.
1277     */
1278    @SuppressWarnings("unchecked")
1279    private final boolean find(long[] oid) {
1280        log.trace("find(): start: oid({}, {})", oid[0], oid[1]);
1281
1282        boolean existed = false;
1283
1284        if (objList == null) {
1285            log.debug("find(): objList is null");
1286            return false;
1287        }
1288
1289        int n = objList.size();
1290        long[] theOID = null;
1291
1292        for (int i = 0; i < n; i++) {
1293            theOID = (long[]) objList.get(i);
1294            if ((theOID[0] == oid[0]) && (theOID[1] == oid[1])) {
1295                log.trace("find(): matched object in objList");
1296                existed = true;
1297                break;
1298            }
1299        }
1300
1301        if (!existed) {
1302            objList.add(oid);
1303        }
1304
1305        return existed;
1306    }
1307
1308    /**
1309     * Returns the GR identifier, which is returned from GRstart(fid).
1310     *
1311     * @return the identifier.
1312     */
1313    long getGRAccessID() {
1314        return grid;
1315    }
1316
1317    /**
1318     * Returns the SDS identifier, which is returned from SDstart(fname, flag).
1319     *
1320     * @return the identifier.
1321     */
1322    long getSDAccessID() {
1323        return sdid;
1324    }
1325
1326    /**
1327     * Reads HDF file annotation (file labels and descriptions) into memory.
1328     * The file annotation is stored as an attribute of the root group.
1329     *
1330     * @param fid
1331     *            the file identifier.
1332     * @param attrList
1333     *            the list of attributes.
1334     *
1335     * @return the updated attribute list.
1336     *
1337     * @throws Exception if the annotation can not be read
1338     */
1339    @SuppressWarnings({"rawtypes", "unchecked"})
1340    private List getFileAnnotation(long fid, List attrList) throws HDFException {
1341        log.trace("getFileAnnotation(): start: FID={}", fid);
1342
1343        if (fid < 0) {
1344            log.debug("getFileAnnotation(): Invalid FID");
1345            return attrList;
1346        }
1347
1348        long anid = HDFConstants.FAIL;
1349        try {
1350            anid = HDFLibrary.ANstart(fid);
1351            // fileInfo[0] = n_file_label, fileInfo[1] = n_file_desc,
1352            // fileInfo[2] = n_data_label, fileInfo[3] = n_data_desc
1353            int[] fileInfo = new int[4];
1354            HDFLibrary.ANfileinfo(anid, fileInfo);
1355
1356            if (fileInfo[0] + fileInfo[1] <= 0) {
1357                try {
1358                    HDFLibrary.ANend(anid);
1359                }
1360                catch (HDFException ex) {
1361                    log.debug("getFileAnnotation(): ANend failure: ", ex);
1362                }
1363
1364                log.debug("getFileAnnotation(): n_file_labels + n_file_descriptions <= 0");
1365                return attrList;
1366            }
1367
1368            if (attrList == null)
1369                attrList = new Vector(fileInfo[0] + fileInfo[1], 5);
1370
1371            // load file labels and descriptions
1372            long id = -1;
1373            int[] annTypes = { HDFConstants.AN_FILE_LABEL,
1374                    HDFConstants.AN_FILE_DESC };
1375            for (int j = 0; j < 2; j++) {
1376                String annName = null;
1377                if (j == 0)
1378                    annName = "File Label";
1379                else
1380                    annName = "File Description";
1381
1382                for (int i = 0; i < fileInfo[j]; i++) {
1383                    try {
1384                        id = HDFLibrary.ANselect(anid, i, annTypes[j]);
1385                    }
1386                    catch (HDFException ex) {
1387                        log.debug("getFileAnnotation(): ANselect failure: ", ex);
1388                        id = HDFConstants.FAIL;
1389                    }
1390
1391                    if (id == HDFConstants.FAIL) {
1392                        log.trace("getFileAnnotation(): ANselect({}, {}, {}) failure", anid, i, annTypes[j]);
1393                        try {
1394                            HDFLibrary.ANendaccess(id);
1395                        }
1396                        catch (HDFException ex) {
1397                            log.debug("getFileAnnotation(): ANendaccess failure: ", ex);
1398                        }
1399                        continue;
1400                    }
1401
1402                    int length = 0;
1403                    try {
1404                        length = HDFLibrary.ANannlen(id) + 1;
1405                    }
1406                    catch (HDFException ex) {
1407                        log.debug("getFileAnnotation(): ANannlen failure: ", ex);
1408                        length = 0;
1409                    }
1410
1411                    if (length > 0) {
1412                        boolean b = false;
1413                        String[] str = { "" };
1414                        try {
1415                            b = HDFLibrary.ANreadann(id, str, length);
1416                        }
1417                        catch (HDFException ex) {
1418                            log.debug("getFileAnnotation(): ANreadann failure: ", ex);
1419                            b = false;
1420                        }
1421
1422                        if (b && (str[0].length() > 0)) {
1423                            long[] attrDims = { str.length };
1424                            Datatype attrType = null;
1425                            try {
1426                                attrType = new H4Datatype(HDFConstants.DFNT_CHAR);
1427                            }
1428                            catch (Exception ex) {
1429                                log.debug("getFileAnnotation(): failed to create datatype for attribute: ", ex);
1430                            }
1431                            H4ScalarAttribute newAttr = new H4ScalarAttribute(getRootObject(), annName + " #" + i, attrType, attrDims);
1432                            attrList.add(newAttr);
1433                            newAttr.setData(str);
1434                        }
1435                    }
1436
1437                    try {
1438                        HDFLibrary.ANendaccess(id);
1439                    }
1440                    catch (HDFException ex) {
1441                        log.debug("getFileAnnotation(): ANendaccess failure: ", ex);
1442                    }
1443                } // (int i=0; i < fileInfo[annTYpe]; i++)
1444            } // (int annType=0; annType<2; annType++)
1445        }
1446        finally {
1447            if (anid >= 0) {
1448                try {
1449                    HDFLibrary.ANend(anid);
1450                }
1451                catch (HDFException ex) {
1452                    log.debug("getFileAnnotation(): ANend failure: ", ex);
1453                }
1454            }
1455        }
1456
1457        return attrList;
1458    }
1459
1460    /**
1461     * Reads GR global attributes into memory. The attributes are stored as
1462     * attributes of the root group.
1463     *
1464     * @param grid
1465     *            the GR identifier.
1466     * @param attrList
1467     *            the list of attributes.
1468     *
1469     * @return the updated attribute list.
1470     *
1471     * @throws HDFException if the GR attributes can not be read
1472     */
1473    @SuppressWarnings({"rawtypes", "unchecked"})
1474    private List getGRglobalAttribute(long grid, List attrList) throws HDFException {
1475        log.trace("getGRglobalAttribute(): start: GRID={}", grid);
1476
1477        if (grid == HDFConstants.FAIL) {
1478            log.debug("getGRglobalAttribute(): Invalid GRID");
1479            return attrList;
1480        }
1481
1482        int[] attrInfo = { 0, 0 };
1483        HDFLibrary.GRfileinfo(grid, attrInfo);
1484        int numberOfAttributes = attrInfo[1];
1485
1486        if (numberOfAttributes > 0) {
1487            if (attrList == null)
1488                attrList = new Vector(numberOfAttributes, 5);
1489
1490            String[] attrName = new String[1];
1491            for (int i = 0; i < numberOfAttributes; i++) {
1492                attrName[0] = "";
1493                boolean b = false;
1494                try {
1495                    b = HDFLibrary.GRattrinfo(grid, i, attrName, attrInfo);
1496                    // mask off the litend bit
1497                    attrInfo[0] = attrInfo[0] & (~HDFConstants.DFNT_LITEND);
1498                }
1499                catch (HDFException ex) {
1500                    log.debug("getGRglobalAttribute(): GRattrinfo failure: ", ex);
1501                    b = false;
1502                }
1503
1504                if (!b)
1505                    continue;
1506
1507                long[] attrDims = { attrInfo[1] };
1508
1509                Object buf = null;
1510                try {
1511                    buf = H4Datatype.allocateArray(attrInfo[0], attrInfo[1]);
1512                }
1513                catch (OutOfMemoryError e) {
1514                    log.debug("getGRglobalAttribute(): out of memory: ", e);
1515                    buf = null;
1516                }
1517
1518                try {
1519                    HDFLibrary.GRgetattr(grid, i, buf);
1520                }
1521                catch (HDFException ex) {
1522                    log.debug("getGRglobalAttribute(): GRgetattr failure: ", ex);
1523                    buf = null;
1524                }
1525
1526                if ((buf != null) && ((attrInfo[0] == HDFConstants.DFNT_CHAR) || (attrInfo[0] == HDFConstants.DFNT_UCHAR8))) {
1527                    buf = Dataset.byteToString((byte[]) buf, attrInfo[1]);
1528                    attrDims[0] = ((String[]) buf).length;
1529                }
1530
1531                Datatype attrType = null;
1532                try {
1533                    attrType = new H4Datatype(attrInfo[0]);
1534                }
1535                catch (Exception ex) {
1536                    log.debug("getGRglobalAttribute(): failed to create datatype for attribute: ", ex);
1537                }
1538
1539                H4ScalarAttribute attr = new H4ScalarAttribute(getRootObject(), attrName[0], attrType, attrDims, buf);
1540                attrList.add(attr);
1541            } // (int i=0; i<numberOfAttributes; i++)
1542        } // (b && numberOfAttributes>0)
1543
1544        return attrList;
1545    }
1546
1547    /**
1548     * Reads SDS global attributes into memory. The attributes are stored as
1549     * attributes of the root group.
1550     *
1551     * @param sdid
1552     *            the SD identifier.
1553     * @param attrList
1554     *            the list of attributes.
1555     *
1556     * @return the updated attribute list.
1557     *
1558     * @throws HDFException if the SDS attributes can not be read
1559     */
1560    @SuppressWarnings({"rawtypes", "unchecked"})
1561    private List getSDSglobalAttribute(long sdid, List attrList) throws HDFException {
1562        log.trace("getSDSglobalAttribute(): start: SDID:{}", sdid);
1563
1564        if (sdid == HDFConstants.FAIL) {
1565            log.debug("getSDSglobalAttribute(): Invalid SDID");
1566            return attrList;
1567        }
1568
1569        int[] attrInfo = { 0, 0 };
1570        HDFLibrary.SDfileinfo(sdid, attrInfo);
1571
1572        int numberOfAttributes = attrInfo[1];
1573        if (numberOfAttributes > 0) {
1574            if (attrList == null)
1575                attrList = new Vector(numberOfAttributes, 5);
1576
1577            String[] attrName = new String[1];
1578            for (int i = 0; i < numberOfAttributes; i++) {
1579                attrName[0] = "";
1580                boolean b = false;
1581                try {
1582                    b = HDFLibrary.SDattrinfo(sdid, i, attrName, attrInfo);
1583                    // mask off the litend bit
1584                    attrInfo[0] = attrInfo[0] & (~HDFConstants.DFNT_LITEND);
1585                }
1586                catch (HDFException ex) {
1587                    log.debug("getSDSglobalAttribute(): SDattrinfo failure: ", ex);
1588                    b = false;
1589                }
1590
1591                if (!b)
1592                    continue;
1593
1594                long[] attrDims = { attrInfo[1] };
1595
1596                Object buf = null;
1597                try {
1598                    buf = H4Datatype.allocateArray(attrInfo[0], attrInfo[1]);
1599                }
1600                catch (OutOfMemoryError e) {
1601                    log.debug("getSDSglobalAttribute(): out of memory: ", e);
1602                    buf = null;
1603                }
1604
1605                try {
1606                    HDFLibrary.SDreadattr(sdid, i, buf);
1607                }
1608                catch (HDFException ex) {
1609                    log.debug("getSDSglobalAttribute(): SDreadattr failure: ", ex);
1610                    buf = null;
1611                }
1612
1613                if ((buf != null) && ((attrInfo[0] == HDFConstants.DFNT_CHAR) || (attrInfo[0] == HDFConstants.DFNT_UCHAR8))) {
1614                    buf = Dataset.byteToString((byte[]) buf, attrInfo[1]);
1615                    attrDims[0] = ((String[]) buf).length;
1616                }
1617
1618                Datatype attrType = null;
1619                try {
1620                    attrType = new H4Datatype(attrInfo[0]);
1621                }
1622                catch (Exception ex) {
1623                    log.debug("getSDSglobalAttribute(): failed to create datatype for attribute: ", ex);
1624                }
1625
1626                H4ScalarAttribute attr = new H4ScalarAttribute(getRootObject(), attrName[0], attrType, attrDims, buf);
1627                attrList.add(attr);
1628            } // (int i=0; i<numberOfAttributes; i++)
1629        } // (b && numberOfAttributes>0)
1630
1631        return attrList;
1632    }
1633
1634    /**
1635     * Returns the version of the HDF4 library.
1636     */
1637    @Override
1638    public String getLibversion() {
1639        int[] vers = new int[3];
1640        String ver = "HDF ";
1641        String[] verStr = { "" };
1642
1643        try {
1644            HDFLibrary.Hgetlibversion(vers, verStr);
1645        }
1646        catch (HDFException ex) {
1647            log.debug("getLibVersion(): Hgetlibversion failure: ", ex);
1648        }
1649
1650        ver += vers[0] + "." + vers[1] + "." + vers[2];
1651        log.debug("getLibversion(): libversion is {}", ver);
1652
1653        return ver;
1654    }
1655
1656    /** HDF4 library supports netCDF version 2.3.2. It only supports SDS APIs. */
1657    private boolean isNetCDF(String filename) {
1658        log.trace("isNetCDF(): start: filename={}", filename);
1659
1660        boolean isnetcdf = false;
1661
1662        try (java.io.RandomAccessFile raf = new java.io.RandomAccessFile(filename, "r")) {
1663            byte[] header = new byte[4];
1664            raf.read(header);
1665            // netCDF
1666            if (((header[0] == 67) && (header[1] == 68) && (header[2] == 70) && (header[3] == 1)))
1667                isnetcdf = true;
1668            else
1669                isnetcdf = false;
1670        }
1671        catch (Exception ex) {
1672            log.debug("RandomAccessFile {}", filename, ex);
1673        }
1674
1675        return isnetcdf;
1676    }
1677
1678    /**
1679     * Get an individual HObject with a given path. It does not load the whole
1680     * file structure.
1681     *
1682     * @param path the path of the object
1683     *
1684     * @throws Exception if the object cannot be found
1685     */
1686    @Override
1687    @SuppressWarnings("rawtypes")
1688    public HObject get(String path) throws Exception {
1689        log.trace("get(): start: path={}", path);
1690
1691        if (objList == null)
1692            objList = new Vector();
1693
1694        if ((path == null) || (path.length() <= 0)) {
1695            log.debug("get(): path is null or invalid path length");
1696            return null;
1697        }
1698
1699        path = path.replace('\\', '/');
1700        if (!path.startsWith("/"))
1701            path = "/" + path;
1702
1703        String name = null;
1704        String pPath = null;
1705        boolean isRoot = false;
1706
1707        if (path.equals("/")) {
1708            name = "/"; // the root
1709            isRoot = true;
1710        }
1711        else {
1712            if (path.endsWith("/")) {
1713                path = path.substring(0, path.length() - 2);
1714            }
1715            int idx = path.lastIndexOf('/');
1716            name = path.substring(idx + 1);
1717            if (idx == 0)
1718                pPath = "/";
1719            else
1720                pPath = path.substring(0, idx);
1721        }
1722
1723        log.trace("get(): isRoot={}", isRoot);
1724
1725        HObject obj = null;
1726        isReadOnly = false;
1727
1728        if (fid < 0) {
1729            fid = HDFLibrary.Hopen(fullFileName, HDFConstants.DFACC_WRITE);
1730            if (fid < 0) {
1731                isReadOnly = true;
1732                fid = HDFLibrary.Hopen(fullFileName, HDFConstants.DFACC_READ);
1733            }
1734            HDFLibrary.Vstart(fid);
1735            grid = HDFLibrary.GRstart(fid);
1736            sdid = HDFLibrary.SDstart(fullFileName, flag);
1737        }
1738
1739        if (isRoot)
1740            obj = getRootGroup();
1741        else
1742            obj = getAttachedObject(pPath, name);
1743
1744        return obj;
1745    }
1746
1747    /** Get the root group and all the alone objects */
1748    private H4Group getRootGroup() {
1749        long[] oid = { 0, 0 };
1750        int n = 0;
1751        int ref = -1;
1752        int[] argv = null;
1753
1754        H4Group rootGroup = new H4Group(this, "/", null, null, oid);
1755
1756        // get top level VGroup
1757        int[] tmpN = new int[1];
1758        int[] refs = null;
1759        try {
1760            // first call to get the number of lone Vgroups
1761            log.trace("getRootGroup(): first call to Vlone, get number of lone Vgroups");
1762            n = HDFLibrary.Vlone(fid, tmpN, 0);
1763            log.trace("getRootGroup(): number of lone Vgroups={}", n);
1764            refs = new int[n];
1765            // second call to get the references of all lone Vgroups
1766            log.trace("getRootGroup(): second call to Vlone, get references of lone Vgroups");
1767            n = HDFLibrary.Vlone(fid, refs, n);
1768        }
1769        catch (HDFException ex) {
1770            log.debug("getRootGroup(): Vlone failure: ", ex);
1771            n = 0;
1772        }
1773
1774        // Iterate through the file to see members of the group
1775        for (int i = 0; i < n; i++) {
1776            ref = refs[i];
1777            H4Group g = getVGroup(HDFConstants.DFTAG_VG, ref, HObject.SEPARATOR, rootGroup, false);
1778            if (g != null)
1779                rootGroup.addToMemberList(g);
1780        }
1781
1782        // get the top level GR images
1783        argv = new int[2];
1784        boolean b = false;
1785        try {
1786            b = HDFLibrary.GRfileinfo(grid, argv);
1787        }
1788        catch (HDFException ex) {
1789            log.debug("getRootGroup(): GRfileinfo failure: ", ex);
1790            b = false;
1791        }
1792
1793        if (b) {
1794            n = argv[0];
1795            for (int i = 0; i < n; i++) {
1796                // no duplicate object at top level
1797                H4GRImage gr = getGRImage(HDFConstants.DFTAG_RIG, i,
1798                        HObject.SEPARATOR, false);
1799                if (gr != null)
1800                    rootGroup.addToMemberList(gr);
1801            }
1802        } // ( HDFLibrary.GRfileinfo(grid, argv) )
1803
1804        // get top level SDS
1805        try {
1806            b = HDFLibrary.SDfileinfo(sdid, argv);
1807        }
1808        catch (HDFException ex) {
1809            log.debug("getRootGroup(): SDfileinfo failure: ", ex);
1810            b = false;
1811        }
1812
1813        if (b) {
1814            n = argv[0];
1815
1816            for (int i = 0; i < n; i++) {
1817                // no duplicate object at top level
1818                H4SDS sds = getSDS(HDFConstants.DFTAG_NDG, i, HObject.SEPARATOR, false);
1819                if (sds != null)
1820                    rootGroup.addToMemberList(sds);
1821            }
1822        } // (HDFLibrary.SDfileinfo(sdid, argv))
1823
1824        // get top level VData
1825        try {
1826            log.trace("getRootGroup(): first call to VSlone, get number of lone VDatas");
1827            n = HDFLibrary.VSlone(fid, tmpN, 0);
1828            log.trace("getRootGroup(): number of lone Vdatas={}", n);
1829            refs = new int[n];
1830            log.trace("getRootGroup(): second call to VSlone, get references of lone VDatas");
1831            n = HDFLibrary.VSlone(fid, refs, n);
1832        }
1833        catch (HDFException ex) {
1834            log.debug("getRootGroup(): VSlone failure: ex");
1835            n = 0;
1836        }
1837
1838        for (int i = 0; i < n; i++) {
1839            ref = refs[i];
1840
1841            // no duplicate object at top level
1842            H4Vdata vdata = getVdata(HDFConstants.DFTAG_VS, ref, HObject.SEPARATOR, false);
1843
1844            if (vdata != null)
1845                rootGroup.addToMemberList(vdata);
1846        }
1847
1848        if (rootGroup != null) {
1849            // retrieve file annotation, GR and SDS globle attributes
1850            @SuppressWarnings("rawtypes")
1851            List attributeList = null;
1852            try {
1853                attributeList = rootGroup.getMetadata();
1854            }
1855            catch (HDFException ex) {
1856                log.debug("getRootGroup(): getMetadata() failure: ", ex);
1857            }
1858
1859            if (attributeList != null) {
1860                try {
1861                    getFileAnnotation(fid, attributeList);
1862                }
1863                catch (HDFException ex) {
1864                    log.debug("getRootGroup(): getFileAnnotation() failure: ", ex);
1865                }
1866                try {
1867                    getGRglobalAttribute(grid, attributeList);
1868                }
1869                catch (HDFException ex) {
1870                    log.debug("getRootGroup(): getGRglobalAttribute() failure: ", ex);
1871                }
1872                try {
1873                    getSDSglobalAttribute(sdid, attributeList);
1874                }
1875                catch (HDFException ex) {
1876                    log.debug("getRootGroup(): getSDSglobalAttribute() failure: ", ex);
1877                }
1878            }
1879        }
1880
1881        return rootGroup;
1882    }
1883
1884    /** Get the object attached to a vgroup */
1885    private HObject getAttachedObject(String path, String name) {
1886        if ((name == null) || (name.length() <= 0)) {
1887            log.debug("getAttachedObject(): name is null or invalid name length");
1888            return null;
1889        }
1890
1891        // get top level VGroup
1892        String[] objName = { "" };
1893        // check if it is an image
1894        int idx = -1;
1895        try {
1896            idx = HDFLibrary.GRnametoindex(grid, name);
1897        }
1898        catch (HDFException ex) {
1899            log.debug("getAttachedObject(): GRnametoindex failure: ", ex);
1900            idx = -1;
1901        }
1902
1903        if (idx >= 0) {
1904            H4GRImage img = getGRImage(HDFConstants.DFTAG_RIG, idx, HObject.SEPARATOR, false);
1905            return img;
1906        }
1907
1908        // get top level SDS
1909        try {
1910            idx = HDFLibrary.SDnametoindex(sdid, name);
1911        }
1912        catch (HDFException ex) {
1913            log.debug("getAttachedObject(): SDnametoindex failure: ", ex);
1914            idx = -1;
1915        }
1916
1917        if (idx >= 0) {
1918            H4SDS sds = getSDS(HDFConstants.DFTAG_NDG, idx, HObject.SEPARATOR, false);
1919            return sds;
1920        }
1921
1922        int ref = 0;
1923        try {
1924            ref = HDFLibrary.Vfind(fid, name);
1925        }
1926        catch (HDFException ex) {
1927            log.debug("getAttachedObject(): Vfind failure: ", ex);
1928            ref = -1;
1929        }
1930
1931        if (ref > 0) {
1932            long oid[] = { HDFConstants.DFTAG_VG, ref };
1933            H4Group g = new H4Group(this, objName[0], path, null, oid);
1934            depth_first(g);
1935            return g;
1936        }
1937
1938        // get top level VData
1939        try {
1940            ref = HDFLibrary.VSfind(fid, name);
1941        }
1942        catch (HDFException ex) {
1943            log.debug("getAttachedObject(): VSfind failure: ", ex);
1944            ref = -1;
1945        }
1946
1947        if (ref > 0) {
1948            H4Vdata vdata = getVdata(HDFConstants.DFTAG_VS, ref, HObject.SEPARATOR, false);
1949            return vdata;
1950        }
1951
1952        log.debug("getAttachedObject(): Object not found");
1953        return null;
1954    }
1955
1956    @Override
1957    public void setNewLibBounds(String lowStr, String highStr) throws Exception {
1958        // HDF4 does not have this feature
1959    }
1960
1961    @Override
1962    public int getIndexType(String strtype) {
1963        return -1;
1964    }
1965
1966    @Override
1967    public void setIndexType(int indexType) {
1968        // HDF4 does not have this feature
1969    }
1970
1971    @Override
1972    public int getIndexOrder(String strorder) {
1973        return -1;
1974    }
1975
1976    @Override
1977    public void setIndexOrder(int indexOrder) {
1978        // HDF4 does not have this feature
1979    }
1980
1981}