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