All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micronaut.validation.validator.constraints.DefaultConstraintValidators Maven / Gradle / Ivy

There is a newer version: 3.10.4
Show newest version
/*
 * Copyright 2017-2020 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.micronaut.validation.validator.constraints;

import io.micronaut.context.BeanContext;
import io.micronaut.context.Qualifier;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.beans.BeanProperty;
import io.micronaut.core.beans.BeanWrapper;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.core.type.Argument;
import io.micronaut.core.util.ArgumentUtils;
import io.micronaut.core.util.ArrayUtils;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.core.util.clhm.ConcurrentLinkedHashMap;
import io.micronaut.inject.qualifiers.Qualifiers;
import io.micronaut.inject.qualifiers.TypeArgumentQualifier;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.validation.ValidationException;
import javax.validation.constraints.*;
import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.*;
import java.time.chrono.HijrahDate;
import java.time.chrono.JapaneseDate;
import java.time.chrono.MinguoDate;
import java.time.chrono.ThaiBuddhistDate;
import java.time.temporal.TemporalAccessor;
import java.util.*;
import java.util.concurrent.atomic.DoubleAccumulator;
import java.util.concurrent.atomic.DoubleAdder;

/**
 * A factory bean that contains implementation for many of the default validations.
 * This approach is preferred as it generates less classes and smaller byte code than defining a
 * validator class for each case.
 *
 * @author graemerocher
 * @since 1.2
 */
@Singleton
@Introspected
public class DefaultConstraintValidators implements ConstraintValidatorRegistry {

    private final Map validatorCache = new ConcurrentLinkedHashMap.Builder().initialCapacity(10).maximumWeightedCapacity(40).build();

    private final ConstraintValidator assertFalseValidator =
            (value, annotationMetadata, context) -> value == null || !value;

    private final ConstraintValidator assertTrueValidator =
            (value, annotationMetadata, context) -> value == null || value;

    private final DecimalMaxValidator decimalMaxValidatorCharSequence =
            (value, bigDecimal) -> new BigDecimal(value.toString()).compareTo(bigDecimal);

    private final DecimalMaxValidator decimalMaxValidatorNumber = DefaultConstraintValidators::compareNumber;

    private final DecimalMinValidator decimalMinValidatorCharSequence =
            (value, bigDecimal) -> new BigDecimal(value.toString()).compareTo(bigDecimal);

    private final DecimalMinValidator decimalMinValidatorNumber = DefaultConstraintValidators::compareNumber;

    private final DigitsValidator digitsValidatorNumber = value -> {
            if (value instanceof BigDecimal) {
                return (BigDecimal) value;
            }
            return new BigDecimal(value.toString());
        };

    private final DigitsValidator digitsValidatorCharSequence =
            value -> new BigDecimal(value.toString());

    private final ConstraintValidator maxNumberValidator =
            (value, annotationMetadata, context) -> {
            if (value == null) {
                return true; // nulls are allowed according to spec
            }
            final Long max = annotationMetadata.getValue(Long.class).orElseThrow(() ->
                    new ValidationException("@Max annotation specified without value")
            );

            if (value instanceof BigInteger) {
                return ((BigInteger) value).compareTo(BigInteger.valueOf(max)) < 0;
            } else if (value instanceof BigDecimal) {
                return ((BigDecimal) value).compareTo(BigDecimal.valueOf(max)) < 0;
            }
            return value.longValue() <= max;
        };

    private final ConstraintValidator minNumberValidator =
            (value, annotationMetadata, context) -> {
            if (value == null) {
                return true; // nulls are allowed according to spec
            }
            final Long max = annotationMetadata.getValue(Long.class).orElseThrow(() ->
                    new ValidationException("@Min annotation specified without value")
            );

            if (value instanceof BigInteger) {
                return ((BigInteger) value).compareTo(BigInteger.valueOf(max)) >= 0;
            } else if (value instanceof BigDecimal) {
                return ((BigDecimal) value).compareTo(BigDecimal.valueOf(max)) >= 0;
            }
            return value.longValue() >= max;
        };

