    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}