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