    private final ConstraintValidator negativeNumberValidator =
            (value, annotationMetadata, context) -> {
            // null is allowed according to spec
            if (value == null) {
                return true;
            }
            if (value instanceof  BigDecimal) {
                return ((BigDecimal) value).signum() < 0;
            }
            if (value instanceof BigInteger) {
                return ((BigInteger) value).signum() < 0;
            }
            if (value instanceof Double ||
                value instanceof Float  ||
                value instanceof DoubleAdder ||
                value instanceof DoubleAccumulator) {
                return value.doubleValue() < 0;
            }
            return value.longValue() < 0;
        };

    private final ConstraintValidator negativeOrZeroNumberValidator =
            (value, annotationMetadata, context) -> {
            // null is allowed according to spec
            if (value == null) {
                return true;
            }
            if (value instanceof  BigDecimal) {
                return ((BigDecimal) value).signum() <= 0;
            }
            if (value instanceof BigInteger) {
                return ((BigInteger) value).signum() <= 0;
            }
            if (value instanceof Double ||
                value instanceof Float  ||
                value instanceof DoubleAdder ||
                value instanceof DoubleAccumulator) {
                return value.doubleValue() <= 0;
            }
            return value.longValue() <= 0;
        };

    private final ConstraintValidator positiveNumberValidator =
            (value, annotationMetadata, context) -> {
            // null is allowed according to spec
            if (value == null) {
                return true;
            }
            if (value instanceof  BigDecimal) {
                return ((BigDecimal) value).signum() > 0;
            }
            if (value instanceof BigInteger) {
                return ((BigInteger) value).signum() > 0;
            }
            if (value instanceof Double ||
                value instanceof Float  ||
                value instanceof DoubleAdder ||
                value instanceof DoubleAccumulator) {
                return value.doubleValue() > 0;
            }
            return value.longValue() > 0;
        };

    private final ConstraintValidator positiveOrZeroNumberValidator =
            (value, annotationMetadata, context) -> {
            // null is allowed according to spec
            if (value == null) {
                return true;
            }
            if (value instanceof  BigDecimal) {
                return ((BigDecimal) value).signum() >= 0;
            }
            if (value instanceof BigInteger) {
                return ((BigInteger) value).signum() >= 0;
            }
            if (value instanceof Double ||
                value instanceof Float  ||
                value instanceof DoubleAdder ||
                value instanceof DoubleAccumulator) {
                return value.doubleValue() >= 0;
            }
            return value.longValue() >= 0;
        };

    private final ConstraintValidator notBlankValidator =
            (value, annotationMetadata, context) ->
                StringUtils.isNotEmpty(value) && value.toString().trim().length() > 0;

    private final ConstraintValidator notNullValidator =
            (value, annotationMetadata, context) -> value != null;

    private final ConstraintValidator nullValidator =
            (value, annotationMetadata, context) -> value == null;

    private final ConstraintValidator notEmptyByteArrayValidator =
            (value, annotationMetadata, context) -> value != null && value.length > 0;

    private final ConstraintValidator notEmptyCharArrayValidator =
            (value, annotationMetadata, context) -> value != null && value.length > 0;

    private final ConstraintValidator notEmptyBooleanArrayValidator =
            (value, annotationMetadata, context) -> value != null && value.length > 0;

    private final ConstraintValidator notEmptyDoubleArrayValidator =
            (value, annotationMetadata, context) -> value != null && value.length > 0;

    private final ConstraintValidator notEmptyFloatArrayValidator =
            (value, annotationMetadata, context) -> value != null && value.length > 0;

    private final ConstraintValidator notEmptyIntArrayValidator =
            (value, annotationMetadata, context) -> value != null && value.length > 0;

    private final ConstraintValidator notEmptyLongArrayValidator =
            (value, annotationMetadata, context) -> value != null && value.length > 0;

