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