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}