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 http://hdfgroup.org/products/hdf-java/doc/Copyright.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="http://hdfgroup.org/HDF5/doc/UG/">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     *
050     */
051    private static final long serialVersionUID = -951164512330444150L;
052
053    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(H5Group.class);
054
055    /**
056     * The list of attributes of this data object. Members of the list are
057     * instance of Attribute.
058     */
059    protected List            attributeList;
060
061    private int               nAttributes      = -1;
062
063    private H5O_info_t        obj_info;
064
065    /**
066     * Constructs an HDF5 group with specific name, path, and parent.
067     *
068     * @param theFile
069     *            the file which containing the group.
070     * @param name
071     *            the name of this group, e.g. "grp01".
072     * @param path
073     *            the full path of this group, e.g. "/groups/".
074     * @param parent
075     *            the parent of this group.
076     */
077    public H5Group(FileFormat theFile, String name, String path, Group parent) {
078        this(theFile, name, path, parent, 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 which containing the group.
087     * @param name
088     *            the name of this group, e.g. "grp01".
089     * @param path
090     *            the full path of this group, e.g. "/groups/".
091     * @param parent
092     *            the parent of this group.
093     * @param oid
094     *            the oid of this group.
095     */
096    @Deprecated
097    public H5Group(FileFormat theFile, String name, String path, Group parent, long[] oid) {
098        super(theFile, name, path, parent, oid);
099        nMembersInFile = -1;
100        obj_info = new H5O_info_t(-1L, -1L, 0, 0, -1L, 0L, 0L, 0L, 0L, null, null, null);
101
102        if ((oid == null) && (theFile != null)) {
103            // retrieve the object ID
104            try {
105                byte[] ref_buf = H5.H5Rcreate(theFile.getFID(), this.getFullName(), HDF5Constants.H5R_OBJECT, -1);
106                this.oid = new long[1];
107                this.oid[0] = HDFNativeData.byteToLong(ref_buf, 0);
108            }
109            catch (Exception ex) {
110                this.oid = new long[1];
111                this.oid[0] = 0;
112            }
113        }
114    }
115
116    /*
117     * (non-Javadoc)
118     *
119     * @see hdf.object.DataFormat#hasAttribute()
120     */
121    public boolean hasAttribute() {
122        obj_info.num_attrs = nAttributes;
123
124        if (obj_info.num_attrs < 0) {
125            int 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        return (obj_info.num_attrs > 0);
139    }
140
141    /*
142     * (non-Javadoc)
143     *
144     * @see hdf.object.Group#getNumberOfMembersInFile()
145     */
146    @Override
147    public int getNumberOfMembersInFile() {
148        if (nMembersInFile < 0) {
149            int gid = open();
150            if (gid > 0) {
151                try {
152                    H5G_info_t group_info = null;
153                    group_info = H5.H5Gget_info(gid);
154                    nMembersInFile = (int) group_info.nlinks;
155                }
156                catch (Exception ex) {
157                    nMembersInFile = 0;
158                }
159                close(gid);
160            }
161        }
162        return nMembersInFile;
163    }
164
165    /*
166     * (non-Javadoc)
167     *
168     * @see hdf.object.Group#clear()
169     */
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    public List getMetadata() throws HDF5Exception {
185        return this.getMetadata(fileFormat.getIndexType(null), fileFormat.getIndexOrder(null));
186    }
187
188    /*
189     * (non-Javadoc)
190     *
191     * @see hdf.object.DataFormat#getMetadata(int...)
192     */
193    public List getMetadata(int... attrPropList) throws HDF5Exception {
194        if (attributeList == null) {
195            int gid = open();
196            if(gid >= 0) {
197                int indxType = fileFormat.getIndexType(null);
198                int order = fileFormat.getIndexOrder(null);
199
200                if (attrPropList.length > 0) {
201                    indxType = attrPropList[0];
202                    if (attrPropList.length > 1) {
203                        order = attrPropList[1];
204                    }
205                }
206                try {
207                    attributeList = H5File.getAttribute(gid, indxType, order);
208                }
209                finally {
210                    close(gid);
211                }
212            }
213            else {
214                log.debug("failed to open group");
215            }
216        }
217
218        try {
219            this.linkTargetObjName = H5File.getLinkTargetName(this);
220        }
221        catch (Exception ex) {
222            log.debug("getLinkTargetName:", ex);
223        }
224
225        return attributeList;
226    }
227
228    /*
229     * (non-Javadoc)
230     *
231     * @see hdf.object.DataFormat#writeMetadata(java.lang.Object)
232     */
233    public void writeMetadata(Object info) throws Exception {
234        // only attribute metadata is supported.
235        if (!(info instanceof Attribute)) {
236            return;
237        }
238
239        boolean attrExisted = false;
240        Attribute attr = (Attribute) info;
241        log.trace("writeMetadata: {}", attr.getName());
242
243        if (attributeList == null) {
244            this.getMetadata();
245        }
246
247        if (attributeList != null) attrExisted = attributeList.contains(attr);
248
249        getFileFormat().writeAttribute(this, attr, attrExisted);
250        // add the new attribute into attribute list
251        if (!attrExisted) {
252            attributeList.add(attr);
253            nAttributes = attributeList.size();
254        }
255    }
256
257    /*
258     * (non-Javadoc)
259     *
260     * @see hdf.object.DataFormat#removeMetadata(java.lang.Object)
261     */
262    public void removeMetadata(Object info) throws HDF5Exception {
263        // only attribute metadata is supported.
264        if (!(info instanceof Attribute)) {
265            return;
266        }
267
268        Attribute attr = (Attribute) info;
269        log.trace("removeMetadata: {}", attr.getName());
270        int gid = open();
271        if(gid >= 0) {
272            try {
273                H5.H5Adelete(gid, attr.getName());
274                List attrList = getMetadata();
275                attrList.remove(attr);
276                nAttributes = attributeList.size();
277            }
278            finally {
279                close(gid);
280            }
281        }
282        else {
283            log.debug("failed to open group");
284        }
285    }
286
287    /*
288     * (non-Javadoc)
289     *
290     * @see hdf.object.DataFormat#updateMetadata(java.lang.Object)
291     */
292    public void updateMetadata(Object info) throws HDF5Exception {
293        // only attribute metadata is supported.
294        if (!(info instanceof Attribute)) {
295            return;
296        }
297        log.trace("updateMetadata");
298
299        nAttributes = -1;
300    }
301
302    /*
303     * (non-Javadoc)
304     *
305     * @see hdf.object.HObject#open()
306     */
307    @Override
308    public int open() {
309        int gid = -1;
310
311        try {
312            if (isRoot()) {
313                gid = H5.H5Gopen(getFID(), separator, HDF5Constants.H5P_DEFAULT);
314            }
315            else {
316                gid = H5.H5Gopen(getFID(), getPath() + getName(), HDF5Constants.H5P_DEFAULT);
317            }
318
319        }
320        catch (HDF5Exception ex) {
321            gid = -1;
322        }
323
324        return gid;
325    }
326
327    /*
328     * (non-Javadoc)
329     *
330     * @see hdf.object.HObject#close(int)
331     */
332    @Override
333    public void close(int gid) {
334        try {
335            H5.H5Gclose(gid);
336        }
337        catch (HDF5Exception ex) {
338            log.debug("H5Gclose:", ex);
339        }
340    }
341
342    /**
343     * Creates a new group with a name in a group and with the group creation
344     * properties specified in gplist.
345     * <p>
346     * The gplist contains a sequence of group creation property list
347     * identifiers, lcpl, gcpl, gapl. It allows the user to create a group with
348     * group creation properties. It will close the group creation properties
349     * specified in gplist.
350     *
351     * @see hdf.hdf5lib.H5#H5Gcreate(int, String, int, int, int) for the
352     *      order of property list identifiers.
353     *
354     * @param name
355     *            The name of a new group.
356     * @param pgroup
357     *            The parent group object.
358     * @param gplist
359     *            The group creation properties, in which the order of the
360     *            properties conforms the HDF5 library API, H5Gcreate(), i.e.
361     *            lcpl, gcpl and gapl, where
362     *            <ul>
363     *            <li>lcpl : Property list for link creation <li>gcpl : Property
364     *            list for group creation <li>gapl : Property list for group
365     *            access
366     *            </ul>
367     *
368     * @return The new group if successful; otherwise returns null.
369     *
370     * @throws Exception if there is a failure.
371     */
372    public static H5Group create(String name, Group pgroup, int... gplist) throws Exception {
373        H5Group group = null;
374        String fullPath = null;
375        int lcpl = HDF5Constants.H5P_DEFAULT;
376        int gcpl = HDF5Constants.H5P_DEFAULT;
377        int gapl = HDF5Constants.H5P_DEFAULT;
378
379        if (gplist.length > 0) {
380            lcpl = gplist[0];
381            if (gplist.length > 1) {
382                gcpl = gplist[1];
383                if (gplist.length > 2) gapl = gplist[2];
384            }
385        }
386
387        if ((name == null) || (pgroup == null)) {
388            System.err.println("(name == null) || (pgroup == null)");
389            return null;
390        }
391
392        H5File file = (H5File) pgroup.getFileFormat();
393
394        if (file == null) {
395            System.err.println("Could not get file that contains object");
396            return null;
397        }
398
399        String path = HObject.separator;
400        if (!pgroup.isRoot()) {
401            path = pgroup.getPath() + pgroup.getName() + HObject.separator;
402            if (name.endsWith("/")) {
403                name = name.substring(0, name.length() - 1);
404            }
405            int idx = name.lastIndexOf("/");
406            if (idx >= 0) {
407                name = name.substring(idx + 1);
408            }
409        }
410
411        fullPath = path + name;
412
413        // create a new group and add it to the parent node
414        int gid = H5.H5Gcreate(file.open(), fullPath, lcpl, gcpl, gapl);
415        try {
416            H5.H5Gclose(gid);
417        }
418        catch (Exception ex) {
419            log.debug("H5Gcreate {} H5Gclose:", fullPath, ex);
420        }
421
422        byte[] ref_buf = H5.H5Rcreate(file.open(), fullPath, HDF5Constants.H5R_OBJECT, -1);
423        long l = HDFNativeData.byteToLong(ref_buf, 0);
424        long[] oid = { l };
425
426        group = new H5Group(file, name, path, pgroup, oid);
427
428        if (group != null) {
429            pgroup.addToMemberList(group);
430        }
431
432        if (gcpl > 0) {
433            try {
434                H5.H5Pclose(gcpl);
435            }
436            catch (final Exception ex) {
437                log.debug("create prop H5Pclose:", ex);
438            }
439        }
440
441        return group;
442    }
443
444    /*
445     * (non-Javadoc)
446     *
447     * @see hdf.object.HObject#setName(java.lang.String)
448     */
449    @Override
450    public void setName(String newName) throws Exception {
451        H5File.renameObject(this, newName);
452        super.setName(newName);
453    }
454
455    /*
456     * (non-Javadoc)
457     *
458     * @see hdf.object.HObject#setPath(java.lang.String)
459     */
460    @Override
461    public void setPath(String newPath) throws Exception {
462        super.setPath(newPath);
463
464        List members = this.getMemberList();
465        if (members == null) {
466            return;
467        }
468
469        int n = members.size();
470        HObject obj = null;
471        for (int i = 0; i < n; i++) {
472            obj = (HObject) members.get(i);
473            obj.setPath(getPath() + getName() + HObject.separator);
474        }
475    }
476}