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 files COPYING and Copyright.html. *
008 * COPYING can be found at the root of the source code distribution tree.    *
009 * Or, see https://support.hdfgroup.org/products/licenses.html               *
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.HashMap;
019import java.util.List;
020import java.util.StringTokenizer;
021
022import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
023
024import hdf.object.CompoundDataFormat;
025import hdf.object.DataFormat;
026import hdf.object.Datatype;
027import hdf.object.Utils;
028import hdf.view.Tools;
029
030/**
031 * A Factory class to return a concrete class implementing the IDataProvider
032 * interface in order to provide data for a NatTable.
033 *
034 * @author Jordan T. Henderson
035 * @version 1.0 2/9/2019
036 *
037 */
038public class DataProviderFactory
039{
040    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DataProviderFactory.class);
041
042    /**
043     * To keep things clean from an API perspective, keep a static reference to the last
044     * DataFormat that was passed in. This keeps us from needing to pass the DataFormat
045     * object as a parameter to every DataProvider class, since it's really only needed
046     * during the HDFDataProvider constructor.
047     */
048    private static DataFormat dataFormatReference = null;
049
050    /**
051     * Get the Data Display Provider for the supplied data object
052     *
053     * @param dataObject
054     *        the data object
055     * @param dataBuf
056     *        the data buffer to use
057     * @param dataTransposed
058     *        if the data should be transposed
059     *
060     * @return the provider instance
061     *
062     * @throws Exception if a failure occurred
063     */
064    public static HDFDataProvider getDataProvider(final DataFormat dataObject, final Object dataBuf, final boolean dataTransposed) throws Exception {
065        if (dataObject == null) {
066            log.debug("getDataProvider(DataFormat): data object is null");
067            return null;
068        }
069
070        dataFormatReference = dataObject;
071
072        HDFDataProvider dataProvider = getDataProvider(dataObject.getDatatype(), dataBuf, dataTransposed);
073
074        return dataProvider;
075    }
076
077    private static final HDFDataProvider getDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
078        HDFDataProvider dataProvider = null;
079
080        try {
081            if (dtype.isCompound())
082                dataProvider = new CompoundDataProvider(dtype, dataBuf, dataTransposed);
083            else if (dtype.isArray())
084                dataProvider = new ArrayDataProvider(dtype, dataBuf, dataTransposed);
085            else if (dtype.isVLEN() && !dtype.isVarStr())
086                dataProvider = new VlenDataProvider(dtype, dataBuf, dataTransposed);
087            else if (dtype.isString() || dtype.isVarStr())
088                dataProvider = new StringDataProvider(dtype, dataBuf, dataTransposed);
089            else if (dtype.isChar())
090                dataProvider = new CharDataProvider(dtype, dataBuf, dataTransposed);
091            else if (dtype.isInteger() || dtype.isFloat())
092                dataProvider = new NumericalDataProvider(dtype, dataBuf, dataTransposed);
093            else if (dtype.isEnum())
094                dataProvider = new EnumDataProvider(dtype, dataBuf, dataTransposed);
095            else if (dtype.isOpaque() || dtype.isBitField())
096                dataProvider = new BitfieldDataProvider(dtype, dataBuf, dataTransposed);
097            else if (dtype.isRef())
098                dataProvider = new RefDataProvider(dtype, dataBuf, dataTransposed);
099        }
100        catch (Exception ex) {
101            log.debug("getDataProvider(): error occurred in retrieving a DataProvider: ", ex);
102            dataProvider = null;
103        }
104
105        /*
106         * Try to use a default DataProvider.
107         */
108        if (dataProvider == null) {
109            log.debug("getDataProvider(): using a default data provider");
110
111            dataProvider = new HDFDataProvider(dtype, dataBuf, dataTransposed);
112        }
113
114        return dataProvider;
115    }
116
117    /**
118     * The base DataProvider which pulls data from a given Array object using direct
119     * indices.
120     */
121    public static class HDFDataProvider implements IDataProvider
122    {
123        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(HDFDataProvider.class);
124
125        /**
126         * In order to support 3-dimensional datasets, which may need to update the data
127         * buffer object after flipping through a 'page', this field is not marked as
128         * final. However, it is important that subclasses DO NOT override this field.
129         */
130        protected Object           dataBuf;
131
132        /** the data value */
133        protected Object           theValue;
134
135        /** the data format class */
136        protected final Class      originalFormatClass;
137
138        /** if the data value has changed */
139        protected boolean          isValueChanged;
140
141        /** the type of the parent*/
142        protected final boolean    isContainerType;
143
144        /** the rank */
145        protected final int        rank;
146
147        /** if the data is in original order */
148        protected final boolean    isNaturalOrder;
149        /** if the data is transposed */
150        protected final boolean    isDataTransposed;
151
152        /** the column */
153        protected long       colCount;
154        /** the row */
155        protected long       rowCount;
156
157        /**
158         * Create the HDF extended Data Display Provider for the supplied data object
159         *
160         * @param dtype
161         *        the datatype object
162         * @param dataBuf
163         *        the data buffer to use
164         * @param dataTransposed
165         *        if the data should be transposed
166         *
167         * @throws Exception if a failure occurred
168         */
169        HDFDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
170            this.dataBuf = dataBuf;
171
172            this.originalFormatClass = dataFormatReference.getOriginalClass();
173
174            char runtimeTypeClass = Utils.getJavaObjectRuntimeClass(dataBuf);
175            if (runtimeTypeClass == ' ') {
176                log.debug("invalid data value runtime type class: runtimeTypeClass={}", runtimeTypeClass);
177                throw new IllegalStateException("Invalid data value runtime type class: " + runtimeTypeClass);
178            }
179
180            rank = dataFormatReference.getRank();
181
182            isNaturalOrder = ((rank == 1) || (dataFormatReference.getSelectedIndex()[0] < dataFormatReference.getSelectedIndex()[1]));
183            isDataTransposed = dataTransposed;
184
185            if (rank > 1) {
186                rowCount = dataFormatReference.getHeight();
187                colCount = dataFormatReference.getWidth();
188            }
189            else {
190                rowCount = (int) dataFormatReference.getSelectedDims()[0];
191                colCount = 1;
192            }
193            log.trace("constructor: rowCount={} colCount={}", rowCount, colCount);
194
195            theValue = null;
196            isValueChanged = false;
197
198            isContainerType = (this instanceof CompoundDataProvider
199                            || this instanceof ArrayDataProvider
200                            || this instanceof VlenDataProvider);
201        }
202
203        /**
204         * A utility method used to translate a set of physical table coordinates to an
205         * index into a data buffer.
206         *
207         * @param rowIndex
208         *        the row
209         * @param columnIndex
210         *        the column
211         *
212         * @return physical location in 1D notation
213         */
214        public int physicalLocationToBufIndex(int rowIndex, int columnIndex) {
215            long index = rowIndex * colCount + columnIndex;
216
217            if (rank > 1) {
218                log.trace("physicalLocationToBufIndex({}, {}): rank > 1; adjusting for multi-dimensional dataset", rowIndex, columnIndex);
219
220                if (isDataTransposed && isNaturalOrder)
221                    index = columnIndex * rowCount + rowIndex;
222                else if (!isDataTransposed && !isNaturalOrder)
223                    // Reshape Data
224                    index = rowIndex * colCount + columnIndex;
225                else if (isDataTransposed && !isNaturalOrder)
226                    // Transpose Data
227                    index = columnIndex * rowCount + rowIndex;
228                else
229                    index = rowIndex * colCount + columnIndex;
230            }
231
232            log.trace("physicalLocationToBufIndex({}, {}, {}): finish", rowIndex, columnIndex, index);
233
234            return (int) index;
235        }
236
237        @Override
238        public Object getDataValue(int columnIndex, int rowIndex) {
239            try {
240                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
241
242                theValue = Array.get(dataBuf, bufIndex);
243            }
244            catch (Exception ex) {
245                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
246                theValue = DataFactoryUtils.errStr;
247            }
248
249            log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue);
250
251            return theValue;
252        }
253
254        /**
255         * When a CompoundDataProvider wants to pass a List of data down to a nested
256         * CompoundDataProvider, or when a top-level container DataProvider (such as an
257         * ArrayDataProvider) wants to hand data down to a base CompoundDataProvider, we
258         * need to pass down a List of data, plus a field and row index. This method is
259         * for facilitating this behavior.
260         *
261         * In general, all "container" DataProviders that have a "container" base
262         * DataProvider should call down into their base DataProvider(s) using this
263         * method, in order to ensure that buried CompoundDataProviders get handled
264         * correctly. When their base DataProvider is not a "container" type, the method
265         * getDataValue(Object, index) should be used instead.
266         *
267         * For atomic type DataProviders, we treat this method as directly calling into
268         * getDataValue(Object, index) using the passed rowIndex. However, this method
269         * should, in general, not be called by atomic type DataProviders.
270         *
271         * @param obj
272         *        the data object
273         * @param rowIndex
274         *        the row
275         * @param columnIndex
276         *        the column
277         *
278         * @return value of the data
279         */
280        public Object getDataValue(Object obj, int columnIndex, int rowIndex) {
281            return getDataValue(obj, rowIndex);
282        }
283
284        /**
285         * When a parent HDFDataProvider (such as an ArrayDataProvider) wants to
286         * retrieve a data value by routing the operation through its base
287         * HDFDataProvider, the parent HDFDataProvider will generally know the direct
288         * index to have the base provider use. This method is to facilitate this kind
289         * of behavior.
290         *
291         * Note that this method takes an Object parameter, which is the object that the
292         * method should pull its data from. This is to be able to nicely support nested
293         * compound DataProviders.
294         *
295         * @param obj
296         *        the data object
297         * @param index
298         *        the index into the data array
299         *
300         * @return the data object
301         */
302        public Object getDataValue(Object obj, int index) {
303            try {
304                theValue = Array.get(obj, index);
305            }
306            catch (Exception ex) {
307                log.debug("getDataValue({}): failure: ", index, ex);
308                theValue = DataFactoryUtils.errStr;
309            }
310
311            log.trace("getDataValue({})=({}): finish", index, theValue);
312
313            return theValue;
314        }
315
316        /**
317         * update the data value of a compound type.
318         *
319         * @param columnIndex
320         *        the column
321         * @param rowIndex
322         *        the row
323         * @param newValue
324         *        the new data value object
325         */
326        @Override
327        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
328            try {
329                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
330
331                updateAtomicValue(dataBuf, newValue, bufIndex);
332            }
333            catch (Exception ex) {
334                log.debug("setDataValue({}, {})=({}): cell value update failure: ", rowIndex, columnIndex, newValue, ex);
335            }
336            log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue);
337
338            /*
339             * TODO: throwing error dialogs when something fails?
340             *
341             * Tools.showError(shell, "Select", "Unable to set new value:\n\n " + ex);
342             */
343        }
344
345        /**
346         * When a CompoundDataProvider wants to pass a List of data down to a nested
347         * CompoundDataProvider, or when a top-level container DataProvider (such as an
348         * ArrayDataProvider) wants to hand data down to a base CompoundDataProvider, we
349         * need to pass down a List of data and the new value, plus a field and row
350         * index. This method is for facilitating this behavior.
351         *
352         * In general, all "container" DataProviders that have a "container" base
353         * DataProvider should call down into their base DataProvider(s) using this{},
354         * method, in order to ensure that buried CompoundDataProviders get handled
355         * correctly. When their base DataProvider is not a "container" type, the method
356         * setDataValue(index, Object, Object) should be used instead.
357         *
358         * For atomic type DataProviders, we treat this method as directly calling into
359         * setDataValue(index, Object, Object) using the passed rowIndex. However, this
360         * method should, in general, not be called by atomic type DataProviders.
361         *
362         * @param columnIndex
363         *        the column
364         * @param rowIndex
365         *        the row
366         * @param bufObject
367         *        the data object
368         * @param newValue
369         *        the new data object
370         */
371        public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) {
372            setDataValue(rowIndex, bufObject, newValue);
373        }
374
375        /**
376         * When a parent HDFDataProvider (such as an ArrayDataProvider) wants to set a
377         * data value by routing the operation through its base HDFDataProvider, the
378         * parent HDFDataProvider will generally know the direct index to have the base
379         * provider use. This method is to facilitate this kind of behavior.
380         *
381         * Note that this method takes two Object parameters, one which is the object
382         * that the method should set its data inside of and one which is the new value
383         * to set. This is to be able to nicely support nested compound DataProviders.
384         *
385         * @param index
386         *        the index into the data array
387         * @param bufObject
388         *        the data object
389         * @param newValue
390         *        the new data object
391         */
392        public void setDataValue(int index, Object bufObject, Object newValue) {
393            try {
394                updateAtomicValue(bufObject, newValue, index);
395            }
396            catch (Exception ex) {
397                log.debug("setDataValue({}, {})=({}): updateAtomicValue failure: ", index, bufObject, newValue, ex);
398            }
399            log.trace("setDataValue({}, {})=({}): finish", index, bufObject, newValue);
400        }
401
402        private void updateAtomicValue(Object bufObject, Object newValue, int bufIndex) {
403            if ((newValue == null) || ((newValue = ((String) newValue).trim()) == null)) {
404                log.debug("updateAtomicValue(): cell value not updated; new value is null");
405                return;
406            }
407
408            // No need to update if values are the same
409            Object oldVal = this.getDataValue(bufObject, bufIndex);
410            if ((oldVal != null) && newValue.equals(oldVal.toString())) {
411                log.debug("updateAtomicValue(): cell value not updated; new value same as old value");
412                return;
413            }
414
415            char runtimeTypeClass = Utils.getJavaObjectRuntimeClass(bufObject);
416
417            log.trace("updateAtomicValue(): runtimeTypeClass={}", runtimeTypeClass);
418
419            switch (runtimeTypeClass) {
420                case 'B':
421                    byte bvalue = 0;
422                    bvalue = Byte.parseByte((String) newValue);
423                    Array.setByte(bufObject, bufIndex, bvalue);
424                    break;
425                case 'S':
426                    short svalue = 0;
427                    svalue = Short.parseShort((String) newValue);
428                    Array.setShort(bufObject, bufIndex, svalue);
429                    break;
430                case 'I':
431                    int ivalue = 0;
432                    ivalue = Integer.parseInt((String) newValue);
433                    Array.setInt(bufObject, bufIndex, ivalue);
434                    break;
435                case 'J':
436                    long lvalue = 0;
437                    String cname = this.originalFormatClass.getName();
438                    char dname = cname.charAt(cname.lastIndexOf('[') + 1);
439                    if (dname == 'J') {
440                        BigInteger big = new BigInteger((String) newValue);
441                        lvalue = big.longValue();
442                    }
443                    else
444                        lvalue = Long.parseLong((String) newValue);
445                    Array.setLong(bufObject, bufIndex, lvalue);
446                    break;
447                case 'F':
448                    float fvalue = 0;
449                    fvalue = Float.parseFloat((String) newValue);
450                    Array.setFloat(bufObject, bufIndex, fvalue);
451                    break;
452                case 'D':
453                    double dvalue = 0;
454                    dvalue = Double.parseDouble((String) newValue);
455                    Array.setDouble(bufObject, bufIndex, dvalue);
456                    break;
457                default:
458                    Array.set(bufObject, bufIndex, newValue);
459                    break;
460            }
461
462            isValueChanged = true;
463        }
464
465        @Override
466        public int getColumnCount() {
467            return (int) colCount;
468        }
469
470        @Override
471        public int getRowCount() {
472            return (int) rowCount;
473        }
474
475        /**
476         * set if the data value has changed
477         *
478         * @param isChanged
479         *        if the data value is changed
480         */
481        public final void setIsValueChanged(boolean isChanged) {
482            isValueChanged = isChanged;
483        }
484
485        /**
486         * @return if the datavalue has chaged
487         */
488        public final boolean getIsValueChanged() {
489            return isValueChanged;
490        }
491
492        /**
493         * Update the data buffer for this HDFDataProvider. This is necessary for when
494         * the data that has been read is invalidated, such as when flipping through
495         * 'pages' in a > 2-dimensional dataset.
496         *
497         * @param newBuf
498         *        the new data buffer
499         */
500        public final void updateDataBuffer(Object newBuf) {
501            this.dataBuf = newBuf;
502
503            if (rank > 1) {
504                rowCount = dataFormatReference.getHeight();
505                colCount = dataFormatReference.getWidth();
506            }
507            else {
508                rowCount = (int) dataFormatReference.getSelectedDims()[0];
509                colCount = 1;
510            }
511            log.trace("updateDataBuffer: rowCount={} colCount={}", rowCount, colCount);
512        }
513    }
514
515    /*
516     * A DataProvider for Compound datatype datasets which is a composite of
517     * DataProviders, one for each selected member of the Compound datatype.
518     */
519    private static class CompoundDataProvider extends HDFDataProvider
520    {
521        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CompoundDataProvider.class);
522
523        private final HashMap<Integer, Integer> baseProviderIndexMap;
524        private final HashMap<Integer, Integer> relCmpdStartIndexMap;
525
526        private final HDFDataProvider[]         baseTypeProviders;
527
528        private final Datatype[]                selectedMemberTypes;
529
530        private final int[]                     selectedMemberOrders;
531
532        private final int                       nSubColumns;
533        private final int                       nCols;
534        private final int                       nRows;
535
536        CompoundDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
537            super(dtype, dataBuf, dataTransposed);
538
539            CompoundDataFormat compoundFormat = (CompoundDataFormat) dataFormatReference;
540            selectedMemberTypes = compoundFormat.getSelectedMemberTypes();
541            selectedMemberOrders = compoundFormat.getSelectedMemberOrders();
542
543            List<Datatype> localSelectedTypes = DataFactoryUtils.filterNonSelectedMembers(compoundFormat, dtype);
544
545            log.trace("setting up {} base HDFDataProviders", localSelectedTypes.size());
546
547            baseTypeProviders = new HDFDataProvider[localSelectedTypes.size()];
548            for (int i = 0; i < baseTypeProviders.length; i++) {
549                log.trace("retrieving DataProvider for member {}", i);
550
551                try {
552                    baseTypeProviders[i] = getDataProvider(localSelectedTypes.get(i), dataBuf, dataTransposed);
553                }
554                catch (Exception ex) {
555                    log.debug("failed to retrieve DataProvider for member {}: ", i, ex);
556                    baseTypeProviders[i] = null;
557                }
558            }
559
560            /*
561             * Build necessary index maps.
562             */
563            HashMap<Integer, Integer>[] maps = DataFactoryUtils.buildIndexMaps(compoundFormat, localSelectedTypes);
564            baseProviderIndexMap = maps[DataFactoryUtils.COL_TO_BASE_CLASS_MAP_INDEX];
565            relCmpdStartIndexMap = maps[DataFactoryUtils.CMPD_START_IDX_MAP_INDEX];
566
567            log.trace("index maps built: baseProviderIndexMap = {}, relColIdxMap = {}",
568                    baseProviderIndexMap.toString(), relCmpdStartIndexMap.toString());
569
570            if (baseProviderIndexMap.size() == 0) {
571                log.debug("base DataProvider index mapping is invalid - size 0");
572                throw new Exception("CompoundDataProvider: invalid DataProvider mapping of size 0 built");
573            }
574
575            if (relCmpdStartIndexMap.size() == 0) {
576                log.debug("compound field start index mapping is invalid - size 0");
577                throw new Exception("CompoundDataProvider: invalid compound field start index mapping of size 0 built");
578            }
579
580            /*
581             * nCols should represent the number of columns covered by this CompoundDataProvider
582             * only. For top-level CompoundDataProviders, this should be the entire width of the
583             * dataset. For nested CompoundDataProviders, nCols will be a subset of these columns.
584             */
585            nCols = (int) compoundFormat.getWidth() * baseProviderIndexMap.size();
586            nRows = (int) compoundFormat.getHeight();
587
588            nSubColumns = (int) compoundFormat.getWidth();
589        }
590
591        @Override
592        public Object getDataValue(int columnIndex, int rowIndex) {
593            try {
594                int fieldIdx = columnIndex;
595                int rowIdx = rowIndex;
596
597                if (nSubColumns > 1) { // multi-dimension compound dataset
598                    /*
599                     * Make sure fieldIdx is within a valid range, since even for multi-dimensional
600                     * compound datasets there will only be as many lists of data as there are
601                     * members in a single compound type.
602                     */
603                    fieldIdx %= selectedMemberTypes.length;
604
605                    int realColIdx = columnIndex / selectedMemberTypes.length;
606                    rowIdx = rowIndex * nSubColumns + realColIdx;
607                }
608
609                int providerIndex = baseProviderIndexMap.get(fieldIdx);
610                Object colValue = ((List<?>) dataBuf).get(providerIndex);
611                if (colValue == null)
612                    return DataFactoryUtils.nullStr;
613
614                /*
615                 * Delegate data retrieval to one of the base DataProviders according to the
616                 * index of the relevant compound field.
617                 */
618                HDFDataProvider base = baseTypeProviders[providerIndex];
619                if (base instanceof CompoundDataProvider)
620                    /*
621                     * Adjust the compound field index by subtracting the starting index of the
622                     * nested compound that we are delegating to. When the nested compound's index
623                     * map is setup correctly, this adjusted index should map to the correct field
624                     * among the nested compound's members.
625                     */
626                    theValue = base.getDataValue(colValue, fieldIdx - relCmpdStartIndexMap.get(fieldIdx), rowIdx);
627                else if (base instanceof ArrayDataProvider) {
628                    /*
629                     * TODO: quick temporary fix for specific compound of array of compound files.
630                     * Transforms the given column index into a relative index from the starting
631                     * index of the array of compound field.
632                     */
633                    int arrCompoundStartIdx = columnIndex;
634                    HDFDataProvider theProvider;
635                    while (arrCompoundStartIdx >= 0) {
636                        try {
637                            theProvider = baseTypeProviders[baseProviderIndexMap.get(arrCompoundStartIdx - 1)];
638                            if (theProvider != base)
639                                break;
640
641                            arrCompoundStartIdx--;
642                        }
643                        catch (Exception ex) {
644                            break;
645                        }
646                    }
647
648                    int adjustedColIndex = columnIndex - arrCompoundStartIdx;
649
650                    theValue = base.getDataValue(colValue, adjustedColIndex, rowIdx);
651                }
652                else
653                    theValue = base.getDataValue(colValue, rowIdx);
654            }
655            catch (Exception ex) {
656                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
657                theValue = DataFactoryUtils.errStr;
658            }
659
660            log.trace("getDataValue({}, {}): finish", rowIndex, columnIndex);
661
662            return theValue;
663        }
664
665        @Override
666        public Object getDataValue(Object obj, int columnIndex, int rowIndex) {
667            try {
668                int providerIndex = baseProviderIndexMap.get(columnIndex);
669                Object colValue = ((List<?>) obj).get(providerIndex);
670                if (colValue == null)
671                    return DataFactoryUtils.nullStr;
672
673                /*
674                 * Delegate data retrieval to one of the base DataProviders according to the
675                 * index of the relevant compound field.
676                 */
677                HDFDataProvider base = baseTypeProviders[providerIndex];
678                if (base instanceof CompoundDataProvider)
679                    /*
680                     * Adjust the compound field index by subtracting the starting index of the
681                     * nested compound that we are delegating to. When the nested compound's index
682                     * map is setup correctly, this adjusted index should map to the correct field
683                     * among the nested compound's members.
684                     */
685                    theValue = base.getDataValue(colValue, columnIndex - relCmpdStartIndexMap.get(columnIndex), rowIndex);
686                else if (base instanceof ArrayDataProvider)
687                    theValue = base.getDataValue(colValue, columnIndex, rowIndex);
688                else
689                    theValue = base.getDataValue(colValue, rowIndex);
690            }
691            catch (Exception ex) {
692                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
693                theValue = DataFactoryUtils.errStr;
694            }
695            log.trace("getDataValue({})=({}): finish", rowIndex, columnIndex);
696
697            return theValue;
698        }
699
700        @Override
701        public Object getDataValue(Object obj, int index) {
702            throw new UnsupportedOperationException("getDataValue(Object, int) should not be called for CompoundDataProviders");
703        }
704
705        @Override
706        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
707            if ((newValue == null) || ((newValue = ((String) newValue).trim()) == null)) {
708                log.debug("setDataValue({}, {})=({}): cell value not updated; new value is null", rowIndex, columnIndex, newValue);
709                return;
710            }
711
712            // No need to update if values are the same
713            Object oldVal = this.getDataValue(columnIndex, rowIndex);
714            if ((oldVal != null) && newValue.equals(oldVal.toString())) {
715                log.debug("setDataValue({}, {})=({}): cell value not updated; new value same as old value", rowIndex, columnIndex, newValue);
716                return;
717            }
718
719            try {
720                int fieldIdx = columnIndex;
721                int rowIdx = rowIndex;
722
723                if (nSubColumns > 1) { // multi-dimension compound dataset
724                    /*
725                     * Make sure fieldIdx is within a valid range, since even for multi-dimensional
726                     * compound datasets there will only be as many lists of data as there are
727                     * members in a single compound type.
728                     */
729                    fieldIdx %= selectedMemberTypes.length;
730
731                    int realColIdx = columnIndex / selectedMemberTypes.length;
732                    rowIdx = rowIndex * nSubColumns + realColIdx;
733                }
734
735                int providerIndex = baseProviderIndexMap.get(fieldIdx);
736                Object colValue = ((List<?>) dataBuf).get(providerIndex);
737                if (colValue == null) {
738                    log.debug("setDataValue({}, {})=({}): colValue is null", rowIndex, columnIndex, newValue);
739                    return;
740                }
741
742                /*
743                 * Delegate data setting to one of the base DataProviders according to the index
744                 * of the relevant compound field.
745                 */
746                HDFDataProvider base = baseTypeProviders[providerIndex];
747                if (base.isContainerType)
748                    /*
749                     * Adjust the compound field index by subtracting the starting index of the
750                     * nested compound that we are delegating to. When the nested compound's index
751                     * map is setup correctly, this adjusted index should map to the correct field
752                     * among the nested compound's members.
753                     */
754                    base.setDataValue(fieldIdx - relCmpdStartIndexMap.get(fieldIdx), rowIdx, colValue, newValue);
755                else
756                    base.setDataValue(rowIdx, colValue, newValue);
757
758                isValueChanged = true;
759            }
760            catch (Exception ex) {
761                log.debug("setDataValue({}, {})=({}): cell value update failure: ", rowIndex, columnIndex, newValue);
762            }
763            log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue);
764
765            /*
766             * TODO: throwing error dialogs when something fails?
767             *
768             * Tools.showError(shell, "Select", "Unable to set new value:\n\n " + ex);
769             */
770        }
771
772        @Override
773        public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) {
774            try {
775                int providerIndex = baseProviderIndexMap.get(columnIndex);
776                Object colValue = ((List<?>) bufObject).get(providerIndex);
777                if (colValue == null) {
778                    log.debug("setDataValue({}, {}, {})=({}): colValue is null", rowIndex, columnIndex, bufObject, newValue);
779                    return;
780                }
781
782                /*
783                 * Delegate data setting to one of the base DataProviders according to the index
784                 * of the relevant compound field.
785                 */
786                HDFDataProvider base = baseTypeProviders[providerIndex];
787                if (base.isContainerType)
788                    /*
789                     * Adjust the compound field index by subtracting the starting index of the
790                     * nested compound that we are delegating to. When the nested compound's index
791                     * map is setup correctly, this adjusted index should map to the correct field
792                     * among the nested compound's members.
793                     */
794                    base.setDataValue(columnIndex - relCmpdStartIndexMap.get(columnIndex), rowIndex, colValue, newValue);
795                else
796                    base.setDataValue(rowIndex, colValue, newValue);
797
798                isValueChanged = true;
799            }
800            catch (Exception ex) {
801                log.debug("setDataValue({}, {}, {})=({}): cell value update failure: ", rowIndex, columnIndex, bufObject, newValue, ex);
802            }
803            log.trace("setDataValue({}, {}, {})=({}): finish", rowIndex, columnIndex, bufObject, newValue);
804        }
805
806        @Override
807        public void setDataValue(int index, Object bufObject, Object newValue) {
808            throw new UnsupportedOperationException("setDataValue(int, Object, Object) should not be called for CompoundDataProviders");
809        }
810
811        @Override
812        public int getColumnCount() {
813            return nCols;
814        }
815
816        @Override
817        public int getRowCount() {
818            return nRows;
819        }
820    }
821
822    private static class ArrayDataProvider extends HDFDataProvider
823    {
824        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ArrayDataProvider.class);
825
826        private final HDFDataProvider baseTypeDataProvider;
827
828        private final Object[]        arrayElements;
829        private final long            arraySize;
830
831        private final int             nCols;
832
833        ArrayDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
834            super(dtype, dataBuf, dataTransposed);
835
836            Datatype baseType = dtype.getDatatypeBase();
837
838            baseTypeDataProvider = getDataProvider(baseType, dataBuf, dataTransposed);
839
840            if (baseType.isVarStr())
841                arraySize = dtype.getArrayDims()[0];
842            else if (baseType.isBitField() || baseType.isOpaque())
843                arraySize = dtype.getDatatypeSize();
844            else
845                arraySize = dtype.getDatatypeSize() / baseType.getDatatypeSize();
846
847            arrayElements = new Object[(int) arraySize];
848
849            if (baseTypeDataProvider instanceof CompoundDataProvider)
850                nCols = (int) arraySize * ((CompoundDataProvider) baseTypeDataProvider).nCols;
851            else
852                nCols = super.getColumnCount();
853        }
854
855        @Override
856        public Object getDataValue(int columnIndex, int rowIndex) {
857            try {
858                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
859
860                bufIndex *= arraySize;
861
862                if (baseTypeDataProvider instanceof CompoundDataProvider) {
863                    /*
864                     * Pass row and column indices down where they will be adjusted.
865                     */
866                    theValue = retrieveArrayOfCompoundElements(dataBuf, columnIndex, rowIndex);
867                }
868                else if (baseTypeDataProvider instanceof ArrayDataProvider) {
869                    /*
870                     * TODO: assign to global arrayElements.
871                     */
872                    theValue = retrieveArrayOfArrayElements(dataBuf, columnIndex, bufIndex);
873                }
874                else {
875                    /*
876                     * TODO: assign to global arrayElements.
877                     */
878                    theValue = retrieveArrayOfAtomicElements(dataBuf, bufIndex);
879                }
880            }
881            catch (Exception ex) {
882                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
883                theValue = DataFactoryUtils.errStr;
884            }
885
886            log.trace("getDataValue({}, {})({}): finish", rowIndex, columnIndex, theValue);
887
888            return theValue;
889        }
890
891        @Override
892        public Object getDataValue(Object obj, int columnIndex, int rowIndex) {
893            try {
894                long index = rowIndex * arraySize;
895
896                if (baseTypeDataProvider instanceof CompoundDataProvider) {
897                    /*
898                     * Pass row and column indices down where they will be adjusted.
899                     */
900                    theValue = retrieveArrayOfCompoundElements(obj, columnIndex, rowIndex);
901                }
902                else if (baseTypeDataProvider instanceof ArrayDataProvider) {
903                    theValue = retrieveArrayOfArrayElements(obj, columnIndex, (int) index);
904                }
905                else {
906                    theValue = retrieveArrayOfAtomicElements(obj, (int) index);
907                }
908            }
909            catch (Exception ex) {
910                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
911                theValue = DataFactoryUtils.errStr;
912            }
913
914            return theValue;
915        }
916
917        private Object[] retrieveArrayOfCompoundElements(Object objBuf, int columnIndex, int rowIndex) {
918            long adjustedRowIdx = (rowIndex * arraySize * colCount)
919                    + (columnIndex / ((CompoundDataProvider) baseTypeDataProvider).baseProviderIndexMap.size());
920            long adjustedColIdx = columnIndex % ((CompoundDataProvider) baseTypeDataProvider).baseProviderIndexMap.size();
921
922            /*
923             * Since we flatten array of compound types, we only need to return a single
924             * value.
925             */
926            return new Object[] { baseTypeDataProvider.getDataValue(objBuf, (int) adjustedColIdx, (int) adjustedRowIdx) };
927        }
928
929        private Object[] retrieveArrayOfArrayElements(Object objBuf, int columnIndex, int startRowIndex) {
930            Object[] tempArray = new Object[(int) arraySize];
931
932            for (int i = 0; i < arraySize; i++)
933                tempArray[i] = baseTypeDataProvider.getDataValue(objBuf, columnIndex, startRowIndex + i);
934
935            return tempArray;
936        }
937
938        private Object[] retrieveArrayOfAtomicElements(Object objBuf, int rowStartIdx) {
939            Object[] tempArray = new Object[(int) arraySize];
940
941            for (int i = 0; i < arraySize; i++)
942                tempArray[i] = baseTypeDataProvider.getDataValue(objBuf, rowStartIdx + i);
943
944            return tempArray;
945        }
946
947        @Override
948        public Object getDataValue(Object obj, int index) {
949            throw new UnsupportedOperationException("getDataValue(Object, int) should not be called for ArrayDataProviders");
950        }
951
952        @Override
953        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
954            try {
955                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
956
957                bufIndex *= arraySize;
958
959                updateArrayElements(dataBuf, newValue, columnIndex, bufIndex);
960            }
961            catch (Exception ex) {
962                log.debug("setDataValue({}, {}, {}): cell value update failure: ", rowIndex, columnIndex, newValue, ex);
963            }
964            log.trace("setDataValue({}, {})=({}): finish", rowIndex, columnIndex, newValue);
965        }
966
967        @Override
968        public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) {
969            try {
970                long bufIndex = rowIndex * arraySize;
971
972                updateArrayElements(bufObject, newValue, columnIndex, (int) bufIndex);
973            }
974            catch (Exception ex) {
975                log.debug("setDataValue({}, {}, {}, {}): cell value update failure: ", rowIndex, columnIndex, bufObject, newValue, ex);
976            }
977            log.trace("setDataValue({}, {}, {})=({}): finish", rowIndex, columnIndex, bufObject, newValue);
978        }
979
980        @Override
981        public void setDataValue(int index, Object bufObject, Object newValue) {
982            throw new UnsupportedOperationException("setDataValue(int, Object, Object) should not be called for ArrayDataProviders");
983        }
984
985        private void updateArrayElements(Object curBuf, Object newValue, int columnIndex, int bufStartIndex) {
986            StringTokenizer st = new StringTokenizer((String) newValue, ",[]");
987            if (st.countTokens() < arraySize) {
988                /*
989                 * TODO:
990                 */
991                /* Tools.showError(shell, "Select", "Number of data points < " + morder + "."); */
992                log.debug("updateArrayElements(): number of data points ({}) < array size {}", st.countTokens(), arraySize);
993                log.trace("updateArrayElements({}, {}, {}): finish", curBuf, newValue, bufStartIndex);
994                return;
995            }
996
997            if (baseTypeDataProvider instanceof CompoundDataProvider)
998                updateArrayOfCompoundElements(st, curBuf, columnIndex, bufStartIndex);
999            else if (baseTypeDataProvider instanceof ArrayDataProvider)
1000                updateArrayOfArrayElements(st, curBuf, columnIndex, bufStartIndex);
1001            else
1002                updateArrayOfAtomicElements(st, curBuf, bufStartIndex);
1003        }
1004
1005        private void updateArrayOfCompoundElements(StringTokenizer tokenizer, Object curBuf, int columnIndex, int bufStartIndex) {
1006            for (int i = 0; i < arraySize; i++) {
1007                List<?> cmpdDataList = (List<?>) ((Object[]) curBuf)[i];
1008                baseTypeDataProvider.setDataValue(columnIndex, bufStartIndex + i, cmpdDataList,
1009                        tokenizer.nextToken().trim());
1010                isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged();
1011            }
1012        }
1013
1014        private void updateArrayOfArrayElements(StringTokenizer tokenizer, Object curBuf, int columnIndex, int bufStartIndex) {
1015            for (int i = 0; i < arraySize; i++) {
1016                /*
1017                 * TODO: not quite right.
1018                 */
1019                baseTypeDataProvider.setDataValue(columnIndex, bufStartIndex + i, curBuf, tokenizer.nextToken().trim());
1020                isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged();
1021            }
1022        }
1023
1024        private void updateArrayOfAtomicElements(StringTokenizer tokenizer, Object curBuf, int bufStartIndex) {
1025            for (int i = 0; i < arraySize; i++) {
1026                baseTypeDataProvider.setDataValue(bufStartIndex + i, curBuf, tokenizer.nextToken().trim());
1027                isValueChanged = isValueChanged || baseTypeDataProvider.getIsValueChanged();
1028            }
1029        }
1030
1031        @Override
1032        public int getColumnCount() {
1033            return nCols;
1034        }
1035    }
1036
1037    private static class VlenDataProvider extends HDFDataProvider
1038    {
1039        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(VlenDataProvider.class);
1040
1041        private final HDFDataProvider baseTypeDataProvider;
1042
1043        private final StringBuilder   buffer;
1044
1045        VlenDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
1046            super(dtype, dataBuf, dataTransposed);
1047
1048            Datatype baseType = dtype.getDatatypeBase();
1049
1050            baseTypeDataProvider = getDataProvider(baseType, dataBuf, dataTransposed);
1051
1052            buffer = new StringBuilder();
1053        }
1054
1055        @Override
1056        public Object getDataValue(int columnIndex, int rowIndex) {
1057            buffer.setLength(0);
1058
1059            try {
1060                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
1061
1062                if (baseTypeDataProvider instanceof CompoundDataProvider) {
1063                    /*
1064                     * TODO:
1065                     */
1066                    /*
1067                     * buffer.append(baseTypeDataProvider.getDataValue(dataBuf, columnIndex, (int) index));
1068                     */
1069                    if (dataBuf instanceof String[])
1070                        buffer.append(Array.get(dataBuf, bufIndex));
1071                    else
1072                        buffer.append("*UNSUPPORTED*");
1073                }
1074                else if (baseTypeDataProvider instanceof ArrayDataProvider) {
1075                    buffer.append(baseTypeDataProvider.getDataValue(dataBuf, columnIndex, bufIndex));
1076                }
1077                else {
1078                    buffer.append(baseTypeDataProvider.getDataValue(dataBuf, bufIndex));
1079                }
1080
1081                theValue = buffer.toString();
1082            }
1083            catch (Exception ex) {
1084                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
1085                theValue = DataFactoryUtils.errStr;
1086            }
1087
1088            log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue);
1089
1090            return theValue;
1091        }
1092
1093        @Override
1094        public Object getDataValue(Object obj, int columnIndex, int rowIndex) {
1095            buffer.setLength(0);
1096
1097            try {
1098                if (baseTypeDataProvider instanceof CompoundDataProvider) {
1099                    /*
1100                     * TODO:
1101                     */
1102                    /*
1103                     * buffer.append(baseTypeDataProvider.getDataValue(obj, columnIndex, rowIndex));
1104                     */
1105                    if (obj instanceof String[])
1106                        buffer.append(Array.get(obj, rowIndex));
1107                    else
1108                        buffer.append("*UNSUPPORTED*");
1109                }
1110                else if (baseTypeDataProvider instanceof ArrayDataProvider) {
1111                    buffer.append(baseTypeDataProvider.getDataValue(obj, columnIndex, rowIndex));
1112                }
1113                else {
1114                    buffer.append(baseTypeDataProvider.getDataValue(obj, rowIndex));
1115                }
1116
1117                theValue = buffer.toString();
1118            }
1119            catch (Exception ex) {
1120                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
1121                theValue = DataFactoryUtils.errStr;
1122            }
1123
1124            log.trace("getDataValue({}, {}, {})=({}): finish", obj, rowIndex, columnIndex, theValue);
1125
1126            return theValue;
1127        }
1128
1129        /* @Override
1130        public Object getDataValue(Object obj, int index) {
1131            throw new UnsupportedOperationException("getDataValue(Object, int) should not be called for VlenDataProviders");
1132        } */
1133
1134        @Override
1135        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
1136            /*
1137             * TODO:
1138             */
1139        }
1140
1141        @Override
1142        public void setDataValue(int columnIndex, int rowIndex, Object bufObject, Object newValue) {
1143            /*
1144             * TODO:
1145             */
1146        }
1147
1148        @Override
1149        public void setDataValue(int index, Object bufObject, Object newValue) {
1150            throw new UnsupportedOperationException("setDataValue(int, Object, Object) should not be called for VlenDataProviders");
1151        }
1152    }
1153
1154    private static class StringDataProvider extends HDFDataProvider
1155    {
1156        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(StringDataProvider.class);
1157
1158        private final long typeSize;
1159
1160        StringDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
1161            super(dtype, dataBuf, dataTransposed);
1162
1163            typeSize = dtype.getDatatypeSize();
1164        }
1165
1166        @Override
1167        public Object getDataValue(Object obj, int index) {
1168            if (obj instanceof byte[]) {
1169                int strlen = (int) typeSize;
1170
1171                log.trace("getDataValue({}, {}): converting byte[] to String", obj, index);
1172
1173                String str = new String((byte[]) obj, index * strlen, strlen);
1174                int idx = str.indexOf('\0');
1175                if (idx > 0)
1176                    str = str.substring(0, idx);
1177
1178                theValue = str.trim();
1179            }
1180            else
1181                super.getDataValue(obj, index);
1182
1183            log.trace("getDataValue({}, {})=({}): finish", obj, index, theValue);
1184
1185            return theValue;
1186        }
1187
1188        @Override
1189        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
1190            try {
1191                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
1192
1193                updateStringBytes(dataBuf, newValue, bufIndex);
1194            }
1195            catch (Exception ex) {
1196                log.debug("setDataValue({}, {}, {}): cell value update failure: ", rowIndex, columnIndex, newValue, ex);
1197            }
1198            log.trace("setDataValue({}, {}, {}): finish", rowIndex, columnIndex, newValue);
1199        }
1200
1201        @Override
1202        public void setDataValue(int index, Object bufObject, Object newValue) {
1203            try {
1204                updateStringBytes(bufObject, newValue, index);
1205            }
1206            catch (Exception ex) {
1207                log.debug("setDataValue({}, {}, {}): cell value update failure: ", index, bufObject, newValue, ex);
1208            }
1209            log.trace("setDataValue({}, {}, {}): finish", index, bufObject, newValue);
1210        }
1211
1212        private void updateStringBytes(Object curBuf, Object newValue, int bufStartIndex) {
1213            if (curBuf instanceof String[]) {
1214                Array.set(curBuf, bufStartIndex, newValue);
1215            }
1216            else if (curBuf instanceof byte[]) {
1217                // Update String using data represented as a byte[]
1218                int strLen = (int) typeSize;
1219                byte[] newValueBytes = ((String) newValue).getBytes();
1220                byte[] curBytes = (byte[]) curBuf;
1221                int n = Math.min(strLen, newValueBytes.length);
1222
1223                bufStartIndex *= typeSize;
1224
1225                System.arraycopy(newValueBytes, 0, curBytes, bufStartIndex, n);
1226
1227                bufStartIndex += n;
1228                n = strLen - newValueBytes.length;
1229
1230                // space padding
1231                for (int i = 0; i < n; i++)
1232                    curBytes[bufStartIndex + i] = ' ';
1233            }
1234
1235            isValueChanged = true;
1236        }
1237    }
1238
1239    private static class CharDataProvider extends HDFDataProvider
1240    {
1241        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CharDataProvider.class);
1242
1243        CharDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
1244            super(dtype, dataBuf, dataTransposed);
1245        }
1246
1247        @Override
1248        public Object getDataValue(int columnIndex, int rowIndex) {
1249            /*
1250             * Compatibility with HDF4 8-bit character types that get converted to a String
1251             * ahead of time.
1252             */
1253            if (dataBuf instanceof String) {
1254                log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, dataBuf);
1255                return dataBuf;
1256            }
1257
1258            return super.getDataValue(columnIndex, rowIndex);
1259        }
1260    }
1261
1262    private static class NumericalDataProvider extends HDFDataProvider
1263    {
1264        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NumericalDataProvider.class);
1265
1266        private final boolean isUINT64;
1267
1268        private final long    typeSize;
1269
1270        NumericalDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
1271            super(dtype, dataBuf, dataTransposed);
1272
1273            typeSize = dtype.getDatatypeSize();
1274            isUINT64 = dtype.isUnsigned() && (typeSize == 8);
1275        }
1276
1277        @Override
1278        public Object getDataValue(int columnIndex, int rowIndex) {
1279            super.getDataValue(columnIndex, rowIndex);
1280
1281            try {
1282                if (isUINT64)
1283                    theValue = Tools.convertUINT64toBigInt(Long.valueOf((long) theValue));
1284            }
1285            catch (Exception ex) {
1286                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
1287                theValue = DataFactoryUtils.errStr;
1288            }
1289
1290            log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue);
1291
1292            return theValue;
1293        }
1294
1295        @Override
1296        public Object getDataValue(Object obj, int index) {
1297            super.getDataValue(obj, index);
1298
1299            try {
1300                if (isUINT64)
1301                    theValue = Tools.convertUINT64toBigInt(Long.valueOf((long) theValue));
1302            }
1303            catch (Exception ex) {
1304                log.debug("getDataValue({}): failure: ", index, ex);
1305                theValue = DataFactoryUtils.errStr;
1306            }
1307
1308            log.trace("getDataValue({})=({}): finish", index, theValue);
1309
1310            return theValue;
1311        }
1312    }
1313
1314    private static class EnumDataProvider extends HDFDataProvider
1315    {
1316        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EnumDataProvider.class);
1317
1318        EnumDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
1319            super(dtype, dataBuf, dataTransposed);
1320        }
1321    }
1322
1323    private static class BitfieldDataProvider extends HDFDataProvider
1324    {
1325        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BitfieldDataProvider.class);
1326
1327        private final long typeSize;
1328
1329        BitfieldDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
1330            super(dtype, dataBuf, dataTransposed);
1331
1332            typeSize = dtype.getDatatypeSize();
1333        }
1334
1335        @Override
1336        public Object getDataValue(int columnIndex, int rowIndex) {
1337            try {
1338                int bufIndex = physicalLocationToBufIndex(rowIndex, columnIndex);
1339
1340                bufIndex *= typeSize;
1341                theValue = populateByteArray(dataBuf, bufIndex);
1342            }
1343            catch (Exception ex) {
1344                log.debug("getDataValue({}, {}): failure: ", rowIndex, columnIndex, ex);
1345                theValue = DataFactoryUtils.errStr;
1346            }
1347
1348            log.trace("getDataValue({}, {})=({}): finish", rowIndex, columnIndex, theValue);
1349
1350            return theValue;
1351        }
1352
1353        @Override
1354        public Object getDataValue(Object obj, int index) {
1355            try {
1356                index *= typeSize;
1357                theValue = populateByteArray(obj, index);
1358            }
1359            catch (Exception ex) {
1360                log.debug("getDataValue({}): ", index, ex);
1361                theValue = DataFactoryUtils.errStr;
1362            }
1363
1364            log.trace("getDataValue({})=({}): finish", index, theValue);
1365
1366            return theValue;
1367        }
1368
1369        private byte[] populateByteArray(Object byteBuf, int startIndex) {
1370            byte[] byteElements = new byte[(int) typeSize];
1371
1372            for (int i = 0; i < typeSize; i++)
1373                byteElements[i] = Array.getByte(byteBuf, startIndex + i);
1374
1375            return byteElements;
1376        }
1377    }
1378
1379    private static class RefDataProvider extends HDFDataProvider
1380    {
1381        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RefDataProvider.class);
1382
1383        RefDataProvider(final Datatype dtype, final Object dataBuf, final boolean dataTransposed) throws Exception {
1384            super(dtype, dataBuf, dataTransposed);
1385        }
1386    }
1387
1388}