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