    private final ConstraintValidator notEmptyObjectArrayValidator = (value, annotationMetadata, context) -> value != null && value.length > 0;

    private final ConstraintValidator notEmptyShortArrayValidator =
            (value, annotationMetadata, context) -> value != null && value.length > 0;

    private final ConstraintValidator notEmptyCharSequenceValidator =
            (value, annotationMetadata, context) -> StringUtils.isNotEmpty(value);

    private final ConstraintValidator notEmptyCollectionValidator =
            (value, annotationMetadata, context) -> CollectionUtils.isNotEmpty(value);

    private final ConstraintValidator notEmptyMapValidator =
            (value, annotationMetadata, context) -> CollectionUtils.isNotEmpty(value);

    private final SizeValidator sizeObjectArrayValidator = value -> value.length;

    private final SizeValidator sizeByteArrayValidator = value -> value.length;

    private final SizeValidator sizeCharArrayValidator = value -> value.length;

    private final SizeValidator sizeBooleanArrayValidator = value -> value.length;

    private final SizeValidator sizeDoubleArrayValidator = value -> value.length;

    private final SizeValidator sizeFloatArrayValidator = value -> value.length;

    private final SizeValidator sizeIntArrayValidator = value -> value.length;

    private final SizeValidator sizeLongArrayValidator = value -> value.length;

    private final SizeValidator sizeShortArrayValidator = value -> value.length;

    private final SizeValidator sizeCharSequenceValidator = CharSequence::length;

    private final SizeValidator sizeCollectionValidator = Collection::size;

    private final SizeValidator sizeMapValidator = Map::size;

    private final ConstraintValidator pastTemporalAccessorConstraintValidator =
            (value, annotationMetadata, context) -> {
            if (value == null) {
                // null is valid according to spec
                return true;
            }
            Comparable comparable = getNow(value, context.getClockProvider().getClock());
            return comparable.compareTo(value) > 0;
        };

    private final ConstraintValidator pastOrPresentTemporalAccessorConstraintValidator =
            (value, annotationMetadata, context) -> {
            if (value == null) {
                // null is valid according to spec
                return true;
            }
            Comparable comparable = getNow(value, context.getClockProvider().getClock());
            return comparable.compareTo(value) >= 0;
        };

    private final ConstraintValidator futureTemporalAccessorConstraintValidator = (value, annotationMetadata, context) -> {
            if (value == null) {
                // null is valid according to spec
                return true;
            }
            Comparable comparable = getNow(value, context.getClockProvider().getClock());
            return comparable.compareTo(value) < 0;
        };

    private final ConstraintValidator futureOrPresentTemporalAccessorConstraintValidator = (value, annotationMetadata, context) -> {
            if (value == null) {
                // null is valid according to spec
                return true;
            }
            Comparable comparable = getNow(value, context.getClockProvider().getClock());
            return comparable.compareTo(value) <= 0;
        };

    private final @Nullable BeanContext beanContext;
    private final Map localValidators;

    /**
     * Default constructor.
     */
    public DefaultConstraintValidators() {
        this(null);
    }

