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