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