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