diff --git a/src/main/java/am/ik/yavi/builder/ValidatorBuilder.java b/src/main/java/am/ik/yavi/builder/ValidatorBuilder.java index b3d17449..e7dcbf0d 100644 --- a/src/main/java/am/ik/yavi/builder/ValidatorBuilder.java +++ b/src/main/java/am/ik/yavi/builder/ValidatorBuilder.java @@ -15,29 +15,6 @@ */ package am.ik.yavi.builder; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.OffsetDateTime; -import java.time.Year; -import java.time.YearMonth; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Deque; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - import am.ik.yavi.constraint.BigDecimalConstraint; import am.ik.yavi.constraint.BigIntegerConstraint; import am.ik.yavi.constraint.BooleanConstraint; @@ -77,6 +54,7 @@ import am.ik.yavi.core.ConstraintPredicate; import am.ik.yavi.core.ConstraintPredicates; import am.ik.yavi.core.CustomConstraint; +import am.ik.yavi.core.InheritanceValidator; import am.ik.yavi.core.NestedCollectionValidator; import am.ik.yavi.core.NestedConstraintCondition; import am.ik.yavi.core.NestedConstraintPredicates; @@ -90,26 +68,16 @@ import am.ik.yavi.fn.Pair; import am.ik.yavi.message.MessageFormatter; import am.ik.yavi.message.SimpleMessageFormatter; -import am.ik.yavi.meta.BigDecimalConstraintMeta; -import am.ik.yavi.meta.BigIntegerConstraintMeta; -import am.ik.yavi.meta.BooleanConstraintMeta; -import am.ik.yavi.meta.ByteConstraintMeta; -import am.ik.yavi.meta.CharacterConstraintMeta; -import am.ik.yavi.meta.DoubleConstraintMeta; -import am.ik.yavi.meta.FloatConstraintMeta; -import am.ik.yavi.meta.InstantConstraintMeta; -import am.ik.yavi.meta.IntegerConstraintMeta; -import am.ik.yavi.meta.LocalDateConstraintMeta; -import am.ik.yavi.meta.LocalDateTimeConstraintMeta; -import am.ik.yavi.meta.LocalTimeConstraintMeta; -import am.ik.yavi.meta.LongConstraintMeta; -import am.ik.yavi.meta.ObjectConstraintMeta; -import am.ik.yavi.meta.OffsetDateTimeConstraintMeta; -import am.ik.yavi.meta.ShortConstraintMeta; -import am.ik.yavi.meta.StringConstraintMeta; -import am.ik.yavi.meta.YearConstraintMeta; -import am.ik.yavi.meta.YearMonthConstraintMeta; -import am.ik.yavi.meta.ZonedDateTimeConstraintMeta; +import am.ik.yavi.meta.*; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.*; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; public class ValidatorBuilder implements Cloneable { private static final String DEFAULT_SEPARATOR = "."; @@ -732,6 +700,34 @@ public ValidatorBuilder _doubleArray(ToDoubleArray f, String name, return this.constraint(f, name, c, DoubleArrayConstraint::new); } + /** + * @since 0.14.0 + */ + public ValidatorBuilder constraintOnClass(Class clazz, + Validator cValidator) { + Validatable validatable = new InheritanceValidator<>(clazz, cValidator); + + this.conditionalValidators + .add(new Pair<>(getClassConstraintCondition(clazz), validatable)); + + return this; + } + + /** + * @since 0.14.0 + */ + public ValidatorBuilder constraintOnClass(Class clazz, + ValidatorBuilderConverter converter) { + Validator cValidator = converter.apply(new ValidatorBuilder<>()).build(); + + return constraintOnClass(clazz, cValidator); + } + + private ConstraintCondition getClassConstraintCondition( + Class classCondition) { + return (t, c) -> classCondition.isInstance(t); + } + /** * @since 0.11.0 */ @@ -998,6 +994,7 @@ protected final ValidatorBuilder nest(Function nested, String name, .forEach(this.appendNestedConditionalValidator(nested, name)); builder.collectionValidators .forEach(appendNestedCollectionValidator(nested, name)); + return this; } diff --git a/src/main/java/am/ik/yavi/core/InheritanceValidator.java b/src/main/java/am/ik/yavi/core/InheritanceValidator.java new file mode 100644 index 00000000..f70b8fa7 --- /dev/null +++ b/src/main/java/am/ik/yavi/core/InheritanceValidator.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018-2023 Toshiaki Maki + * + * 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 + * + * http://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 am.ik.yavi.core; + +import java.util.Locale; + +/** + * @since 0.14.0 + */ +public class InheritanceValidator implements Validatable { + private final Class nClass; + private final Validatable validator; + + public InheritanceValidator(Class nClass, Validatable validator) { + this.nClass = nClass; + this.validator = validator; + } + + @Override + public ConstraintViolations validate(T target, Locale locale, + ConstraintContext constraintContext) { + final N n = nClass.cast(target); + + return this.validator.validate(n, locale, constraintContext); + } + + @Override + public Validatable failFast(boolean failFast) { + final Validatable validatable = this.validator.failFast(failFast); + return new InheritanceValidator<>(nClass, validatable); + } + + @Override + public boolean isFailFast() { + return this.validator.isFailFast(); + } +} diff --git a/src/test/java/am/ik/yavi/core/ConstraintOnClassTest.java b/src/test/java/am/ik/yavi/core/ConstraintOnClassTest.java new file mode 100644 index 00000000..4d42f9c5 --- /dev/null +++ b/src/test/java/am/ik/yavi/core/ConstraintOnClassTest.java @@ -0,0 +1,83 @@ +package am.ik.yavi.core; + +import am.ik.yavi.User; +import am.ik.yavi.builder.ValidatorBuilder; +import am.ik.yavi.constraint.CharSequenceConstraint; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.function.Function; +import java.util.stream.Stream; + +import static com.google.common.truth.Truth.assertThat; + +public class ConstraintOnClassTest { + + @ParameterizedTest + @MethodSource("provideValidators") + void testConstraintOnConditionClass(Validator validator) { + Parent validChild = new Child(23, "drawing"); + Parent invalidChild = new Child(6, "drawing"); + + assertThat(validator.validate(validChild).isValid()).isTrue(); + assertThat(validator.validate(invalidChild).isValid()).isFalse(); + } + + @ParameterizedTest + @MethodSource("provideValidators") + void testConstraintOnNonConditionClass(Validator validator) { + Parent validParent = new Parent(35); + Parent invalidParent = new Parent(19); + + assertThat(validator.validate(validParent).isValid()).isTrue(); + assertThat(validator.validate(invalidParent).isValid()).isFalse(); + } + + static Stream provideValidators() { + ValidatorBuilder userValidatorBuilder = ValidatorBuilder.of(Parent.class) + .constraint(Parent::getAge, "age", c -> c.greaterThan(20)); + Function, CharSequenceConstraint> equalsToDrawing = ( + CharSequenceConstraint c) -> c.equalTo("drawing"); + + return Stream + .of(Arguments.of(new ValidatorBuilder<>(userValidatorBuilder) + .constraintOnClass(Child.class, + ValidatorBuilder.of(Child.class) + .constraint(Child::getHobby, "hobby", + equalsToDrawing) + .build()) + .build()), Arguments + .of(new ValidatorBuilder<>(userValidatorBuilder) + .constraintOnClass(Child.class, + b -> b.constraint(Child::getHobby, + "hobby", equalsToDrawing)) + .build())); + } + + private static class Child extends Parent { + private String hobby; + + public Child(int age, String hobby) { + super(age); + this.hobby = hobby; + } + + public String getHobby() { + return hobby; + } + } + + private static class Parent { + private final int age; + + public Parent(int age) { + this.age = age; + } + + public int getAge() { + return age; + } + } +}