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