feat: 为 CollectionPropertyValidator 添加 allMatch 方法

- 新增 allMatch 方法,用于校验集合中的所有元素是否满足指定条件。支持自定义异常信息和异常类型。
- 添加相关单元测试用例。
This commit is contained in:
zhouxy108 2025-06-01 18:10:36 +08:00
parent 3f23d5383d
commit b29e1e07aa
3 changed files with 176 additions and 3 deletions

View File

@ -53,11 +53,22 @@ public abstract class BasePropertyValidator<T, TProperty, TPropertyValidator ext
*/
protected final <E extends RuntimeException> TPropertyValidator withRule(
Predicate<? super TProperty> rule, Function<TProperty, E> e) {
this.consumers.add(v -> {
return withRule(v -> {
if (!rule.test(v)) {
throw e.apply(v);
}
});
}
/**
* 添加一条校验属性的规则
*
* @param rule 校验规则
* @param e 自定义异常
* @return 属性校验器
*/
protected final TPropertyValidator withRule(Consumer<? super TProperty> rule) {
this.consumers.add(rule);
return thisObject();
}

View File

@ -18,6 +18,7 @@ package xyz.zhouxy.plusone.validator;
import java.util.Collection;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import xyz.zhouxy.plusone.commons.collection.CollectionTools;
@ -37,7 +38,9 @@ public class CollectionPropertyValidator<T, TElement>
super(getter);
}
// ====== notEmpty =====
// ================================
// #region - notEmpty
// ================================
/**
* 添加一条校验属性的规则校验属性是否非空
@ -83,7 +86,13 @@ public class CollectionPropertyValidator<T, TElement>
return this;
}
// ====== isEmpty =====
// ================================
// #endregion - notEmpty
// ================================
// ================================
// #region - isEmpty
// ================================
/**
* 添加一条校验属性的规则校验属性是否为空
@ -127,6 +136,68 @@ public class CollectionPropertyValidator<T, TElement>
return this;
}
// ================================
// #endregion - isEmpty
// ================================
// ================================
// #region - allMatch
// ================================
/**
* 添加一条校验属性的规则校验是否所有元素都满足条件
*
* @param condition 校验规则
* @return 属性校验器
*/
public CollectionPropertyValidator<T, TElement> allMatch(Predicate<TElement> condition) {
return allMatch(condition, convertToExceptionFunction("All elements must match the condition."));
}
/**
* 添加一条校验属性的规则校验是否所有元素都满足条件
*
* @param condition 校验规则
* @param errMsg 异常信息
* @return 属性校验器
*/
public CollectionPropertyValidator<T, TElement> allMatch(Predicate<TElement> condition, String errMsg) {
return allMatch(condition, convertToExceptionFunction(errMsg));
}
/**
* 添加一条校验属性的规则校验是否所有元素都满足条件
*
* @param condition 校验规则
* @param e 自定义异常
* @return 属性校验器
*/
public <E extends RuntimeException> CollectionPropertyValidator<T, TElement> allMatch(
Predicate<TElement> condition, Supplier<E> e) {
return allMatch(condition, convertToExceptionFunction(e));
}
/**
* 添加一条校验属性的规则校验是否所有元素都满足条件
*
* @param condition 校验规则
* @param e 自定义异常
* @return 属性校验器
*/
public <E extends RuntimeException> CollectionPropertyValidator<T, TElement> allMatch(
Predicate<TElement> condition, Function<TElement, E> e) {
withRule(c -> c.stream().forEach(element -> {
if (!condition.test(element)) {
throw e.apply(element);
}
}));
return this;
}
// ================================
// #endregion - allMatch
// ================================
@Override
protected CollectionPropertyValidator<T, TElement> thisObject() {
return this;

View File

@ -271,6 +271,97 @@ public class CollectionPropertyValidatorTests {
// #endregion - isEmpty
// ================================
// ================================
// #region - allMatch
// ================================
static boolean checkStringLength(String str, int min, int max) {
return str != null && (str.length() >= min && str.length() <= max);
}
@Test
void allMatch_validInput() {
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
{
ruleForCollection(ExampleCommand::getStringListProperty)
.allMatch(str -> checkStringLength(str, 4, 6))
.allMatch(str -> checkStringLength(str, 4, 6),
"String length must in the interval [4,6].")
.allMatch(str -> checkStringLength(str, 4, 6),
() -> ExampleException.withMessage("String length must in the interval [4,6]."))
.allMatch(str -> checkStringLength(str, 4, 6),
str -> ExampleException.withMessage("Validation failed: '%s'.", str));
}
};
ExampleCommand command = exampleCommandWithStringListProperty(Lists.newArrayList("1234", "12345", "123456"));
assertDoesNotThrow(() -> validator.validate(command));
}
@Test
void allMatch_default_invalidInput() {
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
{
ruleForCollection(ExampleCommand::getStringListProperty)
.allMatch(str -> checkStringLength(str, 4, 6));
}
};
ExampleCommand command = exampleCommandWithStringListProperty(Lists.newArrayList(null, "1234", "12345", "123456"));
ValidationException e = assertThrows(ValidationException.class, () -> validator.validate(command));
assertEquals("All elements must match the condition.", e.getMessage());
}
@Test
void allMatch_specifiedMessage_invalidInput() {
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
{
ruleForCollection(ExampleCommand::getStringListProperty)
.allMatch(str -> checkStringLength(str, 4, 6),
"String length must in the interval [4,6].");
}
};
ExampleCommand command = exampleCommandWithStringListProperty(Lists.newArrayList("1234", "", "12345", "123456"));
ValidationException e = assertThrows(ValidationException.class, () -> validator.validate(command));
assertEquals("String length must in the interval [4,6].", e.getMessage());
}
@Test
void allMatch_specifiedExceptionSupplier_invalidInput() {
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
{
ruleForCollection(ExampleCommand::getStringListProperty)
.allMatch(str -> checkStringLength(str, 4, 6),
() -> ExampleException.withMessage("String length must in the interval [4,6]."));
}
};
ExampleCommand command = exampleCommandWithStringListProperty(Lists.newArrayList("1234", "12345", "123", "123456"));
ExampleException e = assertThrows(ExampleException.class, () -> validator.validate(command));
assertEquals("String length must in the interval [4,6].", e.getMessage());
}
@Test
void allMatch_specifiedExceptionFunction_invalidInput() {
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
{
ruleForCollection(ExampleCommand::getStringListProperty)
.allMatch(str -> checkStringLength(str, 4, 6),
str -> ExampleException.withMessage("Validation failed: '%s'.", str));
}
};
ExampleCommand command = exampleCommandWithStringListProperty(Lists.newArrayList("1234", "12345", "123456", "1234567"));
ExampleException e = assertThrows(ExampleException.class, () -> validator.validate(command));
assertEquals("Validation failed: '1234567'.", e.getMessage());
}
// ================================
// #endregion - allMatch
// ================================
static ExampleCommand exampleCommandWithStringListProperty(List<String> property) {
ExampleCommand exampleCommand = new ExampleCommand();
exampleCommand.setStringListProperty(property);