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