forked from plusone/plusone-validator
feat: 添加二元组属性校验支持
- 新增 `PairPropertyValidator` 类,用于校验二元组属性 - 在 `BaseValidator` 和 `MapValidator` 中添加对二元组校验器的支持 - 添加相关单元测试
This commit is contained in:
parent
8be8be8f17
commit
3ef2ebac2f
@ -19,6 +19,7 @@ package xyz.zhouxy.plusone.validator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
@ -246,6 +247,19 @@ public abstract class BaseValidator<T> implements IValidator<T> {
|
||||
return validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个针对二元组的校验器
|
||||
* @param <V1> 第一个元素的类型
|
||||
* @param <V2> 第二个元素的类型
|
||||
* @param getter 获取属性值的函数
|
||||
* @return 二元组校验器
|
||||
*/
|
||||
protected final <V1, V2> PairPropertyValidator<T, V1, V2> ruleForPair(Function<T, Entry<V1, V2>> getter) {
|
||||
PairPropertyValidator<T, V1, V2> validator = new PairPropertyValidator<>(getter);
|
||||
this.rules.add(validator::validate);
|
||||
return validator;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void validate(T obj) {
|
||||
|
@ -16,9 +16,11 @@
|
||||
|
||||
package xyz.zhouxy.plusone.validator;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@ -107,6 +109,7 @@ public abstract class MapValidator<K, V> extends BaseValidator<Map<K, V>> {
|
||||
/**
|
||||
* 添加一个属性校验器,对指定 key 对应的 value 进行校验
|
||||
*
|
||||
* @param <T> 属性类型
|
||||
* @param key key
|
||||
* @return 属性校验器
|
||||
*/
|
||||
@ -191,6 +194,21 @@ public abstract class MapValidator<K, V> extends BaseValidator<Map<K, V>> {
|
||||
return ruleForCollection(getter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个属性校验器,对指定的两个 key 对应的 value 进行校验
|
||||
* @param <V1> 第一个属性的类型
|
||||
* @param <V2> 第二个属性的类型
|
||||
* @param k1 第一个 key
|
||||
* @param k2 第二个 key
|
||||
* @return 属性校验器
|
||||
*/
|
||||
protected final <V1 extends V, V2 extends V>
|
||||
PairPropertyValidator<Map<K, V>, V1, V2> ruleForPair(K k1, K k2) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Function<Map<K, V>, Entry<V1, V2>> getter = m -> new SimpleEntry<V1, V2>((V1) m.get(k1), (V2) m.get(k2));
|
||||
return ruleForPair(getter);
|
||||
}
|
||||
|
||||
// ================================
|
||||
// #endregion - ruleFor
|
||||
// ================================
|
||||
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2025 the original author or 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 xyz.zhouxy.plusone.validator;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 针对二元组的属性校验器
|
||||
*
|
||||
* @author ZhouXY
|
||||
*/
|
||||
public class PairPropertyValidator<T, V1, V2>
|
||||
extends BasePropertyValidator<T, Entry<V1, V2>, PairPropertyValidator<T, V1, V2>> {
|
||||
|
||||
protected PairPropertyValidator(Function<T, ? extends Entry<V1, V2>> getter) {
|
||||
super(getter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条校验属性的规则,校验二元组是否满足给定的条件
|
||||
*
|
||||
* @param condition 校验规则
|
||||
* @return 属性校验器
|
||||
*/
|
||||
public final PairPropertyValidator<T, V1, V2> must(BiPredicate<V1, V2> condition) {
|
||||
return must(pair -> condition.test(pair.getKey(), pair.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条校验属性的规则,校验二元组是否满足给定的条件
|
||||
*
|
||||
* @param condition 校验规则
|
||||
* @param errMsg 错误信息
|
||||
* @return 属性校验器
|
||||
*/
|
||||
public final PairPropertyValidator<T, V1, V2> must(BiPredicate<V1, V2> condition, String errMsg) {
|
||||
return must(pair -> condition.test(pair.getKey(), pair.getValue()), errMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条校验属性的规则,校验二元组是否满足给定的条件
|
||||
*
|
||||
* @param condition 校验规则
|
||||
* @param e 自定义异常
|
||||
* @return 属性校验器
|
||||
*/
|
||||
public final <E extends RuntimeException> PairPropertyValidator<T, V1, V2> must(
|
||||
BiPredicate<V1, V2> condition, Supplier<E> e) {
|
||||
return must(pair -> condition.test(pair.getKey(), pair.getValue()), e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条校验属性的规则,校验二元组是否满足给定的条件
|
||||
*
|
||||
* @param condition 校验规则
|
||||
* @param e 自定义异常
|
||||
* @return 属性校验器
|
||||
*/
|
||||
public final <E extends RuntimeException> PairPropertyValidator<T, V1, V2> must(
|
||||
BiPredicate<V1, V2> condition, BiFunction<V1, V2, E> e) {
|
||||
return must(pair -> condition.test(pair.getKey(), pair.getValue()),
|
||||
pair -> e.apply(pair.getKey(), pair.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PairPropertyValidator<T, V1, V2> thisObject() {
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2025 the original author or 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 xyz.zhouxy.plusone.example.validator;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import xyz.zhouxy.plusone.ExampleException;
|
||||
import xyz.zhouxy.plusone.example.ExampleCommand;
|
||||
import xyz.zhouxy.plusone.validator.BaseValidator;
|
||||
import xyz.zhouxy.plusone.validator.IValidator;
|
||||
import xyz.zhouxy.plusone.validator.ValidationException;
|
||||
|
||||
public class PairPropertyValidatorTests {
|
||||
|
||||
static final String MESSAGE = "Validation failed.";
|
||||
|
||||
// ================================
|
||||
// #region - must
|
||||
// ================================
|
||||
|
||||
@Test
|
||||
void must_validInput() {
|
||||
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
|
||||
{
|
||||
ruleForPair((ExampleCommand command) -> new SimpleEntry<String,Integer>(command.getStringProperty(), command.getIntProperty()))
|
||||
.must((str, intValue) -> Objects.equals(str, intValue.toString()))
|
||||
.must((str, intValue) -> Objects.equals(str, intValue.toString()), MESSAGE)
|
||||
.must((str, intValue) -> Objects.equals(str, intValue.toString()), () -> ExampleException.withMessage(MESSAGE))
|
||||
.must((str, intValue) -> Objects.equals(str, intValue.toString()),
|
||||
(str, intValue) -> ExampleException.withMessage("Validation failed: ('%s', %d).", str, intValue));
|
||||
}
|
||||
};
|
||||
|
||||
ExampleCommand command = exampleCommandWithIntAndStringListProperty(100, "100");
|
||||
assertDoesNotThrow(() -> validator.validate(command));
|
||||
}
|
||||
|
||||
@Test
|
||||
void must_default_invalidInput() {
|
||||
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
|
||||
{
|
||||
ruleForPair((ExampleCommand command) -> new SimpleEntry<String,Integer>(command.getStringProperty(), command.getIntProperty()))
|
||||
.must((str, intValue) -> Objects.equals(str, intValue.toString()));
|
||||
}
|
||||
};
|
||||
|
||||
ExampleCommand command = exampleCommandWithIntAndStringListProperty(100, "");
|
||||
|
||||
ValidationException e = assertThrows(ValidationException.class, () -> validator.validate(command));
|
||||
assertEquals("The specified condition was not met for the input.", e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void must_message_invalidInput() {
|
||||
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
|
||||
{
|
||||
ruleForPair((ExampleCommand command) -> new SimpleEntry<String,Integer>(command.getStringProperty(), command.getIntProperty()))
|
||||
.must((str, intValue) -> Objects.equals(str, intValue.toString()), MESSAGE);
|
||||
}
|
||||
};
|
||||
|
||||
ExampleCommand command = exampleCommandWithIntAndStringListProperty(100, "");
|
||||
ValidationException e = assertThrows(ValidationException.class, () -> validator.validate(command));
|
||||
assertEquals(MESSAGE, e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void must_exceptionSupplier_invalidInput() {
|
||||
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
|
||||
{
|
||||
ruleForPair((ExampleCommand command) -> new SimpleEntry<String,Integer>(command.getStringProperty(), command.getIntProperty()))
|
||||
.must((str, intValue) -> Objects.equals(str, intValue.toString()), () -> ExampleException.withMessage(MESSAGE));
|
||||
}
|
||||
};
|
||||
|
||||
ExampleCommand command = exampleCommandWithIntAndStringListProperty(100, "");
|
||||
|
||||
ExampleException e = assertThrows(ExampleException.class, () -> validator.validate(command));
|
||||
assertEquals(MESSAGE, e.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void must_exceptionFunction_invalidInput() {
|
||||
IValidator<ExampleCommand> validator = new BaseValidator<ExampleCommand>() {
|
||||
{
|
||||
ruleForPair((ExampleCommand command) -> new SimpleEntry<String,Integer>(command.getStringProperty(), command.getIntProperty()))
|
||||
.must((str, intValue) -> Objects.equals(str, intValue.toString()),
|
||||
(str, intValue) -> ExampleException.withMessage("Validation failed: ('%s', %d).", str, intValue));
|
||||
}
|
||||
};
|
||||
|
||||
ExampleCommand command = exampleCommandWithIntAndStringListProperty(100, "");
|
||||
|
||||
ExampleException e = assertThrows(ExampleException.class, () -> validator.validate(command));
|
||||
assertEquals("Validation failed: ('', 100).", e.getMessage());
|
||||
}
|
||||
|
||||
// ================================
|
||||
// #endregion - must
|
||||
// ================================
|
||||
|
||||
static ExampleCommand exampleCommandWithIntAndStringListProperty(Integer intValue, String str) {
|
||||
ExampleCommand exampleCommand = new ExampleCommand();
|
||||
exampleCommand.setIntProperty(intValue);
|
||||
exampleCommand.setStringProperty(str);
|
||||
return exampleCommand;
|
||||
}
|
||||
}
|
@ -22,7 +22,6 @@ import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -126,9 +125,9 @@ class ParamsValidator extends MapValidator<String, Object> {
|
||||
ruleForCollection(STRING_LIST_PROPERTY)
|
||||
.notNull(d -> ExampleException.withMessage("The stringListProperty cannot be null, but it was %s", d));
|
||||
|
||||
// 校验到多个属性,只能针对 map 本身进行校验
|
||||
withRule(m -> Objects.equals(m.get(STRING_PROPERTY), m.get(STRING_PROPERTY2)),
|
||||
"'stringProperty' must be equal to 'stringProperty2'.");
|
||||
ruleForPair(STRING_PROPERTY, STRING_PROPERTY2)
|
||||
.must((str1, str2) -> str1 != null && str1.equals(str2),
|
||||
"'stringProperty' must be equal to 'stringProperty2'.");
|
||||
}
|
||||
|
||||
public static Set<String> keySet() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user