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