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.h5;
016
017import java.lang.reflect.Array;
018import java.math.BigInteger;
019import java.util.Arrays;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Vector;
025
026import hdf.hdf5lib.H5;
027import hdf.hdf5lib.HDF5Constants;
028import hdf.hdf5lib.HDFNativeData;
029import hdf.hdf5lib.exceptions.HDF5Exception;
030import hdf.object.Attribute;
031import hdf.object.DataFormat;
032import hdf.object.Dataset;
033import hdf.object.Datatype;
034import hdf.object.FileFormat;
035import hdf.object.Group;
036import hdf.object.HObject;
037import hdf.object.MetaDataContainer;
038
039/**
040 * An attribute is a (name, value) pair of metadata attached to a primary data object such as a
041 * dataset, group or named datatype.
042 * <p>
043 * Like a dataset, an attribute has a name, datatype and dataspace.
044 *
045 * <p>
046 * For more details on attributes, <a href=
047 * "https://support.hdfgroup.org/HDF5/doc/UG/HDF5_Users_Guide-Responsive%20HTML5/index.html">HDF5
048 * User's Guide</a>
049 * <p>
050 *
051 * The following code is an example of an attribute with 1D integer array of two elements.
052 *
053 * <pre>
054 * // Example of creating a new attribute
055 * // The name of the new attribute
056 * String name = "Data range";
057 * // Creating an unsigned 1-byte integer datatype
058 * Datatype type = new Datatype(Datatype.CLASS_INTEGER, // class
059 *                              1,                      // size in bytes
060 *                              Datatype.ORDER_LE,      // byte order
061 *                              Datatype.SIGN_NONE);    // unsigned
062 * // 1-D array of size two
063 * long[] dims = {2};
064 * // The value of the attribute
065 * int[] value = {0, 255};
066 * // Create a new attribute
067 * Attribute dataRange = new Attribute(name, type, dims);
068 * // Set the attribute value
069 * dataRange.setValue(value);
070 * // See FileFormat.writeAttribute() for how to attach an attribute to an object,
071 * &#64;see hdf.object.FileFormat#writeAttribute(HObject, Attribute, boolean)
072 * </pre>
073 *
074 *
075 * For an atomic datatype, the value of an Attribute will be a 1D array of integers, floats and
076 * strings. For a compound datatype, it will be a 1D array of strings with field members separated
077 * by a comma. For example, "{0, 10.5}, {255, 20.0}, {512, 30.0}" is a compound attribute of {int,
078 * float} of three data points.
079 *
080 * @see hdf.object.Datatype
081 *
082 * @version 2.0 4/2/2018
083 * @author Peter X. Cao, Jordan T. Henderson
084 */
085public class H5Attribute extends Attribute {
086
087    private static final long serialVersionUID = 2072473407027648309L;
088
089    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(H5Attribute.class);
090
091
092    /**
093     * Create an attribute with specified name, data type and dimension sizes.
094     *
095     * For scalar attribute, the dimension size can be either an array of size one
096     * or null, and the rank can be either 1 or zero. Attribute is a general class
097     * and is independent of file format, e.g., the implementation of attribute
098     * applies to both HDF4 and HDF5.
099     * <p>
100     * The following example creates a string attribute with the name "CLASS" and
101     * value "IMAGE".
102     *
103     * <pre>
104     * long[] attrDims = { 1 };
105     * String attrName = &quot;CLASS&quot;;
106     * String[] classValue = { &quot;IMAGE&quot; };
107     * Datatype attrType = null;
108     * try {
109     *     attrType = new H5Datatype(Datatype.CLASS_STRING, classValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE);
110     * }
111     * catch (Exception ex) {}
112     * Attribute attr = new Attribute(attrName, attrType, attrDims);
113     * attr.setValue(classValue);
114     * </pre>
115     *
116     * @param parentObj
117     *            the HObject to which this Attribute is attached.
118     * @param attrName
119     *            the name of the attribute.
120     * @param attrType
121     *            the datatype of the attribute.
122     * @param attrDims
123     *            the dimension sizes of the attribute, null for scalar attribute
124     *
125     * @see hdf.object.Datatype
126     */
127    public H5Attribute(HObject parentObj, String attrName, Datatype attrType, long[] attrDims) {
128        this(parentObj, attrName, attrType, attrDims, null);
129    }
130
131    /**
132     * Create an attribute with specific name and value.
133     *
134     * For scalar attribute, the dimension size can be either an array of size one
135     * or null, and the rank can be either 1 or zero. Attribute is a general class
136     * and is independent of file format, e.g., the implementation of attribute
137     * applies to both HDF4 and HDF5.
138     * <p>
139     * The following example creates a string attribute with the name "CLASS" and
140     * value "IMAGE".
141     *
142     * <pre>
143     * long[] attrDims = { 1 };
144     * String attrName = &quot;CLASS&quot;;
145     * String[] classValue = { &quot;IMAGE&quot; };
146     * Datatype attrType = null;
147     * try {
148     *     attrType = new H5Datatype(Datatype.CLASS_STRING, classValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE);
149     * }
150     * catch (Exception ex) {}
151     * Attribute attr = new Attribute(attrName, attrType, attrDims, classValue);
152     * </pre>
153     *
154     * @param parentObj
155     *            the HObject to which this Attribute is attached.
156     * @param attrName
157     *            the name of the attribute.
158     * @param attrType
159     *            the datatype of the attribute.
160     * @param attrDims
161     *            the dimension sizes of the attribute, null for scalar attribute
162     * @param attrValue
163     *            the value of the attribute, null if no value
164     *
165     * @see hdf.object.Datatype
166     */
167    @SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
168    public H5Attribute(HObject parentObj, String attrName, Datatype attrType, long[] attrDims, Object attrValue) {
169        super(parentObj, attrName, attrType, null);
170    }
171
172    /*
173     * (non-Javadoc)
174     *
175     * @see hdf.object.HObject#open()
176     */
177    @Override
178    public long open() {
179        long aid = super.open();
180        long pObjID = -1;
181
182        try {
183            pObjID = parentObject.open();
184            if (pObjID >= 0) {
185                if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
186                    log.trace("open(): FILE_TYPE_HDF5");
187                    if (H5.H5Aexists(pObjID, getName()))
188                        aid = H5.H5Aopen(pObjID, getName(), HDF5Constants.H5P_DEFAULT);
189                }
190            }
191
192            log.trace("open(): aid={}", aid);
193        }
194        catch (Exception ex) {
195            log.debug("open(): Failed to open attribute {}: ", getName(), ex);
196            aid = -1;
197        }
198        finally {
199            parentObject.close(pObjID);
200        }
201
202        return aid;
203    }
204
205    /*
206     * (non-Javadoc)
207     *
208     * @see hdf.object.HObject#close(int)
209     */
210    @Override
211    public void close(long aid) {
212        if (aid >= 0) {
213            if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
214                log.trace("close(): FILE_TYPE_HDF5");
215                try {
216                    H5.H5Aclose(aid);
217                }
218                catch (HDF5Exception ex) {
219                    log.debug("close(): H5Aclose({}) failure: ", aid, ex);
220                }
221            }
222        }
223    }
224
225    @Override
226    public void init() {
227        super.init();
228        if (inited) {
229            return;
230        }
231
232        if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
233            long aid = -1;
234            long tid = -1;
235            int tclass = -1;
236            flatNameList = new Vector<>();
237            flatTypeList = new Vector<>();
238            long[] memberTIDs = null;
239
240            log.trace("init(): FILE_TYPE_HDF5");
241            aid = open();
242            if (aid >= 0) {
243                try {
244                    tid = H5.H5Aget_type(aid);
245                    tclass = H5.H5Tget_class(tid);
246
247                    long tmptid = 0;
248
249                    // Handle ARRAY and VLEN types by getting the base type
250                    if (tclass == HDF5Constants.H5T_ARRAY || tclass == HDF5Constants.H5T_VLEN) {
251                        try {
252                            tmptid = tid;
253                            tid = H5.H5Tget_super(tmptid);
254                            log.trace("init(): H5T_ARRAY or H5T_VLEN class old={}, new={}", tmptid, tid);
255                        }
256                        catch (Exception ex) {
257                            log.debug("init(): H5T_ARRAY or H5T_VLEN H5Tget_super({}) failure: ", tmptid, ex);
258                            tid = -1;
259                        }
260                        finally {
261                            try {
262                                H5.H5Tclose(tmptid);
263                            }
264                            catch (HDF5Exception ex) {
265                                log.debug("init(): H5Tclose({}) failure: ", tmptid, ex);
266                            }
267                        }
268                    }
269
270                    if (H5.H5Tget_class(tid) == HDF5Constants.H5T_COMPOUND) {
271                        // initialize member information
272                        H5Datatype.extractCompoundInfo((H5Datatype) getDatatype(), "", flatNameList, flatTypeList);
273                        numberOfMembers = flatNameList.size();
274                        log.trace("init(): numberOfMembers={}", numberOfMembers);
275
276                        memberNames = new String[numberOfMembers];
277                        memberTIDs = new long[numberOfMembers];
278                        memberTypes = new Datatype[numberOfMembers];
279                        memberOrders = new int[numberOfMembers];
280                        isMemberSelected = new boolean[numberOfMembers];
281                        memberDims = new Object[numberOfMembers];
282
283                        for (int i = 0; i < numberOfMembers; i++) {
284                            isMemberSelected[i] = true;
285                            memberTIDs[i] = flatTypeList.get(i).createNative();
286
287                            try {
288                                memberTypes[i] = flatTypeList.get(i);
289                            }
290                            catch (Exception ex) {
291                                log.debug("init(): failed to create datatype for member[{}]: ", i, ex);
292                                memberTypes[i] = null;
293                            }
294
295                            memberNames[i] = flatNameList.get(i);
296                            memberOrders[i] = 1;
297                            memberDims[i] = null;
298                            log.trace("init()[{}]: memberNames[{}]={}, memberTIDs[{}]={}, memberTypes[{}]={}", i, i,
299                                    memberNames[i], i, memberTIDs[i], i, memberTypes[i]);
300
301                            try {
302                                tclass = H5.H5Tget_class(memberTIDs[i]);
303                            }
304                            catch (HDF5Exception ex) {
305                                log.debug("init(): H5Tget_class({}) failure: ", memberTIDs[i], ex);
306                            }
307
308                            if (tclass == HDF5Constants.H5T_ARRAY) {
309                                int n = H5.H5Tget_array_ndims(memberTIDs[i]);
310                                long mdim[] = new long[n];
311                                H5.H5Tget_array_dims(memberTIDs[i], mdim);
312                                int idim[] = new int[n];
313                                for (int j = 0; j < n; j++)
314                                    idim[j] = (int) mdim[j];
315                                memberDims[i] = idim;
316                                tmptid = H5.H5Tget_super(memberTIDs[i]);
317                                memberOrders[i] = (int) (H5.H5Tget_size(memberTIDs[i]) / H5.H5Tget_size(tmptid));
318                                try {
319                                    H5.H5Tclose(tmptid);
320                                }
321                                catch (HDF5Exception ex) {
322                                    log.debug("init(): memberTIDs[{}] H5Tclose(tmptid {}) failure: ", i, tmptid, ex);
323                                }
324                            }
325                        } // (int i=0; i<numberOfMembers; i++)
326                    }
327
328                    inited = true;
329                }
330                catch (HDF5Exception ex) {
331                    numberOfMembers = 0;
332                    memberNames = null;
333                    memberTypes = null;
334                    memberOrders = null;
335                    log.debug("init(): ", ex);
336                }
337                finally {
338                    try {
339                        H5.H5Tclose(tid);
340                    }
341                    catch (HDF5Exception ex2) {
342                        log.debug("init(): H5Tclose({}) failure: ", tid, ex2);
343                    }
344
345                    if (memberTIDs != null) {
346                        for (int i = 0; i < memberTIDs.length; i++) {
347                            try {
348                                H5.H5Tclose(memberTIDs[i]);
349                            }
350                            catch (Exception ex) {
351                                log.debug("init(): H5Tclose(memberTIDs[{}] {}) failure: ", i, memberTIDs[i], ex);
352                            }
353                        }
354                    }
355                }
356
357                close(aid);
358            }
359        }
360
361        resetSelection();
362    }
363
364    /**
365     * Given an array of bytes representing a compound Datatype and a start index
366     * and length, converts len number of bytes into the correct Object type and
367     * returns it.
368     *
369     * @param data
370     *            The byte array representing the data of the compound Datatype
371     * @param data_type
372     *            The type of data to convert the bytes to
373     * @param start
374     *            The start index of the bytes to get
375     * @param len
376     *            The number of bytes to convert
377     * @return The converted type of the bytes
378     */
379    @Override
380    protected Object convertCompoundByteMember(byte[] data, long data_type, long start, long len) {
381        Object currentData = null;
382
383        try {
384            long typeClass = H5.H5Tget_class(data_type);
385            long size = H5.H5Tget_size(data_type);
386
387            if (typeClass == HDF5Constants.H5T_INTEGER) {
388                currentData = HDFNativeData.byteToInt((int) start, (int) (len / size), data);
389            }
390            else if (typeClass == HDF5Constants.H5T_FLOAT) {
391                currentData = HDFNativeData.byteToDouble((int) start, 1, data);
392            }
393        }
394        catch (Exception ex) {
395            log.debug("convertCompoundByteMember(): conversion failure: ", ex);
396        }
397
398        return currentData;
399    }
400}