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