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