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