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