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