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