    /**
     * Constructor used for DI.
     *
     * @param beanContext The bean context
     */
    @Inject
    protected DefaultConstraintValidators(@Nullable BeanContext beanContext) {
        this.beanContext = beanContext;
        BeanWrapper wrapper = BeanWrapper.findWrapper(DefaultConstraintValidators.class, this).orElse(null);
        if (wrapper != null) {

            final Collection> beanProperties = wrapper.getBeanProperties();
            Map validatorMap = new HashMap<>(beanProperties.size());
            for (BeanProperty property : beanProperties) {
                if (ConstraintValidator.class.isAssignableFrom(property.getType())) {
                    final Argument[] typeParameters = property.asArgument().getTypeParameters();
                    if (ArrayUtils.isNotEmpty(typeParameters)) {
                        final int len = typeParameters.length;

                        wrapper.getProperty(property.getName(), ConstraintValidator.class).ifPresent(constraintValidator -> {
                            if (len == 2) {
                                final Class targetType = ReflectionUtils.getWrapperType(typeParameters[1].getType());
                                final ValidatorKey key = new ValidatorKey(typeParameters[0].getType(), targetType);
                                validatorMap.put(key, constraintValidator);
                            } else if (len == 1) {
                                if (constraintValidator instanceof SizeValidator) {
                                    final ValidatorKey key = new ValidatorKey(Size.class, typeParameters[0].getType());
                                    validatorMap.put(key, constraintValidator);
                                } else if (constraintValidator instanceof DigitsValidator) {
                                    final ValidatorKey key = new ValidatorKey(Digits.class, typeParameters[0].getType());
                                    validatorMap.put(key, constraintValidator);
                                } else if (constraintValidator instanceof DecimalMaxValidator) {
                                    final ValidatorKey key = new ValidatorKey(DecimalMax.class, typeParameters[0].getType());
                                    validatorMap.put(key, constraintValidator);
                                } else if (constraintValidator instanceof DecimalMinValidator) {
                                    final ValidatorKey key = new ValidatorKey(DecimalMin.class, typeParameters[0].getType());
                                    validatorMap.put(key, constraintValidator);
                                }
                            }
                        });
                    }
                }
            }
            validatorMap.put(
                    new ValidatorKey(Pattern.class, CharSequence.class),
                    new PatternValidator()
            );
            validatorMap.put(
                    new ValidatorKey(Email.class, CharSequence.class),
                    new EmailValidator()
            );
            this.localValidators = validatorMap;
        } else {
            this.localValidators = Collections.emptyMap();
        }
    }

    @SuppressWarnings("unchecked")
    @NonNull
    @Override
    public  Optional> findConstraintValidator(@NonNull Class constraintType, @NonNull Class targetType) {
        ArgumentUtils.requireNonNull("constraintType", constraintType);
        ArgumentUtils.requireNonNull("targetType", targetType);
        final ValidatorKey key = new ValidatorKey(constraintType, targetType);
        targetType = ReflectionUtils.getWrapperType(targetType);
        ConstraintValidator constraintValidator = localValidators.get(key);
        if (constraintValidator != null) {
            return Optional.of(constraintValidator);
        } else {
            constraintValidator = validatorCache.get(key);
            if (constraintValidator != null) {
                return Optional.of(constraintValidator);
            } else {
                final Qualifier qualifier = Qualifiers.byTypeArguments(
                        constraintType,
                        ReflectionUtils.getWrapperType(targetType)
                );
                Class finalTargetType = targetType;
                final Optional local = localValidators.entrySet().stream().filter(entry -> {
                            final ValidatorKey k = entry.getKey();
                            return TypeArgumentQualifier.areTypesCompatible(
                                    new Class[]{constraintType, finalTargetType},
                                    Arrays.asList(k.constraintType, k.targetType)
                            );
                        }
                ).map(Map.Entry::getValue).findFirst();

                if (local.isPresent()) {
                    validatorCache.put(key, local.get());
                    return (Optional) local;
                } else if (beanContext != null) {
                    final ConstraintValidator cv = beanContext
                            .findBean(ConstraintValidator.class, qualifier).orElse(ConstraintValidator.VALID);
                    validatorCache.put(key, cv);
                    if (cv != ConstraintValidator.VALID) {
                        return Optional.of(cv);
                    }
                } else {
                    // last chance lookup
                    final ConstraintValidator cv = findLocalConstraintValidator(constraintType, targetType)
                                                        .orElse(ConstraintValidator.VALID);
                    validatorCache.put(key, cv);
                    if (cv != ConstraintValidator.VALID) {
                        return Optional.of(cv);
                    }
                }
            }
        }
        return Optional.empty();
    }

