    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.h5;
    016
    017import java.io.File;
    018import java.lang.reflect.Array;
    019import java.nio.ByteBuffer;
    020import java.util.ArrayList;
    021import java.util.Hashtable;
    022import java.util.Iterator;
    023import java.util.LinkedList;
    024import java.util.List;
    025import java.util.Queue;
    026import java.util.Vector;
    027
    028import org.slf4j.Logger;
    029import org.slf4j.LoggerFactory;
    030
    031import hdf.hdf5lib.H5;
    032import hdf.hdf5lib.HDF5Constants;
    033import hdf.hdf5lib.HDFNativeData;
    034import hdf.hdf5lib.exceptions.HDF5Exception;
    035import hdf.hdf5lib.structs.H5G_info_t;
    036import hdf.hdf5lib.structs.H5L_info_t;
    037import hdf.hdf5lib.structs.H5O_info_t;
    038import hdf.hdf5lib.structs.H5O_token_t;
    039
    040import hdf.object.Attribute;
    041import hdf.object.Dataset;
    042import hdf.object.Datatype;
    043import hdf.object.FileFormat;
    044import hdf.object.Group;
    045import hdf.object.HObject;
    046import hdf.object.ScalarDS;
    047
    048import hdf.object.h5.H5Attribute;
    049import hdf.object.h5.H5Datatype;
    050import hdf.object.h5.H5CompoundAttr;
    051import hdf.object.h5.H5ReferenceType;
    052import hdf.object.h5.H5ScalarAttr;
    053import hdf.object.h5.H5ReferenceType.H5ReferenceData;
    054
    055/**
    056 * H5File is an implementation of the FileFormat class for HDF5 files.
    057 *
    058 * The HDF5 file structure is made up of HObjects stored in a tree-like fashion. Each tree node represents an
    059 * HDF5 object: a Group, Dataset, or Named Datatype. Starting from the root of the tree, <i>rootObject</i>, the
    060 * tree can be traversed to find a specific object.
    061 *
    062 * The following example shows the implementation of finding an object for a given path in FileFormat. User applications
    063 * can directly call the static method FileFormat.findObject(file, objPath) to get the object.
    064 *
    065 * <pre>
    066 * HObject findObject(FileFormat file, String path) {
    067 *     if (file == null || path == null)
    068 *         return null;
    069 *     if (!path.endsWith(&quot;/&quot;))
    070 *         path = path + &quot;/&quot;;
    071 *     HObject theRoot = file.getRootObject();
    072 *     if (theRoot == null)
    073 *         return null;
    074 *     else if (path.equals(&quot;/&quot;))
    075 *         return theRoot;
    076 *
    077 *     Iterator local_it = ((Group) theRoot)
    078 *             .breadthFirstMemberList().iterator();
    079 *     HObject theObj = null;
    080 *     while (local_it.hasNext()) {
    081 *         theObj = local_it.next();
    082 *         String fullPath = theObj.getFullName() + &quot;/&quot;;
    083 *         if (path.equals(fullPath) &amp;&amp;  theObj.getPath() != null ) {
    084 *             break;
    085 *     }
    086 *     return theObj;
    087 * }
    088 * </pre>
    089 *
    090 * @author Peter X. Cao
    091 * @version 2.4 9/4/2007
    092 */
    093public class H5File extends FileFormat
    094{
    095    private static final long serialVersionUID = 6247335559471526045L;
    096
    097    private static final Logger log = LoggerFactory.getLogger(H5File.class);
    098
    099    /**
    100     * the file access flag. Valid values are
    101     *   HDF5Constants.H5F_ACC_RDONLY,
    102     *   HDF5Constants.H5F_ACC_SWMR_READ (with H5F_ACC_RDONLY)
    103     *   HDF5Constants.H5F_ACC_RDWR
    104     *   HDF5Constants.H5F_ACC_CREAT
    105     */
    106    private int flag;
    107
    108    /**
    109     * The index type. Valid values are HDF5Constants.H5_INDEX_NAME, HDF5Constants.H5_INDEX_CRT_ORDER.
    110     */
    111    private int indexType = HDF5Constants.H5_INDEX_NAME;
    112
    113    /**
    114     * The index order. Valid values are HDF5Constants.H5_ITER_INC, HDF5Constants.H5_ITER_DEC.
    115     */
    116    private int indexOrder = HDF5Constants.H5_ITER_INC;
    117
    118    /**
    119     * The root object of the file hierarchy.
    120     */
    121    private HObject rootObject;
    122
    123    /**
    124     * How many characters maximum in an attribute name?
    125     */
    126    private static final int attrNameLen = 256;
    127
    128    /**
    129     * The library version bounds
    130     */
    131    private int[] libver;
    132    /** The library latest version value */
    133    public static final int LIBVER_LATEST = HDF5Constants.H5F_LIBVER_LATEST;
    134    /** The library earliest version value */
    135    public static final int LIBVER_EARLIEST = HDF5Constants.H5F_LIBVER_EARLIEST;
    136    /** The library v1.8 version value */
    137    public static final int LIBVER_V18 = HDF5Constants.H5F_LIBVER_V18;
    138    /** The library v1.10 version value */
    139    public static final int LIBVER_V110 = HDF5Constants.H5F_LIBVER_V110;
    140    /** The library v1.12 version value */
    141    public static final int LIBVER_V112 = HDF5Constants.H5F_LIBVER_V112;
    142    /** The library v1.14 version value */
    143    public static final int LIBVER_V114 = HDF5Constants.H5F_LIBVER_V114;
    144
    145    /** Indicate that this file is open for reading in a
    146     * single-writer/multi-reader (SWMR) scenario. Note that
    147     * the process(es) opening the file for SWMR reading must
    148     * also open the file with the #H5F_ACC_RDONLY flag.  */
    149    public static final int SWMR = MULTIREAD;
    150
    151    /**
    152     * Enum to indicate the type of I/O to perform inside of the common I/O
    153     * function.
    154     */
    155    public static enum IO_TYPE {
    156        /** read IO type */
    157        READ,
    158        /** write IO type */
    159        WRITE
    160    };
    161
    162    /***************************************************************************
    163     * Constructor
    164     **************************************************************************/
    165    /**
    166     * Constructs an H5File instance with an empty file name and read-only access.
    167     */
    168    public H5File() {
    169        this("", READ);
    170    }
    171
    172    /**
    173     * Constructs an H5File instance with specified file name and read/write access.
    174     *
    175     * This constructor does not open the file for access, nor does it confirm that the file can be opened read/write.
    176     *
    177     * @param fileName
    178     *            A valid file name, with a relative or absolute path.
    179     *
    180     * @throws NullPointerException
    181     *             If the <code>fileName</code> argument is <code>null</code>.
    182     */
    183    public H5File(String fileName) {
    184        this(fileName, WRITE);
    185    }
    186
    187    /**
    188     * Constructs an H5File instance with specified file name and access.
    189     *
    190     * The access parameter values and corresponding behaviors:
    191     * <ul>
    192     * <li>READ: Read-only access; open() will fail file doesn't exist.</li>
    193     * <li>SWMR: Read-only access; open() will fail file doesn't exist.</li>
    194     * <li>WRITE: Read/Write access; open() will fail if file doesn't exist or if file can't be opened with read/write
    195     * access.</li>
    196     * <li>CREATE: Read/Write access; create a new file or truncate an existing one; open() will fail if file can't be
    197     * created or if file exists but can't be opened read/write.</li>
    198     * </ul>
    199     *
    200     * This constructor does not open the file for access, nor does it confirm that the file can later be opened
    201     * read/write or created.
    202     *
    203     * The flag returned by {@link #isReadOnly()} is set to true if the access parameter value is READ, even though the
    204     * file isn't yet open.
    205     *
    206     * @param fileName
    207     *            A valid file name, with a relative or absolute path.
    208     * @param access
    209     *            The file access flag, which determines behavior when file is opened. Acceptable values are
    210     *            <code> READ, WRITE, </code> and <code>CREATE</code>.
    211     *
    212     * @throws NullPointerException
    213     *             If the <code>fileName</code> argument is <code>null</code>.
    214     */
    215    public H5File(String fileName, int access) {
    216        // Call FileFormat ctor to set absolute path name
    217        super(fileName);
    218        libver = new int[2];
    219        libver[0] = HDF5Constants.H5F_LIBVER_EARLIEST;
    220        libver[1] = HDF5Constants.H5F_LIBVER_LATEST;
    221
    222        if ((access & FILE_CREATE_OPEN) == FILE_CREATE_OPEN) {
    223            File f = new File(fileName);
    224            if (f.exists())
    225                access = WRITE;
    226            else
    227                access = CREATE;
    228        }
    229
    230        // set metadata for the instance
    231        rootObject = null;
    232        this.fid = -1;
    233        isReadOnly = (READ == (access & READ)) || (MULTIREAD == (access & MULTIREAD));
    234
    235        // At this point we just set up the flags for what happens later.
    236        // We just pass unexpected access values on... subclasses may have
    237        // their own values.
    238        if (MULTIREAD == (access & MULTIREAD))
    239            flag = HDF5Constants.H5F_ACC_RDONLY | HDF5Constants.H5F_ACC_SWMR_READ;
    240        else if (READ == (access & READ))
    241            flag = HDF5Constants.H5F_ACC_RDONLY;
    242        else if (access == WRITE)
    243            flag = HDF5Constants.H5F_ACC_RDWR;
    244        else if (access == CREATE)
    245            flag = HDF5Constants.H5F_ACC_CREAT;
    246        else
    247            flag = access;
    248    }
    249
    250    /***************************************************************************
    251     * Class methods
    252     **************************************************************************/
    253
    254    /**
    255     * Copies the attributes of one object to another object.
    256     *
    257     * This method copies all the attributes from one object (source object) to another (destination object). If an
    258     * attribute already exists in the destination object, the attribute will not be copied. Attribute names exceeding
    259     * 256 characters will be truncated in the destination object.
    260     *
    261     * The object can be an H5Group, an H5Dataset, or a named H5Datatype. This method is in the H5File class because
    262     * there is no H5Object class and it is specific to HDF5 objects.
    263     *
    264     * The copy can fail for a number of reasons, including an invalid source or destination object, but no exceptions
    265     * are thrown. The actual copy is carried out by the method: {@link #copyAttributes(long, long)}
    266     *
    267     * @param src
    268     *            The source object.
    269     * @param dst
    270     *            The destination object.
    271     *
    272     * @see #copyAttributes(long, long)
    273     */
    274    public static final void copyAttributes(HObject src, HObject dst) {
    275        if ((src != null) && (dst != null)) {
    276            long srcID = src.open();
    277            long dstID = dst.open();
    278
    279            if ((srcID >= 0) && (dstID >= 0))
    280                copyAttributes(srcID, dstID);
    281
    282            if (srcID >= 0)
    283                src.close(srcID);
    284
    285            if (dstID >= 0)
    286                dst.close(dstID);
    287        }
    288    }
    289
    290    /**
    291     * Copies the attributes of one object to another object.
    292     *
    293     * This method copies all the attributes from one object (source object) to another (destination object). If an
    294     * attribute already exists in the destination object, the attribute will not be copied. Attribute names exceeding
    295     * 256 characters will be truncated in the destination object.
    296     *
    297     * The object can be an H5Group, an H5Dataset, or a named H5Datatype. This method is in the H5File class because
    298     * there is no H5Object class and it is specific to HDF5 objects.
    299     *
    300     * The copy can fail for a number of reasons, including an invalid source or destination object identifier, but no
    301     * exceptions are thrown.
    302     *
    303     * @param src_id
    304     *            The identifier of the source object.
    305     * @param dst_id
    306     *            The identifier of the destination object.
    307     */
    308    public static final void copyAttributes(long src_id, long dst_id) {
    309        log.trace("copyAttributes(): start: src_id={} dst_id={}", src_id, dst_id);
    310        long aid_src = -1;
    311        long aid_dst = -1;
    312        long asid = -1;
    313        long atid = -1;
    314        String aName = null;
    315        H5O_info_t obj_info = null;
    316
    317        try {
    318            obj_info = H5.H5Oget_info(src_id);
    319        }
    320        catch (Exception ex) {
    321            obj_info.num_attrs = -1;
    322        }
    323
    324        if (obj_info.num_attrs < 0) {
    325            log.debug("copyAttributes(): no attributes");
    326            return;
    327        }
    328
    329        for (int i = 0; i < obj_info.num_attrs; i++) {
    330            try {
    331                aid_src = H5.H5Aopen_by_idx(src_id, ".", HDF5Constants.H5_INDEX_CRT_ORDER, HDF5Constants.H5_ITER_INC,
    332                        i, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    333                aName = H5.H5Aget_name(aid_src);
    334                atid = H5.H5Aget_type(aid_src);
    335                asid = H5.H5Aget_space(aid_src);
    336
    337                aid_dst = H5.H5Acreate(dst_id, aName, atid, asid, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    338
    339                // use native data copy
    340                H5.H5Acopy(aid_src, aid_dst);
    341
    342            }
    343            catch (Exception ex) {
    344                log.debug("copyAttributes(): Attribute[{}] failure: ", i, ex);
    345            }
    346
    347            try {
    348                H5.H5Sclose(asid);
    349            }
    350            catch (Exception ex) {
    351                log.debug("copyAttributes(): Attribute[{}] H5Sclose(asid {}) failure: ", i, asid, ex);
    352            }
    353            try {
    354                H5.H5Tclose(atid);
    355            }
    356            catch (Exception ex) {
    357                log.debug("copyAttributes(): Attribute[{}] H5Tclose(atid {}) failure: ", i, atid, ex);
    358            }
    359            try {
    360                H5.H5Aclose(aid_src);
    361            }
    362            catch (Exception ex) {
    363                log.debug("copyAttributes(): Attribute[{}] H5Aclose(aid_src {}) failure: ", i, aid_src, ex);
    364            }
    365            try {
    366                H5.H5Aclose(aid_dst);
    367            }
    368            catch (Exception ex) {
    369                log.debug("copyAttributes(): Attribute[{}] H5Aclose(aid_dst {}) failure: ", i, aid_dst, ex);
    370            }
    371
    372        } // (int i=0; i<num_attr; i++)
    373    }
    374
    375    /**
    376     * Returns a list of attributes for the specified object.
    377     *
    378     * This method returns a list containing the attributes associated with the
    379     * identified object. If there are no associated attributes, an empty list will
    380     * be returned.
    381     *
    382     * Attribute names exceeding 256 characters will be truncated in the returned
    383     * list.
    384     *
    385     * @param obj
    386     *            The HObject whose attributes are to be returned.
    387     *
    388     * @return The list of the object's attributes.
    389     *
    390     * @throws HDF5Exception
    391     *             If an underlying HDF library routine is unable to perform a step
    392     *             necessary to retrieve the attributes. A variety of failures throw
    393     *             this exception.
    394     *
    395     * @see #getAttribute(HObject,int,int)
    396     */
    397    public static final List<Attribute> getAttribute(HObject obj) throws HDF5Exception {
    398        return H5File.getAttribute(obj, HDF5Constants.H5_INDEX_NAME, HDF5Constants.H5_ITER_INC);
    399    }
    400
    401    /**
    402     * Returns a list of attributes for the specified object, in creation or
    403     * alphabetical order.
    404     *
    405     * This method returns a list containing the attributes associated with the
    406     * identified object. If there are no associated attributes, an empty list will
    407     * be returned. The list of attributes returned can be in increasing or
    408     * decreasing, creation or alphabetical order.
    409     *
    410     * Attribute names exceeding 256 characters will be truncated in the returned
    411     * list.
    412     *
    413     * @param obj
    414     *            The HObject whose attributes are to be returned.
    415     * @param idx_type
    416     *            The type of index. Valid values are:
    417     *            <ul>
    418     *            <li>H5_INDEX_NAME: An alpha-numeric index by attribute name
    419     *            <li>H5_INDEX_CRT_ORDER: An index by creation order
    420     *            </ul>
    421     * @param order
    422     *            The index traversal order. Valid values are:
    423     *            <ul>
    424     *            <li>H5_ITER_INC: A top-down iteration incrementing the index
    425     *            position at each step.
    426     *            <li>H5_ITER_DEC: A bottom-up iteration decrementing the index
    427     *            position at each step.
    428     *            </ul>
    429     *
    430     * @return The list of the object's attributes.
    431     *
    432     * @throws HDF5Exception
    433     *             If an underlying HDF library routine is unable to perform a step
    434     *             necessary to retrieve the attributes. A variety of failures throw
    435     *             this exception.
    436     */
    437
    438    public static final List<Attribute> getAttribute(HObject obj, int idx_type, int order) throws HDF5Exception {
    439        log.trace("getAttribute(): start: obj={} idx_type={} order={}", obj, idx_type, order);
    440        List<Attribute> attributeList = null;
    441        long objID = -1;
    442        long aid = -1;
    443        long sid = -1;
    444        long tid = -1;
    445        H5O_info_t obj_info = null;
    446
    447        objID = obj.open();
    448        if (objID >= 0) {
    449            try {
    450                try {
    451                    log.trace("getAttribute(): get obj_info");
    452                    obj_info = H5.H5Oget_info(objID);
    453                }
    454                catch (Exception ex) {
    455                    log.debug("getAttribute(): H5Oget_info(objID {}) failure: ", objID, ex);
    456                }
    457                if (obj_info.num_attrs <= 0) {
    458                    log.trace("getAttribute(): no attributes");
    459                    return (attributeList = new Vector<>());
    460                }
    461
    462                int n = (int) obj_info.num_attrs;
    463                attributeList = new Vector<>(n);
    464                log.trace("getAttribute(): num_attrs={}", n);
    465
    466                for (int i = 0; i < n; i++) {
    467                    long lsize = 1;
    468                    log.trace("getAttribute(): attribute[{}]", i);
    469
    470                    try {
    471                        aid = H5.H5Aopen_by_idx(objID, ".", idx_type, order, i, HDF5Constants.H5P_DEFAULT,
    472                                HDF5Constants.H5P_DEFAULT);
    473                        sid = H5.H5Aget_space(aid);
    474                        log.trace("getAttribute(): Attribute[{}] aid={} sid={}", i, aid, sid);
    475
    476                        long dims[] = null;
    477                        int rank = H5.H5Sget_simple_extent_ndims(sid);
    478
    479                        log.trace("getAttribute(): Attribute[{}] isScalar={}", i, (rank == 0));
    480
    481                        if (rank > 0) {
    482                            dims = new long[rank];
    483                            H5.H5Sget_simple_extent_dims(sid, dims, null);
    484                            log.trace("getAttribute(): Attribute[{}] rank={}, dims={}", i, rank, dims);
    485                            for (int j = 0; j < dims.length; j++) {
    486                                lsize *= dims[j];
    487                            }
    488                        }
    489
    490                        String nameA = H5.H5Aget_name(aid);
    491                        log.trace("getAttribute(): Attribute[{}] is {} with lsize={}", i, nameA, lsize);
    492
    493                        long tmptid = -1;
    494                        try {
    495                            tmptid = H5.H5Aget_type(aid);
    496                            tid = H5.H5Tget_native_type(tmptid);
    497                            log.trace("getAttribute(): Attribute[{}] tid={} native tmptid={} from aid={}", i, tid,
    498                                    tmptid, aid);
    499                        }
    500                        finally {
    501                            try {
    502                                H5.H5Tclose(tmptid);
    503                            }
    504                            catch (Exception ex) {
    505                                log.debug("getAttribute(): Attribute[{}] H5Tclose(tmptid {}) failure: ", i, tmptid, ex);
    506                            }
    507                        }
    508
    509                        H5Datatype attrType = null;
    510                        try {
    511                            int nativeClass = H5.H5Tget_class(tid);
    512                            if (nativeClass == HDF5Constants.H5T_REFERENCE)
    513                                attrType = new H5ReferenceType(obj.getFileFormat(), lsize, tid);
    514                            else
    515                                attrType = new H5Datatype(obj.getFileFormat(), tid);
    516
    517                            log.trace("getAttribute(): Attribute[{}] Datatype={}", i, attrType.getDescription());
    518                            log.trace("getAttribute(): Attribute[{}] has size={} isCompound={} is_variable_str={} isVLEN={}",
    519                                    i, lsize, attrType.isCompound(), attrType.isVarStr(), attrType.isVLEN());
    520                        }
    521                        catch (Exception ex) {
    522                            log.debug("getAttribute(): failed to create datatype for Attribute[{}]: ", i, ex);
    523                            attrType = null;
    524                        }
    525
    526                        Attribute attr = null;
    527                        if (attrType.isCompound())
    528                            attr = (Attribute)new H5CompoundAttr(obj, nameA, attrType, dims);
    529                        else
    530                            attr = (Attribute)new H5ScalarAttr(obj, nameA, attrType, dims);
    531                        attributeList.add(attr);
    532
    533                        // retrieve the attribute value
    534                        if (lsize <= 0) {
    535                            log.debug("getAttribute(): Attribute[{}] lsize <= 0", i);
    536                            continue;
    537                        }
    538
    539                        if (lsize < Integer.MIN_VALUE || lsize > Integer.MAX_VALUE) {
    540                            log.debug("getAttribute(): Attribute[{}] lsize outside valid Java int range; unsafe cast", i);
    541                            continue;
    542                        }
    543
    544                        try {
    545                            //attr.AttributeCommonIO(aid, H5File.IO_TYPE.READ, null);
    546                            Object attrData = attr.getAttributeData();
    547                            log.trace("getAttribute(): attrType.isRef()={}", attrType.isRef());
    548                            if (attrType.isRef()) {
    549                                if (attr.getAttributeRank() > 2)
    550                                    ((H5ReferenceType)attrType).setRefSize(attr.getAttributePlane());
    551                                ((H5ReferenceType)attrType).setData(attrData);
    552                            }
    553                        }
    554                        catch (Exception ex) {
    555                            log.debug("getAttribute(): failed to read attribute: ", ex);
    556                        }
    557                    }
    558                    catch (HDF5Exception ex) {
    559                        log.debug("getAttribute(): Attribute[{}] inspection failure: ", i, ex);
    560                    }
    561                    finally {
    562                        try {
    563                            H5.H5Tclose(tid);
    564                        }
    565                        catch (Exception ex) {
    566                            log.debug("getAttribute(): Attribute[{}] H5Tclose(tid {}) failure: ", i, tid, ex);
    567                        }
    568                        try {
    569                            H5.H5Sclose(sid);
    570                        }
    571                        catch (Exception ex) {
    572                            log.debug("getAttribute(): Attribute[{}] H5Sclose(aid {}) failure: ", i, sid, ex);
    573                        }
    574                        try {
    575                            H5.H5Aclose(aid);
    576                        }
    577                        catch (Exception ex) {
    578                            log.debug("getAttribute(): Attribute[{}] H5Aclose(aid {}) failure: ", i, aid, ex);
    579                        }
    580                    }
    581                } // (int i=0; i<obj_info.num_attrs; i++)
    582                for (int i = 0; i < n; i++) {
    583                    Attribute attr = (Attribute) attributeList.get(i);
    584                    H5Datatype atype = (H5Datatype)attr.getAttributeDatatype();
    585                    H5Datatype aBasetype = (H5Datatype)atype.getDatatypeBase();
    586                    boolean BDTisRef = false;
    587                    if (aBasetype != null)
    588                        BDTisRef = aBasetype.isRef();
    589                    if (atype.isRef() || BDTisRef) {
    590                        H5ReferenceType rtype = null;
    591                        if (BDTisRef)
    592                            rtype = (H5ReferenceType)aBasetype;
    593                        else
    594                            rtype = (H5ReferenceType)atype;
    595                        try {
    596                            List<H5ReferenceData> refdata = (List)rtype.getData();
    597                            for (int r = 0; r < (int)rtype.getRefSize(); r++) {
    598                                H5ReferenceData rf = refdata.get(r);
    599                                log.trace("getAttribute(): refdata {}", rf.ref_array);
    600                            }
    601                        }
    602                        catch (Exception ex) {
    603                            log.trace("Error retrieving H5ReferenceData of object ", ex);
    604                        }
    605                    }
    606                }
    607            }
    608            finally {
    609                obj.close(objID);
    610            }
    611        }
    612
    613        return attributeList;
    614    }
    615
    616    /**
    617     * Creates attributes for an HDF5 image dataset.
    618     *
    619     * This method creates attributes for two common types of HDF5 images. It provides a way of adding multiple
    620     * attributes to an HDF5 image dataset with a single call. The {@link #writeAttribute(HObject, Attribute, boolean)}
    621     * method may be used to write image attributes that are not handled by this method.
    622     *
    623     * For more information about HDF5 image attributes, read <a href="https://hdfgroup.github.io/hdf5/_i_m_g.html">HDF5
    624     * Image and Palette Specification</a>
    625     *
    626     * This method can be called to create attributes for 24-bit true color and indexed images. The
    627     * <code>selectionFlag</code> parameter controls whether this will be an indexed or true color image. If
    628     * <code>selectionFlag</code> is <code>-1</code>, this will be an indexed image. If the value is
    629     * <code>ScalarDS.INTERLACE_PIXEL</code> or <code>ScalarDS.INTERLACE_PLANE</code>, it will be a 24-bit true color
    630     * image with the indicated interlace mode.
    631     *
    632     * <ul>
    633     * The created attribute descriptions, names, and values are:
    634     * <li>The image identifier: name="CLASS", value="IMAGE"
    635     * <li>The version of image: name="IMAGE_VERSION", value="1.2"
    636     * <li>The range of data values: name="IMAGE_MINMAXRANGE", value=[0, 255]
    637     * <li>The type of the image: name="IMAGE_SUBCLASS", value="IMAGE_TRUECOLOR" or "IMAGE_INDEXED"
    638     * <li>For IMAGE_TRUECOLOR, the interlace mode: name="INTERLACE_MODE", value="INTERLACE_PIXEL" or "INTERLACE_PLANE"
    639     * <li>For IMAGE_INDEXED, the palettes to use in viewing the image: name="PALETTE", value= 1-d array of references
    640     * to the palette datasets, with initial value of {-1}
    641     * </ul>
    642     *
    643     * This method is in the H5File class rather than H5ScalarDS because images are typically thought of at the File
    644     * Format implementation level.
    645     *
    646     * @param dataset       The image dataset the attributes are added to.
    647     * @param selectionFlag Selects the image type and, for 24-bit true color images, the interlace mode. Valid values
    648     *                      are:
    649     *                      <ul>
    650     *                      <li>-1: Indexed Image.
    651     *                      <li>ScalarDS.INTERLACE_PIXEL: True Color Image. The component values for a pixel are stored
    652     *                      contiguously.
    653     *                      <li>ScalarDS.INTERLACE_PLANE: True Color Image. Each component is stored in a separate
    654     *                      plane.
    655     *                      </ul>
    656     *
    657     * @throws Exception If there is a problem creating the attributes, or if the selectionFlag is invalid.
    658     */
    659    private static final void createImageAttributes(Dataset dataset, int selectionFlag) throws Exception {
    660        log.trace("createImageAttributes(): start: dataset={}", dataset.toString());
    661        String subclass = null;
    662        String interlaceMode = null;
    663
    664        if (selectionFlag == ScalarDS.INTERLACE_PIXEL) {
    665            log.trace("createImageAttributes(): subclass IMAGE_TRUECOLOR selectionFlag INTERLACE_PIXEL");
    666            subclass = "IMAGE_TRUECOLOR";
    667            interlaceMode = "INTERLACE_PIXEL";
    668        }
    669        else if (selectionFlag == ScalarDS.INTERLACE_PLANE) {
    670            log.trace("createImageAttributes(): subclass IMAGE_TRUECOLOR selectionFlag INTERLACE_PLANE");
    671            subclass = "IMAGE_TRUECOLOR";
    672            interlaceMode = "INTERLACE_PLANE";
    673        }
    674        else if (selectionFlag == -1) {
    675            log.trace("createImageAttributes(): subclass IMAGE_INDEXED");
    676            subclass = "IMAGE_INDEXED";
    677        }
    678        else {
    679            log.debug("createImageAttributes(): invalid selectionFlag");
    680            throw new HDF5Exception("The selectionFlag is invalid.");
    681        }
    682
    683        String attrName = "CLASS";
    684        String[] classValue = { "IMAGE" };
    685        Datatype attrType = new H5Datatype(Datatype.CLASS_STRING, classValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE);
    686        Attribute attr = (Attribute)new H5ScalarAttr(dataset, attrName, attrType, null);
    687        attr.writeAttribute(classValue);
    688
    689        attrName = "IMAGE_VERSION";
    690        String[] versionValue = { "1.2" };
    691        attrType = new H5Datatype(Datatype.CLASS_STRING, versionValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE);
    692        attr = (Attribute)new H5ScalarAttr(dataset, attrName, attrType, null);
    693        attr.writeAttribute(versionValue);
    694
    695        long[] attrDims = { 2 };
    696        attrName = "IMAGE_MINMAXRANGE";
    697        byte[] attrValueInt = { 0, (byte) 255 };
    698        attrType = new H5Datatype(Datatype.CLASS_CHAR, 1, Datatype.NATIVE, Datatype.SIGN_NONE);
    699        attr = (Attribute)new H5ScalarAttr(dataset, attrName, attrType, attrDims);
    700        attr.writeAttribute(attrValueInt);
    701
    702        attrName = "IMAGE_SUBCLASS";
    703        String[] subclassValue = { subclass };
    704        attrType = new H5Datatype(Datatype.CLASS_STRING, subclassValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE);
    705        attr = (Attribute)new H5ScalarAttr(dataset, attrName, attrType, null);
    706        attr.writeAttribute(subclassValue);
    707
    708        if ((selectionFlag == ScalarDS.INTERLACE_PIXEL) || (selectionFlag == ScalarDS.INTERLACE_PLANE)) {
    709            attrName = "INTERLACE_MODE";
    710            String[] interlaceValue = { interlaceMode };
    711            attrType = new H5Datatype(Datatype.CLASS_STRING, interlaceValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE);
    712            attr = (Attribute)new H5ScalarAttr(dataset, attrName, attrType, null);
    713            attr.writeAttribute(interlaceValue);
    714        }
    715        else {
    716            attrName = "PALETTE";
    717            String palRef = "."; // set ref to null
    718            attrType = new H5Datatype(Datatype.CLASS_REFERENCE, 1, Datatype.NATIVE, Datatype.SIGN_NONE);
    719            attr = (Attribute)new H5ScalarAttr(dataset, attrName, attrType, null);
    720            attr.writeAttribute(palRef);
    721        }
    722    }
    723
    724    /**
    725     * Updates values of scalar dataset object references in copied file.
    726     *
    727     * This method has very specific functionality as documented below, and the user is advised to pay close attention
    728     * when dealing with files that contain references.
    729     *
    730     * When a copy is made from one HDF file to another, object references and dataset region references are copied, but
    731     * the references in the destination file are not updated by the copy and are therefore invalid.
    732     *
    733     * When an entire file is copied, this method updates the values of the object references and dataset region
    734     * references that are in scalar datasets in the destination file so that they point to the correct object(s) in the
    735     * destination file. The method does not update references that occur in objects other than scalar datasets.
    736     *
    737     * In the current release, the updating of object references is not handled completely as it was not required by the
    738     * projects that funded development. There is no support for updates when the copy does not include the entire file.
    739     * Nor is there support for updating objects other than scalar datasets in full-file copies. This functionality will
    740     * be extended as funding becomes available or, possibly, when the underlying HDF library supports the reference
    741     * updates itself.
    742     *
    743     * @param srcFile
    744     *            The file that was copied.
    745     * @param dstFile
    746     *            The destination file where the object references will be updated.
    747     *
    748     * @throws Exception
    749     *             If there is a problem in the update process.
    750     */
    751    public static final void updateReferenceDataset(H5File srcFile, H5File dstFile) throws Exception {
    752        if ((srcFile == null) || (dstFile == null)) {
    753            log.debug("updateReferenceDataset(): srcFile or dstFile is null");
    754            return;
    755        }
    756
    757        HObject srcRoot = srcFile.getRootObject();
    758        HObject newRoot = dstFile.getRootObject();
    759
    760        Iterator<HObject> srcIt = getMembersBreadthFirst(srcRoot).iterator();
    761        Iterator<HObject> newIt = getMembersBreadthFirst(newRoot).iterator();
    762
    763        long did = -1;
    764        // build one-to-one table of between objects in
    765        // the source file and new file
    766        long tid = -1;
    767        HObject srcObj, newObj;
    768        Hashtable<String, long[]> oidMap = new Hashtable<>();
    769        List<ScalarDS> refDatasets = new Vector<>();
    770        while (newIt.hasNext() && srcIt.hasNext()) {
    771            srcObj = srcIt.next();
    772            newObj = newIt.next();
    773            oidMap.put(String.valueOf((srcObj.getOID())[0]), newObj.getOID());
    774            did = -1;
    775            tid = -1;
    776
    777            // for Scalar DataSets in destination, if there is an object
    778            // reference in the dataset, add it to the refDatasets list for
    779            // later updating.
    780            if (newObj instanceof ScalarDS) {
    781                ScalarDS sd = (ScalarDS) newObj;
    782                did = sd.open();
    783                if (did >= 0) {
    784                    try {
    785                        tid = H5.H5Dget_type(did);
    786                        if (H5.H5Tequal(tid, HDF5Constants.H5T_STD_REF)) {
    787                            refDatasets.add(sd);
    788                        }
    789                    }
    790                    catch (Exception ex) {
    791                        log.debug("updateReferenceDataset(): ScalarDS reference failure: ", ex);
    792                    }
    793                    finally {
    794                        try {
    795                            H5.H5Tclose(tid);
    796                        }
    797                        catch (Exception ex) {
    798                            log.debug("updateReferenceDataset(): ScalarDS reference H5Tclose(tid {}) failure: ", tid, ex);
    799                        }
    800                    }
    801                }
    802                sd.close(did);
    803            } // (newObj instanceof ScalarDS)
    804        }
    805
    806        // Update the references in the scalar datasets in the dest file.
    807        H5ScalarDS d = null;
    808        long sid = -1;
    809        int size = 0;
    810        int rank = 0;
    811        int space_type = -1;
    812        int n = refDatasets.size();
    813        for (int i = 0; i < n; i++) {
    814            log.trace("updateReferenceDataset(): Update the references in the scalar datasets in the dest file");
    815            d = (H5ScalarDS) refDatasets.get(i);
    816            byte[] buf = null;
    817            long[] refs = null;
    818
    819            try {
    820                did = d.open();
    821                if (did >= 0) {
    822                    tid = H5.H5Dget_type(did);
    823                    sid = H5.H5Dget_space(did);
    824                    rank = H5.H5Sget_simple_extent_ndims(sid);
    825                    space_type = H5.H5Sget_simple_extent_type(sid);
    826                    size = 1;
    827                    if (rank > 0) {
    828                        long[] dims = new long[rank];
    829                        H5.H5Sget_simple_extent_dims(sid, dims, null);
    830                        log.trace("updateReferenceDataset(): rank={}, dims={}, space_type={}", rank, dims, space_type);
    831                        for (int j = 0; j < rank; j++) {
    832                            size *= (int) dims[j];
    833                        }
    834                        dims = null;
    835                    }
    836
    837                    buf = new byte[size * 8];
    838                    H5.H5Dread(did, tid, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, HDF5Constants.H5P_DEFAULT, buf);
    839
    840                    // update the ref values
    841                    refs = HDFNativeData.byteToLong(buf);
    842                    size = refs.length;
    843                    for (int j = 0; j < size; j++) {
    844                        long[] theOID = oidMap.get(String.valueOf(refs[j]));
    845                        if (theOID != null) {
    846                            refs[j] = theOID[0];
    847                        }
    848                    }
    849
    850                    // write back to file
    851                    H5.H5Dwrite(did, tid, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, HDF5Constants.H5P_DEFAULT, refs);
    852                }
    853                else {
    854                    log.debug("updateReferenceDataset(): dest file dataset failed to open");
    855                }
    856            }
    857            catch (Exception ex) {
    858                log.debug("updateReferenceDataset(): Reference[{}] failure: ", i, ex);
    859                continue;
    860            }
    861            finally {
    862                try {
    863                    H5.H5Tclose(tid);
    864                }
    865                catch (Exception ex) {
    866                    log.debug("updateReferenceDataset(): H5ScalarDS reference[{}] H5Tclose(tid {}) failure: ", i, tid, ex);
    867                }
    868                try {
    869                    H5.H5Sclose(sid);
    870                }
    871                catch (Exception ex) {
    872                    log.debug("updateReferenceDataset(): H5ScalarDS reference[{}] H5Sclose(sid {}) failure: ", i, sid, ex);
    873                }
    874                try {
    875                    H5.H5Dclose(did);
    876                }
    877                catch (Exception ex) {
    878                    log.debug("updateReferenceDataset(): H5ScalarDS reference[{}] H5Dclose(did {}) failure: ", i, did, ex);
    879                }
    880            }
    881
    882            refs = null;
    883            buf = null;
    884        } // (int i=0; i<n; i++)
    885    }
    886
    887    /***************************************************************************
    888     * Implementation Class methods. These methods are related to the implementing H5File class, but not to a particular
    889     * instance of the class. Since we can't override class methods (they can only be shadowed in Java), these are
    890     * instance methods.
    891     **************************************************************************/
    892
    893    /**
    894     * Returns the version of the HDF5 library.
    895     *
    896     * @see hdf.object.FileFormat#getLibversion()
    897     */
    898    @Override
    899    public String getLibversion() {
    900        int[] vers = new int[3];
    901        String ver = "HDF5 ";
    902
    903        try {
    904            H5.H5get_libversion(vers);
    905        }
    906        catch (Exception ex) {
    907            ex.printStackTrace();
    908        }
    909
    910        ver += vers[0] + "." + vers[1] + "." + vers[2];
    911        log.debug("getLibversion(): libversion is {}", ver);
    912
    913        return ver;
    914    }
    915
    916    /**
    917     * Checks if the specified FileFormat instance has the HDF5 format.
    918     *
    919     * @see hdf.object.FileFormat#isThisType(hdf.object.FileFormat)
    920     */
    921    @Override
    922    public boolean isThisType(FileFormat theFile) {
    923        return (theFile instanceof H5File);
    924    }
    925
    926    /**
    927     * Checks if the specified file has the HDF5 format.
    928     *
    929     * @see hdf.object.FileFormat#isThisType(java.lang.String)
    930     */
    931    @Override
    932    public boolean isThisType(String filename) {
    933        boolean isH5 = false;
    934
    935        try {
    936            isH5 = H5.H5Fis_hdf5(filename);
    937        }
    938        catch (HDF5Exception ex) {
    939            isH5 = false;
    940        }
    941
    942        return isH5;
    943    }
    944
    945    /**
    946     * Creates an HDF5 file with the specified name and returns a new H5File instance associated with the file.
    947     *
    948     * @throws Exception
    949     *             If the file cannot be created or if createFlag has unexpected value.
    950     *
    951     * @see hdf.object.FileFormat#createFile(java.lang.String, int)
    952     * @see #H5File(String, int)
    953     */
    954    @Override
    955    public FileFormat createFile(String filename, int createFlag) throws Exception {
    956        log.trace("createFile(): start: filename={} createFlag={}", filename, createFlag);
    957        // Flag if we need to create or truncate the file.
    958        Boolean doCreateFile = true;
    959
    960        // Won't create or truncate if CREATE_OPEN specified and file exists
    961        if ((createFlag & FILE_CREATE_OPEN) == FILE_CREATE_OPEN) {
    962            File f = new File(filename);
    963            if (f.exists()) {
    964                doCreateFile = false;
    965            }
    966        }
    967        log.trace("createFile(): doCreateFile={}", doCreateFile);
    968
    969        if (doCreateFile) {
    970            long fapl = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
    971
    972            if ((createFlag & FILE_CREATE_EARLY_LIB) == FILE_CREATE_EARLY_LIB) {
    973                int[] newlibver = getLibBounds();
    974                H5.H5Pset_libver_bounds(fapl, newlibver[0], newlibver[1]);
    975            }
    976
    977            long fileid = H5.H5Fcreate(filename, HDF5Constants.H5F_ACC_TRUNC, HDF5Constants.H5P_DEFAULT, fapl);
    978            try {
    979                H5.H5Pclose(fapl);
    980                H5.H5Fclose(fileid);
    981            }
    982            catch (HDF5Exception ex) {
    983                log.debug("H5 file, {} failure: ", filename, ex);
    984            }
    985        }
    986
    987        return new H5File(filename, WRITE);
    988    }
    989
    990    /**
    991     * Creates an H5File instance with specified file name and access.
    992     *
    993     * @see hdf.object.FileFormat#createInstance(java.lang.String, int)
    994     * @see #H5File(String, int)
    995     *
    996     * @throws Exception
    997     *            If there is a failure.
    998     */
    999    @Override
    1000    public FileFormat createInstance(String filename, int access) throws Exception {
    1001        log.trace("createInstance() for {} with {}", filename, access);
    1002        return new H5File(filename, access);
    1003    }
    1004
    1005    /***************************************************************************
    1006     * Instance Methods
    1007     *
    1008     * These methods are related to the H5File class and to particular instances of objects with this class type.
    1009     **************************************************************************/
    1010
    1011    /**
    1012     * Opens file and returns a file identifier.
    1013     *
    1014     * @see hdf.object.FileFormat#open()
    1015     */
    1016    @Override
    1017    public long open() throws Exception {
    1018        return open(true);
    1019    }
    1020
    1021    /**
    1022     * Opens file and returns a file identifier.
    1023     *
    1024     * @see hdf.object.FileFormat#open(int...)
    1025     */
    1026    @Override
    1027    public long open(int... indexList) throws Exception {
    1028        setIndexType(indexList[0]);
    1029        setIndexOrder(indexList[1]);
    1030        return open(true);
    1031    }
    1032
    1033    /**
    1034     * Sets the bounds of new library versions.
    1035     *
    1036     * @param lowStr
    1037     *            The earliest version of the library.
    1038     * @param highStr
    1039     *            The latest version of the library.
    1040     *
    1041     * @throws Exception
    1042     *             If there is an error at the HDF5 library level.
    1043     */
    1044    @Override
    1045    public void setNewLibBounds(String lowStr, String highStr) throws Exception {
    1046        int low = -1;
    1047        int high = -1;
    1048
    1049        if (lowStr == null)
    1050            low = HDF5Constants.H5F_LIBVER_EARLIEST;
    1051        else if(lowStr.equals("Earliest"))
    1052            low = HDF5Constants.H5F_LIBVER_EARLIEST;
    1053        else if(lowStr.equals("V18"))
    1054            low = HDF5Constants.H5F_LIBVER_V18;
    1055        else if(lowStr.equals("V110"))
    1056            low = HDF5Constants.H5F_LIBVER_V110;
    1057        else if(lowStr.equals("V112"))
    1058            low = HDF5Constants.H5F_LIBVER_V112;
    1059        else if(lowStr.equals("V114"))
    1060            low = HDF5Constants.H5F_LIBVER_V114;
    1061        else if(lowStr.equals("Latest"))
    1062            low = HDF5Constants.H5F_LIBVER_LATEST;
    1063        else
    1064            low = HDF5Constants.H5F_LIBVER_EARLIEST;
    1065
    1066        if (highStr == null)
    1067            high = HDF5Constants.H5F_LIBVER_LATEST;
    1068        else if(highStr.equals("V18"))
    1069            high = HDF5Constants.H5F_LIBVER_V18;
    1070        else if(highStr.equals("V110"))
    1071            high = HDF5Constants.H5F_LIBVER_V110;
    1072        else if(highStr.equals("V112"))
    1073            high = HDF5Constants.H5F_LIBVER_V112;
    1074        else if(highStr.equals("V114"))
    1075            high = HDF5Constants.H5F_LIBVER_V114;
    1076        else if(highStr.equals("Latest"))
    1077            high = HDF5Constants.H5F_LIBVER_LATEST;
    1078        else
    1079            high = HDF5Constants.H5F_LIBVER_LATEST;
    1080        libver[0] = low;
    1081        libver[1] = high;
    1082    }
    1083
    1084    /**
    1085     * Sets the bounds of library versions.
    1086     *
    1087     * @param lowStr
    1088     *            The earliest version of the library.
    1089     * @param highStr
    1090     *            The latest version of the library.
    1091     *
    1092     * @throws Exception
    1093     *             If there is an error at the HDF5 library level.
    1094     */
    1095    @Override
    1096    public void setLibBounds(String lowStr, String highStr) throws Exception {
    1097        long fapl = HDF5Constants.H5P_DEFAULT;
    1098
    1099        if (fid < 0)
    1100            return;
    1101
    1102        fapl = H5.H5Fget_access_plist(fid);
    1103
    1104        try {
    1105            int low = -1;
    1106            int high = -1;
    1107
    1108            if (lowStr == null)
    1109                low = HDF5Constants.H5F_LIBVER_EARLIEST;
    1110            else if(lowStr.equals("Earliest"))
    1111                low = HDF5Constants.H5F_LIBVER_EARLIEST;
    1112            else if(lowStr.equals("V18"))
    1113                low = HDF5Constants.H5F_LIBVER_V18;
    1114            else if(lowStr.equals("V110"))
    1115                low = HDF5Constants.H5F_LIBVER_V110;
    1116            else if(lowStr.equals("V112"))
    1117                low = HDF5Constants.H5F_LIBVER_V112;
    1118            else if(lowStr.equals("V114"))
    1119                low = HDF5Constants.H5F_LIBVER_V114;
    1120            else if(lowStr.equals("Latest"))
    1121                low = HDF5Constants.H5F_LIBVER_LATEST;
    1122            else
    1123                low = HDF5Constants.H5F_LIBVER_EARLIEST;
    1124
    1125            if (highStr == null)
    1126                high = HDF5Constants.H5F_LIBVER_LATEST;
    1127            else if(highStr.equals("V18"))
    1128                high = HDF5Constants.H5F_LIBVER_V18;
    1129            else if(highStr.equals("V110"))
    1130                high = HDF5Constants.H5F_LIBVER_V110;
    1131            else if(highStr.equals("V112"))
    1132                high = HDF5Constants.H5F_LIBVER_V112;
    1133            else if(highStr.equals("V114"))
    1134                high = HDF5Constants.H5F_LIBVER_V114;
    1135            else if(highStr.equals("Latest"))
    1136                high = HDF5Constants.H5F_LIBVER_LATEST;
    1137            else
    1138                high = HDF5Constants.H5F_LIBVER_LATEST;
    1139
    1140            H5.H5Pset_libver_bounds(fapl, low, high);
    1141            H5.H5Pget_libver_bounds(fapl, libver);
    1142        }
    1143        finally {
    1144            try {
    1145                H5.H5Pclose(fapl);
    1146            }
    1147            catch (Exception e) {
    1148                log.debug("setLibBounds(): libver bounds H5Pclose(fapl {}) failure: ", fapl, e);
    1149            }
    1150        }
    1151    }
    1152
    1153    /**
    1154     * Gets the bounds of library versions.
    1155     *
    1156     * @return libver The earliest and latest version of the library.
    1157     *
    1158     * @throws Exception
    1159     *             If there is an error at the HDF5 library level.
    1160     */
    1161    @Override
    1162    public int[] getLibBounds() throws Exception {
    1163        if (libver.length == 0)
    1164            initLibBounds();
    1165        return libver;
    1166    }
    1167
    1168    /**
    1169     * Initialize the bounds of library versions
    1170     *
    1171     * @throws Exception
    1172     *             The exceptions thrown vary depending on the implementing class.
    1173     */
    1174    @Override
    1175    public void initLibBounds() throws Exception {
    1176        if (fid >= 0) {
    1177            /* Get the file's file access property list */
    1178            long fapl = H5.H5Fget_access_plist(fid);
    1179            /* Get library format */
    1180            H5.H5Pget_libver_bounds(fapl, libver);
    1181            /* Close FAPL */
    1182            H5.H5Pclose(fapl);
    1183        }
    1184    }
    1185
    1186
    1187    /**
    1188     * Gets the bounds of library versions as text.
    1189     *
    1190     * @return libversion The earliest and latest version of the library.
    1191     */
    1192    @Override
    1193    public String getLibBoundsDescription() {
    1194        String libversion = "";
    1195
    1196        if (libver[0] == HDF5Constants.H5F_LIBVER_EARLIEST)
    1197            libversion = "Earliest and ";
    1198        else if (libver[0] == HDF5Constants.H5F_LIBVER_V18)
    1199            libversion = "V18 and ";
    1200        else if (libver[0] == HDF5Constants.H5F_LIBVER_V110)
    1201            libversion = "V110 and ";
    1202        else if (libver[0] == HDF5Constants.H5F_LIBVER_V112)
    1203            libversion = "V112 and ";
    1204        else if (libver[0] == HDF5Constants.H5F_LIBVER_V114)
    1205            libversion = "V114 and ";
    1206        else if (libver[0] == HDF5Constants.H5F_LIBVER_LATEST)
    1207            libversion = "Latest and ";
    1208
    1209        if (libver[1] == HDF5Constants.H5F_LIBVER_EARLIEST)
    1210            libversion += "Earliest";
    1211        else if (libver[1] == HDF5Constants.H5F_LIBVER_V18)
    1212            libversion += "V18";
    1213        else if (libver[1] == HDF5Constants.H5F_LIBVER_V110)
    1214            libversion += "V110";
    1215        else if (libver[1] == HDF5Constants.H5F_LIBVER_V112)
    1216            libversion += "V112";
    1217        else if (libver[1] == HDF5Constants.H5F_LIBVER_V114)
    1218            libversion += "V114";
    1219        else if (libver[1] == HDF5Constants.H5F_LIBVER_LATEST)
    1220            libversion += "Latest";
    1221        return libversion;
    1222    }
    1223
    1224    /**
    1225     * Closes file associated with this H5File instance.
    1226     *
    1227     * @see hdf.object.FileFormat#close()
    1228     *
    1229     * @throws HDF5Exception
    1230     *             If there is an error at the HDF5 library level.
    1231     */
    1232    @Override
    1233    public void close() throws HDF5Exception {
    1234        if (fid < 0) {
    1235            log.debug("close(): file {} is not open", fullFileName);
    1236            return;
    1237        }
    1238        // The current working directory may be changed at Dataset.read()
    1239        // by System.setProperty("user.dir", newdir) to make it work for external
    1240        // datasets. We need to set it back to the original current working
    1241        // directory (when hdf-java application started) before the file
    1242        // is closed/opened. Otherwise, relative path, e.g. "./test.h5" may
    1243        // not work
    1244        String rootPath = System.getProperty("hdfview.workdir");
    1245        if (rootPath == null) {
    1246            rootPath = System.getProperty("user.dir");
    1247        }
    1248        System.setProperty("user.dir", rootPath);//H5.H5Dchdir_ext(rootPath);
    1249
    1250        // clean up unused objects
    1251        if (rootObject != null) {
    1252            HObject theObj = null;
    1253            Iterator<HObject> it = getMembersBreadthFirst(rootObject).iterator();
    1254            while (it.hasNext()) {
    1255                theObj = it.next();
    1256
    1257                if (theObj instanceof Dataset) {
    1258                    log.trace("close(): clear Dataset {}", ((Dataset) theObj).toString());
    1259                    ((Dataset) theObj).clear();
    1260                }
    1261                else if (theObj instanceof Group) {
    1262                    log.trace("close(): clear Group {}", ((Group) theObj).toString());
    1263                    ((Group) theObj).clear();
    1264                }
    1265            }
    1266        }
    1267
    1268        // Close all open objects associated with this file.
    1269        try {
    1270            int type = -1;
    1271            long[] objids;
    1272            long n = H5.H5Fget_obj_count(fid, HDF5Constants.H5F_OBJ_ALL);
    1273            log.trace("close(): open objects={}", n);
    1274
    1275            if (n > 0) {
    1276                if (n < Integer.MIN_VALUE || n > Integer.MAX_VALUE) throw new Exception("Invalid int size");
    1277
    1278                objids = new long[(int)n];
    1279                H5.H5Fget_obj_ids(fid, HDF5Constants.H5F_OBJ_ALL, n, objids);
    1280
    1281                for (int i = 0; i < (int)n; i++) {
    1282                    log.trace("close(): object[{}] id={}", i, objids[i]);
    1283                    type = H5.H5Iget_type(objids[i]);
    1284
    1285                    if (HDF5Constants.H5I_DATASET == type) {
    1286                        try {
    1287                            H5.H5Dclose(objids[i]);
    1288                        }
    1289                        catch (Exception ex2) {
    1290                            log.debug("close(): Object[{}] H5Dclose(objids[{}] {}) failure: ", i, i, objids[i], ex2);
    1291                        }
    1292                    }
    1293                    else if (HDF5Constants.H5I_GROUP == type) {
    1294                        try {
    1295                            H5.H5Gclose(objids[i]);
    1296                        }
    1297                        catch (Exception ex2) {
    1298                            log.debug("close(): Object[{}] H5Gclose(objids[{}] {}) failure: ", i, i, objids[i], ex2);
    1299                        }
    1300                    }
    1301                    else if (HDF5Constants.H5I_DATATYPE == type) {
    1302                        try {
    1303                            H5.H5Tclose(objids[i]);
    1304                        }
    1305                        catch (Exception ex2) {
    1306                            log.debug("close(): Object[{}] H5Tclose(objids[{}] {}) failure: ", i, i, objids[i], ex2);
    1307                        }
    1308                    }
    1309                    else if (HDF5Constants.H5I_ATTR == type) {
    1310                        try {
    1311                            H5.H5Aclose(objids[i]);
    1312                        }
    1313                        catch (Exception ex2) {
    1314                            log.debug("close(): Object[{}] H5Aclose(objids[{}] {}) failure: ", i, i, objids[i], ex2);
    1315                        }
    1316                    }
    1317                    else if (HDF5Constants.H5I_FILE == type) {
    1318                        int file_ref = H5.H5Iget_ref(objids[i]);
    1319                        log.debug("close(): Object[{}] objids[{}] is type File with ref count of {}", i, i, file_ref);
    1320                    }
    1321                    else {
    1322                        log.debug("close(): Object[{}] objids[{}] is type {}", i, i, type);
    1323                    }
    1324                } // (int i=0; i<n; i++)
    1325            } // ( n>0)
    1326        }
    1327        catch (Exception ex) {
    1328            log.debug("close(): failure: ", ex);
    1329        }
    1330
    1331        try {
    1332            H5.H5Fflush(fid, HDF5Constants.H5F_SCOPE_GLOBAL);
    1333        }
    1334        catch (Exception ex) {
    1335            log.debug("close(): H5Fflush(fid {}) failure: ", fid, ex);
    1336        }
    1337
    1338        try {
    1339            H5.H5Fclose(fid);
    1340        }
    1341        catch (Exception ex) {
    1342            log.debug("close(): H5Fclose(fid {}) failure: ", fid, ex);
    1343        }
    1344
    1345        // Set fid to -1 but don't reset rootObject
    1346        fid = -1;
    1347    }
    1348
    1349    /**
    1350     * Returns the root object of the open HDF5 File.
    1351     *
    1352     * @see hdf.object.FileFormat#getRootObject()
    1353     */
    1354    @Override
    1355    public HObject getRootObject() {
    1356        return rootObject;
    1357    }
    1358
    1359    /*
    1360     * (non-Javadoc)
    1361     *
    1362     * @see hdf.object.FileFormat#get(java.lang.String)
    1363     */
    1364    @Override
    1365    public HObject get(String path) throws Exception {
    1366        log.trace("get({}): start", path);
    1367        HObject obj = null;
    1368
    1369        if ((path == null) || (path.length() <= 0)) {
    1370            log.debug("get(): path is null or invalid path length");
    1371            System.err.println("(path == null) || (path.length() <= 0)");
    1372            return null;
    1373        }
    1374
    1375        // replace the wrong slash and get rid of "//"
    1376        path = path.replace('\\', '/');
    1377        path = "/" + path;
    1378        path = path.replaceAll("//", "/");
    1379
    1380        // the whole file tree is loaded. find the object in the tree
    1381        if (rootObject != null) {
    1382            obj = findObject(this, path);
    1383        }
    1384
    1385        // found object in memory
    1386        if (obj != null) {
    1387            log.trace("get(): Found object in memory");
    1388            return obj;
    1389        }
    1390
    1391        // open only the requested object
    1392        String name = null;
    1393        String pPath = null;
    1394        if (path.equals("/")) {
    1395            name = "/"; // the root
    1396        }
    1397        else {
    1398            // separate the parent path and the object name
    1399            if (path.endsWith("/")) {
    1400                path = path.substring(0, path.length() - 1);
    1401            }
    1402
    1403            int idx = path.lastIndexOf('/');
    1404            name = path.substring(idx + 1);
    1405            if (idx == 0) {
    1406                pPath = "/";
    1407            }
    1408            else {
    1409                pPath = path.substring(0, idx);
    1410            }
    1411        }
    1412
    1413        // do not open the full tree structure, only the file handler
    1414        long fid_before_open = fid;
    1415        fid = open(false);
    1416        if (fid < 0) {
    1417            log.debug("get(): Invalid FID");
    1418            System.err.println("Could not open file handler");
    1419            return null;
    1420        }
    1421
    1422        try {
    1423            H5O_info_t info;
    1424            int objType;
    1425            long objid = H5.H5Oopen(fid, path, HDF5Constants.H5P_DEFAULT);
    1426
    1427            if (objid >= 0) {
    1428                info = H5.H5Oget_info(objid);
    1429                objType = info.type;
    1430                if (objType == HDF5Constants.H5O_TYPE_DATASET) {
    1431                    long did = -1;
    1432                    try {
    1433                        did = H5.H5Dopen(fid, path, HDF5Constants.H5P_DEFAULT);
    1434                        obj = getDataset(did, name, pPath);
    1435                    }
    1436                    finally {
    1437                        try {
    1438                            H5.H5Dclose(did);
    1439                        }
    1440                        catch (Exception ex) {
    1441                            log.debug("get(): {} H5Dclose(did {}) failure: ", path, did, ex);
    1442                        }
    1443                    }
    1444                }
    1445                else if (objType == HDF5Constants.H5O_TYPE_GROUP) {
    1446                    long gid = -1;
    1447                    try {
    1448                        gid = H5.H5Gopen(fid, path, HDF5Constants.H5P_DEFAULT);
    1449                        H5Group pGroup = null;
    1450                        if (pPath != null) {
    1451                            pGroup = new H5Group(this, null, pPath, null);
    1452                            obj = getGroup(gid, name, pGroup);
    1453                            pGroup.addToMemberList(obj);
    1454                        }
    1455                        else {
    1456                            obj = getGroup(gid, name, pGroup);
    1457                        }
    1458                    }
    1459                    finally {
    1460                        try {
    1461                            H5.H5Gclose(gid);
    1462                        }
    1463                        catch (Exception ex) {
    1464                            log.debug("get(): {} H5Gclose(gid {}) failure: ", path, gid, ex);
    1465                        }
    1466                    }
    1467                }
    1468                else if (objType == HDF5Constants.H5O_TYPE_NAMED_DATATYPE) {
    1469                    obj = new H5Datatype(this, name, pPath);
    1470                }
    1471            }
    1472            try {
    1473                H5.H5Oclose(objid);
    1474            }
    1475            catch (Exception ex) {
    1476                log.debug("get(): H5Oclose(objid {}) failure: ", objid, ex);
    1477                ex.printStackTrace();
    1478            }
    1479        }
    1480        catch (Exception ex) {
    1481            log.debug("get(): Exception finding obj {}", path, ex);
    1482            obj = null;
    1483        }
    1484        finally {
    1485            if ((fid_before_open <= 0) && (obj == null)) {
    1486                // close the fid that is not attached to any object
    1487                try {
    1488                    H5.H5Fclose(fid);
    1489                }
    1490                catch (Exception ex) {
    1491                    log.debug("get(): {} H5Fclose(fid {}) failure: ", path, fid, ex);
    1492                }
    1493                fid = fid_before_open;
    1494            }
    1495        }
    1496
    1497        return obj;
    1498    }
    1499
    1500
    1501    /**
    1502     * Creates a named datatype in a file.
    1503     *
    1504     * The following code creates a named datatype in a file.
    1505     *
    1506     * <pre>
    1507     * H5File file = (H5File) h5file.createInstance(&quot;test_hdf5.h5&quot;, FileFormat.WRITE);
    1508     * Datatype dtype = file.createDatatype(
    1509     *                             Datatype.CLASS_INTEGER,
    1510     *                             4,
    1511     *                             Datatype.NATIVE,
    1512     *                             Datatype.NATIVE,
    1513     *                             basetype);
    1514     * H5Datatype h5dtype = file.createNamedDatatype(
    1515     *                             dtype,
    1516     *                             null,
    1517     *                             &quot;Native Integer&quot;);
    1518     * </pre>
    1519     *
    1520     * @param tnative
    1521     *            native datatype previously created
    1522     * @param name
    1523     *            name of the datatype to create, e.g. "Native Integer".
    1524     * @return The new datatype if successful; otherwise returns null.
    1525     * @throws Exception
    1526     *             The exceptions thrown vary depending on the implementing class.
    1527     */
    1528    @Override
    1529    public Datatype createNamedDatatype(Datatype tnative, String name) throws Exception {
    1530        log.trace("createNamedDatatype(): start: name={}", name);
    1531
    1532        H5Datatype dtype = null;
    1533
    1534        if (name != null ) {
    1535            long tid = -1;
    1536            log.trace("createNamedDatatype(): name={}", name);
    1537            try {
    1538                tnative.setFullname(name, null);
    1539            }
    1540            catch (Exception ex) {
    1541                log.debug("createNamedDatatype():setName(): {} failure: {}", name, ex.getMessage());
    1542            }
    1543            try {
    1544                if ((tid = tnative.createNative()) < 0) {
    1545                    log.debug("createNamedDatatype(): createNative() failure");
    1546                    throw new Exception("createNative() failed");
    1547                }
    1548                log.trace("createNamedDatatype(): createNative gets id={}", tid);
    1549
    1550                H5.H5Tcommit(fid, name, tid, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    1551
    1552                int nativeClass = H5.H5Tget_class(tid);
    1553                if (nativeClass == HDF5Constants.H5T_REFERENCE)
    1554                    dtype = new H5ReferenceType(this, name, null);
    1555                else
    1556                    dtype = new H5Datatype(this, name, null);
    1557            }
    1558            finally {
    1559                H5.H5Tclose(tid);
    1560            }
    1561        }
    1562        else {
    1563            dtype = (H5Datatype) tnative;
    1564        }
    1565
    1566        return dtype;
    1567    }
    1568
    1569    /***************************************************************************
    1570     * Methods related to Datatypes and HObjects in HDF5 Files. Strictly speaking, these methods aren't related to
    1571     * H5File and the actions could be carried out through the H5Group, H5Datatype and H5*DS classes. But, in some cases
    1572     * they allow a null input and expect the generated object to be of HDF5 type. So, we put them in the H5File class
    1573     * so that we create the proper type of HObject... H5Group for example.
    1574     *
    1575     * Here again, if there could be Implementation Class methods we'd use those. But, since we can't override class
    1576     * methods (they can only be shadowed in Java), these are instance methods.
    1577     *
    1578     **************************************************************************/
    1579
    1580    /*
    1581     * (non-Javadoc)
    1582     *
    1583     * @see hdf.object.FileFormat#createDatatype(int, int, int, int)
    1584     */
    1585    @Override
    1586    public Datatype createDatatype(int tclass, int tsize, int torder, int tsign) throws Exception {
    1587        return new H5Datatype(tclass, tsize, torder, tsign);
    1588    }
    1589
    1590    /*
    1591     * (non-Javadoc)
    1592     *
    1593     * @see hdf.object.FileFormat#createDatatype(int, int, int, int, Datatype)
    1594     */
    1595    @Override
    1596    public Datatype createDatatype(int tclass, int tsize, int torder, int tsign, Datatype tbase) throws Exception {
    1597        return new H5Datatype(tclass, tsize, torder, tsign, tbase);
    1598    }
    1599
    1600    /*
    1601     * (non-Javadoc)
    1602     *
    1603     * @see hdf.object.FileFormat#createScalarDS(java.lang.String, hdf.object.Group, hdf.object.Datatype,
    1604     * long[], long[], long[], int, java.lang.Object)
    1605     */
    1606    @Override
    1607    public Dataset createScalarDS(String name, Group pgroup, Datatype type,
    1608            long[] dims, long[] maxdims, long[] chunks,
    1609            int gzip, Object fillValue, Object data) throws Exception {
    1610        log.trace("createScalarDS(): name={}", name);
    1611        // create new dataset at the root group by default
    1612        if (pgroup == null)
    1613            pgroup = (Group) get("/");
    1614
    1615        return H5ScalarDS.create(name, pgroup, type, dims, maxdims, chunks, gzip, fillValue, data);
    1616    }
    1617
    1618    /*
    1619     * (non-Javadoc)
    1620     *
    1621     * @see hdf.object.FileFormat#createCompoundDS(java.lang.String, hdf.object.Group, long[], long[], long[],
    1622     * int, java.lang.String[], hdf.object.Datatype[], int[], java.lang.Object)
    1623     */
    1624    @Override
    1625    public Dataset createCompoundDS(String name, Group pgroup,
    1626            long[] dims, long[] maxdims, long[] chunks,  int gzip,
    1627            String[] memberNames, Datatype[] memberDatatypes, int[] memberSizes, Object data) throws Exception {
    1628        log.trace("createCompoundDS(): start: name={}", name);
    1629        int nMembers = memberNames.length;
    1630        int memberRanks[] = new int[nMembers];
    1631        long memberDims[][] = new long[nMembers][1];
    1632        Dataset ds = null;
    1633
    1634        for (int i = 0; i < nMembers; i++) {
    1635            memberRanks[i] = 1;
    1636            if (memberSizes == null)
    1637                memberDims[i][0] = 1;
    1638            else
    1639                memberDims[i][0] = memberSizes[i];
    1640        }
    1641
    1642        // create new dataset at the root group by default
    1643        if (pgroup == null)
    1644            pgroup = (Group) get("/");
    1645        ds = H5CompoundDS.create(name, pgroup, dims, maxdims, chunks, gzip,
    1646                memberNames, memberDatatypes, memberRanks, memberDims, data);
    1647
    1648        return ds;
    1649    }
    1650
    1651    /*
    1652     * (non-Javadoc)
    1653     *
    1654     * @see hdf.object.FileFormat#createImage(java.lang.String, hdf.object.Group, hdf.object.Datatype,
    1655     * long[], long[], long[], int, int, int, java.lang.Object)
    1656     */
    1657    @Override
    1658    public Dataset createImage(String name, Group pgroup, Datatype type,
    1659            long[] dims, long[] maxdims, long[] chunks,
    1660            int gzip, int ncomp, int interlace, Object data) throws Exception {
    1661        log.trace("createImage(): start: name={}", name);
    1662        // create at the root group by default
    1663        if (pgroup == null)
    1664            pgroup = (Group) get("/");
    1665
    1666        H5ScalarDS dataset = (H5ScalarDS)H5ScalarDS.create(name, pgroup, type, dims, maxdims, chunks, gzip, data);
    1667
    1668        try {
    1669            H5File.createImageAttributes(dataset, interlace);
    1670            dataset.setIsImage(true);
    1671        }
    1672        catch (Exception ex) {
    1673            log.debug("createImage(): {} createImageAttributtes failure: ", name, ex);
    1674        }
    1675
    1676        return dataset;
    1677    }
    1678
    1679    /***
    1680     * Creates a new group with specified name in existing group.
    1681     *
    1682     * @see hdf.object.FileFormat#createGroup(java.lang.String, hdf.object.Group)
    1683     */
    1684    @Override
    1685    public Group createGroup(String name, Group pgroup) throws Exception {
    1686        return this.createGroup(name, pgroup, HDF5Constants.H5P_DEFAULT);
    1687    }
    1688
    1689    /***
    1690     * Creates a new group with specified name in existing group and with the group creation properties list, gplist.
    1691     *
    1692     * @see hdf.object.h5.H5Group#create(java.lang.String, hdf.object.Group, long...)
    1693     *
    1694     */
    1695    @Override
    1696    public Group createGroup(String name, Group pgroup, long... gplist) throws Exception {
    1697        // create new group at the root
    1698        if (pgroup == null)
    1699            pgroup = (Group) this.get("/");
    1700
    1701        return H5Group.create(name, pgroup, gplist);
    1702    }
    1703
    1704    /***
    1705     * Creates the group creation property list identifier, gcpl. This identifier is used when creating Groups.
    1706     *
    1707     * @see hdf.object.FileFormat#createGcpl(int, int, int)
    1708     *
    1709     */
    1710    @Override
    1711    public long createGcpl(int creationorder, int maxcompact, int mindense) throws Exception {
    1712        long gcpl = -1;
    1713        try {
    1714            gcpl = H5.H5Pcreate(HDF5Constants.H5P_GROUP_CREATE);
    1715            if (gcpl >= 0) {
    1716                // Set link creation order.
    1717                if (creationorder == Group.CRT_ORDER_TRACKED) {
    1718                    log.trace("createGcpl(): creation order ORDER_TRACKED");
    1719                    H5.H5Pset_link_creation_order(gcpl, HDF5Constants.H5P_CRT_ORDER_TRACKED);
    1720                }
    1721                else if (creationorder == Group.CRT_ORDER_INDEXED) {
    1722                    log.trace("createGcpl(): creation order ORDER_INDEXED");
    1723                    H5.H5Pset_link_creation_order(gcpl, HDF5Constants.H5P_CRT_ORDER_TRACKED + HDF5Constants.H5P_CRT_ORDER_INDEXED);
    1724                }
    1725                // Set link storage.
    1726                H5.H5Pset_link_phase_change(gcpl, maxcompact, mindense);
    1727            }
    1728        }
    1729        catch (Exception ex) {
    1730            log.debug("createGcpl(): failure: ", ex);
    1731            ex.printStackTrace();
    1732        }
    1733
    1734        return gcpl;
    1735    }
    1736
    1737    /*
    1738     * (non-Javadoc)
    1739     *
    1740     * @see hdf.object.FileFormat#createLink(hdf.object.Group, java.lang.String, hdf.object.HObject)
    1741     */
    1742    @Override
    1743    public HObject createLink(Group parentGroup, String name, Object currentObj) throws Exception {
    1744        if (currentObj instanceof HObject)
    1745            return this.createLink(parentGroup, name, (HObject) currentObj, Group.LINK_TYPE_HARD);
    1746        else if (currentObj instanceof String)
    1747            return this.createLink(parentGroup, name, (String) currentObj, Group.LINK_TYPE_HARD);
    1748
    1749        return null;
    1750    }
    1751
    1752    /**
    1753     * Creates a link to an object in the open file.
    1754     *
    1755     * If parentGroup is null, the new link is created in the root group.
    1756     *
    1757     * @param parentGroup
    1758     *            The group where the link is created.
    1759     * @param name
    1760     *            The name of the link.
    1761     * @param currentObj
    1762     *            The existing object the new link will reference.
    1763     * @param lType
    1764     *            The type of link to be created. It can be a hard link, a soft link or an external link.
    1765     *
    1766     * @return The object pointed to by the new link if successful; otherwise returns null.
    1767     *
    1768     * @throws Exception
    1769     *             The exceptions thrown vary depending on the implementing class.
    1770     */
    1771    @Override
    1772    public HObject createLink(Group parentGroup, String name, HObject currentObj, int lType) throws Exception {
    1773        log.trace("createLink(): start: name={}", name);
    1774        HObject obj = null;
    1775        int type = 0;
    1776        String current_full_name = null;
    1777        String new_full_name = null;
    1778        String parent_path = null;
    1779
    1780        if (currentObj == null) {
    1781            log.debug("createLink(): Link target is null");
    1782            throw new HDF5Exception("The object pointed to by the link cannot be null.");
    1783        }
    1784        if ((parentGroup == null) || parentGroup.isRoot())
    1785            parent_path = HObject.SEPARATOR;
    1786        else
    1787            parent_path = parentGroup.getPath() + HObject.SEPARATOR + parentGroup.getName() + HObject.SEPARATOR;
    1788
    1789        new_full_name = parent_path + name;
    1790
    1791        if (lType == Group.LINK_TYPE_HARD) {
    1792            type = HDF5Constants.H5L_TYPE_HARD;
    1793            log.trace("createLink(): type H5L_TYPE_HARD");
    1794        }
    1795        else if (lType == Group.LINK_TYPE_SOFT) {
    1796            type = HDF5Constants.H5L_TYPE_SOFT;
    1797            log.trace("createLink(): type H5L_TYPE_SOFT");
    1798        }
    1799        else if (lType == Group.LINK_TYPE_EXTERNAL) {
    1800            type = HDF5Constants.H5L_TYPE_EXTERNAL;
    1801            log.trace("createLink(): type H5L_TYPE_EXTERNAL");
    1802        }
    1803
    1804        if (H5.H5Lexists(fid, new_full_name, HDF5Constants.H5P_DEFAULT)) {
    1805            H5.H5Ldelete(fid, new_full_name, HDF5Constants.H5P_DEFAULT);
    1806        }
    1807
    1808        if (type == HDF5Constants.H5L_TYPE_HARD) {
    1809            if ((currentObj instanceof Group) && ((Group) currentObj).isRoot()) {
    1810                log.debug("createLink(): cannot create link to root group");
    1811                throw new HDF5Exception("Cannot make a link to the root group.");
    1812            }
    1813            current_full_name = currentObj.getPath() + HObject.SEPARATOR + currentObj.getName();
    1814
    1815            H5.H5Lcreate_hard(fid, current_full_name, fid, new_full_name, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    1816        }
    1817
    1818        else if (type == HDF5Constants.H5L_TYPE_SOFT) {
    1819            log.trace("createLink(): H5Lcreate_soft: {} in {} as {}", currentObj.getFullName(), fid, new_full_name);
    1820            H5.H5Lcreate_soft(currentObj.getFullName(), fid, new_full_name, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    1821        }
    1822
    1823        else if (type == HDF5Constants.H5L_TYPE_EXTERNAL) {
    1824            log.trace("createLink(): H5Lcreate_external: File={} {} in {} as {}", currentObj.getFile(), currentObj.getFullName(), fid, new_full_name);
    1825            H5.H5Lcreate_external(currentObj.getFile(), currentObj.getFullName(), fid, new_full_name, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    1826        }
    1827
    1828        if (currentObj instanceof Group) {
    1829            log.trace("createLink(): Link target is type H5Group");
    1830            obj = new H5Group(this, name, parent_path, parentGroup);
    1831        }
    1832        else if (currentObj instanceof H5ReferenceType) {
    1833            log.trace("createLink(): Link target is type H5Datatype");
    1834            obj = new H5ReferenceType(this, name, parent_path);
    1835        }
    1836        else if (currentObj instanceof H5Datatype) {
    1837            log.trace("createLink(): Link target is type H5Datatype");
    1838            obj = new H5Datatype(this, name, parent_path);
    1839        }
    1840        else if (currentObj instanceof H5CompoundDS) {
    1841            log.trace("createLink(): Link target is type H5CompoundDS");
    1842            obj = new H5CompoundDS(this, name, parent_path);
    1843        }
    1844        else if (currentObj instanceof H5ScalarDS) {
    1845            log.trace("createLink(): Link target is type H5ScalarDS");
    1846            obj = new H5ScalarDS(this, name, parent_path);
    1847        }
    1848        else
    1849            log.trace("createLink(): Link target is type unknown");
    1850
    1851        return obj;
    1852    }
    1853
    1854    /**
    1855     * Creates a soft or external link to object in a file that does not exist at the time the link is created.
    1856     *
    1857     * @param parentGroup
    1858     *            The group where the link is created.
    1859     * @param name
    1860     *            The name of the link.
    1861     * @param currentObj
    1862     *            The name of the object the new link will reference. The object doesn't have to exist.
    1863     * @param lType
    1864     *            The type of link to be created.
    1865     *
    1866     * @return The H5Link object pointed to by the new link if successful; otherwise returns null.
    1867     *
    1868     * @throws Exception
    1869     *             The exceptions thrown vary depending on the implementing class.
    1870     */
    1871    @Override
    1872    public HObject createLink(Group parentGroup, String name, String currentObj, int lType) throws Exception {
    1873        log.trace("createLink(): start: name={}", name);
    1874        HObject obj = null;
    1875        int type = 0;
    1876        String new_full_name = null;
    1877        String parent_path = null;
    1878
    1879        if (currentObj == null) {
    1880            log.debug("createLink(): Link target is null");
    1881            throw new HDF5Exception("The object pointed to by the link cannot be null.");
    1882        }
    1883        if ((parentGroup == null) || parentGroup.isRoot())
    1884            parent_path = HObject.SEPARATOR;
    1885        else
    1886            parent_path = parentGroup.getPath() + HObject.SEPARATOR + parentGroup.getName() + HObject.SEPARATOR;
    1887
    1888        new_full_name = parent_path + name;
    1889
    1890        if (lType == Group.LINK_TYPE_HARD) {
    1891            type = HDF5Constants.H5L_TYPE_HARD;
    1892            log.trace("createLink(): type H5L_TYPE_HARD");
    1893        }
    1894        else if (lType == Group.LINK_TYPE_SOFT) {
    1895            type = HDF5Constants.H5L_TYPE_SOFT;
    1896            log.trace("createLink(): type H5L_TYPE_SOFT");
    1897        }
    1898        else if (lType == Group.LINK_TYPE_EXTERNAL) {
    1899            type = HDF5Constants.H5L_TYPE_EXTERNAL;
    1900            log.trace("createLink(): type H5L_TYPE_EXTERNAL");
    1901        }
    1902
    1903        if (H5.H5Lexists(fid, new_full_name, HDF5Constants.H5P_DEFAULT)) {
    1904            H5.H5Ldelete(fid, new_full_name, HDF5Constants.H5P_DEFAULT);
    1905        }
    1906
    1907        if (type == HDF5Constants.H5L_TYPE_SOFT) {
    1908            H5.H5Lcreate_soft(currentObj, fid, new_full_name, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    1909        }
    1910
    1911        else if (type == HDF5Constants.H5L_TYPE_EXTERNAL) {
    1912            String fileName = null;
    1913            String objectName = null;
    1914
    1915            // separate the object name and the file name
    1916            fileName = currentObj.substring(0, currentObj.lastIndexOf(FileFormat.FILE_OBJ_SEP));
    1917            objectName = currentObj.substring(currentObj.indexOf(FileFormat.FILE_OBJ_SEP));
    1918            objectName = objectName.substring(3);
    1919
    1920            H5.H5Lcreate_external(fileName, objectName, fid, new_full_name, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    1921        }
    1922
    1923        if (name.startsWith(HObject.SEPARATOR)) {
    1924            name = name.substring(1);
    1925        }
    1926        obj = new H5Link(this, name, parent_path);
    1927
    1928        return obj;
    1929    }
    1930
    1931    /**
    1932     * reload the sub-tree structure from file.
    1933     *
    1934     * reloadTree(Group g) is useful when the structure of the group in file is changed while the group structure in
    1935     * memory is not changed.
    1936     *
    1937     * @param g
    1938     *            the group where the structure is to be reloaded in memory
    1939     */
    1940    public void reloadTree(Group g) {
    1941        if (fid < 0 || rootObject == null || g == null) {
    1942            log.debug("reloadTree(): Invalid fid or null object");
    1943            return;
    1944        }
    1945
    1946        depth_first(g, Integer.MIN_VALUE);
    1947    }
    1948
    1949    /*
    1950     * (non-Javadoc) NOTE: Object references are copied but not updated by this method.
    1951     *
    1952     * @see hdf.object.FileFormat#copy(hdf.object.HObject, hdf.object.Group, java.lang.String)
    1953     */
    1954    @Override
    1955    public HObject copy(HObject srcObj, Group dstGroup, String dstName) throws Exception {
    1956        log.trace("copy(): start: srcObj={} dstGroup={} dstName={}", srcObj, dstGroup, dstName);
    1957        if ((srcObj == null) || (dstGroup == null)) {
    1958            log.debug("copy(): srcObj or dstGroup is null");
    1959            return null;
    1960        }
    1961
    1962        if (dstName == null)
    1963            dstName = srcObj.getName();
    1964
    1965        List<HObject> members = dstGroup.getMemberList();
    1966        int n = members.size();
    1967        for (int i = 0; i < n; i++) {
    1968            HObject obj = members.get(i);
    1969            String name = obj.getName();
    1970            while (name.equals(dstName))
    1971                dstName += "~copy";
    1972        }
    1973
    1974        HObject newObj = null;
    1975        if (srcObj instanceof Dataset) {
    1976            log.trace("copy(): srcObj instanceof Dataset");
    1977            newObj = copyDataset((Dataset) srcObj, (H5Group) dstGroup, dstName);
    1978        }
    1979        else if (srcObj instanceof H5Group) {
    1980            log.trace("copy(): srcObj instanceof H5Group");
    1981            newObj = copyGroup((H5Group) srcObj, (H5Group) dstGroup, dstName);
    1982        }
    1983        else if (srcObj instanceof H5Datatype) {
    1984            log.trace("copy(): srcObj instanceof H5Datatype");
    1985            newObj = copyDatatype((H5Datatype) srcObj, (H5Group) dstGroup, dstName);
    1986        }
    1987
    1988        return newObj;
    1989    }
    1990
    1991    /*
    1992     * (non-Javadoc)
    1993     *
    1994     * @see hdf.object.FileFormat#delete(hdf.object.HObject)
    1995     */
    1996    @Override
    1997    public void delete(HObject obj) throws Exception {
    1998        if ((obj == null) || (fid < 0)) {
    1999            log.debug("delete(): Invalid FID or object is null");
    2000            return;
    2001        }
    2002
    2003        String name = obj.getPath() + obj.getName();
    2004
    2005        H5.H5Ldelete(fid, name, HDF5Constants.H5P_DEFAULT);
    2006    }
    2007
    2008    /*
    2009     * (non-Javadoc)
    2010     *
    2011     * @see hdf.object.FileFormat#writeAttribute(hdf.object.HObject, hdf.object.Attribute, boolean)
    2012     */
    2013    @Override
    2014    public void writeAttribute(HObject obj, Attribute attr, boolean attrExisted) throws HDF5Exception {
    2015        String obj_name = obj.getFullName();
    2016        String name = attr.getAttributeName();
    2017        long tid = -1;
    2018        long sid = -1;
    2019        long aid = -1;
    2020        log.trace("writeAttribute(): name is {}", name);
    2021
    2022        long objID = obj.open();
    2023        if (objID < 0) {
    2024            log.debug("writeAttribute(): Invalid Object ID");
    2025            return;
    2026        }
    2027
    2028        if ((tid = attr.getAttributeDatatype().createNative()) >= 0) {
    2029            log.trace("writeAttribute(): tid {} from toNative :{}", tid, attr.getAttributeDatatype().getDescription());
    2030            try {
    2031                if (attr.isAttributeNULL())
    2032                    sid = H5.H5Screate(HDF5Constants.H5S_NULL);
    2033                else if (attr.isAttributeScalar())
    2034                    sid = H5.H5Screate(HDF5Constants.H5S_SCALAR);
    2035                else
    2036                    sid = H5.H5Screate_simple(attr.getAttributeRank(), attr.getAttributeDims(), null);
    2037
    2038                if (attrExisted)
    2039                    aid = H5.H5Aopen_by_name(objID, obj_name, name, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    2040                else
    2041                    aid = H5.H5Acreate(objID, name, tid, sid, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    2042                log.trace("writeAttribute(): aid {} opened/created", aid);
    2043
    2044                if (!attr.isAttributeNULL()) {
    2045                    // update value of the attribute
    2046                    Object attrValue;
    2047                    try {
    2048                        attrValue = attr.getAttributeData();
    2049                    }
    2050                    catch (Exception ex) {
    2051                        attrValue = null;
    2052                        log.trace("writeAttribute(): getAttributeData() failure:", ex);
    2053                    }
    2054
    2055                    // log.trace("writeAttribute(): attrValue={}", attrValue);
    2056                    if (attrValue != null) {
    2057                        try {
    2058                            ((H5Attribute) attr).AttributeCommonIO(aid, H5File.IO_TYPE.WRITE, attrValue);
    2059                        }
    2060                        catch (Exception ex) {
    2061                            log.debug("writeAttribute(): failed to write attribute: ", ex);
    2062                        }
    2063                    } // (attrValue != null)
    2064                }
    2065            }
    2066            finally {
    2067                try {
    2068                    H5.H5Tclose(tid);
    2069                }
    2070                catch (Exception ex) {
    2071                    log.debug("writeAttribute(): H5Tclose(tid {}) failure: ", tid, ex);
    2072                }
    2073                try {
    2074                    H5.H5Sclose(sid);
    2075                }
    2076                catch (Exception ex) {
    2077                    log.debug("writeAttribute(): H5Sclose(sid {}) failure: ", sid, ex);
    2078                }
    2079                try {
    2080                    H5.H5Aclose(aid);
    2081                }
    2082                catch (Exception ex) {
    2083                    log.debug("writeAttribute(): H5Aclose(aid {}) failure: ", aid, ex);
    2084                }
    2085            }
    2086        }
    2087        else {
    2088            log.debug("writeAttribute(): toNative failure");
    2089        }
    2090
    2091        obj.close(objID);
    2092    }
    2093
    2094    /***************************************************************************
    2095     * Implementations for methods specific to H5File
    2096     **************************************************************************/
    2097
    2098    /**
    2099     * Opens a file with specific file access property list.
    2100     *
    2101     * This function does the same as "long open()" except the you can also pass an HDF5 file access property to file
    2102     * open. For example,
    2103     *
    2104     * <pre>
    2105     * // All open objects remaining in the file are closed then file is closed
    2106     * long plist = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
    2107     * H5.H5Pset_fclose_degree(plist, HDF5Constants.H5F_CLOSE_STRONG);
    2108     * long fid = open(plist);
    2109     * </pre>
    2110     *
    2111     * @param plist
    2112     *            a file access property list identifier.
    2113     *
    2114     * @return the file identifier if successful; otherwise returns negative value.
    2115     *
    2116     * @throws Exception
    2117     *            If there is a failure.
    2118     */
    2119    public long open(long plist) throws Exception {
    2120        return open(true, plist);
    2121    }
    2122
    2123    /***************************************************************************
    2124     * Private methods.
    2125     **************************************************************************/
    2126
    2127    /**
    2128     * Opens access to this file.
    2129     *
    2130     * @param loadFullHierarchy
    2131     *            if true, load the full hierarchy into memory; otherwise just opens the file identifier.
    2132     *
    2133     * @return the file identifier if successful; otherwise returns negative value.
    2134     *
    2135     * @throws Exception
    2136     *            If there is a failure.
    2137     */
    2138    private long open(boolean loadFullHierarchy) throws Exception {
    2139        long the_fid = -1;
    2140
    2141        long plist = HDF5Constants.H5P_DEFAULT;
    2142
    2143        // BUG: HDF5Constants.H5F_CLOSE_STRONG does not flush cache
    2144        /**
    2145         * try { //All open objects remaining in the file are closed // then file is closed plist =
    2146         * H5.H5Pcreate (HDF5Constants.H5P_FILE_ACCESS); H5.H5Pset_fclose_degree ( plist,
    2147         * HDF5Constants.H5F_CLOSE_STRONG); } catch (Exception ex) {} the_fid = open(loadFullHierarchy,
    2148         * plist); try { H5.H5Pclose(plist); } catch (Exception ex) {}
    2149         */
    2150
    2151        log.trace("open(): loadFull={}", loadFullHierarchy);
    2152        the_fid = open(loadFullHierarchy, plist);
    2153
    2154        return the_fid;
    2155    }
    2156
    2157    /**
    2158     * Opens access to this file.
    2159     *
    2160     * @param loadFullHierarchy
    2161     *            if true, load the full hierarchy into memory; otherwise just opens the file identifier.
    2162     *
    2163     * @return the file identifier if successful; otherwise returns negative value.
    2164     *
    2165     * @throws Exception
    2166     *            If there is a failure.
    2167     */
    2168    private long open(boolean loadFullHierarchy, long plist) throws Exception {
    2169        log.trace("open(loadFullHierarchy = {}, plist = {}): start", loadFullHierarchy, plist);
    2170        if (fid > 0) {
    2171            log.trace("open(): FID already opened");
    2172            return fid; // file is opened already
    2173        }
    2174
    2175        // The cwd may be changed at Dataset.read() by System.setProperty("user.dir", newdir)
    2176        // to make it work for external datasets. We need to set it back
    2177        // before the file is closed/opened.
    2178        String rootPath = System.getProperty("hdfview.workdir");
    2179        if (rootPath == null) {
    2180            rootPath = System.getProperty("user.dir");
    2181        }
    2182        System.setProperty("user.dir", rootPath);
    2183
    2184        log.trace("open(): flag={}", flag);
    2185        // check for valid file access permission
    2186        if (flag < 0) {
    2187            log.debug("open(): Invalid access identifier -- " + flag);
    2188            throw new HDF5Exception("Invalid access identifer -- " + flag);
    2189        }
    2190        else if (HDF5Constants.H5F_ACC_CREAT == flag) {
    2191            // create a new file
    2192            log.trace("open(): create file");
    2193            fid = H5.H5Fcreate(fullFileName, HDF5Constants.H5F_ACC_TRUNC, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    2194            H5.H5Fflush(fid, HDF5Constants.H5F_SCOPE_LOCAL);
    2195            H5.H5Fclose(fid);
    2196            flag = HDF5Constants.H5F_ACC_RDWR;
    2197        }
    2198        else if (!exists()) {
    2199            log.debug("open(): File {} does not exist", fullFileName);
    2200            throw new HDF5Exception("File does not exist -- " + fullFileName);
    2201        }
    2202        else if (((flag == HDF5Constants.H5F_ACC_RDWR) || (flag == HDF5Constants.H5F_ACC_CREAT)) && !canWrite()) {
    2203            log.debug("open(): Cannot write file {}", fullFileName);
    2204            throw new HDF5Exception("Cannot write file, try opening as read-only -- " + fullFileName);
    2205        }
    2206        else if ((flag == HDF5Constants.H5F_ACC_RDONLY) && !canRead()) {
    2207            log.debug("open(): Cannot read file {}", fullFileName);
    2208            throw new HDF5Exception("Cannot read file -- " + fullFileName);
    2209        }
    2210
    2211        try {
    2212            fid = H5.H5Fopen(fullFileName, flag, plist);
    2213        }
    2214        catch (Exception ex) {
    2215            try {
    2216                log.debug("open(): open failed, attempting to open file read-only", ex);
    2217                fid = H5.H5Fopen(fullFileName, HDF5Constants.H5F_ACC_RDONLY, HDF5Constants.H5P_DEFAULT);
    2218                isReadOnly = true;
    2219            }
    2220            catch (Exception ex2) {
    2221                // Attempt to open the file as a split file or family file
    2222                try {
    2223                    File tmpf = new File(fullFileName);
    2224                    String tmpname = tmpf.getName();
    2225                    int idx = tmpname.lastIndexOf('.');
    2226
    2227                    if (tmpname.contains("-m")) {
    2228                        log.debug("open(): open read-only failed, attempting to open split file");
    2229
    2230                        while (idx > 0) {
    2231                            char c = tmpname.charAt(idx - 1);
    2232                            if (c != '-')
    2233                                idx--;
    2234                            else
    2235                                break;
    2236                        }
    2237
    2238                        if (idx > 0) {
    2239                            tmpname = tmpname.substring(0, idx - 1);
    2240                            log.trace("open(): attempting to open split file with name {}", tmpname);
    2241                            long pid = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
    2242                            H5.H5Pset_fapl_split(pid, "-m.h5", HDF5Constants.H5P_DEFAULT, "-r.h5", HDF5Constants.H5P_DEFAULT);
    2243                            fid = H5.H5Fopen(tmpf.getParent() + File.separator + tmpname, flag, pid);
    2244                            H5.H5Pclose(pid);
    2245                        }
    2246                    }
    2247                    else {
    2248                        log.debug("open(): open read-only failed, checking for file family");
    2249                        // try to see if it is a file family, always open a family file
    2250                        // from the first one since other files will not be recognized
    2251                        // as an HDF5 file
    2252                        int cnt = idx;
    2253                        while (idx > 0) {
    2254                            char c = tmpname.charAt(idx - 1);
    2255                            if (Character.isDigit(c))
    2256                                idx--;
    2257                            else
    2258                                break;
    2259                        }
    2260
    2261                        if (idx > 0) {
    2262                            cnt -= idx;
    2263                            tmpname = tmpname.substring(0, idx) + "%0" + cnt + "d" + tmpname.substring(tmpname.lastIndexOf('.'));
    2264                            log.trace("open(): attempting to open file family with name {}", tmpname);
    2265                            long pid = H5.H5Pcreate(HDF5Constants.H5P_FILE_ACCESS);
    2266                            H5.H5Pset_fapl_family(pid, 0, HDF5Constants.H5P_DEFAULT);
    2267                            fid = H5.H5Fopen(tmpf.getParent() + File.separator + tmpname, flag, pid);
    2268                            H5.H5Pclose(pid);
    2269                        }
    2270                    }
    2271                }
    2272                catch (Exception ex3) {
    2273                    log.debug("open(): open failed: ", ex3);
    2274                }
    2275            }
    2276        }
    2277
    2278        initLibBounds();
    2279
    2280        if ((fid >= 0) && loadFullHierarchy) {
    2281            long n = H5.H5Fget_obj_count(fid, HDF5Constants.H5F_OBJ_ALL);
    2282            log.trace("open(): open objects={}", n);
    2283            // load the hierarchy of the file
    2284            loadIntoMemory();
    2285        }
    2286
    2287        log.trace("open(loadFullHierarchy = {}, plist = {}): finish", loadFullHierarchy, plist);
    2288        return fid;
    2289    }
    2290
    2291    /**
    2292     * Loads the file structure into memory.
    2293     */
    2294    private void loadIntoMemory() {
    2295        if (fid < 0) {
    2296            log.debug("loadIntoMemory(): Invalid FID");
    2297            return;
    2298        }
    2299
    2300        /*
    2301         * TODO: Root group's name should be changed to 'this.getName()' and all
    2302         * previous accesses of this field should now use getPath() instead of getName()
    2303         * to get the root group. The root group actually does have a path of "/". The
    2304         * depth_first method will have to be changed to setup other object paths
    2305         * appropriately, as it currently assumes the root path to be null.
    2306         */
    2307        rootObject = new H5Group(this, "/", null, null);
    2308        log.trace("loadIntoMemory(): depth_first on root");
    2309        depth_first(rootObject, 0);
    2310    }
    2311
    2312    /**
    2313     * Retrieves the file structure by depth-first order, recursively. The current implementation retrieves groups and
    2314     * datasets only. It does not include named datatypes and soft links.
    2315     *
    2316     * It also detects and stops loops. A loop is detected if there exists an object with the same object ID by tracing
    2317     * a path back up to the root.
    2318     *
    2319     * @param parentObject
    2320     *            the parent object.
    2321     */
    2322    @SuppressWarnings("deprecation")
    2323    private int depth_first(HObject parentObject, int nTotal) {
    2324        log.trace("depth_first({}): start", parentObject);
    2325
    2326        int nelems;
    2327        String fullPath = null;
    2328        String ppath = null;
    2329        long gid = -1;
    2330
    2331        H5Group pgroup = (H5Group) parentObject;
    2332        ppath = pgroup.getPath();
    2333
    2334        if (ppath == null)
    2335            fullPath = HObject.SEPARATOR;
    2336        else
    2337            fullPath = ppath + pgroup.getName() + HObject.SEPARATOR;
    2338
    2339        nelems = 0;
    2340        try {
    2341            gid = pgroup.open();
    2342            H5G_info_t info = H5.H5Gget_info(gid);
    2343            nelems = (int) info.nlinks;
    2344        }
    2345        catch (HDF5Exception ex) {
    2346            nelems = -1;
    2347            log.debug("depth_first({}): H5Gget_info(gid {}) failure: ", parentObject, gid, ex);
    2348        }
    2349
    2350        if (nelems <= 0) {
    2351            pgroup.close(gid);
    2352            log.debug("depth_first({}): nelems <= 0", parentObject);
    2353            return nTotal;
    2354        }
    2355
    2356        // since each call of H5.H5Gget_objname_by_idx() takes about one second.
    2357        // 1,000,000 calls take 12 days. Instead of calling it in a loop,
    2358        // we use only one call to get all the information, which takes about
    2359        // two seconds
    2360        int[] objTypes = new int[nelems];
    2361        long[] fNos = new long[nelems];
    2362        hdf.hdf5lib.structs.H5O_token_t[] objTokens = new hdf.hdf5lib.structs.H5O_token_t[nelems];
    2363        String[] objNames = new String[nelems];
    2364
    2365        try {
    2366            H5.H5Gget_obj_info_full(fid, fullPath, objNames, objTypes, null, fNos, objTokens, indexType, indexOrder);
    2367        }
    2368        catch (HDF5Exception ex) {
    2369            log.debug("depth_first({}): failure: ", parentObject, ex);
    2370            ex.printStackTrace();
    2371            return nTotal;
    2372        }
    2373
    2374        int nStart = getStartMembers();
    2375        int nMax = getMaxMembers();
    2376
    2377        String obj_name;
    2378        int obj_type;
    2379
    2380        // Iterate through the file to see members of the group
    2381        for (int i = 0; i < nelems; i++) {
    2382            obj_name = objNames[i];
    2383            obj_type = objTypes[i];
    2384            log.trace("depth_first({}): obj_name={}, obj_type={}", parentObject, obj_name, obj_type);
    2385            log.trace("depth_first({}): objTokens[{}]={}", parentObject, i, objTokens[i].data);
    2386            long[] objtok = HDFNativeData.byteToLong(objTokens[i].data);
    2387            log.trace("depth_first({}): objtok[0]={}, objtok[1]={}, fNos[{}]={}", parentObject, objtok[0], objtok[1], i, fNos[i]);
    2388
    2389            if (obj_name == null) {
    2390                log.trace("depth_first({}): continue after null obj_name", parentObject);
    2391                continue;
    2392            }
    2393
    2394            nTotal++;
    2395
    2396            if (nMax > 0) {
    2397                if ((nTotal - nStart) >= nMax)
    2398                    break; // loaded enough objects
    2399            }
    2400
    2401            boolean skipLoad = false;
    2402            if ((nTotal > 0) && (nTotal < nStart))
    2403                skipLoad = true;
    2404
    2405            // create a new objects
    2406            long[] oid = null;
    2407            if (obj_type == HDF5Constants.H5O_TYPE_GROUP) {
    2408                H5Group g = new H5Group(this, obj_name, fullPath, pgroup);
    2409                oid = g.getOID();
    2410
    2411                pgroup.addToMemberList(g);
    2412
    2413                // detect and stop loops
    2414                // a loop is detected if there exists object with the same
    2415                // object ID by tracing path back up to the root.
    2416                boolean hasLoop = false;
    2417                H5Group tmpObj = (H5Group) parentObject;
    2418
    2419                while (tmpObj != null) {
    2420                    if (tmpObj.equalsOID(oid) && (tmpObj.getPath() != null)) {
    2421                        hasLoop = true;
    2422                        break;
    2423                    }
    2424                    else {
    2425                        tmpObj = (H5Group) tmpObj.getParent();
    2426                    }
    2427                }
    2428
    2429                // recursively go through the next group
    2430                // stops if it has loop.
    2431                if (!hasLoop) {
    2432                    nTotal = depth_first(g, nTotal);
    2433                }
    2434            }
    2435            else if (skipLoad) {
    2436                continue;
    2437            }
    2438            else if (obj_type == HDF5Constants.H5O_TYPE_DATASET) {
    2439                long did = -1;
    2440                long tid = -1;
    2441                int tclass = -1;
    2442                try {
    2443                    did = H5.H5Dopen(fid, fullPath + obj_name, HDF5Constants.H5P_DEFAULT);
    2444                    if (did >= 0) {
    2445                        tid = H5.H5Dget_type(did);
    2446
    2447                        tclass = H5.H5Tget_class(tid);
    2448                        if ((tclass == HDF5Constants.H5T_ARRAY) || (tclass == HDF5Constants.H5T_VLEN)) {
    2449                            // for ARRAY, the type is determined by the base type
    2450                            long btid = H5.H5Tget_super(tid);
    2451
    2452                            tclass = H5.H5Tget_class(btid);
    2453
    2454                            try {
    2455                                H5.H5Tclose(btid);
    2456                            }
    2457                            catch (Exception ex) {
    2458                                log.debug("depth_first({})[{}] dataset {} H5Tclose(btid {}) failure: ", parentObject, i, obj_name, btid, ex);
    2459                            }
    2460                        }
    2461                    }
    2462                    else {
    2463                        log.debug("depth_first({})[{}] {} dataset open failure", parentObject, i, obj_name);
    2464                    }
    2465                }
    2466                catch (Exception ex) {
    2467                    log.debug("depth_first({})[{}] {} dataset access failure: ", parentObject, i, obj_name, ex);
    2468                }
    2469                finally {
    2470                    try {
    2471                        H5.H5Tclose(tid);
    2472                    }
    2473                    catch (Exception ex) {
    2474                        log.debug("depth_first({})[{}] daatset {} H5Tclose(tid {}) failure: ", parentObject, i, obj_name, tid, ex);
    2475                    }
    2476                    try {
    2477                        H5.H5Dclose(did);
    2478                    }
    2479                    catch (Exception ex) {
    2480                        log.debug("depth_first({})[{}] dataset {} H5Dclose(did {}) failure: ", parentObject, i, obj_name, did, ex);
    2481                    }
    2482                }
    2483                Dataset d = null;
    2484                if (tclass == HDF5Constants.H5T_COMPOUND) {
    2485                    // create a new compound dataset
    2486                    d = new H5CompoundDS(this, obj_name, fullPath);
    2487                }
    2488                else {
    2489                    // create a new scalar dataset
    2490                    d = new H5ScalarDS(this, obj_name, fullPath);
    2491                }
    2492                oid = d.getOID();
    2493
    2494                pgroup.addToMemberList(d);
    2495            }
    2496            else if (obj_type == HDF5Constants.H5O_TYPE_NAMED_DATATYPE) {
    2497                Datatype t = new H5Datatype(parentObject.getFileFormat(), obj_name, fullPath);
    2498                log.trace("depth_first({}): H5O_TYPE_NAMED_DATATYPE name={}", parentObject, t.getFullName());
    2499                oid = t.getOID();
    2500
    2501                pgroup.addToMemberList(t);
    2502            }
    2503            else if (obj_type == HDF5Constants.H5O_TYPE_UNKNOWN) {
    2504                H5Link link = new H5Link(this, obj_name, fullPath);
    2505                oid = link.getOID();
    2506
    2507                pgroup.addToMemberList(link);
    2508                continue; // do the next one, if the object is not identified.
    2509            }
    2510        } // ( i = 0; i < nelems; i++)
    2511
    2512        pgroup.close(gid);
    2513
    2514        log.debug("depth_first({}): nTotal={}", parentObject, nTotal);
    2515        return nTotal;
    2516    } // private depth_first()
    2517
    2518    /**
    2519     * Returns a list of all the members of this H5File in a
    2520     * breadth-first ordering that are rooted at the specified
    2521     * object.
    2522     */
    2523    private static List<HObject> getMembersBreadthFirst(HObject obj) {
    2524        List<HObject> allMembers = new Vector<>();
    2525        Queue<HObject> queue = new LinkedList<>();
    2526        HObject currentObject = obj;
    2527
    2528        queue.add(currentObject);
    2529
    2530        while(!queue.isEmpty()) {
    2531            currentObject = queue.remove();
    2532            allMembers.add(currentObject);
    2533
    2534            if(currentObject instanceof Group) {
    2535                queue.addAll(((Group) currentObject).getMemberList());
    2536            }
    2537        }
    2538
    2539        return allMembers;
    2540    }
    2541
    2542    private HObject copyDataset(Dataset srcDataset, H5Group pgroup, String dstName) throws Exception {
    2543        Dataset dataset = null;
    2544        long srcdid = -1;
    2545        long dstdid = -1;
    2546        long ocp_plist_id = -1;
    2547        String dname = null;
    2548        String path = null;
    2549
    2550        if (pgroup.isRoot())
    2551            path = HObject.SEPARATOR;
    2552        else
    2553            path = pgroup.getPath() + pgroup.getName() + HObject.SEPARATOR;
    2554
    2555        if ((dstName == null) || dstName.equals(HObject.SEPARATOR) || (dstName.length() < 1))
    2556            dstName = srcDataset.getName();
    2557        dname = path + dstName;
    2558
    2559        if (((H5Datatype)srcDataset.getDatatype()).isStdRef()) {
    2560            log.debug("copyDataset(): isStdRef");
    2561        }
    2562        try {
    2563            srcdid = srcDataset.open();
    2564            dstdid = pgroup.open();
    2565
    2566            try {
    2567                ocp_plist_id = H5.H5Pcreate(HDF5Constants.H5P_OBJECT_COPY);
    2568                H5.H5Pset_copy_object(ocp_plist_id, HDF5Constants.H5O_COPY_EXPAND_REFERENCE_FLAG);
    2569                H5.H5Ocopy(srcdid, ".", dstdid, dstName, ocp_plist_id, HDF5Constants.H5P_DEFAULT);
    2570            }
    2571            catch (Exception ex) {
    2572                log.debug("copyDataset(): {} failure: ", dname, ex);
    2573            }
    2574            finally {
    2575                try {
    2576                    H5.H5Pclose(ocp_plist_id);
    2577                }
    2578                catch (Exception ex) {
    2579                    log.debug("copyDataset(): {} H5Pclose(ocp_plist_id {}) failure: ", dname, ocp_plist_id, ex);
    2580                }
    2581            }
    2582
    2583            if (srcDataset instanceof H5ScalarDS)
    2584                dataset = new H5ScalarDS(pgroup.getFileFormat(), dstName, path);
    2585            else
    2586                dataset = new H5CompoundDS(pgroup.getFileFormat(), dstName, path);
    2587
    2588            pgroup.addToMemberList(dataset);
    2589        }
    2590        finally {
    2591            try {
    2592                srcDataset.close(srcdid);
    2593            }
    2594            catch (Exception ex) {
    2595                log.debug("copyDataset(): {} srcDataset.close(srcdid {}) failure: ", dname, srcdid, ex);
    2596            }
    2597            try {
    2598                pgroup.close(dstdid);
    2599            }
    2600            catch (Exception ex) {
    2601                log.debug("copyDataset(): {} pgroup.close(dstdid {}) failure: ", dname, dstdid, ex);
    2602            }
    2603        }
    2604
    2605        return dataset;
    2606    }
    2607
    2608    /**
    2609     * Constructs a dataset for specified dataset identifier.
    2610     *
    2611     * @param did
    2612     *            the dataset identifier
    2613     * @param name
    2614     *            the name of the dataset
    2615     * @param path
    2616     *            the path of the dataset
    2617     *
    2618     * @return the dataset if successful; otherwise return null.
    2619     *
    2620     * @throws HDF5Exception
    2621     *             If there is an error at the HDF5 library level.
    2622     */
    2623    private Dataset getDataset(long did, String name, String path) throws HDF5Exception {
    2624        Dataset dataset = null;
    2625        if (did >= 0) {
    2626            long tid = -1;
    2627            int tclass = -1;
    2628            try {
    2629                tid = H5.H5Dget_type(did);
    2630                tclass = H5.H5Tget_class(tid);
    2631                if (tclass == HDF5Constants.H5T_ARRAY) {
    2632                    // for ARRAY, the type is determined by the base type
    2633                    long btid = H5.H5Tget_super(tid);
    2634                    tclass = H5.H5Tget_class(btid);
    2635                    try {
    2636                        H5.H5Tclose(btid);
    2637                    }
    2638                    catch (Exception ex) {
    2639                        log.debug("getDataset(): {} H5Tclose(btid {}) failure: ", name, btid, ex);
    2640                    }
    2641                }
    2642            }
    2643            finally {
    2644                try {
    2645                    H5.H5Tclose(tid);
    2646                }
    2647                catch (Exception ex) {
    2648                    log.debug("getDataset(): {} H5Tclose(tid {}) failure: ", name, tid, ex);
    2649                }
    2650            }
    2651
    2652            if (tclass == HDF5Constants.H5T_COMPOUND)
    2653                dataset = new H5CompoundDS(this, name, path);
    2654            else
    2655                dataset = new H5ScalarDS(this, name, path);
    2656        }
    2657        else {
    2658            log.debug("getDataset(): id failure");
    2659        }
    2660
    2661        return dataset;
    2662    }
    2663
    2664    /**
    2665     * Copies a named datatype to another location.
    2666     *
    2667     * @param srcType
    2668     *            the source datatype
    2669     * @param pgroup
    2670     *            the group which the new datatype is copied to
    2671     * @param dstName
    2672     *            the name of the new dataype
    2673     *
    2674     * @throws Exception
    2675     *            If there is a failure.
    2676     */
    2677    private HObject copyDatatype(Datatype srcType, H5Group pgroup, String dstName) throws Exception {
    2678        Datatype datatype = null;
    2679        long tid_src = -1;
    2680        long gid_dst = -1;
    2681        String path = null;
    2682
    2683        if (pgroup.isRoot())
    2684            path = HObject.SEPARATOR;
    2685        else
    2686            path = pgroup.getPath() + pgroup.getName() + HObject.SEPARATOR;
    2687
    2688        if ((dstName == null) || dstName.equals(HObject.SEPARATOR) || (dstName.length() < 1))
    2689            dstName = srcType.getName();
    2690
    2691        try {
    2692            tid_src = srcType.open();
    2693            gid_dst = pgroup.open();
    2694
    2695            try {
    2696                H5.H5Ocopy(tid_src, ".", gid_dst, dstName, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    2697            }
    2698            catch (Exception ex) {
    2699                log.debug("copyDatatype(): {} H5Ocopy(tid_src {}) failure: ", dstName, tid_src, ex);
    2700            }
    2701            int nativeClass = H5.H5Tget_class(tid_src);
    2702            if (nativeClass == HDF5Constants.H5T_REFERENCE)
    2703                datatype = new H5ReferenceType(pgroup.getFileFormat(), dstName, path);
    2704            else
    2705                datatype = new H5Datatype(pgroup.getFileFormat(), dstName, path);
    2706
    2707            pgroup.addToMemberList(datatype);
    2708        }
    2709        finally {
    2710            try {
    2711                srcType.close(tid_src);
    2712            }
    2713            catch (Exception ex) {
    2714                log.debug("copyDatatype(): {} srcType.close(tid_src {}) failure: ", dstName, tid_src, ex);
    2715            }
    2716            try {
    2717                pgroup.close(gid_dst);
    2718            }
    2719            catch (Exception ex) {
    2720                log.debug("copyDatatype(): {} pgroup.close(gid_dst {}) failure: ", dstName, gid_dst, ex);
    2721            }
    2722        }
    2723
    2724        return datatype;
    2725    }
    2726
    2727    /**
    2728     * Copies a group and its members to a new location.
    2729     *
    2730     * @param srcGroup
    2731     *            the source group
    2732     * @param dstGroup
    2733     *            the location where the new group is located
    2734     * @param dstName
    2735     *            the name of the new group
    2736     *
    2737     * @throws Exception
    2738     *            If there is a failure.
    2739     */
    2740    private HObject copyGroup(H5Group srcGroup, H5Group dstGroup, String dstName) throws Exception {
    2741        H5Group group = null;
    2742        long srcgid = -1, dstgid = -1;
    2743        String path = null;
    2744
    2745        if (dstGroup.isRoot())
    2746            path = HObject.SEPARATOR;
    2747        else
    2748            path = dstGroup.getPath() + dstGroup.getName() + HObject.SEPARATOR;
    2749
    2750        if ((dstName == null) || dstName.equals(HObject.SEPARATOR) || (dstName.length() < 1))
    2751            dstName = srcGroup.getName();
    2752
    2753        try {
    2754            srcgid = srcGroup.open();
    2755            dstgid = dstGroup.open();
    2756            try {
    2757                H5.H5Ocopy(srcgid, ".", dstgid, dstName, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    2758            }
    2759            catch (Exception ex) {
    2760                log.debug("copyGroup(): {} H5Ocopy(srcgid {}) failure: ", dstName, srcgid, ex);
    2761            }
    2762
    2763            group = new H5Group(dstGroup.getFileFormat(), dstName, path, dstGroup);
    2764            depth_first(group, Integer.MIN_VALUE); // reload all
    2765            dstGroup.addToMemberList(group);
    2766        }
    2767
    2768        finally {
    2769            try {
    2770                srcGroup.close(srcgid);
    2771            }
    2772            catch (Exception ex) {
    2773                log.debug("copyGroup(): {} srcGroup.close(srcgid {}) failure: ", dstName, srcgid, ex);
    2774            }
    2775            try {
    2776                dstGroup.close(dstgid);
    2777            }
    2778            catch (Exception ex) {
    2779                log.debug("copyGroup(): {} pgroup.close(dstgid {}) failure: ", dstName, dstgid, ex);
    2780            }
    2781        }
    2782
    2783        return group;
    2784    }
    2785
    2786    /**
    2787     * Constructs a group for specified group identifier and retrieves members.
    2788     *
    2789     * @param gid
    2790     *            The group identifier.
    2791     * @param name
    2792     *            The group name.
    2793     * @param pGroup
    2794     *            The parent group, or null for the root group.
    2795     *
    2796     * @return The group if successful; otherwise returns false.
    2797     *
    2798     * @throws HDF5Exception
    2799     *             If there is an error at the HDF5 library level.
    2800     */
    2801    private H5Group getGroup(long gid, String name, Group pGroup) throws HDF5Exception {
    2802        String parentPath = null;
    2803        String thisFullName = null;
    2804        String memberFullName = null;
    2805
    2806        if (pGroup == null) {
    2807            thisFullName = name = "/";
    2808        }
    2809        else {
    2810            parentPath = pGroup.getFullName();
    2811            if ((parentPath == null) || parentPath.equals("/"))
    2812                thisFullName = "/" + name;
    2813            else
    2814                thisFullName = parentPath + "/" + name;
    2815        }
    2816
    2817        // get rid of any extra "/"
    2818        if (parentPath != null)
    2819            parentPath = parentPath.replaceAll("//", "/");
    2820        if (thisFullName != null)
    2821            thisFullName = thisFullName.replaceAll("//", "/");
    2822
    2823        log.trace("getGroup(): fullName={}", thisFullName);
    2824
    2825        H5Group group = new H5Group(this, name, parentPath, pGroup);
    2826
    2827        H5G_info_t group_info = null;
    2828        H5O_info_t obj_info = null;
    2829        long objid = -1;
    2830        String link_name = null;
    2831        try {
    2832            group_info = H5.H5Gget_info(gid);
    2833        }
    2834        catch (Exception ex) {
    2835            log.debug("getGroup(): {} H5Gget_info(gid {}) failure: ", name, gid, ex);
    2836        }
    2837        try {
    2838            objid = H5.H5Oopen(gid, thisFullName, HDF5Constants.H5P_DEFAULT);
    2839        }
    2840        catch (Exception ex) {
    2841            log.debug("getGroup(): {} H5Oopen(gid {}) failure: ", name, gid, ex);
    2842        }
    2843
    2844        // retrieve only the immediate members of the group, do not follow
    2845        // subgroups
    2846        for (int i = 0; i < group_info.nlinks; i++) {
    2847            try {
    2848                link_name = H5.H5Lget_name_by_idx(gid, thisFullName, indexType, indexOrder, i, HDF5Constants.H5P_DEFAULT);
    2849                obj_info = H5.H5Oget_info_by_idx(objid, thisFullName, indexType, indexOrder, i, HDF5Constants.H5P_DEFAULT);
    2850            }
    2851            catch (HDF5Exception ex) {
    2852                log.debug("getGroup()[{}]: {} name,info failure: ", i, name, ex);
    2853                // do not stop if accessing one member fails
    2854                continue;
    2855            }
    2856            // create a new group
    2857            if (obj_info.type == HDF5Constants.H5O_TYPE_GROUP) {
    2858                H5Group g = new H5Group(this, link_name, thisFullName, group);
    2859                group.addToMemberList(g);
    2860            }
    2861            else if (obj_info.type == HDF5Constants.H5O_TYPE_DATASET) {
    2862                long did = -1;
    2863                Dataset d = null;
    2864
    2865                if ((thisFullName == null) || thisFullName.equals("/"))
    2866                    memberFullName = "/" + link_name;
    2867                else
    2868                    memberFullName = thisFullName + "/" + link_name;
    2869
    2870                try {
    2871                    did = H5.H5Dopen(fid, memberFullName, HDF5Constants.H5P_DEFAULT);
    2872                    d = getDataset(did, link_name, thisFullName);
    2873                }
    2874                finally {
    2875                    try {
    2876                        H5.H5Dclose(did);
    2877                    }
    2878                    catch (Exception ex) {
    2879                        log.debug("getGroup()[{}]: {} H5Dclose(did {}) failure: ", i, name, did, ex);
    2880                    }
    2881                }
    2882                group.addToMemberList(d);
    2883            }
    2884            else if (obj_info.type == HDF5Constants.H5O_TYPE_NAMED_DATATYPE) {
    2885                Datatype t = new H5Datatype(group.getFileFormat(), link_name, thisFullName);
    2886                group.addToMemberList(t);
    2887            }
    2888        } // End of for loop.
    2889        try {
    2890            if (objid >= 0)
    2891                H5.H5Oclose(objid);
    2892        }
    2893        catch (Exception ex) {
    2894            log.debug("getGroup(): {} H5Oclose(oid {}) failure: ", name, objid, ex);
    2895        }
    2896
    2897        return group;
    2898    }
    2899
    2900    /**
    2901     * Retrieves the name of the target object that is being linked to.
    2902     *
    2903     * @param obj
    2904     *            The current link object.
    2905     *
    2906     * @return The name of the target object.
    2907     *
    2908     * @throws Exception
    2909     *             If there is an error at the HDF5 library level.
    2910     */
    2911    public static String getLinkTargetName(HObject obj) throws Exception {
    2912        String[] link_value = { null, null };
    2913        String targetObjName = null;
    2914
    2915        if (obj == null) {
    2916            log.debug("getLinkTargetName(): object is null");
    2917            return null;
    2918        }
    2919
    2920        if (obj.getFullName().equals("/")) {
    2921            log.debug("getLinkTargetName(): object is root group, links not allowed");
    2922            return null;
    2923        }
    2924
    2925        H5L_info_t link_info = null;
    2926        if (obj.getFID() < 0)
    2927            log.trace("getLinkTargetName(): file id for:{} is invalid", obj.getFullName());
    2928        else {
    2929            try {
    2930                link_info = H5.H5Lget_info(obj.getFID(), obj.getFullName(), HDF5Constants.H5P_DEFAULT);
    2931            }
    2932            catch (Exception err) {
    2933                log.debug("getLinkTargetName(): H5Lget_info {} failure: ", obj.getFullName(), err);
    2934            }
    2935        }
    2936        if (link_info != null) {
    2937            if ((link_info.type == HDF5Constants.H5L_TYPE_SOFT) || (link_info.type == HDF5Constants.H5L_TYPE_EXTERNAL)) {
    2938                try {
    2939                    H5.H5Lget_value(obj.getFID(), obj.getFullName(), link_value, HDF5Constants.H5P_DEFAULT);
    2940                }
    2941                catch (Exception ex) {
    2942                    log.debug("getLinkTargetName(): H5Lget_value {} failure: ", obj.getFullName(), ex);
    2943                }
    2944                if (link_info.type == HDF5Constants.H5L_TYPE_SOFT)
    2945                    targetObjName = link_value[0];
    2946                else if (link_info.type == HDF5Constants.H5L_TYPE_EXTERNAL)
    2947                    targetObjName = link_value[1] + FileFormat.FILE_OBJ_SEP + link_value[0];
    2948            }
    2949        }
    2950
    2951        return targetObjName;
    2952    }
    2953
    2954    /**
    2955     * Export dataset.
    2956     *
    2957     * @param file_export_name
    2958     *            The file name to export data into.
    2959     * @param object
    2960     *            The id of the HDF5 dataset.
    2961     * @param binary_order
    2962     *            The data byte order
    2963     *
    2964     * @throws Exception
    2965     *            If there is a failure.
    2966     */
    2967    @Override
    2968    public void exportDataset(String file_export_name, Dataset object, int binary_order) throws Exception {
    2969        long did = object.open();
    2970        H5.H5export_dataset(file_export_name, did, object.getFullName(), binary_order);
    2971        object.close(did);
    2972    }
    2973
    2974    /**
    2975     * Renames an attribute.
    2976     *
    2977     * @param obj
    2978     *            The object whose attribute is to be renamed.
    2979     * @param oldAttrName
    2980     *            The current name of the attribute.
    2981     * @param newAttrName
    2982     *            The new name of the attribute.
    2983     *
    2984     * @throws Exception
    2985     *             If there is an error at the HDF5 library level.
    2986     */
    2987    @Override
    2988    public void renameAttribute(HObject obj, String oldAttrName, String newAttrName) throws Exception {
    2989        log.trace("renameAttribute(): rename {} to {}", oldAttrName, newAttrName);
    2990        H5.H5Arename_by_name(obj.getFID(), obj.getFullName(), oldAttrName, newAttrName, HDF5Constants.H5P_DEFAULT);
    2991    }
    2992
    2993    /**
    2994     * Rename the given object
    2995     *
    2996     * @param obj
    2997     *            the object to be renamed.
    2998     * @param newName
    2999     *            the new name of the object.
    3000     *
    3001     * @throws Exception
    3002     *            If there is a failure.
    3003     */
    3004    public static void renameObject(HObject obj, String newName) throws Exception {
    3005        renameObject(obj, obj.getPath(), newName);
    3006    }
    3007
    3008    /**
    3009     * Rename the given object
    3010     *
    3011     * @param obj
    3012     *            the object to be renamed.
    3013     * @param newPath
    3014     *            the new path of the object.
    3015     * @param newName
    3016     *            the new name of the object.
    3017     *
    3018     * @throws Exception
    3019     *            If there is a failure.
    3020     */
    3021    public static void renameObject(HObject obj, String newPath, String newName) throws Exception {
    3022        String currentFullPath = obj.getFullName();
    3023        String newFullPath = obj.createFullname(newPath, newName);
    3024
    3025        log.trace("renameObject(): currentFullPath={} newFullPath={}", currentFullPath, newFullPath);
    3026        if ((currentFullPath != null) && (newFullPath != null)) {
    3027            currentFullPath = currentFullPath.replaceAll("//", "/");
    3028            newFullPath = newFullPath.replaceAll("//", "/");
    3029
    3030            if (currentFullPath.equals("/") && obj instanceof Group)
    3031                throw new HDF5Exception("Can't rename the root group.");
    3032
    3033            if (currentFullPath.equals(newFullPath))
    3034                throw new HDF5Exception("The new name is the same as the current name.");
    3035
    3036            // Call the library to move things in the file if object exists
    3037            if (obj.getName() != null)
    3038                H5.H5Lmove(obj.getFID(), currentFullPath, obj.getFID(), newFullPath, HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT);
    3039        }
    3040    }
    3041
    3042    /** @return the int value of the index type value.
    3043     *
    3044     * @param strtype
    3045     *            The name of the index type.
    3046     */
    3047    public static int getIndexTypeValue(String strtype) {
    3048        if (strtype.compareTo("H5_INDEX_NAME") == 0)
    3049            return HDF5Constants.H5_INDEX_NAME;
    3050        if (strtype.compareTo("H5_INDEX_CRT_ORDER") == 0)
    3051            return HDF5Constants.H5_INDEX_CRT_ORDER;
    3052        if (strtype.compareTo("H5_INDEX_N") == 0)
    3053            return HDF5Constants.H5_INDEX_N;
    3054        return HDF5Constants.H5_INDEX_UNKNOWN;
    3055    }
    3056
    3057    /** @return the int value of the index order.
    3058     *
    3059     * @param strorder
    3060     *            The name of the index order.
    3061     */
    3062    public static int getIndexOrderValue(String strorder) {
    3063        if (strorder.compareTo("H5_ITER_INC") == 0)
    3064            return HDF5Constants.H5_ITER_INC;
    3065        if (strorder.compareTo("H5_ITER_DEC") == 0)
    3066            return HDF5Constants.H5_ITER_DEC;
    3067        if (strorder.compareTo("H5_ITER_NATIVE") == 0)
    3068            return HDF5Constants.H5_ITER_NATIVE;
    3069        if (strorder.compareTo("H5_ITER_N") == 0)
    3070            return HDF5Constants.H5_ITER_N;
    3071        return HDF5Constants.H5_ITER_UNKNOWN;
    3072    }
    3073
    3074    @Override
    3075    /** @return the int value of the index type.
    3076     *
    3077     * @param strtype
    3078     *            The name of the index type.
    3079     */
    3080    public int getIndexType(String strtype) {
    3081        if (strtype != null) {
    3082            if (strtype.compareTo("H5_INDEX_NAME") == 0)
    3083                return HDF5Constants.H5_INDEX_NAME;
    3084            if (strtype.compareTo("H5_INDEX_CRT_ORDER") == 0)
    3085                return HDF5Constants.H5_INDEX_CRT_ORDER;
    3086            return HDF5Constants.H5_INDEX_UNKNOWN;
    3087        }
    3088        return getIndexType();
    3089    }
    3090
    3091    /** @return the current value of the index type. */
    3092    public int getIndexType() {
    3093        return indexType;
    3094    }
    3095
    3096    @Override
    3097    /** set the int value of the index type.
    3098     *
    3099     * @param indexType
    3100     *            The value of the index type.
    3101     */
    3102    public void setIndexType(int indexType) {
    3103        this.indexType = indexType;
    3104    }
    3105
    3106    @Override
    3107    /** @return the int value of the index order value.
    3108     *
    3109     * @param strorder
    3110     *            The name of the index order.
    3111     */
    3112    public int getIndexOrder(String strorder) {
    3113        if (strorder != null) {
    3114            if (strorder.compareTo("H5_ITER_INC") == 0)
    3115                return HDF5Constants.H5_ITER_INC;
    3116            if (strorder.compareTo("H5_ITER_DEC") == 0)
    3117                return HDF5Constants.H5_ITER_DEC;
    3118            if (strorder.compareTo("H5_ITER_NATIVE") == 0)
    3119                return HDF5Constants.H5_ITER_NATIVE;
    3120            if (strorder.compareTo("H5_ITER_N") == 0)
    3121                return HDF5Constants.H5_ITER_N;
    3122            return HDF5Constants.H5_ITER_UNKNOWN;
    3123        }
    3124        return getIndexOrder();
    3125    }
    3126
    3127    /** @return the current value of the index order. */
    3128    public int getIndexOrder() {
    3129        return indexOrder;
    3130    }
    3131
    3132    @Override
    3133    /** set the current value of the index order.
    3134     *
    3135     * @param indexOrder
    3136     *            The index order.
    3137     */
    3138    public void setIndexOrder(int indexOrder) {
    3139        this.indexOrder = indexOrder;
    3140    }
    3141}