    001/*****************************************************************************
    002 * Copyright by The HDF Group.                                               *
    003 * All rights reserved.                                                      *
    004 *                                                                           *
    005 * This file is part of the HDF Java Products distribution.                  *
    006 * The full copyright notice, including terms governing use, modification,   *
    007 * and redistribution, is contained in the COPYING file, which can be found  *
    008 * at the root of the source code distribution tree,                         *
    009 * or in https://www.hdfgroup.org/licenses.                                  *
    010 * If you do not have access to either file, you may request a copy from     *
    011 * help@hdfgroup.org.                                                        *
    012 ****************************************************************************/
    013
    014package hdf.view.TableView;
    015
    016import java.lang.reflect.Array;
    017import java.math.BigInteger;
    018
    019import java.util.ArrayList;
    020import java.util.Arrays;
    021import java.util.Collection;
    022import java.util.HashMap;
    023import java.util.Iterator;
    024import java.util.List;
    025import java.util.Map;
    026import java.util.StringTokenizer;
    027import java.util.Vector;
    028
    029import org.slf4j.Logger;
    030import org.slf4j.LoggerFactory;
    031
    032import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
    033
    034import hdf.hdf5lib.H5;
    035import hdf.hdf5lib.HDF5Constants;
    036
    037import hdf.object.CompoundDataFormat;
    038import hdf.object.DataFormat;
    039import hdf.object.Datatype;
    040import hdf.object.FileFormat;
    041import hdf.object.HObject;
    042import hdf.object.Utils;
    043import hdf.object.h5.H5Datatype;
    044import hdf.object.h5.H5ReferenceType;
    045import hdf.view.Tools;
    046
    047/**
    048 * A Factory class to return a concrete class implementing the IDataProvider
    049 * interface in order to provide data for a NatTable.
    050 *
    051 * @author Jordan T. Henderson
    052 * @version 1.0 2/9/2019
    053 */
    054public class DataProviderFactory
    055{
    056    private static final Logger log = LoggerFactory.getLogger(DataProviderFactory.class);
    057
    058    /**
    059     * To keep things clean from an API perspective, keep a static reference to the last
    060     * DataFormat that was passed in. This keeps us from needing to pass the DataFormat
    061     * object as a parameter to every DataProvider class, since it's really only needed
    062     * during the HDFDataProvider constructor.
    063     */
    064    private static DataFormat dataFormatReference = null;
    065
    066    /**
    067     * Get the Data Display Provider for the supplied data object
    068     *
    069     * @param dataObject
    070     *        the data object
    071     * @param dataBuf
    072     *        the data buffer to use
    073     * @param dataTransposed
    074     *        if the data should be transposed
    075     *
    076     * @return the provider instance
    077     *
    078     * @throws Exception if a failure occurred
    079     */
    080    public static HDFDataProvider getDataProvider(final DataFormat dataObject, final Object dataBuf, final boolean dataTransposed) throws Exception {
    081        if (dataObject == null) {
    082            log.debug("getDataProvider(DataFormat): data object is null");
    083            return null;
    084        }
    085
    086        dataFormatReference = dataObject;
    087
    088        HDFDataProvider dataProvider = getDataProvider(dataObject.getDatatype(), dataBuf, dataTransposed);
    089
    090        return dataProvider;
    091    }
    092
    093    private static final HDFDataProvider getDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    094        HDFDataProvider dataProvider = null;
    095
    096        try {
    097            if (dtype.isCompound())
    098                dataProvider = new CompoundDataProvider(dtype, dataBuf, dataTransposed);
    099            else if (dtype.isArray())
    100                dataProvider = new ArrayDataProvider(dtype, dataBuf, dataTransposed);
    101            else if (dtype.isVLEN() && !dtype.isVarStr())
    102                dataProvider = new VlenDataProvider(dtype, dataBuf, dataTransposed);
    103            else if (dtype.isString() || dtype.isVarStr())
    104                dataProvider = new StringDataProvider(dtype, dataBuf, dataTransposed);
    105            else if (dtype.isChar())
    106                dataProvider = new CharDataProvider(dtype, dataBuf, dataTransposed);
    107            else if (dtype.isInteger() || dtype.isFloat())
    108                dataProvider = new NumericalDataProvider(dtype, dataBuf, dataTransposed);
    109            else if (dtype.isEnum())
    110                dataProvider = new EnumDataProvider(dtype, dataBuf, dataTransposed);
    111            else if (dtype.isOpaque() || dtype.isBitField())
    112                dataProvider = new BitfieldDataProvider(dtype, dataBuf, dataTransposed);
    113            else if (dtype.isRef())
    114                dataProvider = new RefDataProvider(dtype, dataBuf, dataTransposed);
    115        }
    116        catch (Exception ex) {
    117            log.debug("getDataProvider(): error occurred in retrieving a DataProvider: ", ex);
    118            dataProvider = null;
    119        }
    120
    121        /*
    122         * Try to use a default DataProvider.
    123         */
    124        if (dataProvider == null) {
    125            log.debug("getDataProvider(): using a default data provider");
    126
    127            dataProvider = new HDFDataProvider(dtype, dataBuf, dataTransposed);
    128        }
    129
    130        return dataProvider;
    131    }
    132
    133    /**
    134     * The base DataProvider which pulls data from a given Array object using direct
    135     * indices.
    136     */
    137    public static class HDFDataProvider implements IDataProvider
    138    {
    139        private static final Logger log = LoggerFactory.getLogger(HDFDataProvider.class);
    140
    141        /**
    142         * In order to support 3-dimensional datasets, which may need to update the data
    143         * buffer object after flipping through a 'page', this field is not marked as
    144         * final. However, it is important that subclasses DO NOT override this field.
    145         */
    146        protected Object dataBuf;
    147
    148        /** the data value */
    149        protected Object theValue;
    150
    151        /** the data format class */
    152        protected final Class originalFormatClass;
    153
    154        /** if the data value has changed */
    155        protected boolean isValueChanged;
    156
    157        /** the type of the parent */
    158        protected final boolean isContainerType;
    159
    160        /** the rank */
    161        protected final int rank;
    162
    163        /** if the data is in original order */
    164        protected final boolean isNaturalOrder;
    165        /** if the data is transposed */
    166        protected final boolean isDataTransposed;
    167
    168        /** the column */
    169        protected long colCount;
    170        /** the row */
    171        protected long rowCount;
    172
    173        /**
    174         * Create the HDF extended Data Display Provider for the supplied data object
    175         *
    176         * @param dtype
    177         *        the datatype object
    178         * @param dataBuf
    179         *        the data buffer to use
    180         * @param dataTransposed
    181         *        if the data should be transposed
    182         *
    183         * @throws Exception if a failure occurred
    184         */
    185        HDFDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    186            this.dataBuf = dataBuf;
    187
    188            this.originalFormatClass = dataFormatReference.getOriginalClass();
    189
    190            char runtimeTypeClass = Utils.getJavaObjectRuntimeClass(dataBuf);
    191            if (runtimeTypeClass == ' ') {
    192                log.debug("invalid data value runtime type class: runtimeTypeClass={}", runtimeTypeClass);
    193                throw new IllegalStateException("Invalid data value runtime type class: " + runtimeTypeClass);
    194            }
    195
    196            rank = dataFormatReference.getRank();
    197
    198            isNaturalOrder = ((rank == 1)
    199                    || (dataFormatReference.getSelectedIndex()[0] < dataFormatReference.getSelectedIndex()[1]));
    200            isDataTransposed = dataTransposed;
    201
    202            if (rank > 1) {
    203                rowCount = dataFormatReference.getHeight();
    204                colCount = dataFormatReference.getWidth();
    205            }
    206            else {
    207                rowCount = (int) dataFormatReference.getSelectedDims()[0];
    208                colCount = 1;
    209            }
    210            log.trace("constructor:class={} rowCount={} colCount={}", runtimeTypeClass, rowCount, colCount);
    211
    212            theValue = null;
    213            isValueChanged = false;
    214
    215            isContainerType = (this instanceof CompoundDataProvider
    216                            || this instanceof ArrayDataProvider
    217                            || this instanceof VlenDataProvider);
    218        }
    219
    220        /**
    221         * A utility method used to translate a set of physical table coordinates to an
    222         * index into a data buffer.
    223         *
    224         * @param rowIndex
    225         *        the row
    226         * @param columnIndex
    227         *        the column
    228         *
    229         * @return physical location in 1D notation
    230         */
    231        public int physicalLocationToBufIndex(int rowIndex, int columnIndex) {
    232            long index = rowIndex * colCount + columnIndex;
    233
    234            if (rank > 1) {
    235                log.trace("physicalLocationToBufIndex({}, {}): rank > 1; adjusting for multi-dimensional dataset",
    236                          rowIndex, columnIndex);
    237
    238                if (isDataTransposed && isNaturalOrder)
    239                    index = columnIndex * rowCount + rowIndex;
    240                else if (!isDataTransposed && !isNaturalOrder)
    241                    // Reshape Data
    242                    index = rowIndex * colCount + columnIndex;
    243                else if (isDataTransposed && !isNaturalOrder)
    244                    // Transpose Data
    245                    index = columnIndex * rowCount + rowIndex;
    246                else
    247                    index = rowIndex * colCount + columnIndex;
    248            }
    249
    250            log.trace("physicalLocationToBufIndex({}, {}, {}): finish", rowIndex, columnIndex, index);
    251
    252            return (int) index;
    253        }
    254
    255        @Override
    256        public Object getDataValue(int columnIndex, int rowIndex) {
    257            try {
    258                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
    259                if (dataBuf instanceof ArrayList)
    260                    theValue = ((ArrayList)dataBuf).get(bufIndex);
    261                else
    262                    theValue = Array.get(dataBuf, bufIndex);
    263            }
    264            catch (Exception ex) {
    265                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
    266                theValue = DataFactoryUtils.errStr;
    267            }
    268
    269            log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue);
    270
    271            return theValue;
    272        }
    273
    274        /**
    275         * When a CompoundDataProvider wants to pass a List of data down to a nested
    276         * CompoundDataProvider, or when a top-level container DataProvider (such as an
    277         * ArrayDataProvider) wants to hand data down to a base CompoundDataProvider, we
    278         * need to pass down a List of data, plus a field and row index. This method is
    279         * for facilitating this behavior.
    280         *
    281         * In general, all "container" DataProviders that have a "container" base
    282         * DataProvider should call down into their base DataProvider(s) using this
    283         * method, in order to ensure that buried CompoundDataProviders get handled
    284         * correctly. When their base DataProvider is not a "container" type, the method
    285         * getDataValue(Object, index) should be used instead.
    286         *
    287         * For atomic type DataProviders, we treat this method as directly calling into
    288         * getDataValue(Object, index) using the passed rowIndex. However, this method
    289         * should, in general, not be called by atomic type DataProviders.
    290         *
    291         * @param obj
    292         *        the data object
    293         * @param rowIndex
    294         *        the row
    295         * @param columnIndex
    296         *        the column
    297         *
    298         * @return value of the data
    299         */
    300        public Object getDataValue(Object obj, int columnIndex, int rowIndex) {
    301            return getDataValue(obj, rowIndex);
    302        }
    303
    304        /**
    305         * When a parent HDFDataProvider (such as an ArrayDataProvider) wants to
    306         * retrieve a data value by routing the operation through its base
    307         * HDFDataProvider, the parent HDFDataProvider will generally know the direct
    308         * index to have the base provider use. This method is to facilitate this kind
    309         * of behavior.
    310         *
    311         * Note that this method takes an Object parameter, which is the object that the
    312         * method should pull its data from. This is to be able to nicely support nested
    313         * compound DataProviders.
    314         *
    315         * @param obj
    316         *        the data object
    317         * @param index
    318         *        the index into the data array
    319         *
    320         * @return the data object
    321         */
    322        public Object getDataValue(Object obj, int index) {
    323            try {
    324                if (obj instanceof ArrayList)
    325                    theValue = ((ArrayList)obj).get(index);
    326                else
    327                    theValue = Array.get(obj, index);
    328            }
    329            catch (Exception ex) {
    330                log.debug("getDataValue({}): failure: ", index, ex);
    331                theValue = DataFactoryUtils.errStr;
    332            }
    333
    334            log.trace("getDataValue({})=({}): finish", index, theValue);
    335
    336            return theValue;
    337        }
    338
    339        /**
    340         * update the data value of a compound type.
    341         *
    342         * @param columnIndex
    343         *        the column
    344         * @param rowIndex
    345         *        the row
    346         * @param newValue
    347         *        the new data value object
    348         */
    349        @Override
    350        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
    351            try {
    352                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
    353
    354                updateAtomicValue(dataBuf, newValue, bufIndex);
    355            }
    356            catch (Exception ex) {
    357                log.debug("setDataValue({}, {})=({}): cell value update failure: ", rowIndex, columnIndex, newValue, ex);
    358            }
    359            log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue);
    360
    361            /*
    362             * TODO: throwing error dialogs when something fails?
    363             *
    364             * Tools.showError(shell, "Select", "Unable to set new value:\n\n " + ex);
    365             */
    366        }
    367
    368        /**
    369         * When a CompoundDataProvider wants to pass a List of data down to a nested
    370         * CompoundDataProvider, or when a top-level container DataProvider (such as an
    371         * ArrayDataProvider) wants to hand data down to a base CompoundDataProvider, we
    372         * need to pass down a List of data and the new value, plus a field and row
    373         * index. This method is for facilitating this behavior.
    374         *
    375         * In general, all "container" DataProviders that have a "container" base
    376         * DataProvider should call down into their base DataProvider(s) using this{},
    377         * method, in order to ensure that buried CompoundDataProviders get handled
    378         * correctly. When their base DataProvider is not a "container" type, the method
    379         * setDataValue(index, Object, Object) should be used instead.
    380         *
    381         * For atomic type DataProviders, we treat this method as directly calling into
    382         * setDataValue(index, Object, Object) using the passed rowIndex. However, this
    383         * method should, in general, not be called by atomic type DataProviders.
    384         *
    385         * @param columnIndex
    386         *        the column
    387         * @param rowIndex
    388         *        the row
    389         * @param bufObject
    390         *        the data object
    391         * @param newValue
    392         *        the new data object
    393         */
    394        public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) {
    395            setDataValue(rowIndex, bufObject, newValue);
    396        }
    397
    398        /**
    399         * When a parent HDFDataProvider (such as an ArrayDataProvider) wants to set a
    400         * data value by routing the operation through its base HDFDataProvider, the
    401         * parent HDFDataProvider will generally know the direct index to have the base
    402         * provider use. This method is to facilitate this kind of behavior.
    403         *
    404         * Note that this method takes two Object parameters, one which is the object
    405         * that the method should set its data inside of and one which is the new value
    406         * to set. This is to be able to nicely support nested compound DataProviders.
    407         *
    408         * @param index
    409         *        the index into the data array
    410         * @param bufObject
    411         *        the data object
    412         * @param newValue
    413         *        the new data object
    414         */
    415        public void setDataValue(int index, Object bufObject, Object newValue) {
    416            try {
    417                updateAtomicValue(bufObject, newValue, index);
    418            }
    419            catch (Exception ex) {
    420                log.debug("setDataValue({}, {})=({}): updateAtomicValue failure: ", index, bufObject, newValue, ex);
    421            }
    422            log.trace("setDataValue({}, {})=({}): finish", index, bufObject, newValue);
    423        }
    424
    425        private void updateAtomicValue(Object bufObject, Object newValue, int bufIndex) {
    426            if ((newValue == null) || ((newValue = ((String) newValue).trim()) == null)) {
    427                log.debug("updateAtomicValue(): cell value not updated; new value is null");
    428                return;
    429            }
    430
    431            // No need to update if values are the same
    432            int buf_size = Array.getLength(bufObject);
    433            log.trace("updateAtomicValue(): bufObject size is {}", buf_size);
    434            if (buf_size > 0) {
    435                Object oldVal = this.getDataValue(bufObject, bufIndex);
    436                if ((oldVal != null) && newValue.equals(oldVal.toString())) {
    437                    log.debug("updateAtomicValue(): cell value not updated; new value same as old value");
    438                    return;
    439                }
    440            }
    441
    442            String bname = bufObject.getClass().getName();
    443            String nname = newValue.getClass().getName();
    444            log.trace("updateArrayOfAtomicElements(): bufObject cname={} of data newValue={}", bname, nname);
    445            char runtimeTypeClass = Utils.getJavaObjectRuntimeClass(bufObject);
    446            log.trace("updateAtomicValue(): runtimeTypeClass={}", runtimeTypeClass);
    447
    448            switch (runtimeTypeClass) {
    449            case 'B':
    450                byte bvalue = 0;
    451                bvalue = Byte.parseByte((String) newValue);
    452                Array.setByte(bufObject, bufIndex, bvalue);
    453                break;
    454            case 'S':
    455                short svalue = 0;
    456                svalue = Short.parseShort((String) newValue);
    457                Array.setShort(bufObject, bufIndex, svalue);
    458                break;
    459            case 'I':
    460                int ivalue = 0;
    461                ivalue = Integer.parseInt((String) newValue);
    462                Array.setInt(bufObject, bufIndex, ivalue);
    463                break;
    464            case 'J':
    465                long lvalue = 0;
    466                String cname = this.originalFormatClass.getName();
    467                char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    468                if (dname == 'J') {
    469                    BigInteger big = new BigInteger((String) newValue);
    470                    lvalue = big.longValue();
    471                }
    472                else
    473                    lvalue = Long.parseLong((String) newValue);
    474                Array.setLong(bufObject, bufIndex, lvalue);
    475                break;
    476            case 'F':
    477                float fvalue = 0;
    478                fvalue = Float.parseFloat((String) newValue);
    479                Array.setFloat(bufObject, bufIndex, fvalue);
    480                break;
    481            case 'D':
    482                double dvalue = 0;
    483                dvalue = Double.parseDouble((String) newValue);
    484                Array.setDouble(bufObject, bufIndex, dvalue);
    485                break;
    486            default:
    487                String rname = bufObject.getClass().getSimpleName();
    488                log.trace("updateAtomicValue(): getSimpleName={}", rname);
    489                switch (rname.charAt(0)) {
    490                case 'B':
    491                    Byte bValue = Byte.valueOf((String) newValue);
    492                    Array.set(bufObject, bufIndex, bValue);
    493                    break;
    494                case 'S':
    495                    Short sValue = Short.valueOf((String) newValue);
    496                    Array.set(bufObject, bufIndex, sValue);
    497                    break;
    498                case 'I':
    499                    Integer iValue = Integer.valueOf((String) newValue);
    500                    Array.set(bufObject, bufIndex, iValue);
    501                    break;
    502                case 'J':
    503                    Long lValue = 0L;
    504                    String cAname = this.originalFormatClass.getName();
    505                    char dAname = cAname.charAt(cAname.lastIndexOf('[') + 1);
    506                    if (dAname == 'J') {
    507                        BigInteger big = new BigInteger((String) newValue);
    508                        lValue = big.longValue();
    509                    }
    510                    else
    511                        lValue = Long.valueOf((String) newValue);
    512                    Array.set(bufObject, bufIndex, lValue);
    513                    break;
    514                case 'F':
    515                    Float fValue = Float.valueOf((String) newValue);
    516                    Array.set(bufObject, bufIndex, fValue);
    517                    break;
    518                case 'D':
    519                    Double dValue = Double.valueOf((String) newValue);
    520                    Array.set(bufObject, bufIndex, dValue);
    521                    break;
    522                default:
    523                    log.trace("updateAtomicValue(): bufObject={} bufIndex={} newValue={}", bufObject, bufIndex, newValue);
    524                    Array.set(bufObject, bufIndex, newValue);
    525                    break;
    526                }
    527                break;
    528            }
    529
    530            isValueChanged = true;
    531        }
    532
    533        @Override
    534        public int getColumnCount() {
    535            return (int) colCount;
    536        }
    537
    538        @Override
    539        public int getRowCount() {
    540            return (int) rowCount;
    541        }
    542
    543        /**
    544         * set if the data value has changed
    545         *
    546         * @param isChanged
    547         *        if the data value is changed
    548         */
    549        public final void setIsValueChanged(boolean isChanged) {
    550            isValueChanged = isChanged;
    551        }
    552
    553        /**
    554         * @return if the datavalue has chaged
    555         */
    556        public final boolean getIsValueChanged() {
    557            return isValueChanged;
    558        }
    559
    560        /**
    561         * Update the data buffer for this HDFDataProvider. This is necessary for when
    562         * the data that has been read is invalidated, such as when flipping through
    563         * 'pages' in a > 2-dimensional dataset.
    564         *
    565         * @param newBuf
    566         *        the new data buffer
    567         */
    568        public final void updateDataBuffer(Object newBuf) {
    569            this.dataBuf = newBuf;
    570
    571            if (rank > 1) {
    572                rowCount = dataFormatReference.getHeight();
    573                colCount = dataFormatReference.getWidth();
    574            }
    575            else {
    576                rowCount = (int) dataFormatReference.getSelectedDims()[0];
    577                colCount = 1;
    578            }
    579            log.trace("updateDataBuffer: rowCount={} colCount={}", rowCount, colCount);
    580        }
    581    }
    582
    583    /*
    584     * A DataProvider for Compound datatype datasets which is a composite of
    585     * DataProviders, one for each selected member of the Compound datatype.
    586     */
    587    private static class CompoundDataProvider extends HDFDataProvider
    588    {
    589        private static final Logger log = LoggerFactory.getLogger(CompoundDataProvider.class);
    590
    591        private final HashMap<Integer, Integer> baseProviderIndexMap;
    592        private final HashMap<Integer, Integer> relCmpdStartIndexMap;
    593
    594        private final HDFDataProvider[] baseTypeProviders;
    595
    596        private final Datatype[] selectedMemberTypes;
    597
    598        private final int[] selectedMemberOrders;
    599
    600        private final int nSubColumns;
    601        private final int nCols;
    602        private final int nRows;
    603
    604        CompoundDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    605            super(dtype, dataBuf, dataTransposed);
    606
    607            CompoundDataFormat compoundFormat = (CompoundDataFormat) dataFormatReference;
    608            selectedMemberTypes = compoundFormat.getSelectedMemberTypes();
    609            selectedMemberOrders = compoundFormat.getSelectedMemberOrders();
    610
    611            List<Datatype> localSelectedTypes = DataFactoryUtils.filterNonSelectedMembers(compoundFormat, dtype);
    612
    613            log.trace("setting up {} base HDFDataProviders", localSelectedTypes.size());
    614
    615            baseTypeProviders = new HDFDataProvider[localSelectedTypes.size()];
    616            for (int i = 0; i < baseTypeProviders.length; i++) {
    617                log.trace("retrieving DataProvider for member {}", i);
    618
    619                try {
    620                    baseTypeProviders[i] = getDataProvider(localSelectedTypes.get(i), dataBuf, dataTransposed);
    621                }
    622                catch (Exception ex) {
    623                    log.debug("failed to retrieve DataProvider for member {}: ", i, ex);
    624                    baseTypeProviders[i] = null;
    625                }
    626            }
    627
    628            /*
    629             * Build necessary index maps.
    630             */
    631            HashMap<Integer, Integer>[] maps = DataFactoryUtils.buildIndexMaps(compoundFormat, localSelectedTypes);
    632            baseProviderIndexMap = maps[DataFactoryUtils.COL_TO_BASE_CLASS_MAP_INDEX];
    633            relCmpdStartIndexMap = maps[DataFactoryUtils.CMPD_START_IDX_MAP_INDEX];
    634
    635            log.trace("index maps built: baseProviderIndexMap = {}, relColIdxMap = {}",
    636                    baseProviderIndexMap.toString(), relCmpdStartIndexMap.toString());
    637
    638            if (baseProviderIndexMap.size() == 0) {
    639                log.debug("base DataProvider index mapping is invalid - size 0");
    640                throw new Exception("CompoundDataProvider: invalid DataProvider mapping of size 0 built");
    641            }
    642
    643            if (relCmpdStartIndexMap.size() == 0) {
    644                log.debug("compound field start index mapping is invalid - size 0");
    645                throw new Exception("CompoundDataProvider: invalid compound field start index mapping of size 0 built");
    646            }
    647
    648            /*
    649             * nCols should represent the number of columns covered by this CompoundDataProvider
    650             * only. For top-level CompoundDataProviders, this should be the entire width of the
    651             * dataset. For nested CompoundDataProviders, nCols will be a subset of these columns.
    652             */
    653            nCols = (int) compoundFormat.getWidth() * baseProviderIndexMap.size();
    654            nRows = (int) compoundFormat.getHeight();
    655
    656            nSubColumns = (int) compoundFormat.getWidth();
    657        }
    658
    659        @Override
    660        public Object getDataValue(int columnIndex, int rowIndex) {
    661            try {
    662                int fieldIdx = columnIndex;
    663                int rowIdx = rowIndex;
    664
    665                if (nSubColumns > 1) { // multi-dimension compound dataset
    666                    /*
    667                     * Make sure fieldIdx is within a valid range, since even for multi-dimensional
    668                     * compound datasets there will only be as many lists of data as there are
    669                     * members in a single compound type.
    670                     */
    671                    fieldIdx %= selectedMemberTypes.length;
    672
    673                    int realColIdx = columnIndex / selectedMemberTypes.length;
    674                    rowIdx = rowIndex * nSubColumns + realColIdx;
    675                }
    676
    677                int providerIndex = baseProviderIndexMap.get(fieldIdx);
    678                Object colValue = ((List<?>) dataBuf).get(providerIndex);
    679                if (colValue == null)
    680                    return DataFactoryUtils.nullStr;
    681
    682                /*
    683                 * Delegate data retrieval to one of the base DataProviders according to the
    684                 * index of the relevant compound field.
    685                 */
    686                HDFDataProvider base = baseTypeProviders[providerIndex];
    687                if (base instanceof CompoundDataProvider)
    688                    /*
    689                     * Adjust the compound field index by subtracting the starting index of the
    690                     * nested compound that we are delegating to. When the nested compound's index
    691                     * map is setup correctly, this adjusted index should map to the correct field
    692                     * among the nested compound's members.
    693                     */
    694                    theValue = base.getDataValue(colValue, fieldIdx - relCmpdStartIndexMap.get(fieldIdx), rowIdx);
    695                else if (base instanceof ArrayDataProvider) {
    696                    /*
    697                     * TODO: quick temporary fix for specific compound of array of compound files.
    698                     * Transforms the given column index into a relative index from the starting
    699                     * index of the array of compound field.
    700                     */
    701                    int arrCompoundStartIdx = columnIndex;
    702                    HDFDataProvider theProvider;
    703                    while (arrCompoundStartIdx >= 0) {
    704                        try {
    705                            theProvider = baseTypeProviders[baseProviderIndexMap.get(arrCompoundStartIdx - 1)];
    706                            if (theProvider != base)
    707                                break;
    708
    709                            arrCompoundStartIdx--;
    710                        }
    711                        catch (Exception ex) {
    712                            break;
    713                        }
    714                    }
    715
    716                    int adjustedColIndex = columnIndex - arrCompoundStartIdx;
    717
    718                    theValue = base.getDataValue(colValue, adjustedColIndex, rowIdx);
    719                }
    720                else
    721                    theValue = base.getDataValue(colValue, rowIdx);
    722            }
    723            catch (Exception ex) {
    724                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
    725                theValue = DataFactoryUtils.errStr;
    726            }
    727
    728            log.trace("getDataValue({}, {}): finish", rowIndex, columnIndex);
    729
    730            return theValue;
    731        }
    732
    733        @Override
    734        public Object getDataValue(Object obj, int columnIndex, int rowIndex) {
    735            try {
    736                int providerIndex = baseProviderIndexMap.get(columnIndex);
    737                Object colValue = ((List<?>) obj).get(providerIndex);
    738                if (colValue == null)
    739                    return DataFactoryUtils.nullStr;
    740
    741                /*
    742                 * Delegate data retrieval to one of the base DataProviders according to the
    743                 * index of the relevant compound field.
    744                 */
    745                HDFDataProvider base = baseTypeProviders[providerIndex];
    746                if (base instanceof CompoundDataProvider)
    747                    /*
    748                     * Adjust the compound field index by subtracting the starting index of the
    749                     * nested compound that we are delegating to. When the nested compound's index
    750                     * map is setup correctly, this adjusted index should map to the correct field
    751                     * among the nested compound's members.
    752                     */
    753                    theValue = base.getDataValue(colValue, columnIndex - relCmpdStartIndexMap.get(columnIndex), rowIndex);
    754                else if (base instanceof ArrayDataProvider)
    755                    theValue = base.getDataValue(colValue, columnIndex, rowIndex);
    756                else
    757                    theValue = base.getDataValue(colValue, rowIndex);
    758            }
    759            catch (Exception ex) {
    760                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
    761                theValue = DataFactoryUtils.errStr;
    762            }
    763            log.trace("getDataValue({})=({}): finish", rowIndex, columnIndex);
    764
    765            return theValue;
    766        }
    767
    768        @Override
    769        public Object getDataValue(Object obj, int index) {
    770            throw new UnsupportedOperationException("getDataValue(Object, int) should not be called for CompoundDataProviders");
    771        }
    772
    773        @Override
    774        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
    775            if ((newValue == null) || ((newValue = ((String) newValue).trim()) == null)) {
    776                log.debug("setDataValue({}, {})=({}): cell value not updated; new value is null", rowIndex, columnIndex, newValue);
    777                return;
    778            }
    779
    780            // No need to update if values are the same
    781            Object oldVal = this.getDataValue(columnIndex, rowIndex);
    782            if ((oldVal != null) && newValue.equals(oldVal.toString())) {
    783                log.debug("setDataValue({}, {})=({}): cell value not updated; new value same as old value", rowIndex, columnIndex, newValue);
    784                return;
    785            }
    786
    787            try {
    788                int fieldIdx = columnIndex;
    789                int rowIdx = rowIndex;
    790
    791                if (nSubColumns > 1) { // multi-dimension compound dataset
    792                    /*
    793                     * Make sure fieldIdx is within a valid range, since even for multi-dimensional
    794                     * compound datasets there will only be as many lists of data as there are
    795                     * members in a single compound type.
    796                     */
    797                    fieldIdx %= selectedMemberTypes.length;
    798
    799                    int realColIdx = columnIndex / selectedMemberTypes.length;
    800                    rowIdx = rowIndex * nSubColumns + realColIdx;
    801                }
    802
    803                int providerIndex = baseProviderIndexMap.get(fieldIdx);
    804                Object colValue = ((List<?>) dataBuf).get(providerIndex);
    805                if (colValue == null) {
    806                    log.debug("setDataValue({}, {})=({}): colValue is null", rowIndex, columnIndex, newValue);
    807                    return;
    808                }
    809
    810                /*
    811                 * Delegate data setting to one of the base DataProviders according to the index
    812                 * of the relevant compound field.
    813                 */
    814                HDFDataProvider base = baseTypeProviders[providerIndex];
    815                if (base.isContainerType)
    816                    /*
    817                     * Adjust the compound field index by subtracting the starting index of the
    818                     * nested compound that we are delegating to. When the nested compound's index
    819                     * map is setup correctly, this adjusted index should map to the correct field
    820                     * among the nested compound's members.
    821                     */
    822                    base.setDataValue(fieldIdx - relCmpdStartIndexMap.get(fieldIdx), rowIdx, colValue, newValue);
    823                else
    824                    base.setDataValue(rowIdx, colValue, newValue);
    825
    826                isValueChanged = true;
    827            }
    828            catch (Exception ex) {
    829                log.debug("setDataValue({}, {})=({}): cell value update failure: ", rowIndex, columnIndex, newValue);
    830            }
    831            log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue);
    832
    833            /*
    834             * TODO: throwing error dialogs when something fails?
    835             *
    836             * Tools.showError(shell, "Select", "Unable to set new value:\n\n " + ex);
    837             */
    838        }
    839
    840        @Override
    841        public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) {
    842            try {
    843                int providerIndex = baseProviderIndexMap.get(columnIndex);
    844                Object colValue = ((List<?>) bufObject).get(providerIndex);
    845                if (colValue == null) {
    846                    log.debug("setDataValue({}, {}, {})=({}): colValue is null", rowIndex, columnIndex, bufObject, newValue);
    847                    return;
    848                }
    849
    850                /*
    851                 * Delegate data setting to one of the base DataProviders according to the index
    852                 * of the relevant compound field.
    853                 */
    854                HDFDataProvider base = baseTypeProviders[providerIndex];
    855                if (base.isContainerType)
    856                    /*
    857                     * Adjust the compound field index by subtracting the starting index of the
    858                     * nested compound that we are delegating to. When the nested compound's index
    859                     * map is setup correctly, this adjusted index should map to the correct field
    860                     * among the nested compound's members.
    861                     */
    862                    base.setDataValue(columnIndex - relCmpdStartIndexMap.get(columnIndex), rowIndex, colValue, newValue);
    863                else
    864                    base.setDataValue(rowIndex, colValue, newValue);
    865
    866                isValueChanged = true;
    867            }
    868            catch (Exception ex) {
    869                log.debug("setDataValue({}, {}, {})=({}): cell value update failure: ", rowIndex, columnIndex, bufObject, newValue, ex);
    870            }
    871            log.trace("setDataValue({}, {}, {})=({}): finish", rowIndex, columnIndex, bufObject, newValue);
    872        }
    873
    874        @Override
    875        public void setDataValue(int index, Object bufObject, Object newValue) {
    876            throw new UnsupportedOperationException("setDataValue(int, Object, Object) should not be called for CompoundDataProviders");
    877        }
    878
    879        @Override
    880        public int getColumnCount() {
    881            return nCols;
    882        }
    883
    884        @Override
    885        public int getRowCount() {
    886            return nRows;
    887        }
    888    }
    889
    890    private static class ArrayDataProvider extends HDFDataProvider
    891    {
    892        private static final Logger log = LoggerFactory.getLogger(ArrayDataProvider.class);
    893
    894        private final HDFDataProvider baseTypeDataProvider;
    895
    896        private final Object[] arrayElements;
    897        private final long arraySize;
    898
    899        private final int nCols;
    900
    901        ArrayDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    902            super(dtype, dataBuf, dataTransposed);
    903
    904            Datatype baseType = dtype.getDatatypeBase();
    905
    906            baseTypeDataProvider = getDataProvider(baseType, dataBuf, dataTransposed);
    907
    908            if (baseType.isVarStr())
    909                arraySize = dtype.getArrayDims()[0];
    910            else if (baseType.isBitField() || baseType.isOpaque())
    911                arraySize = dtype.getDatatypeSize();
    912            else
    913                arraySize = dtype.getDatatypeSize() / baseType.getDatatypeSize();
    914
    915            arrayElements = new Object[(int) arraySize];
    916
    917            if (baseTypeDataProvider instanceof CompoundDataProvider)
    918                nCols = (int) arraySize * ((CompoundDataProvider) baseTypeDataProvider).nCols;
    919            else
    920                nCols = super.getColumnCount();
    921        }
    922
    923        @Override
    924        public Object getDataValue(int columnIndex, int rowIndex) {
    925            try {
    926                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
    927
    928                bufIndex *= arraySize;
    929
    930                if (baseTypeDataProvider instanceof CompoundDataProvider) {
    931                    /*
    932                     * Pass row and column indices down where they will be adjusted.
    933                     */
    934                    theValue = retrieveArrayOfCompoundElements(dataBuf, columnIndex, rowIndex);
    935                }
    936                else if (baseTypeDataProvider instanceof ArrayDataProvider) {
    937                    /*
    938                     * TODO: assign to global arrayElements.
    939                     */
    940                    theValue = retrieveArrayOfArrayElements(dataBuf, columnIndex, bufIndex);
    941                }
    942                else {
    943                    /*
    944                     * TODO: assign to global arrayElements.
    945                     */
    946                    theValue = retrieveArrayOfAtomicElements(dataBuf, bufIndex);
    947                }
    948            }
    949            catch (Exception ex) {
    950                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
    951                theValue = DataFactoryUtils.errStr;
    952            }
    953
    954            log.trace("getDataValue({}, {})({}): finish", rowIndex, columnIndex, theValue);
    955
    956            return theValue;
    957        }
    958
    959        @Override
    960        public Object getDataValue(Object obj, int columnIndex, int rowIndex) {
    961            try {
    962                long index = rowIndex * arraySize;
    963
    964                if (baseTypeDataProvider instanceof CompoundDataProvider) {
    965                    /*
    966                     * Pass row and column indices down where they will be adjusted.
    967                     */
    968                    theValue = retrieveArrayOfCompoundElements(obj, columnIndex, rowIndex);
    969                }
    970                else if (baseTypeDataProvider instanceof ArrayDataProvider) {
    971                    theValue = retrieveArrayOfArrayElements(obj, columnIndex, (int) index);
    972                }
    973                else {
    974                    theValue = retrieveArrayOfAtomicElements(obj, (int) index);
    975                }
    976            }
    977            catch (Exception ex) {
    978                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
    979                theValue = DataFactoryUtils.errStr;
    980            }
    981
    982            return theValue;
    983        }
    984
    985        private Object[] retrieveArrayOfCompoundElements(Object objBuf, int columnIndex, int rowIndex) {
    986            long adjustedRowIdx = (rowIndex * arraySize * colCount)
    987                    + (columnIndex / ((CompoundDataProvider) baseTypeDataProvider).baseProviderIndexMap.size());
    988            long adjustedColIdx = columnIndex % ((CompoundDataProvider) baseTypeDataProvider).baseProviderIndexMap.size();
    989
    990            /*
    991             * Since we flatten array of compound types, we only need to return a single
    992             * value.
    993             */
    994            return new Object[] { baseTypeDataProvider.getDataValue(objBuf, (int) adjustedColIdx, (int) adjustedRowIdx) };
    995        }
    996
    997        private Object[] retrieveArrayOfArrayElements(Object objBuf, int columnIndex, int startRowIndex) {
    998            Object[] tempArray = new Object[(int) arraySize];
    999
    1000            for (int i = 0; i < arraySize; i++)
    1001                tempArray[i] = baseTypeDataProvider.getDataValue(objBuf, columnIndex, startRowIndex + i);
    1002
    1003            return tempArray;
    1004        }
    1005
    1006        private Object[] retrieveArrayOfAtomicElements(Object objBuf, int rowStartIdx) {
    1007            Object[] tempArray = new Object[(int) arraySize];
    1008
    1009            for (int i = 0; i < arraySize; i++)
    1010                tempArray[i] = baseTypeDataProvider.getDataValue(objBuf, rowStartIdx + i);
    1011
    1012            return tempArray;
    1013        }
    1014
    1015        @Override
    1016        public Object getDataValue(Object obj, int index) {
    1017            throw new UnsupportedOperationException("getDataValue(Object, int) should not be called for ArrayDataProviders");
    1018        }
    1019
    1020        @Override
    1021        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
    1022            try {
    1023                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
    1024
    1025                bufIndex *= arraySize;
    1026
    1027                updateArrayElements(dataBuf, newValue, columnIndex, bufIndex);
    1028            }
    1029            catch (Exception ex) {
    1030                log.debug("setDataValue({}, {}, {}): cell value update failure: ", rowIndex, columnIndex, newValue, ex);
    1031            }
    1032            log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue);
    1033        }
    1034
    1035        @Override
    1036        public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) {
    1037            try {
    1038                long bufIndex = rowIndex * arraySize;
    1039
    1040                updateArrayElements(bufObject, newValue, columnIndex, (int) bufIndex);
    1041            }
    1042            catch (Exception ex) {
    1043                log.debug("setDataValue({}, {}, {}, {}): cell value update failure: ", rowIndex, columnIndex, bufObject, newValue, ex);
    1044            }
    1045            log.trace("setDataValue({}, {}, {})=({}): finish", rowIndex, columnIndex, bufObject, newValue);
    1046        }
    1047
    1048        @Override
    1049        public void setDataValue(int index, Object bufObject, Object newValue) {
    1050            throw new UnsupportedOperationException("setDataValue(int, Object, Object) should not be called for ArrayDataProviders");
    1051        }
    1052
    1053        private void updateArrayElements(Object curBuf, Object newValue, int columnIndex, int bufStartIndex) {
    1054            StringTokenizer st = new StringTokenizer((String) newValue, ",[]");
    1055            if (st.countTokens() < arraySize) {
    1056                /*
    1057                 * TODO:
    1058                 */
    1059                /* Tools.showError(shell, "Select", "Number of data points < " + morder + "."); */
    1060                log.debug("updateArrayElements(): number of data points ({}) < array size {}", st.countTokens(), arraySize);
    1061                log.trace("updateArrayElements({}, {}, {}): finish", curBuf, newValue, bufStartIndex);
    1062                return;
    1063            }
    1064
    1065            if (baseTypeDataProvider instanceof CompoundDataProvider)
    1066                updateArrayOfCompoundElements(st, curBuf, columnIndex, bufStartIndex);
    1067            else if (baseTypeDataProvider instanceof ArrayDataProvider)
    1068                updateArrayOfArrayElements(st, curBuf, columnIndex, bufStartIndex);
    1069            else
    1070                updateArrayOfAtomicElements(st, curBuf, bufStartIndex);
    1071        }
    1072
    1073        private void updateArrayOfCompoundElements(StringTokenizer tokenizer, Object curBuf, int columnIndex, int bufStartIndex) {
    1074            for (int i = 0; i < arraySize; i++) {
    1075                List<?> cmpdDataList = (List<?>) ((Object[]) curBuf)[i];
    1076                baseTypeDataProvider.setDataValue(columnIndex, bufStartIndex + i, cmpdDataList,
    1077                        tokenizer.nextToken().trim());
    1078                isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged();
    1079            }
    1080        }
    1081
    1082        private void updateArrayOfArrayElements(StringTokenizer tokenizer, Object curBuf, int columnIndex, int bufStartIndex) {
    1083            for (int i = 0; i < arraySize; i++) {
    1084                /*
    1085                 * TODO: not quite right.
    1086                 */
    1087                baseTypeDataProvider.setDataValue(columnIndex, bufStartIndex + i, curBuf, tokenizer.nextToken().trim());
    1088                isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged();
    1089            }
    1090        }
    1091
    1092        private void updateArrayOfAtomicElements(StringTokenizer tokenizer, Object curBuf, int bufStartIndex) {
    1093            for (int i = 0; i < arraySize; i++) {
    1094                baseTypeDataProvider.setDataValue(bufStartIndex + i, curBuf, tokenizer.nextToken().trim());
    1095                isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged();
    1096            }
    1097        }
    1098
    1099        @Override
    1100        public int getColumnCount() {
    1101            return nCols;
    1102        }
    1103    }
    1104
    1105    private static class VlenDataProvider extends HDFDataProvider
    1106    {
    1107        private static final Logger log = LoggerFactory.getLogger(VlenDataProvider.class);
    1108
    1109        private final HDFDataProvider baseTypeDataProvider;
    1110
    1111        private final StringBuilder buffer;
    1112
    1113        private final int baseTypeClass;
    1114
    1115        VlenDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    1116            super(dtype, dataBuf, dataTransposed);
    1117
    1118            Datatype baseType = dtype.getDatatypeBase();
    1119            baseTypeClass = baseType.getDatatypeClass();
    1120
    1121            baseTypeDataProvider = getDataProvider(baseType, dataBuf, dataTransposed);
    1122
    1123            buffer = new StringBuilder();
    1124        }
    1125
    1126        @Override
    1127        public Object getDataValue(int columnIndex, int rowIndex) {
    1128            buffer.setLength(0);
    1129
    1130            try {
    1131                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
    1132
    1133                if (baseTypeDataProvider instanceof CompoundDataProvider) {
    1134                    /*
    1135                     * Pass row and column indices down where they will be adjusted.
    1136                     */
    1137                    theValue = retrieveArrayOfCompoundElements(dataBuf, columnIndex, rowIndex);
    1138                }
    1139                else if (baseTypeDataProvider instanceof ArrayDataProvider) {
    1140                    /*
    1141                     * TODO: assign to global arrayElements.
    1142                     */
    1143                    theValue = retrieveArrayOfArrayElements(dataBuf, columnIndex, bufIndex);
    1144                }
    1145                else if (baseTypeDataProvider instanceof RefDataProvider) {
    1146                    /*
    1147                     * TODO: assign to global arrayElements.
    1148                     */
    1149                    theValue = retrieveArrayOfArrayElements(dataBuf, columnIndex, bufIndex);
    1150                }
    1151                else {
    1152                    /*
    1153                     * TODO: assign to global arrayElements.
    1154                     */
    1155                    theValue = retrieveArrayOfAtomicElements(dataBuf, bufIndex);
    1156                }
    1157            }
    1158            catch (Exception ex) {
    1159                log.debug("getDataValue(rowIndex={}, columnIndex={}): failure: ", rowIndex, columnIndex, ex);
    1160                theValue = DataFactoryUtils.errStr;
    1161            }
    1162
    1163            log.trace("getDataValue(rowIndex={}, columnIndex={})=({}): finish", rowIndex, columnIndex, theValue);
    1164
    1165            return theValue;
    1166        }
    1167
    1168        @Override
    1169        public Object getDataValue(Object obj, int columnIndex, int rowIndex) {
    1170            buffer.setLength(0);
    1171
    1172            try {
    1173                long vlSize = Array.getLength(obj);
    1174                log.debug("getDataValue(): vlSize={} obj={}", vlSize, obj);
    1175
    1176                if (baseTypeDataProvider instanceof CompoundDataProvider) {
    1177                    /*
    1178                     * Pass row and column indices down where they will be adjusted.
    1179                     */
    1180                    theValue = retrieveArrayOfCompoundElements(obj, columnIndex, rowIndex);
    1181                }
    1182                else if (baseTypeDataProvider instanceof ArrayDataProvider) {
    1183                    theValue = retrieveArrayOfArrayElements(obj, columnIndex, rowIndex);
    1184                }
    1185                else if (baseTypeDataProvider instanceof RefDataProvider) {
    1186                    theValue = retrieveArrayOfArrayElements(obj, columnIndex, rowIndex);
    1187                }
    1188                else {
    1189                    theValue = retrieveArrayOfAtomicElements(obj, rowIndex);
    1190                }
    1191            }
    1192            catch (Exception ex) {
    1193                log.debug("getDataValue(rowIndex={}, columnIndex={}): failure: ", rowIndex, columnIndex, ex);
    1194                theValue = DataFactoryUtils.errStr;
    1195            }
    1196
    1197            log.trace("getDataValue(obj={}, rowIndex={}, columnIndex={})=({}): finish", obj, rowIndex, columnIndex, theValue);
    1198
    1199            return theValue;
    1200        }
    1201
    1202        private Object[] retrieveArrayOfCompoundElements(Object objBuf, int columnIndex, int rowIndex) {
    1203            long vlSize = Array.getLength(objBuf);
    1204            log.debug("retrieveArrayOfCompoundElements(): vlSize={}", vlSize);
    1205            long adjustedRowIdx = (rowIndex * vlSize * colCount)
    1206                    + (columnIndex / ((CompoundDataProvider) baseTypeDataProvider).baseProviderIndexMap.size());
    1207            long adjustedColIdx = columnIndex % ((CompoundDataProvider) baseTypeDataProvider).baseProviderIndexMap.size();
    1208
    1209            /*
    1210             * Since we flatten array of compound types, we only need to return a single
    1211             * value.
    1212             */
    1213            return new Object[] { baseTypeDataProvider.getDataValue(objBuf, (int) adjustedColIdx, (int) adjustedRowIdx) };
    1214        }
    1215
    1216        private Object[] retrieveArrayOfArrayElements(Object objBuf, int columnIndex, int startRowIndex) {
    1217            log.debug("retrieveArrayOfArrayElements(): objBuf={}", objBuf);
    1218            ArrayList<byte[]> vlElements = ((ArrayList[])objBuf)[startRowIndex];
    1219            log.debug("retrieveArrayOfArrayElements(): vlElements={}", vlElements);
    1220            long vlSize = vlElements.size();
    1221            log.debug("retrieveArrayOfArrayElements(): vlSize={} length={}", vlSize, vlElements.size());
    1222            Object[] tempArray = new Object[(int) vlSize];
    1223
    1224            for (int i = 0; i < vlSize; i++) {
    1225                ArrayList<byte[]> ref_value = vlElements;
    1226                StringBuilder sb = new StringBuilder();
    1227                sb.append("{");
    1228                for (int m = 0; m < ref_value.size(); m++) {
    1229                    if (m > 0)
    1230                        sb.append(", ");
    1231                    byte[] byteElements = ref_value.get(m);
    1232                    log.trace("retrieveArrayOfArrayElements byteElements={}", byteElements);
    1233                    sb.append(baseTypeDataProvider.getDataValue(byteElements, columnIndex, i));
    1234                }
    1235                sb.append("}");
    1236                tempArray[i] = sb.toString();
    1237            }
    1238
    1239            return tempArray;
    1240        }
    1241
    1242        private Object[] retrieveArrayOfAtomicElements(Object objBuf, int rowStartIdx) {
    1243            ArrayList vlElements = ((ArrayList[])objBuf)[rowStartIdx];
    1244            long vlSize = vlElements.size();
    1245            log.debug("retrieveArrayOfAtomicElements(): vlSize={}", vlSize);
    1246            Object[] tempArray = new Object[(int) vlSize];
    1247
    1248            for (int i = 0; i < vlSize; i++)
    1249                tempArray[i] = baseTypeDataProvider.getDataValue(vlElements.toArray(), i);
    1250
    1251            return tempArray;
    1252        }
    1253
    1254        @Override
    1255        public Object getDataValue(Object obj, int index) {
    1256            throw new UnsupportedOperationException("getDataValue(Object, int) should not be called for VlenDataProviders");
    1257        }
    1258
    1259        @Override
    1260        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
    1261            try {
    1262                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
    1263
    1264                long vlSize = Array.getLength(dataBuf);
    1265                log.debug("setDataValue(): vlSize={}", vlSize);
    1266
    1267                updateArrayElements(dataBuf, newValue, columnIndex, rowIndex);
    1268            }
    1269            catch (Exception ex) {
    1270                log.debug("setDataValue(rowIndex={}, columnIndex={}, {}): cell value update failure: ", rowIndex, columnIndex, newValue, ex);
    1271            }
    1272            log.trace("setDataValue(rowIndex={}, columnIndex={})=({}): finish", rowIndex, columnIndex, newValue);
    1273        }
    1274
    1275        @Override
    1276        public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) {
    1277            try {
    1278                long vlSize = Array.getLength(bufObject);
    1279                log.debug("setDataValue(): vlSize={} for [c{}, r{}]", vlSize, columnIndex, rowIndex);
    1280
    1281                updateArrayElements(bufObject, newValue, columnIndex, rowIndex);
    1282            }
    1283            catch (Exception ex) {
    1284                log.debug("setDataValue(rowIndex={}, columnIndex={}, bufObject={}, {}): cell value update failure: ", rowIndex, columnIndex, bufObject, newValue, ex);
    1285            }
    1286            log.trace("setDataValue(rowIndex={}, columnIndex={}, bufObject={})=({}): finish", rowIndex, columnIndex, bufObject, newValue);
    1287        }
    1288
    1289        @Override
    1290        public void setDataValue(int index, Object bufObject, Object newValue) {
    1291            throw new UnsupportedOperationException("setDataValue(int, Object, Object) should not be called for VlenDataProviders");
    1292        }
    1293
    1294        private void updateArrayElements(Object curBuf, Object newValue, int columnIndex, int rowStartIndex) {
    1295            long vlSize = Array.getLength(curBuf);
    1296            log.debug("updateArrayElements(): vlSize={}", vlSize);
    1297
    1298            if (baseTypeDataProvider instanceof CompoundDataProvider)
    1299                updateArrayOfCompoundElements(newValue, curBuf, columnIndex, rowStartIndex);
    1300            else if (baseTypeDataProvider instanceof ArrayDataProvider)
    1301                updateArrayOfArrayElements(newValue, curBuf, columnIndex, rowStartIndex);
    1302            else if (baseTypeDataProvider instanceof VlenDataProvider)
    1303                updateArrayOfArrayElements(newValue, curBuf, columnIndex, rowStartIndex);
    1304            else
    1305                updateArrayOfAtomicElements(newValue, curBuf, rowStartIndex);
    1306        }
    1307
    1308        private void updateArrayOfCompoundElements(Object newValue, Object curBuf, int columnIndex, int rowIndex) {
    1309            long vlSize = Array.getLength(curBuf);
    1310            log.debug("updateArrayOfCompoundElements(): vlSize={}", vlSize);
    1311            long adjustedRowIdx = (rowIndex * vlSize * colCount)
    1312                    + (columnIndex / ((CompoundDataProvider) baseTypeDataProvider).baseProviderIndexMap.size());
    1313            long adjustedColIdx = columnIndex % ((CompoundDataProvider) baseTypeDataProvider).baseProviderIndexMap.size();
    1314
    1315            /*
    1316             * Since we flatten array of compound types, we only need to update a single value.
    1317             */
    1318            baseTypeDataProvider.setDataValue((int) adjustedColIdx, (int) adjustedRowIdx, curBuf, newValue);
    1319            isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged();
    1320        }
    1321
    1322        private void updateArrayOfArrayElements(Object newValue, Object curBuf, int columnIndex, int rowIndex) {
    1323            ArrayList vlElements = ((ArrayList[])curBuf)[rowIndex];
    1324            log.debug("updateArrayOfArrayElements(): vlElements={}", vlElements);
    1325            long vlSize = vlElements.size();
    1326            log.debug("updateArrayOfArrayElements(): vlSize={}", vlSize);
    1327
    1328            StringTokenizer st = new StringTokenizer((String) newValue, ",[]");
    1329            int newcnt = st.countTokens();
    1330
    1331            Object[] buffer = null;
    1332            switch (baseTypeClass) {
    1333            case Datatype.CLASS_CHAR:
    1334                buffer = new Byte[newcnt];
    1335                break;
    1336            case Datatype.CLASS_INTEGER:
    1337                buffer = new Integer[newcnt];
    1338                break;
    1339            case Datatype.CLASS_FLOAT:
    1340                buffer = new Double[newcnt];
    1341                break;
    1342            case Datatype.CLASS_STRING:
    1343                buffer = new String[newcnt];
    1344                break;
    1345            case Datatype.CLASS_REFERENCE:
    1346            case Datatype.CLASS_OPAQUE:
    1347            case Datatype.CLASS_BITFIELD:
    1348            case Datatype.CLASS_ENUM:
    1349            case Datatype.CLASS_ARRAY:
    1350            case Datatype.CLASS_COMPOUND:
    1351            case Datatype.CLASS_VLEN:
    1352            default:
    1353                buffer = new Object[newcnt];
    1354                break;
    1355            }
    1356            for (int i = 0; i < newcnt; i++) {
    1357                baseTypeDataProvider.setDataValue(columnIndex, i, buffer, st.nextToken().trim());
    1358                isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged();
    1359            }
    1360            vlElements = new ArrayList<>(Arrays.asList(buffer));
    1361            ((ArrayList[])curBuf)[rowIndex] = vlElements;
    1362        }
    1363
    1364        private void updateArrayOfAtomicElements(Object newValue, Object curBuf, int rowStartIdx) {
    1365            ArrayList vlElements = ((ArrayList[])curBuf)[rowStartIdx];
    1366            long vlSize = vlElements.size();
    1367            log.debug("updateArrayOfAtomicElements(): vlSize={}", vlSize);
    1368
    1369            StringTokenizer st = new StringTokenizer((String) newValue, ",[]");
    1370            int newcnt = st.countTokens();
    1371            log.debug("updateArrayOfAtomicElements(): count={}", newcnt);
    1372            Object[] buffer = null;
    1373            switch (baseTypeClass) {
    1374            case Datatype.CLASS_CHAR:
    1375                buffer = new Byte[newcnt];
    1376                break;
    1377            case Datatype.CLASS_INTEGER:
    1378                buffer = new Integer[newcnt];
    1379                break;
    1380            case Datatype.CLASS_FLOAT:
    1381                buffer = new Double[newcnt];
    1382                break;
    1383            case Datatype.CLASS_STRING:
    1384                buffer = new String[newcnt];
    1385                break;
    1386            case Datatype.CLASS_REFERENCE:
    1387            case Datatype.CLASS_OPAQUE:
    1388            case Datatype.CLASS_BITFIELD:
    1389            case Datatype.CLASS_ENUM:
    1390            case Datatype.CLASS_ARRAY:
    1391            case Datatype.CLASS_COMPOUND:
    1392            case Datatype.CLASS_VLEN:
    1393            default:
    1394                buffer = new Object[newcnt];
    1395                break;
    1396            }
    1397            for (int i = 0; i < newcnt; i++) {
    1398                baseTypeDataProvider.setDataValue(i, buffer, st.nextToken().trim());
    1399                isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged();
    1400            }
    1401            String bname = buffer.getClass().getName();
    1402            String cname = curBuf.getClass().getName();
    1403            log.trace("updateArrayOfAtomicElements(): buffer cname={} of data cname={}", bname, cname);
    1404            vlElements = new ArrayList<>(Arrays.asList(buffer));
    1405            log.debug("updateArrayOfAtomicElements(): new vlSize={}", vlElements.size());
    1406            ((ArrayList[])curBuf)[rowStartIdx] = vlElements;
    1407        }
    1408    }
    1409
    1410    private static class StringDataProvider extends HDFDataProvider
    1411    {
    1412        private static final Logger log = LoggerFactory.getLogger(StringDataProvider.class);
    1413
    1414        private final long typeSize;
    1415
    1416        StringDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    1417            super(dtype, dataBuf, dataTransposed);
    1418
    1419            typeSize = dtype.getDatatypeSize();
    1420        }
    1421
    1422        @Override
    1423        public Object getDataValue(Object obj, int index) {
    1424            if (obj instanceof byte[]) {
    1425                int strlen = (int) typeSize;
    1426
    1427                log.trace("getDataValue({}, {}): converting byte[] to String", obj, index);
    1428
    1429                String str = new String((byte[]) obj, index * strlen, strlen);
    1430                int idx = str.indexOf('\0');
    1431                if (idx > 0)
    1432                    str = str.substring(0, idx);
    1433
    1434                theValue = str.trim();
    1435            }
    1436            else
    1437                super.getDataValue(obj, index);
    1438
    1439            log.trace("getDataValue({}, {})=({}): finish", obj, index, theValue);
    1440
    1441            return theValue;
    1442        }
    1443
    1444        @Override
    1445        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
    1446            try {
    1447                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
    1448
    1449                updateStringBytes(dataBuf, newValue, bufIndex);
    1450            }
    1451            catch (Exception ex) {
    1452                log.debug("setDataValue({}, {}, {}): cell value update failure: ", rowIndex, columnIndex, newValue, ex);
    1453            }
    1454            log.trace("setDataValue({}, {}, {}): finish", rowIndex, columnIndex, newValue);
    1455        }
    1456
    1457        @Override
    1458        public void setDataValue(int index, Object bufObject, Object newValue) {
    1459            try {
    1460                updateStringBytes(bufObject, newValue, index);
    1461            }
    1462            catch (Exception ex) {
    1463                log.debug("setDataValue({}, {}, {}): cell value update failure: ", index, bufObject, newValue, ex);
    1464            }
    1465            log.trace("setDataValue({}, {}, {}): finish", index, bufObject, newValue);
    1466        }
    1467
    1468        private void updateStringBytes(Object curBuf, Object newValue, int bufStartIndex) {
    1469            if (curBuf instanceof String[]) {
    1470                Array.set(curBuf, bufStartIndex, newValue);
    1471            }
    1472            else if (curBuf instanceof byte[]) {
    1473                // Update String using data represented as a byte[]
    1474                int strLen = (int) typeSize;
    1475                byte[] newValueBytes = ((String) newValue).getBytes();
    1476                byte[] curBytes = (byte[]) curBuf;
    1477                int n = Math.min(strLen, newValueBytes.length);
    1478
    1479                bufStartIndex *= typeSize;
    1480
    1481                System.arraycopy(newValueBytes, 0, curBytes, bufStartIndex, n);
    1482
    1483                bufStartIndex += n;
    1484                n = strLen - newValueBytes.length;
    1485
    1486                // space padding
    1487                for (int i = 0; i < n; i++)
    1488                    curBytes[bufStartIndex + i] = ' ';
    1489            }
    1490
    1491            isValueChanged = true;
    1492        }
    1493    }
    1494
    1495    private static class CharDataProvider extends HDFDataProvider
    1496    {
    1497        private static final Logger log = LoggerFactory.getLogger(CharDataProvider.class);
    1498
    1499        CharDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    1500            super(dtype, dataBuf, dataTransposed);
    1501        }
    1502
    1503        @Override
    1504        public Object getDataValue(int columnIndex, int rowIndex) {
    1505            /*
    1506             * Compatibility with HDF4 8-bit character types that get converted to a String
    1507             * ahead of time.
    1508             */
    1509            if (dataBuf instanceof String) {
    1510                log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, dataBuf);
    1511                return dataBuf;
    1512            }
    1513
    1514            return super.getDataValue(columnIndex, rowIndex);
    1515        }
    1516    }
    1517
    1518    private static class NumericalDataProvider extends HDFDataProvider
    1519    {
    1520        private static final Logger log = LoggerFactory.getLogger(NumericalDataProvider.class);
    1521
    1522        private final boolean isUINT64;
    1523
    1524        private final long    typeSize;
    1525
    1526        NumericalDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    1527            super(dtype, dataBuf, dataTransposed);
    1528
    1529            typeSize = dtype.getDatatypeSize();
    1530            isUINT64 = dtype.isUnsigned() && (typeSize == 8);
    1531        }
    1532
    1533        @Override
    1534        public Object getDataValue(int columnIndex, int rowIndex) {
    1535            super.getDataValue(columnIndex, rowIndex);
    1536
    1537            try {
    1538                if (isUINT64)
    1539                    theValue = Tools.convertUINT64toBigInt(Long.valueOf((long) theValue));
    1540            }
    1541            catch (Exception ex) {
    1542                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
    1543                theValue = DataFactoryUtils.errStr;
    1544            }
    1545
    1546            log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue);
    1547
    1548            return theValue;
    1549        }
    1550
    1551        @Override
    1552        public Object getDataValue(Object obj, int index) {
    1553            super.getDataValue(obj, index);
    1554
    1555            try {
    1556                if (isUINT64)
    1557                    theValue = Tools.convertUINT64toBigInt(Long.valueOf((long) theValue));
    1558            }
    1559            catch (Exception ex) {
    1560                log.debug("getDataValue({}): failure: ", index, ex);
    1561                theValue = DataFactoryUtils.errStr;
    1562            }
    1563
    1564            log.trace("getDataValue({})=({}): finish", index, theValue);
    1565
    1566            return theValue;
    1567        }
    1568    }
    1569
    1570    private static class EnumDataProvider extends HDFDataProvider
    1571    {
    1572        private static final Logger log = LoggerFactory.getLogger(EnumDataProvider.class);
    1573
    1574        EnumDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    1575            super(dtype, dataBuf, dataTransposed);
    1576        }
    1577    }
    1578
    1579    private static class BitfieldDataProvider extends HDFDataProvider
    1580    {
    1581        private static final Logger log = LoggerFactory.getLogger(BitfieldDataProvider.class);
    1582
    1583        private final long typeSize;
    1584
    1585        BitfieldDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    1586            super(dtype, dataBuf, dataTransposed);
    1587
    1588            typeSize = dtype.getDatatypeSize();
    1589        }
    1590
    1591        @Override
    1592        public Object getDataValue(int columnIndex, int rowIndex) {
    1593            try {
    1594                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
    1595
    1596                bufIndex *= typeSize;
    1597                theValue = populateByteArray(dataBuf, bufIndex);
    1598            }
    1599            catch (Exception ex) {
    1600                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
    1601                theValue = DataFactoryUtils.errStr;
    1602            }
    1603
    1604            log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue);
    1605
    1606            return theValue;
    1607        }
    1608
    1609        @Override
    1610        public Object getDataValue(Object obj, int index) {
    1611            try {
    1612                index *= typeSize;
    1613                theValue = populateByteArray(obj, index);
    1614            }
    1615            catch (Exception ex) {
    1616                log.debug("getDataValue({}): ", index, ex);
    1617                theValue = DataFactoryUtils.errStr;
    1618            }
    1619
    1620            log.trace("getDataValue({})=({}): finish", index, theValue);
    1621
    1622            return theValue;
    1623        }
    1624
    1625        private byte[] populateByteArray(Object byteBuf, int startIndex) {
    1626            byte[] byteElements = new byte[(int) typeSize];
    1627
    1628            for (int i = 0; i < typeSize; i++)
    1629                byteElements[i] = Array.getByte(byteBuf, startIndex + i);
    1630
    1631            return byteElements;
    1632        }
    1633    }
    1634
    1635    private static class RefDataProvider extends HDFDataProvider
    1636    {
    1637        private static final Logger log = LoggerFactory.getLogger(RefDataProvider.class);
    1638
    1639        private final long typeSize;
    1640        private final H5Datatype h5dtype;
    1641
    1642        RefDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
    1643            super(dtype, dataBuf, dataTransposed);
    1644
    1645            h5dtype = (H5Datatype)dtype;
    1646            typeSize = h5dtype.getDatatypeSize();
    1647            log.trace("typeSize={}=", typeSize);
    1648        }
    1649
    1650        @Override
    1651        public Object getDataValue(int columnIndex, int rowIndex) {
    1652            log.trace("getDataValue({}, {}): start", rowIndex, columnIndex);
    1653
    1654            try {
    1655                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
    1656                byte[] rElements = null;
    1657
    1658                log.trace("getDataValue(dataBuf={}): start", dataBuf);
    1659                if (dataBuf instanceof ArrayList)
    1660                    rElements = (byte[]) ((ArrayList) dataBuf).get(bufIndex);
    1661                else
    1662                    rElements = (byte[]) dataBuf;
    1663
    1664                if (h5dtype.isStdRef())
    1665                    theValue = populateReference(rElements, 0);
    1666                else if (h5dtype.isRegRef())
    1667                    theValue = populateReferenceRegion(rElements, 0);
    1668                else if (h5dtype.isRefObj())
    1669                    theValue = populateReferenceObject(rElements, 0);
    1670                else
    1671                    theValue = super.getDataValue(columnIndex, rowIndex);
    1672            }
    1673            catch (Exception ex) {
    1674                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
    1675                theValue = DataFactoryUtils.errStr;
    1676            }
    1677
    1678            log.trace("getDataValue({}, {})({}): finish", rowIndex, columnIndex, theValue);
    1679
    1680            return theValue;
    1681        }
    1682
    1683        @Override
    1684        public Object getDataValue(Object obj, int index) {
    1685            log.trace("getDataValue(Object:{}, {}): start", obj, index);
    1686            byte[] rElements = null;
    1687            if (obj instanceof ArrayList)
    1688                rElements = (byte[]) ((ArrayList) obj).get(index);
    1689            else
    1690                rElements = (byte[]) obj;
    1691
    1692            log.trace("getDataValue(rElements:{})", rElements);
    1693            try {
    1694                if (h5dtype.isStdRef())
    1695                    theValue = populateReference(rElements, 0);
    1696                else if (h5dtype.isRegRef())
    1697                    theValue = populateReferenceRegion(rElements, 0);
    1698                else if (h5dtype.isRefObj())
    1699                    theValue = populateReferenceObject(rElements, 0);
    1700                else
    1701                    theValue = super.getDataValue(obj, index);
    1702            }
    1703            catch (Exception ex) {
    1704                log.debug("getDataValueObject:({}, {}): ", obj, index, ex);
    1705                theValue = DataFactoryUtils.errStr;
    1706            }
    1707
    1708            log.trace("getDataValue(Object:{}, {})({}): finish", obj, index, theValue);
    1709
    1710            return theValue;
    1711        }
    1712
    1713        private String populateReference(Object byteBuf, int startIndex) {
    1714            byte[] rElements = new byte[(int)typeSize];
    1715            try {
    1716                System.arraycopy(byteBuf, startIndex * (int) typeSize, rElements, 0, (int) typeSize);
    1717            }
    1718            catch (Exception err) {
    1719                log.trace("populateReference(): arraycopy failure: ", err);
    1720            }
    1721            String regionStr = null;
    1722            if (H5Datatype.zeroArrayCheck(rElements))
    1723                regionStr = "NULL";
    1724            else
    1725                regionStr = ((H5ReferenceType)h5dtype).getReferenceRegion((byte[])byteBuf, false);
    1726            log.trace("populateReference regionStr={}", regionStr);
    1727
    1728            return regionStr;
    1729        }
    1730
    1731        private String populateReferenceRegion(Object byteBuf, int startIndex) {
    1732            long fid = ((HObject)dataFormatReference).getFileFormat().getFID();
    1733            byte[] rElements = new byte[(int)typeSize];
    1734            try {
    1735                System.arraycopy(byteBuf, startIndex * (int) typeSize, rElements, 0, (int) typeSize);
    1736            }
    1737            catch (Exception err) {
    1738                log.trace("populateReferenceRegion(): arraycopy failure: ", err);
    1739            }
    1740            String regionStr = null;
    1741            if (H5Datatype.zeroArrayCheck(rElements))
    1742                regionStr = "NULL";
    1743            else
    1744                regionStr = H5Datatype.descRegionDataset(fid, rElements);
    1745            log.trace("populateReferenceRegion regionStr={}", regionStr);
    1746
    1747            return regionStr;
    1748        }
    1749
    1750        private String populateReferenceObject(Object byteBuf, int startIndex) {
    1751            long fid = ((HObject)dataFormatReference).getFileFormat().getFID();
    1752            log.trace("populateReferenceObject byteBuf={}", byteBuf);
    1753            byte[] rElements = new byte[(int)typeSize];
    1754            try {
    1755                System.arraycopy(byteBuf, startIndex * (int) typeSize, rElements, 0, (int) typeSize);
    1756            }
    1757            catch (Exception err) {
    1758                log.trace("populateReferenceRegion(): arraycopy failure: ", err);
    1759            }
    1760            String objectStr = null;
    1761            if (H5Datatype.zeroArrayCheck(rElements))
    1762                objectStr = "NULL";
    1763            else
    1764                objectStr = H5Datatype.descReferenceObject(fid, rElements);
    1765            log.trace("populateReferenceObject objectStr={}", objectStr);
    1766
    1767            return objectStr;
    1768        }
    1769    }
    1770
    1771}