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