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