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}