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