    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.util.List;
    018import java.util.Vector;
    019
    020import org.slf4j.Logger;
    021import org.slf4j.LoggerFactory;
    022
    023import hdf.hdf5lib.H5;
    024import hdf.hdf5lib.HDF5Constants;
    025import hdf.hdf5lib.HDFArray;
    026import hdf.hdf5lib.HDFNativeData;
    027import hdf.hdf5lib.exceptions.HDF5Exception;
    028import hdf.hdf5lib.structs.H5G_info_t;
    029import hdf.hdf5lib.structs.H5O_info_t;
    030import hdf.hdf5lib.structs.H5O_token_t;
    031
    032import hdf.object.Attribute;
    033import hdf.object.FileFormat;
    034import hdf.object.Group;
    035import hdf.object.HObject;
    036
    037import hdf.object.h5.H5MetaDataContainer;
    038
    039/**
    040 * An H5Group object represents an existing HDF5 group in file.
    041 *
    042 * In HDF5, every object has at least one name. An HDF5 group is used to store a set of the names together in one place,
    043 * i.e. a group. The general structure of a group is similar to that of the UNIX file system in that the group may
    044 * contain references to other groups or data objects just as the UNIX directory may contain sub-directories or files.
    045 *
    046 * For more information on HDF5 Groups,
    047 *
    048 * <a href="https://hdfgroup.github.io/hdf5/_h5_g__u_g.html#sec_group">HDF5 Groups in HDF5 User Guide</a>
    049 *
    050 * @version 1.1 9/4/2007
    051 * @author Peter X. Cao
    052 */
    053public class H5Group extends Group
    054{
    055    private static final long serialVersionUID = -951164512330444150L;
    056
    057    private static final Logger log = LoggerFactory.getLogger(H5Group.class);
    058
    059    /**
    060     * The metadata object for this data object. Members of the metadata are instances of Attribute.
    061     */
    062    private H5MetaDataContainer objMetadata;
    063
    064    /** the object properties */
    065    private H5O_info_t objInfo;
    066
    067    /**
    068     * Constructs an HDF5 group with specific name, path, and parent.
    069     *
    070     * @param theFile
    071     *            the file that contains the group.
    072     * @param theName
    073     *            the name of this group, e.g. "grp01".
    074     * @param thePath
    075     *            the full path of this group, e.g. "/groups/".
    076     * @param theParent
    077     *            the parent of this group.
    078     */
    079    public H5Group(FileFormat theFile, String theName, String thePath, Group theParent) {
    080        this(theFile, theName, thePath, theParent, null);
    081    }
    082
    083    /**
    084     * @deprecated Not for public use in the future.<br>
    085     *             Using {@link #H5Group(FileFormat, String, String, Group)}
    086     *
    087     * @param theFile
    088     *            the file that contains the group.
    089     * @param theName
    090     *            the name of this group, e.g. "grp01".
    091     * @param thePath
    092     *            the full path of this group, e.g. "/groups/".
    093     * @param theParent
    094     *            the parent of this group.
    095     * @param oid
    096     *            the oid of this group.
    097     */
    098    @Deprecated
    099    public H5Group(FileFormat theFile, String theName, String thePath, Group theParent, long[] oid) {
    100        super(theFile, theName, thePath, theParent, oid);
    101        nMembersInFile = -1;
    102        objMetadata = new H5MetaDataContainer(theFile, theName, thePath, this);
    103
    104        if (theFile != null) {
    105            if (oid == null) {
    106                // retrieve the object ID
    107                byte[] refBuf = null;
    108                try {
    109                    refBuf = H5.H5Rcreate_object(theFile.getFID(), this.getFullName(), HDF5Constants.H5P_DEFAULT);
    110                    this.oid = HDFNativeData.byteToLong(refBuf);
    111                    log.trace("constructor REF {} to OID {}", refBuf, this.oid);
    112                }
    113                catch (Exception ex) {
    114                    log.debug("constructor ID {} for {} failed H5Rcreate_object", theFile.getFID(), this.getFullName());
    115                }
    116                finally {
    117                    if (refBuf != null)
    118                        H5.H5Rdestroy(refBuf);
    119                }
    120            }
    121            log.trace("constructor OID {}", this.oid);
    122            try {
    123                objInfo = H5.H5Oget_info_by_name(theFile.getFID(), this.getFullName(), HDF5Constants.H5O_INFO_BASIC, HDF5Constants.H5P_DEFAULT);
    124            }
    125            catch (Exception ex) {
    126                objInfo = new H5O_info_t(-1L, null, 0, 0, 0L, 0L, 0L, 0L, 0L);
    127            }
    128        }
    129        else {
    130            this.oid = null;
    131            objInfo = new H5O_info_t(-1L, null, 0, 0, 0L, 0L, 0L, 0L, 0L);
    132        }
    133    }
    134
    135    /*
    136     * (non-Javadoc)
    137     *
    138     * @see hdf.object.HObject#open()
    139     */
    140    @Override
    141    public long open() {
    142        long gid = HDF5Constants.H5I_INVALID_HID;
    143
    144        try {
    145            if (isRoot()) {
    146                gid = H5.H5Gopen(getFID(), SEPARATOR, HDF5Constants.H5P_DEFAULT);
    147            }
    148            else {
    149                gid = H5.H5Gopen(getFID(), getPath() + getName(), HDF5Constants.H5P_DEFAULT);
    150            }
    151            log.trace("open(): gid={}", gid);
    152        }
    153        catch (HDF5Exception ex) {
    154            log.debug("open(): Failed to open group {}", getPath() + getName(), ex);
    155            gid = HDF5Constants.H5I_INVALID_HID;
    156        }
    157
    158        return gid;
    159    }
    160
    161    /*
    162     * (non-Javadoc)
    163     *
    164     * @see hdf.object.HObject#close(int)
    165     */
    166    @Override
    167    public void close(long gid) {
    168        if (gid >= 0) {
    169            try {
    170                H5.H5Gclose(gid);
    171            }
    172            catch (HDF5Exception ex) {
    173                log.debug("close(): H5Gclose(gid {}): ", gid, ex);
    174            }
    175        }
    176    }
    177
    178    /**
    179     * Get the token for this object.
    180     *
    181     * @return true if it has any attributes, false otherwise.
    182     */
    183    public long[] getToken() {
    184        H5O_token_t token = objInfo.token;
    185        return HDFNativeData.byteToLong(token.data);
    186    }
    187
    188    /**
    189     * Check if the object has any attributes attached.
    190     *
    191     * @return true if it has any attributes, false otherwise.
    192     */
    193    @Override
    194    public boolean hasAttribute() {
    195        objInfo.num_attrs = objMetadata.getObjectAttributeSize();
    196
    197        if (objInfo.num_attrs < 0) {
    198            long gid = open();
    199            if (gid > 0) {
    200                try {
    201                    objInfo = H5.H5Oget_info(gid);
    202                }
    203                catch (Exception ex) {
    204                    objInfo.num_attrs = 0;
    205                    log.debug("hasAttribute(): get object info failure: ", ex);
    206                }
    207                finally {
    208                    close(gid);
    209                }
    210                objMetadata.setObjectAttributeSize((int) objInfo.num_attrs);
    211            }
    212            else {
    213                log.debug("hasAttribute(): could not open group");
    214            }
    215        }
    216
    217        log.trace("hasAttribute(): nAttributes={}", objInfo.num_attrs);
    218        return (objInfo.num_attrs > 0);
    219    }
    220
    221    /*
    222     * (non-Javadoc)
    223     *
    224     * @see hdf.object.Group#getNumberOfMembersInFile()
    225     */
    226    @Override
    227    public int getNumberOfMembersInFile() {
    228        if (nMembersInFile < 0) {
    229            long gid = open();
    230            if (gid > 0) {
    231                try {
    232                    H5G_info_t group_info = null;
    233                    group_info = H5.H5Gget_info(gid);
    234                    nMembersInFile = (int) group_info.nlinks;
    235                }
    236                catch (Exception ex) {
    237                    nMembersInFile = 0;
    238                }
    239                close(gid);
    240            }
    241        }
    242        return nMembersInFile;
    243    }
    244
    245    /**
    246     * Removes all of the elements from metadata list.
    247     * The list should be empty after this call returns.
    248     */
    249    @Override
    250    public void clear() {
    251        super.clear();
    252        objMetadata.clear();
    253    }
    254
    255    /**
    256     * Retrieves the object's metadata, such as attributes, from the file.
    257     *
    258     * Metadata, such as attributes, is stored in a List.
    259     *
    260     * @return the list of metadata objects.
    261     *
    262     * @throws HDF5Exception
    263     *             if the metadata can not be retrieved
    264     */
    265    @Override
    266    public List<Attribute> getMetadata() throws HDF5Exception {
    267        int gmIndexType = 0;
    268        int gmIndexOrder = 0;
    269
    270        try {
    271            gmIndexType = fileFormat.getIndexType(null);
    272        }
    273        catch (Exception ex) {
    274            log.debug("getMetadata(): getIndexType failed: ", ex);
    275        }
    276        try {
    277            gmIndexOrder = fileFormat.getIndexOrder(null);
    278        }
    279        catch (Exception ex) {
    280            log.debug("getMetadata(): getIndexOrder failed: ", ex);
    281        }
    282        return this.getMetadata(gmIndexType, gmIndexOrder);
    283    }
    284
    285    /**
    286     * Retrieves the object's metadata, such as attributes, from the file.
    287     *
    288     * Metadata, such as attributes, is stored in a List.
    289     *
    290     * @param attrPropList
    291     *             the list of properties to get
    292     *
    293     * @return the list of metadata objects.
    294     *
    295     * @throws HDF5Exception
    296     *             if the metadata can not be retrieved
    297     */
    298    public List<Attribute> getMetadata(int... attrPropList) throws HDF5Exception {
    299        try {
    300            this.linkTargetObjName = H5File.getLinkTargetName(this);
    301        }
    302        catch (Exception ex) {
    303            log.debug("getMetadata(): getLinkTargetName failed: ", ex);
    304        }
    305
    306        List<Attribute> attrlist = null;
    307        try {
    308            attrlist = objMetadata.getMetadata(attrPropList);
    309        }
    310        catch (Exception ex) {
    311            log.debug("getMetadata(): getMetadata failed: ", ex);
    312        }
    313        return attrlist;
    314    }
    315
    316    /**
    317     * Writes a specific piece of metadata (such as an attribute) into the file.
    318     *
    319     * If an HDF(4&amp;5) attribute exists in the file, this method updates its
    320     * value. If the attribute does not exist in the file, it creates the
    321     * attribute in the file and attaches it to the object. It will fail to
    322     * write a new attribute to the object where an attribute with the same name
    323     * already exists. To update the value of an existing attribute in the file,
    324     * one needs to get the instance of the attribute by getMetadata(), change
    325     * its values, then use writeMetadata() to write the value.
    326     *
    327     * @param info
    328     *            the metadata to write.
    329     *
    330     * @throws Exception
    331     *             if the metadata can not be written
    332     */
    333    @Override
    334    public void writeMetadata(Object info) throws Exception {
    335        try {
    336            objMetadata.writeMetadata(info);
    337        }
    338        catch (Exception ex) {
    339            log.debug("writeMetadata(): Object not an Attribute");
    340        }
    341    }
    342
    343    /**
    344     * Deletes an existing piece of metadata from this object.
    345     *
    346     * @param info
    347     *            the metadata to delete.
    348     *
    349     * @throws HDF5Exception
    350     *             if the metadata can not be removed
    351     */
    352    @Override
    353    public void removeMetadata(Object info) throws HDF5Exception {
    354        try {
    355            objMetadata.removeMetadata(info);
    356        }
    357        catch (Exception ex) {
    358            log.debug("removeMetadata(): Object not an Attribute");
    359            return;
    360        }
    361
    362        Attribute attr = (Attribute) info;
    363        log.trace("removeMetadata(): {}", attr.getAttributeName());
    364        long gid = open();
    365        if (gid >= 0) {
    366            try {
    367                H5.H5Adelete(gid, attr.getAttributeName());
    368            }
    369            catch (Exception ex) {
    370                log.debug("removeMetadata(): ", ex);
    371            }
    372            finally {
    373                close(gid);
    374            }
    375        }
    376        else {
    377            log.debug("removeMetadata(): failed to open group");
    378        }
    379    }
    380
    381    /**
    382     * Updates an existing piece of metadata attached to this object.
    383     *
    384     * @param info
    385     *            the metadata to update.
    386     *
    387     * @throws HDF5Exception
    388     *             if the metadata can not be updated
    389     */
    390    @Override
    391    public void updateMetadata(Object info) throws HDF5Exception {
    392        try {
    393            objMetadata.updateMetadata(info);
    394        }
    395        catch (Exception ex) {
    396            log.debug("updateMetadata(): Object not an Attribute");
    397            return;
    398        }
    399    }
    400
    401    /*
    402     * (non-Javadoc)
    403     *
    404     * @see hdf.object.HObject#setName(java.lang.String)
    405     */
    406    @Override
    407    public void setName(String newName) throws Exception {
    408        if (newName == null)
    409            throw new IllegalArgumentException("The new name is NULL");
    410
    411        H5File.renameObject(this, newName);
    412        super.setName(newName);
    413    }
    414
    415    /*
    416     * (non-Javadoc)
    417     *
    418     * @see hdf.object.HObject#setPath(java.lang.String)
    419     */
    420    @SuppressWarnings("rawtypes")
    421    @Override
    422    public void setPath(String newPath) throws Exception {
    423        super.setPath(newPath);
    424
    425        List members = this.getMemberList();
    426        if (members == null)
    427            return;
    428
    429        int n = members.size();
    430        HObject obj = null;
    431        for (int i = 0; i < n; i++) {
    432            obj = (HObject) members.get(i);
    433            obj.setPath(getPath() + getName() + HObject.SEPARATOR);
    434        }
    435    }
    436
    437    /**
    438     * Creates a new group with a name in a group and with the group creation
    439     * properties specified in gplist.
    440     *
    441     * The gplist contains a sequence of group creation property list
    442     * identifiers, lcpl, gcpl, gapl. It allows the user to create a group with
    443     * group creation properties. It will close the group creation properties
    444     * specified in gplist.
    445     *
    446     * @see hdf.hdf5lib.H5#H5Gcreate(long, String, long, long, long) for the
    447     *      order of property list identifiers.
    448     *
    449     * @param name
    450     *            The name of a new group.
    451     * @param pgroup
    452     *            The parent group object.
    453     * @param gplist
    454     *            The group creation properties, in which the order of the
    455     *            properties conforms the HDF5 library API, H5Gcreate(), i.e.
    456     *            lcpl, gcpl and gapl, where
    457     *            <ul>
    458     *            <li>lcpl : Property list for link creation <li>gcpl : Property
    459     *            list for group creation <li>gapl : Property list for group
    460     *            access
    461     *            </ul>
    462     *
    463     * @return The new group if successful; otherwise returns null.
    464     *
    465     * @throws Exception if there is a failure.
    466     */
    467    public static H5Group create(String name, Group pgroup, long... gplist) throws Exception {
    468        H5Group group = null;
    469        String fullPath = null;
    470        long lcpl = HDF5Constants.H5P_DEFAULT;
    471        long gcpl = HDF5Constants.H5P_DEFAULT;
    472        long gapl = HDF5Constants.H5P_DEFAULT;
    473
    474        if (gplist.length > 0) {
    475            lcpl = gplist[0];
    476            if (gplist.length > 1) {
    477                gcpl = gplist[1];
    478                if (gplist.length > 2) gapl = gplist[2];
    479            }
    480        }
    481
    482        if ((name == null) || (pgroup == null)) {
    483            log.debug("create(): one or more parameters are null");
    484            return null;
    485        }
    486
    487        H5File file = (H5File) pgroup.getFileFormat();
    488        if (file == null) {
    489            log.debug("create(): parent group FileFormat is null");
    490            return null;
    491        }
    492
    493        String path = HObject.SEPARATOR;
    494        if (!pgroup.isRoot()) {
    495            path = pgroup.getPath() + pgroup.getName() + HObject.SEPARATOR;
    496            if (name.endsWith("/"))
    497                name = name.substring(0, name.length() - 1);
    498            int idx = name.lastIndexOf('/');
    499            if (idx >= 0)
    500                name = name.substring(idx + 1);
    501        }
    502
    503        fullPath = path + name;
    504
    505        // create a new group and add it to the parent node
    506        long gid = H5.H5Gcreate(file.open(), fullPath, lcpl, gcpl, gapl);
    507        try {
    508            H5.H5Gclose(gid);
    509        }
    510        catch (Exception ex) {
    511            log.debug("create(): H5Gcreate {} H5Gclose(gid {}) failure: ", fullPath, gid, ex);
    512        }
    513
    514        group = new H5Group(file, name, path, pgroup);
    515
    516        if (group != null)
    517            pgroup.addToMemberList(group);
    518
    519        if (gcpl > 0) {
    520            try {
    521                H5.H5Pclose(gcpl);
    522            }
    523            catch (final Exception ex) {
    524                log.debug("create(): create prop H5Pclose(gcpl {}) failure: ", gcpl, ex);
    525            }
    526        }
    527
    528        return group;
    529    }
    530
    531}