    /**
     * The {@link AssertFalse} validator.
     *
     * @return The validator
     */
    public ConstraintValidator getAssertFalseValidator() {
        return assertFalseValidator;
    }

    /**
     * The {@link AssertTrue} validator.
     *
     * @return The validator
     */
    public ConstraintValidator getAssertTrueValidator() {
        return assertTrueValidator;
    }

    /**
     * The {@link DecimalMax} validator for char sequences.
     *
     * @return The validator
     */
    public DecimalMaxValidator getDecimalMaxValidatorCharSequence() {
        return decimalMaxValidatorCharSequence;
    }

    /**
     * The {@link DecimalMax} validator for number.
     *
     * @return The validator
     */
    public DecimalMaxValidator getDecimalMaxValidatorNumber() {
        return decimalMaxValidatorNumber;
    }

    /**
     * The {@link DecimalMin} validator for char sequences.
     *
     * @return The validator
     */
    public DecimalMinValidator getDecimalMinValidatorCharSequence() {
        return decimalMinValidatorCharSequence;
    }

    /**
     * The {@link DecimalMin} validator for number.
     *
     * @return The validator
     */
    public DecimalMinValidator getDecimalMinValidatorNumber() {
        return decimalMinValidatorNumber;
    }

    /**
     * The {@link Digits} validator for number.
     *
     * @return The validator
     */
    public DigitsValidator getDigitsValidatorNumber() {
        return digitsValidatorNumber;
    }

    /**
     * The {@link Digits} validator for char sequence.
     *
     * @return The validator
     */
    public DigitsValidator getDigitsValidatorCharSequence() {
        return digitsValidatorCharSequence;
    }

    /**
     * The {@link Max} validator for numbers.
     *
     * @return The validator
     */
    public ConstraintValidator getMaxNumberValidator() {
        return maxNumberValidator;
    }

    /**
     * The {@link Min} validator for numbers.
     *
     * @return The validator
     */
    public ConstraintValidator getMinNumberValidator() {
        return minNumberValidator;
    }

    /**
     * The {@link Negative} validator for numbers.
     *
     * @return The validator
     */
    public ConstraintValidator getNegativeNumberValidator() {
        return negativeNumberValidator;
    }

    /**
     * The {@link NegativeOrZero} validator for numbers.
     *
     * @return The validator
     */
    public ConstraintValidator getNegativeOrZeroNumberValidator() {
        return negativeOrZeroNumberValidator;
    }

    /**
     * The {@link Positive} validator for numbers.
     *
     * @return The validator
     */
    public ConstraintValidator getPositiveNumberValidator() {
        return positiveNumberValidator;
    }

    /**
     * The {@link PositiveOrZero} validator for numbers.
     *
     * @return The validator
     */
    public ConstraintValidator getPositiveOrZeroNumberValidator() {
        return positiveOrZeroNumberValidator;
    }

    /**
     * The {@link NotBlank} validator for char sequences.
     *
     * @return The validator
     */
    public ConstraintValidator getNotBlankValidator() {
        return notBlankValidator;
    }

    /**
     * The {@link NotNull} validator.
     *
     * @return The validator
     */
    public ConstraintValidator getNotNullValidator() {
        return notNullValidator;
    }

    /**
     * The {@link Null} validator.
     *
     * @return The validator
     */
    public ConstraintValidator getNullValidator() {
        return nullValidator;
    }

