io.micronaut.validation.validator.constraints.DefaultConstraintValidators Maven / Gradle / Ivy
/*
* 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.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
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 jakarta.inject.Inject;
import jakarta.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.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Date;
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