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