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.h5;
016
017import java.lang.reflect.Array;
018import java.math.BigDecimal;
019import java.math.BigInteger;
020import java.nio.ByteBuffer;
021import java.nio.ByteOrder;
022import java.text.DecimalFormat;
023
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.Collection;
027import java.util.HashMap;
028import java.util.Iterator;
029import java.util.List;
030import java.util.Map;
031import java.util.Vector;
032
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036import hdf.hdf5lib.H5;
037import hdf.hdf5lib.HDF5Constants;
038import hdf.hdf5lib.HDFNativeData;
039import hdf.hdf5lib.exceptions.HDF5DataFiltersException;
040import hdf.hdf5lib.exceptions.HDF5Exception;
041import hdf.hdf5lib.exceptions.HDF5LibraryException;
042
043import hdf.object.Attribute;
044import hdf.object.DataFormat;
045import hdf.object.Dataset;
046import hdf.object.Datatype;
047import hdf.object.FileFormat;
048import hdf.object.Group;
049import hdf.object.HObject;
050import hdf.object.MetaDataContainer;
051import hdf.object.ScalarDS;
052
053import hdf.object.h5.H5Attribute;
054import hdf.object.h5.H5Datatype;
055import hdf.object.h5.H5ReferenceType;
056
057/**
058 * An attribute is a (name, value) pair of metadata attached to a primary data object such as a dataset, group or named
059 * datatype.
060 *
061 * Like a dataset, an attribute has a name, datatype and dataspace.
062 *
063 * For more details on attributes, <a href="https://hdfgroup.github.io/hdf5/_h5_a__u_g.html#sec_attribute">HDF5
064 * Attributes in HDF5 User Guide</a>
065 *
066 * The following code is an example of an attribute with 1D integer array of two elements.
067 *
068 * <pre>
069 * // Example of creating a new attribute
070 * // The name of the new attribute
071 * String name = "Data range";
072 * // Creating an unsigned 1-byte integer datatype
073 * Datatype type = new Datatype(Datatype.CLASS_INTEGER, // class
074 *                              1,                      // size in bytes
075 *                              Datatype.ORDER_LE,      // byte order
076 *                              Datatype.SIGN_NONE);    // unsigned
077 * // 1-D array of size two
078 * long[] dims = {2};
079 * // The value of the attribute
080 * int[] value = {0, 255};
081 * // Create a new attribute
082 * Attribute dataRange = new H5ScalarAttr(name, type, dims);
083 * // Set the attribute value
084 * dataRange.setValue(value);
085 * // See FileFormat.writeAttribute() for how to attach an attribute to an object,
086 * &#64;see hdf.object.FileFormat#writeAttribute(HObject, Attribute, boolean)
087 * </pre>
088 *
089 *
090 * For an atomic datatype, the value of an Attribute will be a 1D array of integers, floats and strings.
091 *
092 * @see hdf.object.Datatype
093 *
094 * @version 1.0 6/15/2021
095 * @author Allen Byrne
096 */
097public class H5ScalarAttr extends ScalarDS implements H5Attribute
098{
099    private static final long serialVersionUID = 2072473407027648309L;
100
101    private static final Logger log = LoggerFactory.getLogger(H5ScalarAttr.class);
102
103    /** The HObject to which this NC2Attribute is attached, Attribute interface */
104    protected HObject         parentObject;
105
106    /** additional information and properties for the attribute, Attribute interface */
107    private transient Map<String, Object> properties;
108
109    /**
110     * flag to indicate if the datatype in file is the same as dataype in memory
111     */
112    protected boolean isNativeDatatype = false;
113
114    /**
115     * Create an attribute with specified name, data type and dimension sizes.
116     *
117     * For scalar attribute, the dimension size can be either an array of size one
118     * or null, and the rank can be either 1 or zero. Attribute is a general class
119     * and is independent of file format, e.g., the implementation of attribute
120     * applies to both HDF4 and HDF5.
121     *
122     * The following example creates a string attribute with the name "CLASS" and
123     * value "IMAGE".
124     *
125     * <pre>
126     * long[] attrDims = { 1 };
127     * String attrName = &quot;CLASS&quot;;
128     * String[] classValue = { &quot;IMAGE&quot; };
129     * Datatype attrType = null;
130     * try {
131     *     attrType = new H5Datatype(Datatype.CLASS_STRING, classValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE);
132     * }
133     * catch (Exception ex) {}
134     * Attribute attr = new H5ScalarAttr(attrName, attrType, attrDims);
135     * attr.setValue(classValue);
136     * </pre>
137     *
138     * @param parentObj
139     *            the HObject to which this H5ScalarAttr is attached.
140     * @param attrName
141     *            the name of the attribute.
142     * @param attrType
143     *            the datatype of the attribute.
144     * @param attrDims
145     *            the dimension sizes of the attribute, null for scalar attribute
146     *
147     * @see hdf.object.Datatype
148     */
149    public H5ScalarAttr(HObject parentObj, String attrName, Datatype attrType, long[] attrDims) {
150        this(parentObj, attrName, attrType, attrDims, null);
151    }
152
153    /**
154     * Create an attribute with specific name and value.
155     *
156     * For scalar attribute, the dimension size can be either an array of size one
157     * or null, and the rank can be either 1 or zero. Attribute is a general class
158     * and is independent of file format, e.g., the implementation of attribute
159     * applies to both HDF4 and HDF5.
160     *
161     * The following example creates a string attribute with the name "CLASS" and
162     * value "IMAGE".
163     *
164     * <pre>
165     * long[] attrDims = { 1 };
166     * String attrName = &quot;CLASS&quot;;
167     * String[] classValue = { &quot;IMAGE&quot; };
168     * Datatype attrType = null;
169     * try {
170     *     attrType = new H5Datatype(Datatype.CLASS_STRING, classValue[0].length() + 1, Datatype.NATIVE, Datatype.NATIVE);
171     * }
172     * catch (Exception ex) {}
173     * Attribute attr = new H5ScalarAttr(attrName, attrType, attrDims, classValue);
174     * </pre>
175     *
176     * @param parentObj
177     *            the HObject to which this H5ScalarAttr is attached.
178     * @param attrName
179     *            the name of the attribute.
180     * @param attrType
181     *            the datatype of the attribute.
182     * @param attrDims
183     *            the dimension sizes of the attribute, null for scalar attribute
184     * @param attrValue
185     *            the value of the attribute, null if no value
186     *
187     * @see hdf.object.Datatype
188     */
189    @SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
190    public H5ScalarAttr(HObject parentObj, String attrName, Datatype attrType, long[] attrDims, Object attrValue) {
191        super((parentObj == null) ? null : parentObj.getFileFormat(), attrName,
192                (parentObj == null) ? null : parentObj.getFullName(), null);
193
194        log.trace("H5ScalarAttr: start {}", parentObj);
195        this.parentObject = parentObj;
196
197        datatype = attrType;
198
199        if (attrValue != null) {
200            data = attrValue;
201            originalBuf = attrValue;
202            isDataLoaded = true;
203        }
204        properties = new HashMap();
205
206        if (attrDims == null) {
207            rank = 1;
208            dims = new long[] { 1 };
209            isScalar = true;
210        }
211        else {
212            dims = attrDims;
213            rank = dims.length;
214            isScalar = false;
215        }
216
217        selectedDims = new long[rank];
218        startDims = new long[rank];
219        selectedStride = new long[rank];
220
221        log.trace("attrName={}, attrType={}, attrValue={}, rank={}, isUnsigned={}",
222                attrName, attrType.getDescription(), data, rank, getDatatype().isUnsigned());
223
224        resetSelection();
225    }
226
227    /*
228     * (non-Javadoc)
229     *
230     * @see hdf.object.HObject#open()
231     */
232    @Override
233    public long open() {
234        if (parentObject == null) {
235            log.debug("open(): attribute's parent object is null");
236            return HDF5Constants.H5I_INVALID_HID;
237        }
238
239        long aid = HDF5Constants.H5I_INVALID_HID;
240        long pObjID = HDF5Constants.H5I_INVALID_HID;
241
242        try {
243            pObjID = parentObject.open();
244            if (pObjID >= 0) {
245                if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
246                    log.trace("open(): FILE_TYPE_HDF5");
247                    if (H5.H5Aexists(pObjID, getName()))
248                        aid = H5.H5Aopen(pObjID, getName(), HDF5Constants.H5P_DEFAULT);
249                }
250            }
251
252            log.trace("open(): aid={}", aid);
253        }
254        catch (Exception ex) {
255            log.debug("open(): Failed to open attribute {}: ", getName(), ex);
256            aid = HDF5Constants.H5I_INVALID_HID;
257        }
258        finally {
259            parentObject.close(pObjID);
260        }
261
262        return aid;
263    }
264
265    /*
266     * (non-Javadoc)
267     *
268     * @see hdf.object.HObject#close(int)
269     */
270    @Override
271    public void close(long aid) {
272        if (aid >= 0) {
273            if (this.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
274                log.trace("close(): FILE_TYPE_HDF5");
275                try {
276                    H5.H5Aclose(aid);
277                }
278                catch (HDF5Exception ex) {
279                    log.debug("close(): H5Aclose({}) failure: ", aid, ex);
280                }
281            }
282        }
283    }
284
285    /**
286     * Retrieves datatype and dataspace information from file and sets the attribute
287     * in memory.
288     *
289     * The init() is designed to support lazy operation in a attribute object. When a
290     * data object is retrieved from file, the datatype, dataspace and raw data are
291     * not loaded into memory. When it is asked to read the raw data from file,
292     * init() is first called to get the datatype and dataspace information, then
293     * load the raw data from file.
294     */
295    @Override
296    public void init() {
297        if (inited) {
298            // already called. Initialize only once
299            resetSelection();
300            log.trace("init(): H5ScalarAttr already initialized");
301            return;
302        }
303
304        long aid = HDF5Constants.H5I_INVALID_HID;
305        long tid = HDF5Constants.H5I_INVALID_HID;
306        long sid = HDF5Constants.H5I_INVALID_HID;
307        long nativeTID = HDF5Constants.H5I_INVALID_HID;
308
309        aid = open();
310        if (aid >= 0) {
311            try {
312                sid = H5.H5Aget_space(aid);
313                rank = H5.H5Sget_simple_extent_ndims(sid);
314                space_type = H5.H5Sget_simple_extent_type(sid);
315                if (space_type == HDF5Constants.H5S_NULL)
316                    isNULL = true;
317                else
318                    isNULL = false;
319                tid = H5.H5Aget_type(aid);
320                log.trace("init(): tid={} sid={} rank={} space_type={}", tid, sid, rank, space_type);
321
322                if (rank == 0) {
323                    // a scalar data point
324                    isScalar = true;
325                    rank = 1;
326                    dims = new long[] { 1 };
327                    log.trace("init(): rank is a scalar data point");
328                }
329                else {
330                    isScalar = false;
331                    dims = new long[rank];
332                    maxDims = new long[rank];
333                    H5.H5Sget_simple_extent_dims(sid, dims, maxDims);
334                    log.trace("init(): rank={}, dims={}, maxDims={}", rank, dims, maxDims);
335                }
336
337                if (datatype == null) {
338                    try {
339                        int nativeClass = H5.H5Tget_class(tid);
340                        if (nativeClass == HDF5Constants.H5T_REFERENCE) {
341                            long lsize = 1;
342                            if (rank > 0) {
343                                log.trace("init(): rank={}, dims={}", rank, dims);
344                                for (int j = 0; j < dims.length; j++) {
345                                    lsize *= dims[j];
346                                }
347                            }
348                            datatype = new H5ReferenceType(getFileFormat(), lsize, tid);
349                        }
350                        else
351                            datatype = new H5Datatype(getFileFormat(), tid);
352
353                        log.trace("init(): tid={} is tclass={} has isText={} : isNamed={} :  isVLEN={} : isEnum={} : isUnsigned={} : isRegRef={}",
354                                tid, datatype.getDatatypeClass(), ((H5Datatype) datatype).isText(), datatype.isNamed(), datatype.isVLEN(),
355                                datatype.isEnum(), datatype.isUnsigned(), ((H5Datatype) datatype).isRegRef());
356                    }
357                    catch (Exception ex) {
358                        log.debug("init(): failed to create datatype for attribute: ", ex);
359                        datatype = null;
360                    }
361                }
362
363                // Check if the datatype in the file is the native datatype
364                try {
365                    nativeTID = H5.H5Tget_native_type(tid);
366                    isNativeDatatype = H5.H5Tequal(tid, nativeTID);
367                    log.trace("init(): isNativeDatatype={}", isNativeDatatype);
368                }
369                catch (Exception ex) {
370                    log.debug("init(): check if native type failure: ", ex);
371                }
372
373                inited = true;
374            }
375            catch (HDF5Exception ex) {
376                log.debug("init(): ", ex);
377            }
378            finally {
379                try {
380                    H5.H5Tclose(nativeTID);
381                }
382                catch (Exception ex2) {
383                    log.debug("init(): H5Tclose(nativeTID {}) failure: ", nativeTID, ex2);
384                }
385                try {
386                    H5.H5Tclose(tid);
387                }
388                catch (HDF5Exception ex2) {
389                    log.debug("init(): H5Tclose(tid {}) failure: ", tid, ex2);
390                }
391                try {
392                    H5.H5Sclose(sid);
393                }
394                catch (HDF5Exception ex2) {
395                    log.debug("init(): H5Sclose(sid {}) failure: ", sid, ex2);
396                }
397
398            }
399
400            close(aid);
401
402            startDims = new long[rank];
403            selectedDims = new long[rank];
404
405            resetSelection();
406        }
407        else {
408            log.debug("init(): failed to open attribute");
409        }
410    }
411
412    /**
413     * Returns the datatype of the data object.
414     *
415     * @return the datatype of the data object.
416     */
417    @Override
418    public Datatype getDatatype() {
419        if (!inited)
420            init();
421
422        if (datatype == null) {
423            long aid = HDF5Constants.H5I_INVALID_HID;
424            long tid = HDF5Constants.H5I_INVALID_HID;
425
426            aid = open();
427            if (aid >= 0) {
428                try {
429                    tid = H5.H5Aget_type(aid);
430                    log.trace("getDatatype(): isNativeDatatype={}", isNativeDatatype);
431                    if (!isNativeDatatype) {
432                        long tmptid = -1;
433                        try {
434                            tmptid = H5Datatype.toNative(tid);
435                            if (tmptid >= 0) {
436                                try {
437                                    H5.H5Tclose(tid);
438                                }
439                                catch (Exception ex2) {
440                                    log.debug("getDatatype(): H5Tclose(tid {}) failure: ", tid, ex2);
441                                }
442                                tid = tmptid;
443                            }
444                        }
445                        catch (Exception ex) {
446                            log.debug("getDatatype(): toNative: ", ex);
447                        }
448                    }
449                    int nativeClass = H5.H5Tget_class(tid);
450                    if (nativeClass == HDF5Constants.H5T_REFERENCE) {
451                        long lsize = 1;
452                        long sid = H5.H5Aget_space(aid);
453                        int rank = H5.H5Sget_simple_extent_ndims(sid);
454                        if (rank > 0) {
455                            long dims[] = new long[rank];
456                            H5.H5Sget_simple_extent_dims(sid, dims, null);
457                            log.trace("getDatatype(): rank={}, dims={}", rank, dims);
458                            for (int j = 0; j < dims.length; j++) {
459                                lsize *= dims[j];
460                            }
461                        }
462                        datatype = new H5ReferenceType(getFileFormat(), lsize, tid);
463                    }
464                    else
465                        datatype = new H5Datatype(getFileFormat(), tid);
466                }
467                catch (Exception ex) {
468                    log.debug("getDatatype(): ", ex);
469                }
470                finally {
471                    try {
472                        H5.H5Tclose(tid);
473                    }
474                    catch (HDF5Exception ex) {
475                        log.debug("getDatatype(): H5Tclose(tid {}) failure: ", tid, ex);
476                    }
477                    try {
478                        H5.H5Aclose(aid);
479                    }
480                    catch (HDF5Exception ex) {
481                        log.debug("getDatatype(): H5Aclose(aid {}) failure: ", aid, ex);
482                    }
483                }
484            }
485        }
486
487        return datatype;
488    }
489
490    /**
491     * Returns the data buffer of the attribute in memory.
492     *
493     * If data is already loaded into memory, returns the data; otherwise, calls
494     * read() to read data from file into a memory buffer and returns the memory
495     * buffer.
496     *
497     * The whole attribute is read into memory. Users can also select
498     * a subset from the whole data. Subsetting is done in an implicit way.
499     *
500     * <b>How to Select a Subset</b>
501     *
502     * A selection is specified by three arrays: start, stride and count.
503     * <ol>
504     * <li>start: offset of a selection
505     * <li>stride: determines how many elements to move in each dimension
506     * <li>count: number of elements to select in each dimension
507     * </ol>
508     * getStartDims(), getStride() and getSelectedDims() returns the start,
509     * stride and count arrays respectively. Applications can make a selection
510     * by changing the values of the arrays.
511     *
512     * The following example shows how to make a subset. In the example, the
513     * attribute is a 4-dimensional array of [200][100][50][10], i.e. dims[0]=200;
514     * dims[1]=100; dims[2]=50; dims[3]=10; <br>
515     * We want to select every other data point in dims[1] and dims[2]
516     *
517     * <pre>
518     * int rank = attribute.getRank(); // number of dimensions of the attribute
519     * long[] dims = attribute.getDims(); // the dimension sizes of the attribute
520     * long[] selected = attribute.getSelectedDims(); // the selected size of the attribute
521     * long[] start = attribute.getStartDims(); // the offset of the selection
522     * long[] stride = attribute.getStride(); // the stride of the attribute
523     * int[] selectedIndex = attribute.getSelectedIndex(); // the selected dimensions for display
524     *
525     * // select dim1 and dim2 as 2D data for display,and slice through dim0
526     * selectedIndex[0] = 1;
527     * selectedIndex[1] = 2;
528     * selectedIndex[2] = 0;
529     *
530     * // reset the selection arrays
531     * for (int i = 0; i &lt; rank; i++) {
532     *     start[i] = 0;
533     *     selected[i] = 1;
534     *     stride[i] = 1;
535     * }
536     *
537     * // set stride to 2 on dim1 and dim2 so that every other data point is
538     * // selected.
539     * stride[1] = 2;
540     * stride[2] = 2;
541     *
542     * // set the selection size of dim1 and dim2
543     * selected[1] = dims[1] / stride[1];
544     * selected[2] = dims[1] / stride[2];
545     *
546     * // when H5ScalarAttr.getData() is called, the selection above will be used since
547     * // the dimension arrays are passed by reference. Changes of these arrays
548     * // outside the attribute object directly change the values of these array
549     * // in the attribute object.
550     * </pre>
551     *
552     * For H5ScalarAttr, the memory data buffer is a one-dimensional array of byte,
553     * short, int, float, double or String type based on the datatype of the
554     * attribute.
555     *
556     * @return the memory buffer of the attribute.
557     *
558     * @throws Exception if object can not be read
559     * @throws OutOfMemoryError if memory is exhausted
560     */
561    @Override
562    public Object getData() throws Exception, OutOfMemoryError {
563        log.trace("getData(): isDataLoaded={}", isDataLoaded);
564        if (!isDataLoaded)
565            data = read(); // load the data, attributes read all data
566
567        nPoints = 1;
568        log.trace("getData(): selectedDims length={}", selectedDims.length);
569        int point_len = selectedDims.length;
570        //Partial data for 3 or more dimensions
571        if (rank > 2)
572            point_len = 3;
573        for (int j = 0; j < point_len; j++) {
574            log.trace("getData(): selectedDims[{}]={}", j, selectedDims[j]);
575            nPoints *= selectedDims[j];
576        }
577        log.trace("getData: read {}", nPoints);
578
579        // apply the selection for 3 or more dimensions
580        // selection only expects to use 3 selectedDims
581        //     where selectedIndex[0] is the row dimension
582        //     where selectedIndex[1] is the col dimension
583        //     where selectedIndex[2] is the frame dimension
584        if (rank > 2)
585            data = AttributeSelection();
586
587        return data;
588    }
589
590    /*
591     * (non-Javadoc)
592     * @see hdf.object.Dataset#copy(hdf.object.Group, java.lang.String, long[], java.lang.Object)
593     */
594    @Override
595    public Dataset copy(Group pgroup, String dstName, long[] dims, Object buff) throws Exception {
596        // not supported
597        throw new UnsupportedOperationException("copy operation unsupported for H5.");
598    }
599
600    /*
601     * (non-Javadoc)
602     *
603     * @see hdf.object.Attribute#readBytes()
604     */
605    @Override
606    public byte[] readBytes() throws HDF5Exception {
607        byte[] theData = null;
608
609        if (!isInited())
610            init();
611
612        long aid = open();
613        if (aid >= 0) {
614            long tid = HDF5Constants.H5I_INVALID_HID;
615
616            try {
617                long[] lsize = { 1 };
618                for (int j = 0; j < selectedDims.length; j++)
619                    lsize[0] *= selectedDims[j];
620
621                tid = H5.H5Aget_type(aid);
622                long size = H5.H5Tget_size(tid) * lsize[0];
623                log.trace("readBytes(): size = {}", size);
624
625                if (size < Integer.MIN_VALUE || size > Integer.MAX_VALUE)
626                    throw new Exception("Invalid int size");
627
628                theData = new byte[(int)size];
629
630                log.trace("readBytes(): read attribute id {} of size={}", tid, lsize);
631                H5.H5Aread(aid, tid, theData);
632            }
633            catch (Exception ex) {
634                log.debug("readBytes(): failed to read data: ", ex);
635            }
636            finally {
637                try {
638                    H5.H5Tclose(tid);
639                }
640                catch (HDF5Exception ex2) {
641                    log.debug("readBytes(): H5Tclose(tid {}) failure: ", tid, ex2);
642                }
643                close(aid);
644            }
645        }
646
647        return theData;
648    }
649
650    /**
651     * Reads the data from file.
652     *
653     * read() reads the data from file to a memory buffer and returns the memory
654     * buffer. The attribute object does not hold the memory buffer. To store the
655     * memory buffer in the attribute object, one must call getData().
656     *
657     * By default, the whole attribute is read into memory.
658     *
659     * For ScalarAttr, the memory data buffer is a one-dimensional array of byte,
660     * short, int, float, double or String type based on the datatype of the
661     * attribute.
662     *
663     * @return the data read from file.
664     *
665     * @see #getData()
666     * @see hdf.object.DataFormat#read()
667     *
668     * @throws Exception
669     *             if object can not be read
670     */
671    @Override
672    public Object read() throws Exception {
673        Object readData = null;
674
675        if (!isInited())
676            init();
677
678        try {
679            readData = scalarAttributeCommonIO(H5File.IO_TYPE.READ, null);
680        }
681        catch (Exception ex) {
682            log.debug("read(): failed to read scalar attribute: ", ex);
683            throw new Exception("failed to read scalar attribute: " + ex.getMessage(), ex);
684        }
685
686        return readData;
687    }
688
689    /**
690     * Writes the given data buffer into this attribute in a file.
691     *
692     * @param buf
693     *            The buffer that contains the data values.
694     *
695     * @throws Exception
696     *             If there is an error at the HDF5 library level.
697     */
698    @Override
699    public void write(Object buf) throws Exception {
700        if (this.getFileFormat().isReadOnly())
701            throw new Exception("cannot write to scalar attribute in file opened as read-only");
702
703        if (!buf.equals(data))
704            setData(buf);
705
706        if (parentObject == null) {
707            log.debug("write(Object): parent object is null; nowhere to write attribute to");
708            return;
709        }
710
711        ((MetaDataContainer) getParentObject()).writeMetadata(this);
712
713        try {
714            scalarAttributeCommonIO(H5File.IO_TYPE.WRITE, buf);
715        }
716        catch (Exception ex) {
717            log.debug("write(Object): failed to write to scalar attribute: ", ex);
718            throw new Exception("failed to write to scalar attribute: " + ex.getMessage(), ex);
719        }
720        resetSelection();
721    }
722
723    private Object scalarAttributeCommonIO(H5File.IO_TYPE ioType, Object writeBuf) throws Exception {
724        H5Datatype dsDatatype = (H5Datatype)getDatatype();
725        Object theData = null;
726
727        /*
728         * I/O type-specific pre-initialization.
729         */
730        if (ioType == H5File.IO_TYPE.WRITE) {
731            if (writeBuf == null) {
732                log.debug("scalarAttributeCommonIO(): writeBuf is null");
733                throw new Exception("write buffer is null");
734            }
735        }
736
737        long aid = open();
738        if (aid >= 0) {
739            log.trace("scalarAttributeCommonIO(): isDataLoaded={}", isDataLoaded);
740            try {
741                theData = AttributeCommonIO(aid, ioType, writeBuf);
742            }
743            finally {
744                close(aid);
745            }
746        }
747        else
748            log.debug("scalarAttributeCommonIO(): failed to open attribute");
749
750        return theData;
751    }
752
753    /* Implement interface Attribute */
754
755    /**
756     * Returns the HObject to which this Attribute is currently "attached".
757     *
758     * @return the HObject to which this Attribute is currently "attached".
759     */
760    @Override
761    public HObject getParentObject() {
762        return parentObject;
763    }
764
765    /**
766     * Sets the HObject to which this Attribute is "attached".
767     *
768     * @param pObj
769     *            the new HObject to which this Attribute is "attached".
770     */
771    @Override
772    public void setParentObject(HObject pObj) {
773        parentObject = pObj;
774    }
775
776    /**
777     * set a property for the attribute.
778     *
779     * @param key the attribute Map key
780     * @param value the attribute Map value
781     */
782    @Override
783    public void setProperty(String key, Object value) {
784        properties.put(key, value);
785    }
786
787    /**
788     * get a property for a given key.
789     *
790     * @param key the attribute Map key
791     *
792     * @return the property
793     */
794    @Override
795    public Object getProperty(String key) {
796        return properties.get(key);
797    }
798
799    /**
800     * get all property keys.
801     *
802     * @return the Collection of property keys
803     */
804    @Override
805    public Collection<String> getPropertyKeys() {
806        return properties.keySet();
807    }
808
809    /**
810     * Returns the name of the object. For example, "Raster Image #2".
811     *
812     * @return The name of the object.
813     */
814    @Override
815    public final String getAttributeName() {
816        return getName();
817    }
818
819    /**
820     * Retrieves the attribute data from the file.
821     *
822     * @return the attribute data.
823     *
824     * @throws Exception
825     *             if the data can not be retrieved
826     */
827    @Override
828    public final Object getAttributeData() throws Exception, OutOfMemoryError {
829        return getData();
830    }
831
832    /**
833     * Returns the datatype of the attribute.
834     *
835     * @return the datatype of the attribute.
836     */
837    @Override
838    public final Datatype getAttributeDatatype() {
839        return getDatatype();
840    }
841
842    /**
843     * Returns the space type for the attribute. It returns a
844     * negative number if it failed to retrieve the type information from
845     * the file.
846     *
847     * @return the space type for the attribute.
848     */
849    @Override
850    public final int getAttributeSpaceType() {
851        return getSpaceType();
852    }
853
854    /**
855     * Returns the rank (number of dimensions) of the attribute. It returns a
856     * negative number if it failed to retrieve the dimension information from
857     * the file.
858     *
859     * @return the number of dimensions of the attribute.
860     */
861    @Override
862    public final int getAttributeRank() {
863        return getRank();
864    }
865
866    /**
867     * Returns the selected size of the rows and columns of the attribute. It returns a
868     * negative number if it failed to retrieve the size information from
869     * the file.
870     *
871     * @return the selected size of the rows and colums of the attribute.
872     */
873    @Override
874    public final int getAttributePlane() {
875        return (int)getWidth() * (int)getHeight();
876    }
877
878    /**
879     * Returns the array that contains the dimension sizes of the data value of
880     * the attribute. It returns null if it failed to retrieve the dimension
881     * information from the file.
882     *
883     * @return the dimension sizes of the attribute.
884     */
885    @Override
886    public final long[] getAttributeDims() {
887        return getDims();
888    }
889
890    /**
891     * @return true if the dataspace is a NULL; otherwise, returns false.
892     */
893    @Override
894    public boolean isAttributeNULL() {
895        return isNULL();
896    }
897
898    /**
899     * @return true if the data is a single scalar point; otherwise, returns false.
900     */
901    @Override
902    public boolean isAttributeScalar() {
903        return isScalar();
904    }
905
906    /**
907     * Not for public use in the future.
908     *
909     * setData() is not safe to use because it changes memory buffer
910     * of the dataset object. Dataset operations such as write/read
911     * will fail if the buffer type or size is changed.
912     *
913     * @param d  the object data -must be an array of Objects
914     */
915    @Override
916    public void setAttributeData(Object d) {
917        setData(d);
918    }
919
920    /**
921     * Writes the memory buffer of this dataset to file.
922     *
923     * @throws Exception if buffer can not be written
924     */
925    @Override
926    public void writeAttribute() throws Exception {
927        write();
928    }
929
930    /**
931     * Writes the given data buffer into this attribute in a file.
932     *
933     * The data buffer is a vector that contains the data values of compound fields. The data is written
934     * into file as one data blob.
935     *
936     * @param buf
937     *            The vector that contains the data values of compound fields.
938     *
939     * @throws Exception
940     *             If there is an error at the library level.
941     */
942    @Override
943    public void writeAttribute(Object buf) throws Exception {
944        write(buf);
945    }
946
947    /**
948     * Returns a string representation of the data value. For
949     * example, "0, 255".
950     *
951     * For a compound datatype, it will be a 1D array of strings with field
952     * members separated by the delimiter. For example,
953     * "{0, 10.5}, {255, 20.0}, {512, 30.0}" is a compound attribute of {int,
954     * float} of three data points.
955     *
956     * @param delimiter
957     *            The delimiter used to separate individual data points. It
958     *            can be a comma, semicolon, tab or space. For example,
959     *            toString(",") will separate data by commas.
960     *
961     * @return the string representation of the data values.
962     */
963    @Override
964    public String toAttributeString(String delimiter) {
965        return toString(delimiter, -1);
966    }
967
968    /**
969     * Returns a string representation of the data value. For
970     * example, "0, 255".
971     *
972     * For a compound datatype, it will be a 1D array of strings with field
973     * members separated by the delimiter. For example,
974     * "{0, 10.5}, {255, 20.0}, {512, 30.0}" is a compound attribute of {int,
975     * float} of three data points.
976     *
977     * @param delimiter
978     *            The delimiter used to separate individual data points. It
979     *            can be a comma, semicolon, tab or space. For example,
980     *            toString(",") will separate data by commas.
981     * @param maxItems
982     *            The maximum number of Array values to return
983     *
984     * @return the string representation of the data values.
985     */
986    @Override
987    public String toAttributeString(String delimiter, int maxItems) {
988        Object theData = originalBuf;
989        if (theData == null) {
990            log.debug("toAttributeString: value is null");
991            return null;
992        }
993
994        Class<? extends Object> valClass = theData.getClass();
995        if (!valClass.isArray() && !getDatatype().isRef()) {
996            log.trace("toAttributeString: finish - not array");
997            String strValue = theData.toString();
998            if (maxItems > 0 && strValue.length() > maxItems)
999                // truncate the extra characters
1000                strValue = strValue.substring(0, maxItems);
1001            return strValue;
1002        }
1003
1004        int n = 0;
1005        Datatype dtype = getDatatype();
1006        // value is an array
1007        if (valClass.isArray()) {
1008            n = Array.getLength(theData);
1009            if (dtype.isRef())
1010                n /= (int)dtype.getDatatypeSize();
1011        }
1012        else
1013            n = ((ArrayList<Object[]>)theData).size();
1014        if ((maxItems > 0) && (n > maxItems))
1015            n = maxItems;
1016
1017        return toString(theData, dtype, delimiter, n);
1018    }
1019
1020    @Override
1021    protected String toString(Object theData, Datatype theType, String delimiter, int count) {
1022        log.trace("toString: is_enum={} is_unsigned={} count={}", theType.isEnum(),
1023                theType.isUnsigned(), count);
1024        StringBuilder sb = new StringBuilder();
1025        Class<? extends Object> valClass = theData.getClass();
1026        log.trace("toString:valClass={}", valClass);
1027
1028        H5Datatype dtype = (H5Datatype)theType;
1029        log.trace("toString: count={} isStdRef={}", count, dtype.isStdRef());
1030        if (dtype.isStdRef()) {
1031            return ((H5ReferenceType)dtype).toString(delimiter, count);
1032        }
1033        else if (dtype.isVLEN() && !dtype.isVarStr()) {
1034            log.trace("toString: vlen");
1035            String strValue;
1036
1037            for (int k = 0; k < count; k++) {
1038                Object value = Array.get(theData, k);
1039                if (value == null)
1040                    strValue = "null";
1041                else {
1042                    if (dtype.getDatatypeBase().isRef()) {
1043                        ArrayList<byte[]> ref_value = (ArrayList<byte[]>)value;
1044                        log.trace("toString: vlen value={}", ref_value);
1045                        strValue = "{";
1046                        for (int m = 0; m < ref_value.size(); m++) {
1047                            byte[] curBytes = ref_value.get(m);
1048                            if (m > 0)
1049                                strValue += ", ";
1050                            if (H5ReferenceType.zeroArrayCheck(curBytes))
1051                                strValue += "NULL";
1052                            else {
1053                                if (((H5Datatype)dtype.getDatatypeBase()).isStdRef()) {
1054                                    strValue += H5.H5Rget_obj_name(curBytes, HDF5Constants.H5P_DEFAULT);
1055                                }
1056                                else if (dtype.getDatatypeBase().getDatatypeSize() == HDF5Constants.H5R_DSET_REG_REF_BUF_SIZE) {
1057                                    try {
1058                                        strValue += H5Datatype.descRegionDataset(parentObject.getFileFormat().getFID(), curBytes);
1059                                    }
1060                                    catch (Exception ex) {
1061                                        ex.printStackTrace();
1062                                    }
1063                                }
1064                                else if (dtype.getDatatypeBase().getDatatypeSize() == HDF5Constants.H5R_OBJ_REF_BUF_SIZE) {
1065                                    try {
1066                                        strValue += H5Datatype.descReferenceObject(parentObject.getFileFormat().getFID(), curBytes);
1067                                    }
1068                                    catch (Exception ex) {
1069                                        ex.printStackTrace();
1070                                    }
1071                                }
1072                            }
1073                        }
1074                        strValue += "}";
1075                    }
1076                    else
1077                        strValue = value.toString();
1078                }
1079                if (k > 0)
1080                    sb.append(", ");
1081                sb.append(strValue);
1082            }
1083        }
1084        else if (dtype.isRef()) {
1085            log.trace("toString: ref");
1086            String strValue = "NULL";
1087            byte[] rElements = null;
1088
1089            for (int k = 0; k < count; k++) {
1090                // need to iterate if type is ArrayList
1091                if (theData instanceof ArrayList)
1092                    rElements = (byte[]) ((ArrayList) theData).get(k);
1093                else
1094                    rElements = (byte[]) Array.get(theData, k);
1095
1096                if (H5ReferenceType.zeroArrayCheck(rElements))
1097                    strValue = "NULL";
1098                else {
1099                    if (dtype.isStdRef()) {
1100                        strValue = H5.H5Rget_obj_name(rElements, HDF5Constants.H5P_DEFAULT);
1101                    }
1102                    else if (dtype.getDatatypeSize() == HDF5Constants.H5R_DSET_REG_REF_BUF_SIZE) {
1103                        try {
1104                            strValue = H5Datatype.descRegionDataset(parentObject.getFileFormat().getFID(), rElements);
1105                        }
1106                        catch (Exception ex) {
1107                            ex.printStackTrace();
1108                        }
1109                    }
1110                    else if (dtype.getDatatypeSize() == HDF5Constants.H5R_OBJ_REF_BUF_SIZE) {
1111                        try {
1112                            strValue = H5Datatype.descReferenceObject(parentObject.getFileFormat().getFID(), rElements);
1113                        }
1114                        catch (Exception ex) {
1115                            ex.printStackTrace();
1116                        }
1117                    }
1118                }
1119                if (k > 0)
1120                    sb.append(", ");
1121                sb.append(strValue);
1122            }
1123        }
1124        else {
1125            return super.toString(theData, theType, delimiter, count);
1126        }
1127
1128        return sb.toString();
1129    }
1130
1131    /* Implement interface H5Attribute */
1132
1133    /**
1134     * The general read and write attribute operations for hdf5 object data.
1135     *
1136     * @param attr_id
1137     *        the attribute to access
1138     * @param ioType
1139     *        the type of IO operation
1140     * @param objBuf
1141     *        the data buffer to use for write operation
1142     *
1143     * @return the attribute data
1144     *
1145     * @throws Exception
1146     *             if the data can not be retrieved
1147     */
1148    @Override
1149    public Object AttributeCommonIO(long attr_id, H5File.IO_TYPE ioType, Object objBuf) throws Exception {
1150        H5Datatype dsDatatype = (H5Datatype) getDatatype();
1151        Object theData = null;
1152
1153        long dt_size = dsDatatype.getDatatypeSize();
1154        log.trace("AttributeCommonIO(): create native");
1155        long tid = dsDatatype.createNative();
1156
1157        if (ioType == H5File.IO_TYPE.READ) {
1158            log.trace("AttributeCommonIO():read ioType isNamed={} isEnum={} isText={} isRefObj={}", dsDatatype.isNamed(), dsDatatype.isEnum(), dsDatatype.isText(), dsDatatype.isRefObj());
1159            log.trace("AttributeCommonIO():read ioType isVLEN={}", dsDatatype.isVLEN());
1160
1161            long lsize = 1;
1162            for (int j = 0; j < dims.length; j++)
1163                lsize *= dims[j];
1164            log.trace("AttributeCommonIO():read ioType dt_size={} lsize={}", dt_size, lsize);
1165
1166            try {
1167                if (dsDatatype.isVarStr()) {
1168                    String[] strs = new String[(int) lsize];
1169                    for (int j = 0; j < lsize; j++)
1170                        strs[j] = "";
1171                    try {
1172                        log.trace("AttributeCommonIO():read ioType H5Aread_VLStrings");
1173                        H5.H5Aread_VLStrings(attr_id, tid, strs);
1174                    }
1175                    catch (Exception ex) {
1176                        log.debug("AttributeCommonIO():read ioType H5Aread_VLStrings failure: ", ex);
1177                        ex.printStackTrace();
1178                    }
1179                    theData = strs;
1180                }
1181                else if (dsDatatype.isCompound()) {
1182                    String[] strs = new String[(int) lsize];
1183                    for (int j = 0; j < lsize; j++)
1184                        strs[j] = "";
1185                    try {
1186                        log.trace("AttributeCommonIO():read ioType H5AreadComplex");
1187                        H5.H5AreadComplex(attr_id, tid, strs);
1188                    }
1189                    catch (Exception ex) {
1190                        ex.printStackTrace();
1191                    }
1192                    theData = strs;
1193                }
1194                else if (dsDatatype.isVLEN()) {
1195                    log.trace("AttributeCommonIO():read ioType:VLEN-REF H5Aread isArray()={}", dsDatatype.isArray());
1196                    theData = new ArrayList[(int)lsize];
1197                    for (int j = 0; j < lsize; j++)
1198                        ((ArrayList[])theData)[j] = new ArrayList<byte[]>();
1199
1200                        try {
1201                            H5.H5AreadVL(attr_id, tid, (Object[])theData);
1202                        }
1203                        catch (Exception ex) {
1204                            log.debug("AttributeCommonIO():read ioType:VLEN-REF H5Aread failure: ", ex);
1205                            ex.printStackTrace();
1206                        }
1207                }
1208                else {
1209                    Object attr_data = null;
1210                    try {
1211                        attr_data = H5Datatype.allocateArray(dsDatatype, (int) lsize);
1212                    }
1213                    catch (OutOfMemoryError e) {
1214                        log.debug("AttributeCommonIO():read ioType out of memory", e);
1215                        theData = null;
1216                    }
1217                    if (attr_data == null)
1218                        log.debug("AttributeCommonIO():read ioType allocateArray returned null");
1219
1220                    log.trace("AttributeCommonIO():read ioType H5Aread isArray()={}", dsDatatype.isArray());
1221                    try {
1222                        H5.H5Aread(attr_id, tid, attr_data);
1223                    }
1224                    catch (Exception ex) {
1225                        log.debug("AttributeCommonIO():read ioType H5Aread failure: ", ex);
1226                        ex.printStackTrace();
1227                    }
1228
1229                    /*
1230                     * Perform any necessary data conversions.
1231                     */
1232                    if (dsDatatype.isText() && convertByteToString && (attr_data instanceof byte[])) {
1233                        log.trace("AttributeCommonIO():read ioType isText: converting byte array to string array");
1234                        theData = byteToString((byte[]) attr_data, (int) dsDatatype.getDatatypeSize());
1235                    }
1236                    else if (dsDatatype.isFloat() && dt_size == 16) {
1237                        log.trace("AttributeCommonIO():read ioType isFloat: converting byte array to BigDecimal array");
1238                        theData = dsDatatype.byteToBigDecimal(0, (int)nPoints, (byte[]) attr_data);
1239                    }
1240                    else if (dsDatatype.isArray() && dsDatatype.getDatatypeBase().isFloat() && dsDatatype.getDatatypeBase().getDatatypeSize() == 16) {
1241                        log.trace("AttributeCommonIO():read ioType isArray and isFloat: converting byte array to BigDecimal array");
1242                        long[] arrayDims = dsDatatype.getArrayDims();
1243                        int asize = (int)nPoints;
1244                        for (int j = 0; j < arrayDims.length; j++) {
1245                            asize *= arrayDims[j];
1246                        }
1247                        theData = ((H5Datatype)dsDatatype.getDatatypeBase()).byteToBigDecimal(0, asize, (byte[]) attr_data);
1248                    }
1249                    else if (dsDatatype.isRef() && (attr_data instanceof byte[])) {
1250                        log.trace("AttributeCommonIO():read ioType isRef: converting byte array to List of bytes");
1251                        theData = new ArrayList<byte[]>((int)lsize);
1252                        for (int m = 0; m < (int) lsize; m++) {
1253                            byte[] curBytes = new byte[(int)dsDatatype.getDatatypeSize()];
1254                            try {
1255                                System.arraycopy(attr_data, m * (int)dt_size, curBytes, 0, (int)dsDatatype.getDatatypeSize());
1256                                ((ArrayList<byte[]>)theData).add(curBytes);
1257                            }
1258                            catch (Exception err) {
1259                                log.trace("AttributeCommonIO(): arraycopy failure: ", err);
1260                            }
1261                        }
1262                    }
1263                    else
1264                        theData = attr_data;
1265                }
1266            }
1267            catch (HDF5DataFiltersException exfltr) {
1268                log.debug("AttributeCommonIO():read ioType read failure: ", exfltr);
1269                throw new Exception("Filter not available exception: " + exfltr.getMessage(), exfltr);
1270            }
1271            catch (Exception ex) {
1272                log.debug("AttributeCommonIO():read ioType read failure: ", ex);
1273                throw new Exception(ex.getMessage(), ex);
1274            }
1275            finally {
1276                dsDatatype.close(tid);
1277            }
1278            log.trace("AttributeCommonIO():read ioType data: {}", theData);
1279            originalBuf = theData;
1280            isDataLoaded = true;
1281        } // H5File.IO_TYPE.READ
1282        else {
1283            /*
1284             * Perform any necessary data conversions before writing the data.
1285             *
1286             * Note that v-len strings do not get converted, regardless of
1287             * conversion request type.
1288             */
1289            Object tmpData = objBuf;
1290            try {
1291                // Check if we need to convert integer data
1292                String cname = objBuf.getClass().getName();
1293                char dname = cname.charAt(cname.lastIndexOf("[") + 1);
1294                boolean doIntConversion = (((dt_size == 1) && (dname == 'S')) || ((dt_size == 2) && (dname == 'I'))
1295                        || ((dt_size == 4) && (dname == 'J')) || (dsDatatype.isUnsigned() && unsignedConverted));
1296
1297                if (doIntConversion) {
1298                    log.trace("AttributeCommonIO(): converting integer data to unsigned C-type integers");
1299                    tmpData = convertToUnsignedC(objBuf, null);
1300                }
1301                else if (dsDatatype.isText() && !dsDatatype.isVarStr() && convertByteToString && !(objBuf instanceof byte[])) {
1302                    log.trace("AttributeCommonIO(): converting string array to byte array");
1303                    tmpData = stringToByte((String[]) objBuf, (int)dt_size);
1304                }
1305                else if (dsDatatype.isEnum() && (Array.get(objBuf, 0) instanceof String)) {
1306                    log.trace("AttributeCommonIO(): converting enum names to values");
1307                    tmpData = dsDatatype.convertEnumNameToValue((String[]) objBuf);
1308                }
1309                else if (dsDatatype.isFloat() && dsDatatype.getDatatypeSize() == 16) {
1310                    log.trace("AttributeCommonIO(): isFloat: converting BigDecimal array to byte array");
1311                    throw new Exception("data conversion failure: cannot write BigDecimal values");
1312                    //tmpData = dsDatatype.bigDecimalToByte(0, (int)nPoints, (BigDecimal[]) objBuf);
1313                }
1314            }
1315            catch (Exception ex) {
1316                log.debug("AttributeCommonIO(): data conversion failure: ", ex);
1317                throw new Exception("data conversion failure: " + ex.getMessage());
1318            }
1319
1320            /*
1321             * Actually write the data now that everything has been setup.
1322             */
1323            try {
1324                if (dsDatatype.isVarStr()) {
1325                    log.trace("AttributeCommonIO(): H5Awrite_VLStrings aid={} tid={}", attr_id, tid);
1326
1327                    H5.H5Awrite_VLStrings(attr_id, tid, (Object[]) tmpData);
1328                }
1329                else if (dsDatatype.isVLEN() || (dsDatatype.isArray() && dsDatatype.getDatatypeBase().isVLEN())) {
1330                    log.trace("AttributeCommonIO(): H5AwriteVL aid={} tid={}", attr_id, tid);
1331
1332                    H5.H5AwriteVL(attr_id, tid, (Object[]) tmpData);
1333                }
1334                else {
1335                    log.trace("AttributeCommonIO(): dsDatatype.isRef()={} data is String={}", dsDatatype.isRef(), tmpData instanceof String);
1336                    if (dsDatatype.isRef() && tmpData instanceof String) {
1337                        // reference is a path+name to the object
1338                        log.trace("AttributeCommonIO(): Attribute class is CLASS_REFERENCE");
1339                        log.trace("AttributeCommonIO(): H5Awrite aid={} tid={}", attr_id, tid);
1340                        byte[] refBuf = H5.H5Rcreate_object(getFID(), (String) tmpData, HDF5Constants.H5P_DEFAULT);
1341                        if (refBuf != null) {
1342                            H5.H5Awrite(attr_id, tid, refBuf);
1343                            H5.H5Rdestroy(refBuf);
1344                        }
1345                    }
1346                    else if (Array.get(tmpData, 0) instanceof String) {
1347                        int len = ((String[]) tmpData).length;
1348                        byte[] bval = Dataset.stringToByte((String[]) tmpData, (int)dt_size);
1349                        if (bval != null && bval.length == dt_size * len) {
1350                            bval[bval.length - 1] = 0;
1351                            tmpData = bval;
1352                        }
1353                        log.trace("AttributeCommonIO(): String={}: {}", tmpData);
1354                        log.trace("AttributeCommonIO(): H5Awrite aid={} tid={}", attr_id, tid);
1355                        H5.H5Awrite(attr_id, tid, tmpData);
1356                    }
1357                    else {
1358                        log.trace("AttributeCommonIO(): H5Awrite aid={} tid={}", attr_id, tid);
1359                        H5.H5Awrite(attr_id, tid, tmpData);
1360                    }
1361                }
1362            }
1363            catch (Exception ex) {
1364                log.debug("AttributeCommonIO(): write failure: ", ex);
1365                throw new Exception(ex.getMessage());
1366            }
1367            finally {
1368                dsDatatype.close(tid);
1369            }
1370        } // H5File.IO_TYPE.WRITE
1371
1372        return theData;
1373    }
1374
1375    /**
1376     * Read a subset of an attribute for hdf5 object data.
1377     *
1378     * @return the selected attribute data
1379     *
1380     * @throws Exception
1381     *             if the data can not be retrieved
1382     */
1383    @Override
1384    public Object AttributeSelection() throws Exception {
1385        H5Datatype dsDatatype = (H5Datatype) getDatatype();
1386        int dsSize = (int)dsDatatype.getDatatypeSize();
1387        if (dsDatatype.isArray())
1388            dsSize = (int)dsDatatype.getDatatypeBase().getDatatypeSize();
1389        Object theData = H5Datatype.allocateArray(dsDatatype, (int)nPoints);
1390        if (dsDatatype.isText() && convertByteToString && (theData instanceof byte[])) {
1391            log.trace("scalarAttributeSelection(): isText: converting byte array to string array");
1392            theData = byteToString((byte[]) theData, dsSize);
1393        }
1394        else if (dsDatatype.isFloat() && dsSize == 16) {
1395            log.trace("scalarAttributeSelection(): isFloat: converting byte array to BigDecimal array");
1396            theData = dsDatatype.byteToBigDecimal(0, (int)nPoints, (byte[]) theData);
1397        }
1398        else if (dsDatatype.isArray() && dsDatatype.getDatatypeBase().isFloat() && dsSize == 16) {
1399            log.trace("scalarAttributeSelection(): isArray and isFloat: converting byte array to BigDecimal array");
1400            long[] arrayDims = dsDatatype.getArrayDims();
1401            int asize = (int)nPoints;
1402            for (int j = 0; j < arrayDims.length; j++) {
1403                asize *= arrayDims[j];
1404            }
1405            theData = ((H5Datatype)dsDatatype.getDatatypeBase()).byteToBigDecimal(0, asize, (byte[]) theData);
1406        }
1407        Object theOrig = originalBuf;
1408        log.trace("scalarAttributeSelection(): originalBuf={} with datatype size={}", originalBuf, dsSize);
1409
1410        //Copy the selection from originalBuf to theData
1411        //Only three dims are involved and selected data is 2 dimensions
1412        //    getHeight() is the row dimension
1413        //    getWidth() is the col dimension
1414        //    getDepth() is the frame dimension
1415        long[] start = getStartDims();
1416        long curFrame = start[selectedIndex[2]];
1417        int k = (int)startDims[selectedIndex[2]] * (int)getDepth();
1418        for (int col = 0; col < (int)getWidth(); col++) {
1419            for (int row = 0; row < (int)getHeight(); row++) {
1420                int index = row * (int)getWidth() + col;
1421                log.trace("scalarAttributeSelection(): point[{}] row:col:k={}:{}:{}", curFrame, row, col, k);
1422                int fromIndex = ((int)curFrame * (int)getWidth() * (int)getHeight() +
1423                        col * (int)getHeight() + row);
1424                int toIndex = (col * (int)getHeight() + row);
1425                int objSize = 1;
1426                if (dsDatatype.isArray()) {
1427                    long[] arrayDims = dsDatatype.getArrayDims();
1428                    objSize = arrayDims.length;
1429                    try {
1430                        System.arraycopy(theOrig, fromIndex, theData, toIndex, objSize);
1431                    }
1432                    catch (Exception err) {
1433                        log.debug("scalarAttributeSelection(): arraycopy failure: ", err);
1434                    }
1435                }
1436                else if (dsDatatype.isStdRef()) {
1437                    objSize = (int)HDF5Constants.H5R_REF_BUF_SIZE;
1438                    fromIndex = fromIndex * HDF5Constants.H5R_REF_BUF_SIZE;
1439                    toIndex = toIndex * HDF5Constants.H5R_REF_BUF_SIZE;
1440                    try {
1441                        System.arraycopy(theOrig, fromIndex, theData, toIndex, objSize);
1442                    }
1443                    catch (Exception err) {
1444                        log.debug("scalarAttributeSelection(): arraycopy failure: ", err);
1445                    }
1446                }
1447                else {
1448                    if (theOrig instanceof ArrayList) {
1449                        if (dsDatatype.isRef()) {
1450                            byte[] rElements = (byte[]) ((ArrayList) theOrig).get(fromIndex);
1451                            try {
1452                                System.arraycopy(rElements, 0, theData, toIndex * dsSize, dsSize);
1453                            }
1454                            catch (Exception err) {
1455                                log.trace("scalarAttributeSelection(): refarraycopy failure: ", err);
1456                            }
1457                        }
1458                        else {
1459                            Object value = Array.get(theOrig, fromIndex);
1460                            log.trace("scalarAttributeSelection(): value={}", value);
1461                            ((ArrayList<Object>)theData).add(toIndex, value);
1462                        }
1463                    }
1464                    else
1465                        theData = theOrig;
1466                }
1467            }
1468        }
1469
1470        log.trace("scalarAttributeSelection(): theData={}", theData);
1471        return theData;
1472    }
1473}