    /**
     * The {@link NotEmpty} validator for byte[].
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyByteArrayValidator() {
        return notEmptyByteArrayValidator;
    }

    /**
     * The {@link NotEmpty} validator for char[].
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyCharArrayValidator() {
        return notEmptyCharArrayValidator;
    }

    /**
     * The {@link NotEmpty} validator for boolean[].
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyBooleanArrayValidator() {
        return notEmptyBooleanArrayValidator;
    }

    /**
     * The {@link NotEmpty} validator for double[].
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyDoubleArrayValidator() {
        return notEmptyDoubleArrayValidator;
    }

    /**
     * The {@link NotEmpty} validator for float[].
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyFloatArrayValidator() {
        return notEmptyFloatArrayValidator;
    }

    /**
     * The {@link NotEmpty} validator for int[].
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyIntArrayValidator() {
        return notEmptyIntArrayValidator;
    }

    /**
     * The {@link NotEmpty} validator for long[].
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyLongArrayValidator() {
        return notEmptyLongArrayValidator;
    }

    /**
     * The {@link NotEmpty} validator for Object[].
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyObjectArrayValidator() {
        return notEmptyObjectArrayValidator;
    }

    /**
     * The {@link NotEmpty} validator for short[].
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyShortArrayValidator() {
        return notEmptyShortArrayValidator;
    }

    /**
     * The {@link NotEmpty} validator for char sequence.
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyCharSequenceValidator() {
        return notEmptyCharSequenceValidator;
    }

    /**
     * The {@link NotEmpty} validator for collection.
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyCollectionValidator() {
        return notEmptyCollectionValidator;
    }

    /**
     * The {@link NotEmpty} validator for map.
     *
     * @return The validator
     */
    public ConstraintValidator getNotEmptyMapValidator() {
        return notEmptyMapValidator;
    }

    /**
     * The {@link Size} validator for Object[].
     *
     * @return The validator
     */
    public SizeValidator getSizeObjectArrayValidator() {
        return sizeObjectArrayValidator;
    }

    /**
     * The {@link Size} validator for byte[].
     *
     * @return The validator
     */
    public SizeValidator getSizeByteArrayValidator() {
        return sizeByteArrayValidator;
    }

    /**
     * The {@link Size} validator for char[].
     *
     * @return The validator
     */
    public SizeValidator getSizeCharArrayValidator() {
        return sizeCharArrayValidator;
    }

    /**
     * The {@link Size} validator for boolean[].
     *
     * @return The validator
     */
    public SizeValidator getSizeBooleanArrayValidator() {
        return sizeBooleanArrayValidator;
    }

    /**
     * The {@link Size} validator for double[].
     *
     * @return The validator
     */
    public SizeValidator getSizeDoubleArrayValidator() {
        return sizeDoubleArrayValidator;
    }

    /**
     * The {@link Size} validator for float[].
     *
     * @return The validator
     */
    public SizeValidator getSizeFloatArrayValidator() {
        return sizeFloatArrayValidator;
    }

    /**
     * The {@link Size} validator for int[].
     *
     * @return The validator
     */
    public SizeValidator getSizeIntArrayValidator() {
        return sizeIntArrayValidator;
    }

    /**
     * The {@link Size} validator for long[].
     *
     * @return The validator
     */
    public SizeValidator getSizeLongArrayValidator() {
        return sizeLongArrayValidator;
    }

    /**
     * The {@link Size} validator for short[].
     *
     * @return The validator
     */
    public SizeValidator getSizeShortArrayValidator() {
        return sizeShortArrayValidator;
    }

    /**
     * The {@link Size} validator for CharSequence.
     *
     * @return The validator
     */
    public SizeValidator getSizeCharSequenceValidator() {
        return sizeCharSequenceValidator;
    }

    /**
     * The {@link Size} validator for Collection.
     *
     * @return The validator
     */
    public SizeValidator getSizeCollectionValidator() {
        return sizeCollectionValidator;
    }

    /**
     * The {@link Size} validator for Map.
     *
     * @return The validator
     */
    public SizeValidator getSizeMapValidator() {
        return sizeMapValidator;
    }

    /**
     * The {@link Past} validator for temporal accessor.
     *
     * @return The validator
     */
    public ConstraintValidator getPastTemporalAccessorConstraintValidator() {
        return pastTemporalAccessorConstraintValidator;
    }

    /**
     * The {@link PastOrPresent} validator for temporal accessor.
     *
     * @return The validator
     */
    public ConstraintValidator getPastOrPresentTemporalAccessorConstraintValidator() {
        return pastOrPresentTemporalAccessorConstraintValidator;
    }

