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