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.h4;
016
017import java.util.List;
018import java.util.Vector;
019
020import hdf.hdflib.HDFConstants;
021import hdf.hdflib.HDFException;
022import hdf.hdflib.HDFLibrary;
023
024import hdf.object.Attribute;
025import hdf.object.Dataset;
026import hdf.object.FileFormat;
027import hdf.object.Group;
028import hdf.object.HObject;
029
030import hdf.object.h4.H4ScalarAttribute;
031
032/**
033 * An H4Group is a vgroup in HDF4, inheriting from Group.
034 * A vgroup is a structure designed to associate related data objects. The
035 * general structure of a vgroup is similar to that of the UNIX file system in
036 * that the vgroup may contain references to other vgroups or HDF data objects
037 * just as the UNIX directory may contain subdirectories or files.
038 *
039 * @version 1.1 9/4/2007
040 * @author Peter X. Cao
041 */
042public class H4Group extends Group
043{
044    private static final long               serialVersionUID = 3785240955078867900L;
045
046    private static final org.slf4j.Logger   log = org.slf4j.LoggerFactory.getLogger(H4Group.class);
047
048    /**
049     * The list of attributes of this data object. Members of the list are
050     * instance of H4ScalarAttribute.
051     */
052    @SuppressWarnings("rawtypes")
053    private List                            attributeList;
054
055    /** the number of attributes */
056    private int                             nAttributes = -1;
057
058    /** The default object ID for HDF4 objects */
059    private static final long[]             DEFAULT_OID = {0, 0};
060
061    /**
062     * Creates a group object with specific name, path, and parent.
063     *
064     * @param theFile the HDF file.
065     * @param name the name of this group.
066     * @param path the full path of this group.
067     * @param parent the parent of this group.
068     */
069    public H4Group(FileFormat theFile, String name, String path, Group parent)
070    {
071        this(theFile, name, path, parent, null);
072    }
073
074    /**
075     * Creates a group object with specific name, path, parent and oid.
076     *
077     * @param theFile the HDF file.
078     * @param name the name of this group.
079     * @param path the full path of this group.
080     * @param parent the parent of this group.
081     * @param oid the unique identifier of this data object.
082     */
083    @SuppressWarnings("deprecation")
084    public H4Group(FileFormat theFile, String name, String path, Group parent, long[] oid) {
085        super (theFile, name, path, parent, ((oid == null) ? DEFAULT_OID : oid));
086    }
087
088    /*
089     * (non-Javadoc)
090     * @see hdf.object.DataFormat#hasAttribute()
091     */
092    @Override
093    public boolean hasAttribute() {
094        if (nAttributes < 0) {
095            long vgid = open();
096
097            if (vgid > 0) {
098                try {
099                    nAttributes = HDFLibrary.Vnattrs(vgid);
100                    nMembersInFile = HDFLibrary.Vntagrefs(vgid);
101                }
102                catch (Exception ex) {
103                    log.debug("hasAttribute(): failure: ", ex);
104                    nAttributes = 0;
105                }
106
107                log.trace("hasAttribute(): nAttributes={}", nAttributes);
108
109                close(vgid);
110            }
111        }
112
113        return (nAttributes > 0);
114    }
115
116    // Implementing DataFormat
117    /**
118     * Retrieves the object's metadata, such as attributes, from the file.
119     *
120     * Metadata, such as attributes, is stored in a List.
121     *
122     * @return the list of metadata objects.
123     *
124     * @throws HDFException
125     *             if the metadata can not be retrieved
126     */
127    @Override
128    @SuppressWarnings({"rawtypes", "unchecked"})
129    public List getMetadata() throws HDFException {
130        if (attributeList != null) {
131            log.trace("getMetadata(): attributeList != null");
132            return attributeList;
133        }
134        else {
135            attributeList = new Vector();
136        }
137
138        // Library methods cannot be called on HDF4 dummy root group since it has a ref of 0
139        if (oid[1] > 0) {
140            long vgid = open();
141            log.trace("getMetadata(): open: id={}", vgid);
142            if (vgid < 0) {
143                log.debug("getMetadata(): Invalid VG ID");
144                return attributeList;
145            }
146
147            int n = -1;
148
149            try {
150                n = HDFLibrary.Vnattrs(vgid);
151                log.trace("getMetadata(): Vnattrs: n={}", n);
152
153                boolean b = false;
154                String[] attrName = new String[1];
155                int[] attrInfo = new int[5];
156                for (int i=0; i<n; i++) {
157                    attrName[0] = "";
158                    try {
159                        b = HDFLibrary.Vattrinfo(vgid, i, attrName, attrInfo);
160                        // mask off the litend bit
161                        attrInfo[0] = attrInfo[0] & (~HDFConstants.DFNT_LITEND);
162                    }
163                    catch (HDFException ex) {
164                        log.trace("getMetadata(): attribute[{}] Vattrinfo failure: ", i, ex);
165                        b = false;
166                    }
167
168                    if (!b)
169                        continue;
170
171                    long[] attrDims = {attrInfo[1]};
172                    H4ScalarAttribute attr = new H4ScalarAttribute(this, attrName[0], new H4Datatype(attrInfo[0]), attrDims);
173                    attributeList.add(attr);
174
175                    Object buf = null;
176                    try {
177                        buf = H4Datatype.allocateArray(attrInfo[0], attrInfo[1]);
178                    }
179                    catch (OutOfMemoryError e) {
180                        log.debug("getMetadata(): out of memory: ", e);
181                    }
182
183                    try {
184                        HDFLibrary.Vgetattr(vgid, i, buf);
185                    }
186                    catch (HDFException ex) {
187                        log.trace("getMetadata(): attribute[{}] Vgetattr failure: ", i, ex);
188                        buf = null;
189                    }
190
191                    if (buf != null) {
192                        if ((attrInfo[0] == HDFConstants.DFNT_CHAR) || (attrInfo[0] ==  HDFConstants.DFNT_UCHAR8))
193                            buf = Dataset.byteToString((byte[])buf, attrInfo[1]);
194
195                        attr.setAttributeData(buf);
196                    }
197                }
198            }
199            catch (Exception ex) {
200                log.trace("getMetadata(): failure: ", ex);
201            }
202            finally {
203                close(vgid);
204            }
205        }
206
207        return attributeList;
208    }
209
210    // To do: implementing DataFormat
211    /**
212     * Writes a specific piece of metadata (such as an attribute) into the file.
213     *
214     * If an HDF(4&amp;5) attribute exists in the file, this method updates its
215     * value. If the attribute does not exist in the file, it creates the
216     * attribute in the file and attaches it to the object. It will fail to
217     * write a new attribute to the object where an attribute with the same name
218     * already exists. To update the value of an existing attribute in the file,
219     * one needs to get the instance of the attribute by getMetadata(), change
220     * its values, then use writeMetadata() to write the value.
221     *
222     * @param info
223     *            the metadata to write.
224     *
225     * @throws Exception
226     *             if the metadata can not be written
227     */
228    @Override
229    @SuppressWarnings({"rawtypes", "unchecked"})
230    public void writeMetadata(Object info) throws Exception {
231        // only attribute metadata is supported.
232        if (!(info instanceof Attribute)) {
233            log.debug("writeMetadata(): Object not an H4ScalarAttribute");
234            return;
235        }
236
237        try {
238            getFileFormat().writeAttribute(this, (H4ScalarAttribute)info, true);
239
240            if (attributeList == null)
241                attributeList = new Vector();
242
243            attributeList.add(info);
244            nAttributes = attributeList.size();
245        }
246        catch (Exception ex) {
247            log.debug("writeMetadata(): failure: ", ex);
248        }
249    }
250
251
252    /**
253     * Deletes an existing piece of metadata from this object.
254     *
255     * @param info
256     *            the metadata to delete.
257     *
258     * @throws HDFException
259     *             if the metadata can not be removed
260     */
261    @Override
262    public void removeMetadata(Object info) throws HDFException {
263        log.trace("removeMetadata(): disabled");
264    }
265
266    /**
267     * Updates an existing piece of metadata attached to this object.
268     *
269     * @param info
270     *            the metadata to update.
271     *
272     * @throws Exception
273     *             if the metadata can not be updated
274     */
275    @Override
276    public void updateMetadata(Object info) throws Exception {
277        log.trace("updateMetadata(): disabled");
278    }
279
280    // Implementing HObject
281    @Override
282    public long open() {
283        log.trace("open(): start: for file={} with ref={}", getFID(), oid[1]);
284
285        if (oid[1] <= 0) {
286            log.debug("open(): oid[1] <= 0");
287            return -1; // Library methods cannot be called on HDF4 dummy group with ref 0
288        }
289
290        long vgid = -1;
291
292        if (!getFileFormat().isReadOnly()) {
293            // try to open with write permission
294            try {
295                vgid = HDFLibrary.Vattach(getFID(), (int)oid[1], "w");
296                log.trace("open(): Vattach write id={}", vgid);
297            }
298            catch (HDFException ex) {
299                log.debug("open(): Vattach failure: ", ex);
300                vgid = -1;
301            }
302        }
303
304        // try to open with read-only permission
305        if (getFileFormat().isReadOnly() || vgid < 0) {
306            try {
307                vgid = HDFLibrary.Vattach(getFID(), (int)oid[1], "r");
308                log.trace("open(): Vattach readonly id={}", vgid);
309            }
310            catch (HDFException ex) {
311                log.debug("open(): Vattach failure: ", ex);
312                vgid = -1;
313            }
314        }
315
316        return vgid;
317    }
318
319    /** close group access. */
320    @Override
321    public void close(long vgid) {
322        log.trace("close(): id={}", vgid);
323
324        if (vgid >= 0) {
325            try {
326                HDFLibrary.Vdetach(vgid);
327            }
328            catch (Exception ex) {
329                log.debug("close(): Vdetach failure: ", ex);
330            }
331        }
332    }
333
334    /**
335     * Creates a new group.
336     *
337     * @param name the name of the group to create.
338     * @param pgroup the parent group of the new group.
339     *
340     * @return the new group if successful. Otherwise returns null.
341     *
342     * @throws Exception if the group can not be created
343     */
344    public static H4Group create(String name, Group pgroup) throws Exception {
345        log.trace("create(): start: name={} parentGroup={}", name, pgroup);
346
347        H4Group group = null;
348        if ((pgroup == null) ||
349            (name == null)) {
350            log.debug("create(): one or more parameters are null");
351            return null;
352        }
353
354        H4File file = (H4File)pgroup.getFileFormat();
355
356        if (file == null) {
357            log.debug("create(): Parent group FileFormat is null");
358            return null;
359        }
360
361        String path = HObject.SEPARATOR;
362        if (!pgroup.isRoot())
363            path = pgroup.getPath()+pgroup.getName()+HObject.SEPARATOR;
364        long fileid = file.open();
365        if (fileid < 0) {
366            log.debug("create(): Invalid File ID");
367            return null;
368        }
369
370        long gid = HDFLibrary.Vattach(fileid, -1, "w");
371        if (gid < 0) {
372            log.debug("create(): Invalid Group ID");
373            return null;
374        }
375
376        HDFLibrary.Vsetname(gid, name);
377        int ref = HDFLibrary.VQueryref(gid);
378        int tag = HDFLibrary.VQuerytag(gid);
379
380        if (!pgroup.isRoot()) {
381            // add the dataset to the parent group
382            long pid = pgroup.open();
383            if (pid < 0) {
384                log.debug("create(): Invalid Parent Group ID");
385                throw (new HDFException("Unable to open the parent group."));
386            }
387
388            HDFLibrary.Vinsert(pid, gid);
389
390            pgroup.close(pid);
391        }
392
393        try {
394            HDFLibrary.Vdetach(gid);
395        }
396        catch (Exception ex) {
397            log.debug("create(): Vdetach failure: ", ex);
398        }
399
400        long[] oid = {tag, ref};
401        group = new H4Group(file, name, path, pgroup, oid);
402
403        if (group != null)
404            pgroup.addToMemberList(group);
405
406        return group;
407    }
408
409    //Implementing DataFormat
410    /**
411     * Retrieves the object's metadata, such as attributes, from the file.
412     *
413     * Metadata, such as attributes, is stored in a List.
414     *
415     * @param attrPropList
416     *             the list of properties to get
417     *
418     * @return the list of metadata objects.
419     *
420     * @throws Exception
421     *             if the metadata can not be retrieved
422     */
423    @SuppressWarnings("rawtypes")
424    public List getMetadata(int... attrPropList) throws Exception {
425        throw new UnsupportedOperationException("getMetadata(int... attrPropList) is not supported");
426    }
427}