    001/*****************************************************************************
    002 * Copyright by The HDF Group.                                               *
    003 * All rights reserved.                                                      *
    004 *                                                                           *
    005 * This file is part of the HDF Java Products distribution.                  *
    006 * The full copyright notice, including terms governing use, modification,   *
    007 * and redistribution, is contained in the COPYING file, which can be found  *
    008 * at the root of the source code distribution tree,                         *
    009 * or in https://www.hdfgroup.org/licenses.                                  *
    010 * If you do not have access to either file, you may request a copy from     *
    011 * help@hdfgroup.org.                                                        *
    012 ****************************************************************************/
    013
    014package hdf.view.TableView;
    015
    016import java.math.BigDecimal;
    017import java.math.BigInteger;
    018import java.util.HashMap;
    019import java.util.List;
    020import java.util.StringTokenizer;
    021
    022import org.slf4j.Logger;
    023import org.slf4j.LoggerFactory;
    024
    025import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
    026import org.eclipse.nebula.widgets.nattable.data.validate.DataValidator;
    027import org.eclipse.nebula.widgets.nattable.data.validate.ValidationFailedException;
    028import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
    029
    030import hdf.hdf5lib.HDF5Constants;
    031
    032import hdf.object.CompoundDataFormat;
    033import hdf.object.DataFormat;
    034import hdf.object.Datatype;
    035import hdf.object.h5.H5Datatype;
    036
    037/**
    038 * A Factory class to return a DataValidator class for a NatTable instance based
    039 * upon the Datatype that it is supplied.
    040 *
    041 * @author Jordan T. Henderson
    042 * @version 1.0 6/28/2018
    043 *
    044 */
    045public class DataValidatorFactory
    046{
    047    private static final Logger log = LoggerFactory.getLogger(DataValidatorFactory.class);
    048
    049    /**
    050     * To keep things clean from an API perspective, keep a static reference to the
    051     * last CompoundDataFormat that was passed in. This keeps us from needing to
    052     * pass the CompoundDataFormat object as a parameter to every DataValidator
    053     * class, since it's really only needed by the CompoundDataValidator.
    054     */
    055    private static DataFormat dataFormatReference = null;
    056
    057    /**
    058     * Get the Data Validator for the supplied data object
    059     *
    060     * @param dataObject
    061     *        the data object
    062     *
    063     * @return the validator instance
    064     *
    065     * @throws Exception if a failure occurred
    066     */
    067    public static HDFDataValidator getDataValidator(final DataFormat dataObject) throws Exception {
    068        if (dataObject == null) {
    069            log.debug("getDataValidator(DataFormat): data object is null");
    070            throw new Exception("Must supply a valid DataFormat to the DataValidatorFactory");
    071        }
    072
    073        dataFormatReference = dataObject;
    074
    075        HDFDataValidator validator = null;
    076        try {
    077            validator = getDataValidator(dataObject.getDatatype());
    078        }
    079        catch (Exception ex) {
    080            log.debug("getDataValidator(DataFormat): failed to retrieve a DataValidator: ", ex);
    081            validator = null;
    082        }
    083
    084        /*
    085         * By default, never validate if a proper DataValidator was not found.
    086         */
    087        if (validator == null) {
    088            log.debug("getDataValidator(DataFormat): using a default data validator");
    089
    090            validator = new HDFDataValidator(dataObject.getDatatype());
    091        }
    092
    093        return validator;
    094    }
    095
    096    private static HDFDataValidator getDataValidator(Datatype dtype) throws Exception {
    097        HDFDataValidator validator = null;
    098
    099        try {
    100            if (dtype.isCompound())
    101                validator = new CompoundDataValidator(dtype);
    102            else if (dtype.isArray())
    103                validator = new ArrayDataValidator(dtype);
    104            else if (dtype.isVLEN() && !dtype.isVarStr())
    105                validator = new VlenDataValidator(dtype);
    106            else if (dtype.isString() || dtype.isVarStr())
    107                validator = new StringDataValidator(dtype);
    108            else if (dtype.isChar())
    109                validator = new CharDataValidator(dtype);
    110            else if (dtype.isInteger() || dtype.isFloat())
    111                validator = new NumericalDataValidator(dtype);
    112            else if (dtype.isEnum())
    113                validator = new EnumDataValidator(dtype);
    114            else if (dtype.isOpaque() || dtype.isBitField())
    115                validator = new BitfieldDataValidator(dtype);
    116            else if (dtype.isRef())
    117                validator = new RefDataValidator(dtype);
    118        }
    119        catch (Exception ex) {
    120            log.debug("getDataValidator(Datatype): failed to retrieve a DataValidator: ", ex);
    121            validator = null;
    122        }
    123
    124        /*
    125         * By default, never validate if a proper DataValidator was not found.
    126         */
    127        if (validator == null) {
    128            log.debug("getDataValidator(Datatype): using a default data validator");
    129
    130            validator = new HDFDataValidator(dtype);
    131        }
    132
    133        return validator;
    134    }
    135
    136    /** The HDF extension of the data valicdation */
    137    public static class HDFDataValidator extends DataValidator
    138    {
    139        private static final Logger log = LoggerFactory.getLogger(HDFDataValidator.class);
    140
    141        /**
    142         * This field is only used for CompoundDataValidator, but when the top-level
    143         * DataValidator is a "container" type, such as an ArrayDataValidator, we have
    144         * to set this field and pass it through in case there is a
    145         * CompoundDataValidator at the bottom of the chain.
    146         */
    147        protected int cellColIdx;
    148
    149        /**
    150         * Create the HDF extended Data Validator for the datatype object
    151         *
    152         * @param dtype
    153         *        the datatype object
    154         */
    155        HDFDataValidator(final Datatype dtype) {
    156            cellColIdx = -1;
    157        }
    158
    159        /**
    160         * The validate method used to validate the data value for the type.
    161         *
    162         * @param colIndex
    163         *        the column
    164         * @param rowIndex
    165         *        the row
    166         * @param newValue
    167         *        the validated value
    168         *
    169         * @return true if this data is valid
    170         */
    171        @Override
    172        public boolean validate(int colIndex, int rowIndex, Object newValue) {
    173            throwValidationFailedException(rowIndex, colIndex, newValue,
    174                    "A proper DataValidator wasn't found for this type of data. Writing this type of data will be disabled.");
    175
    176            return false;
    177        }
    178
    179        /**
    180         * Validate the data value.
    181         *
    182         * @param newValue
    183         *        the value to validate
    184         */
    185        protected void checkValidValue(Object newValue) throws ValidationFailedException {
    186            if (newValue == null)
    187                throw new ValidationFailedException("value is null");
    188
    189            if (!(newValue instanceof String))
    190                throw new ValidationFailedException("value is not a String");
    191        }
    192
    193        /**
    194         * The validate exception message.
    195         *
    196         * @param rowIndex
    197         *        the row
    198         * @param colIndex
    199         *        the column
    200         * @param newValue
    201         *        the invalid value
    202         * @param reason
    203         *        the reason the value is invalid
    204         */
    205        protected void throwValidationFailedException(int rowIndex, int colIndex, Object newValue, String reason)
    206                throws ValidationFailedException {
    207            throw new ValidationFailedException("Failed to update value at " + "(" + rowIndex + ", "
    208                    + colIndex + ") to '" + newValue.toString() + "': " + reason);
    209        }
    210    }
    211
    212    /*
    213     * NatTable DataValidator to validate entered input for a dataset with
    214     * a Compound datatype by calling the appropriate validator on the member
    215     * at the given row and column index. The correct validator is determined
    216     * by taking the column index modulo the number of selected members in the
    217     * Compound datatype, and grabbing the correct validator from the stored
    218     * list of validators.
    219     */
    220    private static class CompoundDataValidator extends HDFDataValidator
    221    {
    222        private static final Logger log = LoggerFactory.getLogger(CompoundDataValidator.class);
    223
    224        private final HashMap<Integer, Integer> baseValidatorIndexMap;
    225        private final HashMap<Integer, Integer> relCmpdStartIndexMap;
    226        private final HDFDataValidator[]        memberValidators;
    227        private final int                       nTotFields;
    228
    229        CompoundDataValidator(final Datatype dtype) throws Exception {
    230            super(dtype);
    231
    232            if (!dtype.isCompound()) {
    233                log.debug("datatype is not a compound type");
    234                throw new Exception("CompoundDataValidator: datatype is not a compound type");
    235            }
    236
    237            CompoundDataFormat compoundFormat = (CompoundDataFormat) dataFormatReference;
    238
    239            List<Datatype> localSelectedTypes = DataFactoryUtils.filterNonSelectedMembers(compoundFormat, dtype);
    240
    241            log.trace("setting up {} base HDFDataValidators", localSelectedTypes.size());
    242
    243            memberValidators = new HDFDataValidator[localSelectedTypes.size()];
    244            for (int i = 0; i < memberValidators.length; i++) {
    245                log.trace("retrieving DataValidator for member {}", i);
    246
    247                try {
    248                    memberValidators[i] = getDataValidator(localSelectedTypes.get(i));
    249                }
    250                catch (Exception ex) {
    251                    log.debug("failed to retrieve DataValidator for member {}: ", i, ex);
    252                    memberValidators[i] = null;
    253                }
    254            }
    255
    256            /*
    257             * Build necessary index maps.
    258             */
    259            HashMap<Integer, Integer>[] maps = DataFactoryUtils.buildIndexMaps(compoundFormat, localSelectedTypes);
    260            baseValidatorIndexMap = maps[DataFactoryUtils.COL_TO_BASE_CLASS_MAP_INDEX];
    261            relCmpdStartIndexMap = maps[DataFactoryUtils.CMPD_START_IDX_MAP_INDEX];
    262
    263            log.trace("index maps built: baseValidatorIndexMap = {}, relColIdxMap = {}",
    264                    baseValidatorIndexMap.toString(), relCmpdStartIndexMap.toString());
    265
    266            if (baseValidatorIndexMap.size() == 0) {
    267                log.debug("base DataValidator index mapping is invalid - size 0");
    268                throw new Exception("CompoundDataValidator: invalid DataValidator mapping of size 0 built");
    269            }
    270
    271            if (relCmpdStartIndexMap.size() == 0) {
    272                log.debug("compound field start index mapping is invalid - size 0");
    273                throw new Exception("CompoundDataValidator: invalid compound field start index mapping of size 0 built");
    274            }
    275
    276            nTotFields = baseValidatorIndexMap.size();
    277        }
    278
    279        @Override
    280        public boolean validate(ILayerCell cell, IConfigRegistry configRegistry, Object newValue) {
    281            cellColIdx = cell.getColumnIndex() % nTotFields;
    282            return validate(cell.getColumnIndex(), cell.getRowIndex(), newValue);
    283        }
    284
    285        @Override
    286        public boolean validate(int colIndex, int rowIndex, Object newValue) {
    287            log.trace("validate({}, {}, {}): start", rowIndex, colIndex, newValue);
    288
    289            try {
    290                super.checkValidValue(newValue);
    291
    292                if (cellColIdx >= nTotFields)
    293                    cellColIdx %= nTotFields;
    294
    295                HDFDataValidator validator = memberValidators[baseValidatorIndexMap.get(cellColIdx)];
    296                validator.cellColIdx = cellColIdx - relCmpdStartIndexMap.get(cellColIdx);
    297
    298                validator.validate(colIndex, rowIndex, newValue);
    299            }
    300            catch (Exception ex) {
    301                log.debug("validate({}, {}, {}): failed to validate: ", rowIndex, colIndex, newValue, ex);
    302                throw new ValidationFailedException(ex.getMessage(), ex);
    303            }
    304            finally {
    305                log.trace("validate({}, {}, {}): finish", rowIndex, colIndex, newValue);
    306            }
    307
    308            return true;
    309        }
    310    }
    311
    312    /*
    313     * NatTable DataValidator to validate entered input for a dataset with
    314     * an ARRAY datatype by calling the appropriate validator (as determined
    315     * by the supplied datatype) on each of the array's elements.
    316     */
    317    private static class ArrayDataValidator extends HDFDataValidator
    318    {
    319        private static final Logger log = LoggerFactory.getLogger(ArrayDataValidator.class);
    320
    321        private final HDFDataValidator baseValidator;
    322
    323        ArrayDataValidator(final Datatype dtype) throws Exception {
    324            super(dtype);
    325
    326            if (!dtype.isArray()) {
    327                log.debug("datatype is not an array type");
    328                throw new Exception("ArrayDataValidator: datatype is not an array type");
    329            }
    330
    331            Datatype baseType = dtype.getDatatypeBase();
    332            if (baseType == null) {
    333                log.debug("base datatype is null");
    334                throw new Exception("ArrayDataValidator: base datatype is null");
    335            }
    336
    337            log.trace("ArrayDataValidator: base Datatype is {}", baseType.getDescription());
    338
    339            try {
    340                baseValidator = getDataValidator(baseType);
    341            }
    342            catch (Exception ex) {
    343                log.debug("couldn't get DataValidator for base datatype: ", ex);
    344                throw new Exception("ArrayDataValidator: couldn't get DataValidator for base datatype: " + ex.getMessage());
    345            }
    346        }
    347
    348        @Override
    349        public boolean validate(ILayerCell cell, IConfigRegistry configRegistry, Object newValue) {
    350            cellColIdx = cell.getColumnIndex();
    351            return validate(cell.getColumnIndex(), cell.getRowIndex(), newValue);
    352        }
    353
    354        @Override
    355        public boolean validate(int colIndex, int rowIndex, Object newValue) {
    356            log.trace("validate({}, {}, {}): start", rowIndex, colIndex, newValue);
    357
    358            try {
    359                super.checkValidValue(newValue);
    360
    361                baseValidator.cellColIdx = cellColIdx;
    362
    363                StringTokenizer elementReader = new StringTokenizer((String) newValue, " \t\n\r\f,[]");
    364                while (elementReader.hasMoreTokens()) {
    365                    String nextToken = elementReader.nextToken();
    366                    baseValidator.validate(colIndex, rowIndex, nextToken);
    367                }
    368            }
    369            catch (Exception ex) {
    370                log.debug("validate({}, {}, {}): failed to validate: ", rowIndex, colIndex, newValue, ex);
    371                throw new ValidationFailedException(ex.getMessage(), ex);
    372            }
    373            finally {
    374                log.trace("validate({}, {}, {}): finish", rowIndex, colIndex, newValue);
    375            }
    376
    377            return true;
    378        }
    379    }
    380
    381    /*
    382     * NatTable DataValidator to validate entered input for a dataset with
    383     * a variable-length Datatype (note that this DataValidator should not
    384     * be used for String Datatypes that are variable-length).
    385     */
    386    private static class VlenDataValidator extends HDFDataValidator
    387    {
    388        private static final Logger log = LoggerFactory.getLogger(VlenDataValidator.class);
    389
    390        private final HDFDataValidator baseValidator;
    391
    392        VlenDataValidator(Datatype dtype) throws Exception {
    393            super(dtype);
    394
    395            if (!dtype.isVLEN() || dtype.isVarStr()) {
    396                log.debug("datatype is not a variable-length type or is a variable-length string type (use StringDataValidator)");
    397                throw new Exception("VlenDataValidator: datatype is not a variable-length type or is a variable-length string type (use StringDataValidator)");
    398            }
    399
    400            Datatype baseType = dtype.getDatatypeBase();
    401            if (baseType == null) {
    402                log.debug("base datatype is null");
    403                throw new Exception("VlenDataValidator: base datatype is null");
    404            }
    405
    406            log.trace("VlenDataValidator: base Datatype is {}", baseType.getDescription());
    407
    408            try {
    409                baseValidator = getDataValidator(baseType);
    410            }
    411            catch (Exception ex) {
    412                log.debug("couldn't get DataValidator for base datatype: ", ex);
    413                throw new Exception("VlenDataValidator: couldn't get DataValidator for base datatype: " + ex.getMessage());
    414            }
    415        }
    416
    417        @Override
    418        public boolean validate(ILayerCell cell, IConfigRegistry configRegistry, Object newValue) {
    419            cellColIdx = cell.getColumnIndex();
    420            return validate(cell.getColumnIndex(), cell.getRowIndex(), newValue);
    421        }
    422
    423        @Override
    424        public boolean validate(int colIndex, int rowIndex, Object newValue) {
    425            log.trace("validate({}, {}, {}): start", rowIndex, colIndex, newValue);
    426
    427            try {
    428                super.checkValidValue(newValue);
    429
    430                baseValidator.cellColIdx = cellColIdx;
    431
    432                StringTokenizer elementReader = new StringTokenizer((String) newValue, " \t\n\r\f,[]");
    433                while (elementReader.hasMoreTokens()) {
    434                    String nextToken = elementReader.nextToken();
    435                    baseValidator.validate(colIndex, rowIndex, nextToken);
    436                }
    437            }
    438            catch (Exception ex) {
    439                log.debug("validate({}, {}, {}): failed to validate: ", rowIndex, colIndex, newValue, ex);
    440                throw new ValidationFailedException(ex.getMessage(), ex);
    441            }
    442            finally {
    443                log.trace("validate({}, {}, {}): finish", rowIndex, colIndex, newValue);
    444            }
    445
    446            return true;
    447        }
    448    }
    449
    450    /*
    451     * NatTable DataValidator to validate entered input for a dataset with a String
    452     * Datatype (including Strings of variable-length).
    453     */
    454    private static class StringDataValidator extends HDFDataValidator
    455    {
    456        private static final Logger log = LoggerFactory.getLogger(StringDataValidator.class);
    457
    458        private final Datatype datasetDatatype;
    459        private final boolean isH5String;
    460
    461        StringDataValidator(final Datatype dtype) throws Exception {
    462            super(dtype);
    463
    464            if (!dtype.isString()) {
    465                log.debug("datatype is not a String type");
    466                throw new Exception("StringDataValidator: datatype is not a String type");
    467            }
    468
    469            log.trace("StringDataValidator: base Datatype is {}", dtype.getDescription());
    470
    471            this.datasetDatatype = dtype;
    472
    473            this.isH5String = (dtype instanceof H5Datatype);
    474        }
    475
    476        @Override
    477        public boolean validate(int colIndex, int rowIndex, Object newValue) {
    478            log.trace("validate({}, {}, {}): start", rowIndex, colIndex, newValue);
    479
    480            try {
    481                super.checkValidValue(newValue);
    482
    483                /*
    484                 * If this is a fixed-length string type, check to make sure that the data
    485                 * length does not exceed the datatype size.
    486                 */
    487                if (!datasetDatatype.isVarStr()) {
    488                    long lenDiff = ((String) newValue).length() - datasetDatatype.getDatatypeSize();
    489
    490                    if (lenDiff > 0)
    491                        throw new Exception("string size larger than datatype size by " + lenDiff
    492                                + ((lenDiff > 1) ? " bytes." : " byte."));
    493
    494                    /*
    495                     * TODO: Add Warning about overwriting NULL-terminator character.
    496                     */
    497                    if (lenDiff == 0 && isH5String) {
    498                        H5Datatype h5Type = (H5Datatype) datasetDatatype;
    499                        int strPad = h5Type.getNativeStrPad();
    500
    501                        if (strPad == HDF5Constants.H5T_STR_NULLTERM) {
    502
    503                        }
    504                        else if (strPad == HDF5Constants.H5T_STR_NULLPAD) {
    505
    506                        }
    507                        else if (strPad == HDF5Constants.H5T_STR_SPACEPAD) {
    508
    509                        }
    510                    }
    511                }
    512            }
    513            catch (Exception ex) {
    514                log.debug("validate({}, {}, {}): failed to validate: ", rowIndex, colIndex, newValue, ex);
    515                super.throwValidationFailedException(rowIndex, colIndex, newValue, ex.getMessage());
    516            }
    517            finally {
    518                log.trace("validate({}, {}, {}): finish", rowIndex, colIndex, newValue);
    519            }
    520
    521            return true;
    522        }
    523    }
    524
    525    private static class CharDataValidator extends HDFDataValidator
    526    {
    527        private static final Logger log = LoggerFactory.getLogger(CharDataValidator.class);
    528
    529        private final Datatype datasetDatatype;
    530
    531        CharDataValidator(final Datatype dtype) throws Exception {
    532            super(dtype);
    533
    534            if (!dtype.isChar()) {
    535                log.debug("datatype is not a Character type");
    536                throw new Exception("CharDataValidator: datatype is not a Character type");
    537            }
    538
    539            this.datasetDatatype = dtype;
    540        }
    541
    542        @Override
    543        public boolean validate(int colIndex, int rowIndex, Object newValue) {
    544            log.trace("validate({}, {}, {}): start", rowIndex, colIndex, newValue);
    545
    546            try {
    547                if (datasetDatatype.isUnsigned()) {
    548                    /*
    549                     * First try to parse as a larger type in order to catch a NumberFormatException
    550                     */
    551                    Short shortValue = Short.parseShort((String) newValue);
    552                    if (shortValue < 0)
    553                        throw new NumberFormatException("Invalid negative value for unsigned datatype");
    554
    555                    if (shortValue > (Byte.MAX_VALUE * 2) + 1)
    556                        throw new NumberFormatException("Value out of range. Value:\"" + newValue + "\"");
    557                }
    558                else {
    559                    Byte.parseByte((String) newValue);
    560                }
    561            }
    562            catch (Exception ex) {
    563                super.throwValidationFailedException(rowIndex, colIndex, newValue, ex.toString());
    564            }
    565            finally {
    566                log.trace("validate({}, {}, {}): finish", rowIndex, colIndex, newValue);
    567            }
    568
    569            return true;
    570        }
    571
    572    }
    573
    574    /*
    575     * NatTable DataValidator to validate entered input for a dataset with
    576     * a numerical Datatype.
    577     */
    578    private static class NumericalDataValidator extends HDFDataValidator
    579    {
    580        private static final Logger log = LoggerFactory.getLogger(NumericalDataValidator.class);
    581
    582        private final Datatype datasetDatatype;
    583
    584        NumericalDataValidator(Datatype dtype) throws Exception {
    585            super(dtype);
    586
    587            if (!dtype.isInteger() && !dtype.isFloat()) {
    588                log.debug("datatype is not an integer or floating-point type");
    589                throw new Exception("NumericalDataValidator: datatype is not an integer or floating-point type");
    590            }
    591
    592            log.trace("NumericalDataValidator: base Datatype is {}", dtype.getDescription());
    593
    594            this.datasetDatatype = dtype;
    595        }
    596
    597        @Override
    598        public boolean validate(int colIndex, int rowIndex, Object newValue) {
    599            log.trace("validate({}, {}, {}): start", rowIndex, colIndex, newValue);
    600
    601            try {
    602                super.checkValidValue(newValue);
    603
    604                switch ((int) datasetDatatype.getDatatypeSize()) {
    605                case 1:
    606                    if (datasetDatatype.isUnsigned()) {
    607                        /*
    608                         * First try to parse as a larger type in order to catch a NumberFormatException
    609                         */
    610                        Short shortValue = Short.parseShort((String) newValue);
    611                        if (shortValue < 0)
    612                            throw new NumberFormatException("Invalid negative value for unsigned datatype");
    613
    614                        if (shortValue > (Byte.MAX_VALUE * 2) + 1)
    615                            throw new NumberFormatException("Value out of range. Value:\"" + newValue + "\"");
    616                    }
    617                    else {
    618                        Byte.parseByte((String) newValue);
    619                    }
    620                    break;
    621
    622                case 2:
    623                    if (datasetDatatype.isUnsigned()) {
    624                        /*
    625                         * First try to parse as a larger type in order to catch a NumberFormatException
    626                         */
    627                        Integer intValue = Integer.parseInt((String) newValue);
    628                        if (intValue < 0)
    629                            throw new NumberFormatException("Invalid negative value for unsigned datatype");
    630
    631                        if (intValue > (Short.MAX_VALUE * 2) + 1)
    632                            throw new NumberFormatException("Value out of range. Value:\"" + newValue + "\"");
    633                    }
    634                    else {
    635                        Short.parseShort((String) newValue);
    636                    }
    637                    break;
    638
    639                case 4:
    640                    if (datasetDatatype.isInteger()) {
    641                        if (datasetDatatype.isUnsigned()) {
    642                            /*
    643                             * First try to parse as a larger type in order to catch a NumberFormatException
    644                             */
    645                            Long longValue = Long.parseLong((String) newValue);
    646                            if (longValue < 0)
    647                                throw new NumberFormatException("Invalid negative value for unsigned datatype");
    648
    649                            if (longValue > ((long) Integer.MAX_VALUE * 2) + 1)
    650                                throw new NumberFormatException("Value out of range. Value:\"" + newValue + "\"");
    651                        }
    652                        else {
    653                            Integer.parseInt((String) newValue);
    654                        }
    655                    }
    656                    else {
    657                        /* Floating-point type */
    658                        Float.parseFloat((String) newValue);
    659                    }
    660                    break;
    661
    662                case 8:
    663                    if (datasetDatatype.isInteger()) {
    664                        if (datasetDatatype.isUnsigned()) {
    665                            /*
    666                             * First try to parse as a larger type in order to catch a NumberFormatException
    667                             */
    668                            BigInteger bigValue = new BigInteger((String) newValue);
    669                            if (bigValue.compareTo(BigInteger.ZERO) < 0)
    670                                throw new NumberFormatException("Invalid negative value for unsigned datatype");
    671
    672                            BigInteger maxRange = BigInteger.valueOf(Long.MAX_VALUE).multiply(BigInteger.valueOf(2)).add(BigInteger.valueOf(1));
    673                            if (bigValue.compareTo(maxRange) > 0)
    674                                throw new NumberFormatException("Value out of range. Value:\"" + newValue + "\"");
    675                        }
    676                        else {
    677                            Long.parseLong((String) newValue);
    678                        }
    679                    }
    680                    else {
    681                        /* Floating-point type */
    682                        Double.parseDouble((String) newValue);
    683                    }
    684                    break;
    685
    686
    687                case 16:
    688                    if (datasetDatatype.isFloat()) {
    689                        BigDecimal bigValue = new BigDecimal((String) newValue);
    690
    691                        /*
    692                         * BigDecimal maxRange =
    693                         * BigDecimal.valueOf(Long.MAX_VALUE).multiply(BigDecimal.valueOf(2)).add(BigDecimal.valueOf
    694                         * (1)); if (bigValue.compareTo(maxRange) > 0) throw new
    695                         * NumberFormatException("Value out of range. Value:\"" + newValue + "\"");
    696                         */                        }
    697                    break;
    698
    699                default:
    700                    throw new ValidationFailedException("No validation logic for numerical data of size " + datasetDatatype.getDatatypeSize());
    701                }
    702            }
    703            catch (Exception ex) {
    704                super.throwValidationFailedException(rowIndex, colIndex, newValue, ex.toString());
    705            }
    706            finally {
    707                log.trace("validate({}, {}, {}): finish", rowIndex, colIndex, newValue);
    708            }
    709
    710            return true;
    711        }
    712    }
    713
    714    private static class EnumDataValidator extends HDFDataValidator
    715    {
    716        private static final Logger log = LoggerFactory.getLogger(EnumDataValidator.class);
    717
    718        private final HDFDataValidator baseValidator;
    719
    720        EnumDataValidator(final Datatype dtype) throws Exception {
    721            super(dtype);
    722
    723            if (!dtype.isEnum()) {
    724                log.debug("datatype is not an enum type: exit");
    725                throw new Exception("EnumDataValidator: datatype is not an enum type");
    726            }
    727
    728            Datatype baseType = dtype.getDatatypeBase();
    729            if (baseType == null) {
    730                log.debug("base datatype is null: exit");
    731                throw new Exception("EnumDataValidator: base datatype is null");
    732            }
    733            if (!baseType.isInteger()) {
    734                log.debug("base datatype is not an integer type: exit");
    735                throw new Exception("EnumDataValidator: datatype is not an integer type");
    736            }
    737
    738            log.trace("base Datatype is {}", dtype.getDescription());
    739
    740            try {
    741                baseValidator = getDataValidator(baseType);
    742            }
    743            catch (Exception ex) {
    744                log.debug("couldn't get DataValidator for base datatype: exit: ", ex);
    745                throw new Exception("couldn't get DataValidator for base datatype: " + ex.getMessage());
    746            }
    747        }
    748
    749        @Override
    750        public boolean validate(int colIndex, int rowIndex, Object newValue) {
    751            log.trace("validate({}, {}, {}): start", rowIndex, colIndex, newValue);
    752
    753            try {
    754                super.checkValidValue(newValue);
    755
    756                baseValidator.validate(colIndex, rowIndex, newValue);
    757            }
    758            catch (Exception ex) {
    759                super.throwValidationFailedException(rowIndex, colIndex, newValue, ex.toString());
    760            }
    761            finally {
    762                log.trace("validate({}, {}, {}): finish", rowIndex, colIndex, newValue);
    763            }
    764
    765            return true;
    766        }
    767    }
    768
    769    private static class BitfieldDataValidator extends HDFDataValidator
    770    {
    771        private static final Logger log = LoggerFactory.getLogger(BitfieldDataValidator.class);
    772
    773        BitfieldDataValidator(final Datatype dtype) {
    774            super(dtype);
    775        }
    776    }
    777
    778    private static class RefDataValidator extends HDFDataValidator
    779    {
    780        private static final Logger log = LoggerFactory.getLogger(RefDataValidator.class);
    781
    782        RefDataValidator(final Datatype dtype) {
    783            super(dtype);
    784        }
    785    }
    786
    787}