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