    /**
     * The {@link Future} validator for temporal accessor.
     *
     * @return The validator
     */
    public ConstraintValidator getFutureTemporalAccessorConstraintValidator() {
        return futureTemporalAccessorConstraintValidator;
    }

    /**
     * The {@link FutureOrPresent} validator for temporal accessor.
     *
     * @return The validator
     */
    public ConstraintValidator getFutureOrPresentTemporalAccessorConstraintValidator() {
        return futureOrPresentTemporalAccessorConstraintValidator;
    }

    /**
     * Last chance resolve for constraint validator.
     * @param constraintType The constraint type
     * @param targetType The target type
     * @param  The annotation type
     * @param  The target type
     * @return The validator if present
     */
    protected  Optional findLocalConstraintValidator(
            @NonNull Class constraintType, @NonNull Class targetType) {
        return Optional.empty();
    }

    private Comparable getNow(TemporalAccessor value, Clock clock) {
        if (!(value instanceof Comparable)) {
            throw new IllegalArgumentException("TemporalAccessor value must be comparable");
        }

        if (value instanceof LocalDateTime) {
            return LocalDateTime.now(clock);
        } else if (value instanceof Instant) {
            return Instant.now(clock);
        } else if (value instanceof ZonedDateTime) {
            return ZonedDateTime.now(clock);
        } else if (value instanceof OffsetDateTime) {
            return OffsetDateTime.now(clock);
        } else if (value instanceof LocalDate) {
            return LocalDate.now(clock);
        } else if (value instanceof LocalTime) {
            return LocalTime.now(clock);
        } else if (value instanceof OffsetTime) {
            return OffsetTime.now(clock);
        } else if (value instanceof MonthDay) {
            return MonthDay.now(clock);
        } else if (value instanceof Year) {
            return Year.now(clock);
        } else if (value instanceof YearMonth) {
            return YearMonth.now(clock);
        } else if (value instanceof HijrahDate) {
            return HijrahDate.now(clock);
        } else if (value instanceof JapaneseDate) {
            return JapaneseDate.now(clock);
        } else if (value instanceof ThaiBuddhistDate) {
            return ThaiBuddhistDate.now(clock);
        } else if (value instanceof MinguoDate) {
            return MinguoDate.now(clock);
        }
        throw new IllegalArgumentException("TemporalAccessor value type not supported: " + value.getClass());
    }

    /**
     * Performs the comparision for number.
     * @param value The value
     * @param bigDecimal The big decimal
     * @return The result
     */
    private static int compareNumber(@NonNull Number value, @NonNull BigDecimal bigDecimal) {
        int result;
        if (value instanceof BigDecimal) {
            result = ((BigDecimal) value).compareTo(bigDecimal);
        } else if (value instanceof BigInteger) {
            result = new BigDecimal((BigInteger) value).compareTo(bigDecimal);
        } else {
            result = BigDecimal.valueOf(value.doubleValue()).compareTo(bigDecimal);
        }
        return result;
    }

    /**
     * Key for caching validators.
     * @param  The annotation type
     * @param  The target type.
     */
    protected final class ValidatorKey {
        private final Class constraintType;
        private final Class targetType;

        /**
         * The key to lookup the validator.
         * @param constraintType THe constraint type
         * @param targetType The target type
         */
        public ValidatorKey(@NonNull Class constraintType, @NonNull Class targetType) {
            this.constraintType = constraintType;
            this.targetType = targetType;
        }

        /**
         * @return The constraint type
         */
        public Class getConstraintType() {
            return constraintType;
        }

        /**
         * @return The target type
         */
        public Class getTargetType() {
            return targetType;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            ValidatorKey key = (ValidatorKey) o;
            return constraintType.equals(key.constraintType) &&
                    targetType.equals(key.targetType);
        }

        @Override
        public int hashCode() {
            return Objects.hash(constraintType, targetType);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy