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