diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2087dd7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = true diff --git a/.vscode/settings.json b/.vscode/settings.json index 13f5352..4fa5537 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "java.configuration.updateBuildConfiguration": "automatic", - "java.dependency.packagePresentation": "hierarchical" -} \ No newline at end of file + "java.dependency.packagePresentation": "hierarchical", + "java.compile.nullAnalysis.mode": "automatic" +} diff --git a/pom.xml b/pom.xml index 8c9f4fd..d7d1aad 100644 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,12 @@ - + 4.0.0 xyz.zhouxy.plusone plusone-validator - 0.1.2-SNAPSHOT + 0.1.4-SNAPSHOT plusone-validator http://zhouxy.xyz @@ -15,6 +15,7 @@ UTF-8 1.8 1.8 + 3.12.0 @@ -24,15 +25,22 @@ 0.1.0-SNAPSHOT - junit - junit - 4.11 + org.apache.commons + commons-lang3 + ${commons-lang3.version} + test + + + org.junit.jupiter + junit-jupiter-api + 5.9.2 test - + + diff --git a/src/main/java/xyz/zhouxy/plusone/validator/BasePropertyValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/BasePropertyValidator.java new file mode 100644 index 0000000..4913aa5 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/validator/BasePropertyValidator.java @@ -0,0 +1,171 @@ +package xyz.zhouxy.plusone.validator; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +public abstract class BasePropertyValidator< // + TObj, // + TProperty, // + TPropertyValidator extends BasePropertyValidator> { + + private final Function getter; + + private final List> consumers = new LinkedList<>(); + + protected BasePropertyValidator(Function getter) { + this.getter = getter; + } + + public final TPropertyValidator withRule(Predicate rule) { + return withRule(rule, v -> new IllegalArgumentException()); + } + + public final TPropertyValidator withRule( + Predicate rule, String errMsg) { + return withRule(rule, convertExceptionCreator(errMsg)); + } + + public final TPropertyValidator withRule( + Predicate rule, Supplier e) { + return withRule(rule, convertExceptionCreator(e)); + } + + public final TPropertyValidator withRule( + Predicate rule, Function e) { + this.consumers.add(v -> { + if (!rule.test(v)) { + throw e.apply(v); + } + }); + return thisObject(); + } + + public final void validate(T obj) { + for (Consumer consumer : consumers) { + consumer.accept(getter.apply(obj)); + } + } + + protected abstract TPropertyValidator thisObject(); + + // ==================== + // ====== Object ====== + // ==================== + + // ====== notNull ===== + + public TPropertyValidator notNull() { + return notNull("Value could not be null."); + } + + public TPropertyValidator notNull(String errMsg) { + return notNull(convertExceptionCreator(errMsg)); + } + + public TPropertyValidator notNull(Supplier exceptionCreator) { + return notNull(convertExceptionCreator(exceptionCreator)); + } + + public TPropertyValidator notNull(Function exceptionCreator) { + withRule(Objects::nonNull, exceptionCreator); + return thisObject(); + } + + // ====== isNull ===== + + public TPropertyValidator isNull(String errMsg) { + return isNull(convertExceptionCreator(errMsg)); + } + + public TPropertyValidator isNull(Supplier exceptionCreator) { + return isNull(convertExceptionCreator(exceptionCreator)); + } + + public TPropertyValidator isNull(Function exceptionCreator) { + withRule(Objects::isNull, exceptionCreator); + return thisObject(); + } + + // ===== equals ===== + + public TPropertyValidator equalsThat(Object that) { + return equalsThat(that, value -> new IllegalArgumentException(String.format("(%s) 必须与 (%s) 相等", value, that))); + } + + public TPropertyValidator equalsThat(Object that, String errMsg) { + return equalsThat(that, convertExceptionCreator(errMsg)); + } + + public TPropertyValidator equalsThat( + Object that, Supplier exceptionCreator) { + return equalsThat(that, convertExceptionCreator(exceptionCreator)); + } + + public TPropertyValidator equalsThat( + Object that, Function exceptionCreator) { + withRule(value -> Objects.equals(value, that), exceptionCreator); + return thisObject(); + } + + // ===== isTrue ===== + + public TPropertyValidator isTrue(Predicate condition) { + return isTrue(condition, "无效的用户输入"); + } + + public TPropertyValidator isTrue(Predicate condition, String errMsg) { + return isTrue(condition, convertExceptionCreator(errMsg)); + } + + public TPropertyValidator isTrue( + Predicate condition, + Supplier exceptionCreator) { + return isTrue(condition, convertExceptionCreator(exceptionCreator)); + } + + public TPropertyValidator isTrue( + Predicate condition, + Function exceptionCreator) { + withRule(condition, exceptionCreator); + return thisObject(); + } + + // ===== isTrue ===== + + public TPropertyValidator isTrue(Collection> conditions) { + return isTrue(conditions, "无效的用户输入"); + } + + public TPropertyValidator isTrue(Collection> conditions, String errMsg) { + return isTrue(conditions, convertExceptionCreator(errMsg)); + } + + public TPropertyValidator isTrue( + Collection> conditions, + Supplier exceptionCreator) { + return isTrue(conditions, convertExceptionCreator(exceptionCreator)); + } + + public TPropertyValidator isTrue( + Collection> conditions, + Function exceptionCreator) { + for (Predicate condition : conditions) { + withRule(condition, exceptionCreator); + } + return thisObject(); + } + + static Function convertExceptionCreator(String errMsg) { + return value -> new IllegalArgumentException(errMsg); + } + + static Function convertExceptionCreator(Supplier exceptionSupplier) { + return value -> exceptionSupplier.get(); + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/validator/BaseValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/BaseValidator.java index 1782fc9..655b9f4 100644 --- a/src/main/java/xyz/zhouxy/plusone/validator/BaseValidator.java +++ b/src/main/java/xyz/zhouxy/plusone/validator/BaseValidator.java @@ -10,18 +10,17 @@ import java.util.function.Supplier; public class BaseValidator { private final List> rules = new ArrayList<>(); - private final List> propertyValidators = new ArrayList<>(); protected void withRule(final Predicate rule, final String errorMessage) { - withRule(rule, value -> new InvalidInputException(errorMessage)); + withRule(rule, () -> new IllegalArgumentException(errorMessage)); } protected void withRule(Predicate rule, Supplier exceptionBuilder) { withRule(rule, value -> exceptionBuilder.get()); } - protected void withRule(Predicate condition, - Function exceptionBuilder) { + protected void withRule( + Predicate condition, Function exceptionBuilder) { withRule(value -> { if (!condition.test(value)) { throw exceptionBuilder.apply(value); @@ -34,47 +33,42 @@ public class BaseValidator { } protected final ObjectValidator ruleFor(Function getter) { - ObjectValidator validValueHolder = new ObjectValidator<>(getter); - propertyValidators.add(validValueHolder); - return validValueHolder; + ObjectValidator validator = new ObjectValidator<>(getter); + this.rules.add(validator::validate); + return validator; } protected final IntValidator ruleForInt(Function getter) { - IntValidator validValueHolder = new IntValidator<>(getter); - propertyValidators.add(validValueHolder); - return validValueHolder; + IntValidator validator = new IntValidator<>(getter); + this.rules.add(validator::validate); + return validator; } protected final DoubleValidator ruleForDouble(Function getter) { - DoubleValidator validValueHolder = new DoubleValidator<>(getter); - propertyValidators.add(validValueHolder); - return validValueHolder; + DoubleValidator validator = new DoubleValidator<>(getter); + this.rules.add(validator::validate); + return validator; } protected final BoolValidator ruleForBool(Function getter) { - BoolValidator validValueHolder = new BoolValidator<>(getter); - propertyValidators.add(validValueHolder); - return validValueHolder; + BoolValidator validator = new BoolValidator<>(getter); + this.rules.add(validator::validate); + return validator; } protected final StringValidator ruleForString(Function getter) { - StringValidator validValueHolder = new StringValidator<>(getter); - propertyValidators.add(validValueHolder); - return validValueHolder; + StringValidator validator = new StringValidator<>(getter); + this.rules.add(validator::validate); + return validator; } protected final CollectionValidator ruleForCollection(Function> getter) { - CollectionValidator validValueHolder = new CollectionValidator<>(getter); - propertyValidators.add(validValueHolder); - return validValueHolder; + CollectionValidator validator = new CollectionValidator<>(getter); + this.rules.add(validator::validate); + return validator; } public void validate(T obj) { - for (Consumer rule : this.rules) { - rule.accept(obj); - } - for (PropertyValidator valueValidator : this.propertyValidators) { - valueValidator.validate(obj); - } + this.rules.forEach(rule -> rule.accept(obj)); } } diff --git a/src/main/java/xyz/zhouxy/plusone/validator/BoolValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/BoolValidator.java index 9912c37..726c164 100644 --- a/src/main/java/xyz/zhouxy/plusone/validator/BoolValidator.java +++ b/src/main/java/xyz/zhouxy/plusone/validator/BoolValidator.java @@ -3,7 +3,7 @@ package xyz.zhouxy.plusone.validator; import java.util.function.Function; import java.util.function.Supplier; -public class BoolValidator extends PropertyValidator> { +public class BoolValidator extends BasePropertyValidator> { BoolValidator(Function getter) { super(getter); @@ -48,7 +48,7 @@ public class BoolValidator extends PropertyValidator thisObject() { return this; diff --git a/src/main/java/xyz/zhouxy/plusone/validator/CollectionValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/CollectionValidator.java index 5a79048..afbd02e 100644 --- a/src/main/java/xyz/zhouxy/plusone/validator/CollectionValidator.java +++ b/src/main/java/xyz/zhouxy/plusone/validator/CollectionValidator.java @@ -4,7 +4,10 @@ import java.util.Collection; import java.util.function.Function; import java.util.function.Supplier; -public class CollectionValidator extends PropertyValidator, CollectionValidator> { +import xyz.zhouxy.plusone.commons.collection.CollectionTools; + +public class CollectionValidator + extends BasePropertyValidator, CollectionValidator> { CollectionValidator(Function> getter) { super(getter); @@ -22,12 +25,7 @@ public class CollectionValidator extends PropertyValidator CollectionValidator notEmpty( Function, E> exceptionCreator) { - withRule(value -> { - if (value == null) { - return false; - } - return !((Collection) value).isEmpty(); - }, exceptionCreator); + withRule(CollectionTools::isNotEmpty, exceptionCreator); return this; } @@ -43,12 +41,7 @@ public class CollectionValidator extends PropertyValidator CollectionValidator isEmpty( Function, E> exceptionCreator) { - withRule(value -> { - if (value == null) { - return false; - } - return ((Collection) value).isEmpty(); - }, exceptionCreator); + withRule(CollectionTools::isEmpty, exceptionCreator); return this; } diff --git a/src/main/java/xyz/zhouxy/plusone/validator/DoubleValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/DoubleValidator.java index 4174e79..f2bbf4e 100644 --- a/src/main/java/xyz/zhouxy/plusone/validator/DoubleValidator.java +++ b/src/main/java/xyz/zhouxy/plusone/validator/DoubleValidator.java @@ -3,7 +3,7 @@ package xyz.zhouxy.plusone.validator; import java.util.function.Function; import java.util.function.Supplier; -public class DoubleValidator extends PropertyValidator> { +public class DoubleValidator extends BasePropertyValidator> { DoubleValidator(Function getter) { super(getter); diff --git a/src/main/java/xyz/zhouxy/plusone/validator/IntValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/IntValidator.java index 29fc24f..1250f95 100644 --- a/src/main/java/xyz/zhouxy/plusone/validator/IntValidator.java +++ b/src/main/java/xyz/zhouxy/plusone/validator/IntValidator.java @@ -3,14 +3,14 @@ package xyz.zhouxy.plusone.validator; import java.util.function.Function; import java.util.function.Supplier; -public class IntValidator extends PropertyValidator> { +public class IntValidator extends BasePropertyValidator> { IntValidator(Function getter) { super(getter); } public IntValidator between(int min, int max) { - return between(min, max, String.format("数值不在 %s 和 %s 之间", String.valueOf(min), String.valueOf(max))); + return between(min, max, String.format("数值不在 %d 和 %d 之间", min, max)); } public IntValidator between(int min, int max, String errMsg) { diff --git a/src/main/java/xyz/zhouxy/plusone/validator/InvalidInputException.java b/src/main/java/xyz/zhouxy/plusone/validator/InvalidInputException.java deleted file mode 100644 index b770344..0000000 --- a/src/main/java/xyz/zhouxy/plusone/validator/InvalidInputException.java +++ /dev/null @@ -1,53 +0,0 @@ -package xyz.zhouxy.plusone.validator; - -import xyz.zhouxy.plusone.exception.BaseException; - -/** - * 4040200 - 无效的用户输入 - * - * @author ZhouXY - */ -public class InvalidInputException extends BaseException { - - private static final long serialVersionUID = 7956661913360059670L; - - public static final int ERROR_CODE = 4040200; - - private InvalidInputException(int code, String msg) { - super(code, msg); - } - - private InvalidInputException(int code, Throwable cause) { - super(code, cause); - } - - private InvalidInputException(int code, String msg, Throwable cause) { - super(code, msg, cause); - } - - public InvalidInputException(String msg) { - this(ERROR_CODE, msg); - } - - public InvalidInputException(Throwable cause) { - this(ERROR_CODE, cause); - } - - public InvalidInputException(String msg, Throwable cause) { - this(ERROR_CODE, msg, cause); - } - - /** - * 不支持的 Principal 类型出现时抛出的异常 - */ - public static InvalidInputException unsupportedPrincipalTypeException() { - return unsupportedPrincipalTypeException("不支持的 PrincipalType"); - } - - /** - * 不支持的 Principal 类型出现时抛出的异常 - */ - public static InvalidInputException unsupportedPrincipalTypeException(String message) { - return new InvalidInputException(4040201, message); - } -} diff --git a/src/main/java/xyz/zhouxy/plusone/validator/ObjectValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/ObjectValidator.java index 6f2de5f..3941bc0 100644 --- a/src/main/java/xyz/zhouxy/plusone/validator/ObjectValidator.java +++ b/src/main/java/xyz/zhouxy/plusone/validator/ObjectValidator.java @@ -2,7 +2,7 @@ package xyz.zhouxy.plusone.validator; import java.util.function.Function; -public class ObjectValidator extends PropertyValidator> { +public class ObjectValidator extends BasePropertyValidator> { ObjectValidator(Function getter) { super(getter); diff --git a/src/main/java/xyz/zhouxy/plusone/validator/PropertyValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/PropertyValidator.java deleted file mode 100644 index 2adf9b9..0000000 --- a/src/main/java/xyz/zhouxy/plusone/validator/PropertyValidator.java +++ /dev/null @@ -1,155 +0,0 @@ -package xyz.zhouxy.plusone.validator; - -import java.util.Collection; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; - -abstract class PropertyValidator { - Function getter; - Validator validator = new Validator<>(); - - PropertyValidator(Function getter) { - this.getter = getter; - } - - void withRule(Predicate condition, - Function exceptionCreator) { - withRule(value -> { - if (!condition.test(value)) { - throw exceptionCreator.apply(value); - } - }); - } - - private void withRule(Consumer rule) { - this.validator.addRule(rule); - } - - // ==================== - // ====== Object ====== - // ==================== - - // ====== notNull ===== - - public THIS notNull() { - return notNull("Value could not be null."); - } - - public THIS notNull(String errMsg) { - return notNull(convertExceptionCreator(errMsg)); - } - - public THIS notNull(Supplier exceptionCreator) { - return notNull(convertExceptionCreator(exceptionCreator)); - } - - public THIS notNull(Function exceptionCreator) { - withRule(Objects::nonNull, exceptionCreator); - return thisObject(); - } - - // ====== isNull ===== - - public THIS isNull(String errMsg) { - return isNull(convertExceptionCreator(errMsg)); - } - - public THIS isNull(Supplier exceptionCreator) { - return isNull(convertExceptionCreator(exceptionCreator)); - } - - public THIS isNull(Function exceptionCreator) { - withRule(Objects::isNull, exceptionCreator); - return thisObject(); - } - - // ===== equals ===== - - public THIS equalsThat(Object that) { - return equalsThat(that, value -> new InvalidInputException(String.format("(%s) 必须与 (%s) 相等", value, that))); - } - - public THIS equalsThat(Object that, String errMsg) { - return equalsThat(that, convertExceptionCreator(errMsg)); - } - - public THIS equalsThat( - Object that, Supplier exceptionCreator) { - return equalsThat(that, convertExceptionCreator(exceptionCreator)); - } - - public THIS equalsThat( - Object that, Function exceptionCreator) { - withRule(value -> Objects.equals(value, that), exceptionCreator); - return thisObject(); - } - - // ===== state ===== - - public THIS state(Predicate condition) { - return state(condition, "无效的用户输入"); - } - - public THIS state(Predicate condition, String errMsg) { - return state(condition, convertExceptionCreator(errMsg)); - } - - public THIS state( - Predicate condition, - Supplier exceptionCreator) { - return state(condition, convertExceptionCreator(exceptionCreator)); - } - - public THIS state( - Predicate condition, - Function exceptionCreator) { - withRule(condition, exceptionCreator); - return thisObject(); - } - - // ===== state ===== - - public THIS state(Collection> conditions) { - return state(conditions, "无效的用户输入"); - } - - public THIS state(Collection> conditions, String errMsg) { - return state(conditions, convertExceptionCreator(errMsg)); - } - - public THIS state( - Collection> conditions, - Supplier exceptionCreator) { - return state(conditions, convertExceptionCreator(exceptionCreator)); - } - - public THIS state( - Collection> conditions, - Function exceptionCreator) { - for (Predicate condition : conditions) { - withRule(condition, exceptionCreator); - } - return thisObject(); - } - - // ======================================================================== - - void validate(DTO obj) { - PROPERTY value = this.getter.apply(obj); - this.validator.validate(value); - } - - static Function convertExceptionCreator(String errMsg) { - return value -> new InvalidInputException(errMsg); - } - - static Function convertExceptionCreator( - Supplier exceptionSupplier) { - return value -> exceptionSupplier.get(); - } - - protected abstract THIS thisObject(); -} diff --git a/src/main/java/xyz/zhouxy/plusone/validator/StringValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/StringValidator.java index 7e02756..c102318 100644 --- a/src/main/java/xyz/zhouxy/plusone/validator/StringValidator.java +++ b/src/main/java/xyz/zhouxy/plusone/validator/StringValidator.java @@ -1,14 +1,17 @@ package xyz.zhouxy.plusone.validator; +import java.util.Collection; import java.util.List; import java.util.function.Function; import java.util.function.Supplier; +import java.util.regex.Pattern; -import cn.hutool.core.util.StrUtil; -import xyz.zhouxy.plusone.constant.RegexConsts; -import xyz.zhouxy.plusone.util.RegexUtil; +import com.google.common.base.Preconditions; -public class StringValidator extends PropertyValidator> { +import xyz.zhouxy.plusone.commons.constant.PatternConsts; +import xyz.zhouxy.plusone.commons.util.RegexTools; + +public class StringValidator extends BasePropertyValidator> { StringValidator(Function getter) { super(getter); @@ -20,80 +23,109 @@ public class StringValidator extends PropertyValidator matches(String regex, String errMsg) { + public StringValidator matches(Pattern regex, String errMsg) { return matches(regex, convertExceptionCreator(errMsg)); } public StringValidator matches( - String regex, + Pattern regex, Supplier exceptionCreator) { return matches(regex, convertExceptionCreator(exceptionCreator)); } public StringValidator matches( - String regex, + Pattern regex, Function exceptionCreator) { - withRule(input -> RegexUtil.matches(input, regex), exceptionCreator); + withRule(input -> RegexTools.matches(input, regex), exceptionCreator); return this; } - // ===== matchesOr ===== + // ===== matchesOne ===== - public StringValidator matchesOr(String[] regexs, String errMsg) { - return matchesOr(regexs, convertExceptionCreator(errMsg)); + public StringValidator matchesOne(Pattern[] regexs, String errMsg) { + return matchesOne(regexs, convertExceptionCreator(errMsg)); } - public StringValidator matchesOr( - String[] regexs, + public StringValidator matchesOne( + Pattern[] regexs, Supplier exceptionCreator) { - return matchesOr(regexs, convertExceptionCreator(exceptionCreator)); + return matchesOne(regexs, convertExceptionCreator(exceptionCreator)); } - public StringValidator matchesOr( - String[] regexs, + public StringValidator matchesOne( + Pattern[] regexs, Function exceptionCreator) { - withRule(input -> RegexUtil.matchesOr(input, regexs), exceptionCreator); + withRule(input -> RegexTools.matchesOne(input, regexs), exceptionCreator); return this; } - public StringValidator matchesOr(List regexs, String errMsg) { - return matchesOr(regexs, convertExceptionCreator(errMsg)); + public StringValidator matchesOne(List regexs, String errMsg) { + return matchesOne(regexs, convertExceptionCreator(errMsg)); } - public StringValidator matchesOr( - List regexs, + public StringValidator matchesOne( + List regexs, Supplier exceptionCreator) { - return matchesOr(regexs, convertExceptionCreator(exceptionCreator)); + return matchesOne(regexs, convertExceptionCreator(exceptionCreator)); } - public StringValidator matchesOr( - List regexs, + public StringValidator matchesOne( + List regexs, Function exceptionCreator) { - withRule(input -> RegexUtil.matchesOr(input, regexs.toArray(new String[regexs.size()])), exceptionCreator); + withRule(input -> RegexTools.matchesOne(input, regexs.toArray(new Pattern[regexs.size()])), exceptionCreator); return this; } - // ===== matchesAnd ===== + // ===== matchesAll ===== - public StringValidator matchesAnd(String[] regexs, String errMsg) { - return matchesAnd(regexs, convertExceptionCreator(errMsg)); + public StringValidator matchesAll(Pattern[] regexs, String errMsg) { + return matchesAll(regexs, convertExceptionCreator(errMsg)); } - public StringValidator matchesAnd( - String[] regexs, + public StringValidator matchesAll( + Pattern[] regexs, Supplier exceptionCreator) { - return matchesAnd(regexs, convertExceptionCreator(exceptionCreator)); + return matchesAll(regexs, convertExceptionCreator(exceptionCreator)); } - public StringValidator matchesAnd( - String[] regexs, + public StringValidator matchesAll( + Pattern[] regexs, Function exceptionCreator) { - withRule(input -> RegexUtil.matchesAnd(input, regexs), exceptionCreator); + withRule(input -> RegexTools.matchesAll(input, regexs), exceptionCreator); + return this; + } + + public StringValidator matchesAll(Collection regexs, String errMsg) { + return matchesAll(regexs, convertExceptionCreator(errMsg)); + } + + public StringValidator matchesAll( + Collection regexs, + Supplier exceptionCreator) { + return matchesAll(regexs, convertExceptionCreator(exceptionCreator)); + } + + public StringValidator matchesAll( + Collection regexs, + Function exceptionCreator) { + withRule(input -> RegexTools.matchesAll(input, regexs.toArray(new Pattern[regexs.size()])), exceptionCreator); return this; } // ===== notBlank ===== + static boolean isNotBlank(final String cs) { + if (cs == null || cs.isEmpty()) { + return false; + } + for (int i = 0; i < cs.length(); i++) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + return true; + } + public StringValidator notBlank() { return notBlank("This String argument must have text; it must not be null, empty, or blank"); } @@ -108,7 +140,7 @@ public class StringValidator extends PropertyValidator StringValidator notBlank( Function exceptionCreator) { - withRule(input -> StrUtil.isNotBlank(input), exceptionCreator); + withRule(StringValidator::isNotBlank, exceptionCreator); return this; } @@ -127,7 +159,7 @@ public class StringValidator extends PropertyValidator StringValidator email(Function exceptionCreator) { - return matches(RegexConsts.EMAIL, exceptionCreator); + return matches(PatternConsts.EMAIL, exceptionCreator); } // ====== notEmpty ===== @@ -142,33 +174,66 @@ public class StringValidator extends PropertyValidator StringValidator notEmpty( Function exceptionCreator) { - withRule(value -> { - if (value == null) { - return false; - } - return !(value.isEmpty()); - }, exceptionCreator); + withRule(s -> s != null && !s.isEmpty(), exceptionCreator); return this; } - // ====== isEmpty ===== + // ====== isNullOrEmpty ===== - public StringValidator isEmpty(String errMsg) { - return isEmpty(convertExceptionCreator(errMsg)); + public StringValidator isNullOrEmpty(String errMsg) { + return isNullOrEmpty(convertExceptionCreator(errMsg)); } - public StringValidator isEmpty(Supplier exceptionCreator) { - return isEmpty(convertExceptionCreator(exceptionCreator)); + public StringValidator isNullOrEmpty(Supplier exceptionCreator) { + return isNullOrEmpty(convertExceptionCreator(exceptionCreator)); } - public StringValidator isEmpty( + public StringValidator isNullOrEmpty( Function exceptionCreator) { - withRule(value -> { - if (value == null) { - return false; - } - return value.isEmpty(); - }, exceptionCreator); + withRule(s -> s == null || s.isEmpty(), exceptionCreator); + return this; + } + + // ====== length ===== + + public StringValidator length(int length, String errMsg) { + return length(length, convertExceptionCreator(errMsg)); + } + + public StringValidator length(int length, + Supplier exceptionCreator) { + return length(length, convertExceptionCreator(exceptionCreator)); + } + + public StringValidator length(int length, + Function exceptionCreator) { + Preconditions.checkArgument(length >= 0, "The minimum value must be less than the maximum value."); + withRule(s -> s != null && s.length() == length, exceptionCreator); + return this; + } + + static boolean length(String str, int min, int max) { + if (str == null) { + return false; + } + final int len = str.length(); + return len >= min && len < max; + } + + public StringValidator length(int min, int max, String errMsg) { + return length(min, max, convertExceptionCreator(errMsg)); + } + + public StringValidator length(int min, int max, + Supplier exceptionCreator) { + return length(min, max, convertExceptionCreator(exceptionCreator)); + } + + public StringValidator length(int min, int max, + Function exceptionCreator) { + Preconditions.checkArgument(min >= 0, "The minimum value must be greater than equal to 0."); + Preconditions.checkArgument(min < max, "The minimum value must be less than the maximum value."); + withRule(s -> length(s, min, max), exceptionCreator); return this; } diff --git a/src/main/java/xyz/zhouxy/plusone/validator/Validator.java b/src/main/java/xyz/zhouxy/plusone/validator/Validator.java index bbe12ad..f5a730a 100644 --- a/src/main/java/xyz/zhouxy/plusone/validator/Validator.java +++ b/src/main/java/xyz/zhouxy/plusone/validator/Validator.java @@ -47,7 +47,7 @@ public final class Validator extends BaseValidator { withRule(rule, exceptionCreator); return this; } - + public final Validator addRule(Predicate rule, Function exceptionCreator) { withRule(rule, exceptionCreator); return this; diff --git a/src/main/java/xyz/zhouxy/plusone/validator/map/EntryValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/map/EntryValidator.java new file mode 100644 index 0000000..fd42bb0 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/validator/map/EntryValidator.java @@ -0,0 +1,22 @@ +package xyz.zhouxy.plusone.validator.map; + +import java.util.Map; + +import xyz.zhouxy.plusone.validator.BasePropertyValidator; + +public class EntryValidator + extends BasePropertyValidator, V, EntryValidator> { + + public EntryValidator(K key) { + super(m -> { + @SuppressWarnings("unchecked") + V v = (V) m.get(key); + return v; + }); + } + + @Override + protected EntryValidator thisObject() { + return this; + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/validator/map/MapValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/map/MapValidator.java new file mode 100644 index 0000000..e7432dc --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/validator/map/MapValidator.java @@ -0,0 +1,83 @@ +package xyz.zhouxy.plusone.validator.map; + +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public abstract class MapValidator { + + private final List>> consumers = new LinkedList<>(); + + private final Set keys; + + protected MapValidator(K[] keys) { + this(Arrays.asList(keys)); + } + + protected MapValidator(Collection keys) { + this.keys = keys.stream().collect(Collectors.toSet()); + } + + public final Map validateAndCopy(Map obj) { + return validateAndCopyInternal(obj, this.keys); + } + + public final Map validateAndCopy(Map obj, Collection keys) { + return validateAndCopyInternal(obj, keys); + } + + @SafeVarargs + public final Map validateAndCopy(Map obj, K... keys) { + return validateAndCopyInternal(obj, Arrays.asList(keys)); + } + + private final Map validateAndCopyInternal(Map obj, Collection keys) { + validate(obj); + return obj.entrySet().stream() + .filter(kv -> keys.contains(kv.getKey())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + public final void validate(Map obj) { + this.consumers.forEach(consumer -> consumer.accept(obj)); + } + + @SuppressWarnings("unused") + protected final EntryValidator checkValue(K key, Class clazz) { + return checkValue(key); + } + + protected final EntryValidator checkValue(K key) { + EntryValidator validator = new EntryValidator<>(key); + this.consumers.add(validator::validate); + return validator; + } + + protected final void withRule(Predicate> rule, String errMsg) { + withRule(rule, map -> new IllegalArgumentException(errMsg)); + } + + protected final void withRule(Predicate> rule, String errMsgFormat, Object... args) { + withRule(rule, map -> new IllegalArgumentException(String.format(errMsgFormat, args))); + } + + protected final void withRule(Predicate> rule, Supplier e) { + withRule(rule, map -> e.get()); + } + + protected final void withRule(Predicate> rule, Function, E> e) { + this.consumers.add(map -> { + if (!rule.test(map)) { + throw e.apply(map); + } + }); + } +} diff --git a/src/test/java/xyz/zhouxy/plusone/validator/map/test/MapValidatorTests.java b/src/test/java/xyz/zhouxy/plusone/validator/map/test/MapValidatorTests.java new file mode 100644 index 0000000..f8da6dc --- /dev/null +++ b/src/test/java/xyz/zhouxy/plusone/validator/map/test/MapValidatorTests.java @@ -0,0 +1,86 @@ +package xyz.zhouxy.plusone.validator.map.test; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Test; + +import xyz.zhouxy.plusone.commons.collection.CollectionTools; +import xyz.zhouxy.plusone.commons.constant.PatternConsts; +import xyz.zhouxy.plusone.commons.function.PredicateTools; +import xyz.zhouxy.plusone.commons.util.RegexTools; +import xyz.zhouxy.plusone.validator.map.MapValidator; + +public // +class MapValidatorTests { + + private static final String USERNAME = "username"; + private static final String ACCOUNT = "account"; + private static final String PASSWORD = "password"; + private static final String PASSWORD2 = "password2"; + private static final String AGE = "age"; + private static final String BOOLEAN = "boolean"; + private static final String ROLE_LIST = "roleList"; + + private static final MapValidator validator = new MapValidator( + new String[] { USERNAME, ACCOUNT, PASSWORD, AGE, BOOLEAN, ROLE_LIST }) { + { + + checkValue(USERNAME, String.class).withRule( + PredicateTools.from(StringUtils::isNotBlank) + .and(username -> RegexTools.matches(username, PatternConsts.USERNAME)), + username -> new IllegalArgumentException(String.format("用户名【%s】不符合规范", username))); + + checkValue(ACCOUNT, String.class).withRule( + PredicateTools.from(StringUtils::isNotBlank) + .and(account -> RegexTools.matchesOne(account, + new Pattern[] { PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE })), + "请输入正确的邮箱地址或手机号"); + + checkValue(PASSWORD, String.class) + .withRule(StringUtils::isNotEmpty, "密码不能为空") + .withRule(pwd -> RegexTools.matches(pwd, PatternConsts.PASSWORD), "密码不符合规范"); + + // 校验到多个属性,只能针对 map 本身进行校验 + withRule(m -> Objects.equals(m.get(PASSWORD), m.get(PASSWORD2)), + "两次输入的密码不一样!"); + + // 通过泛型方式调用方法,指定数据类型 + this.checkValue(AGE) + .withRule(Objects::nonNull) + .withRule(age -> (18 <= age && 60 >= age)); + + checkValue(BOOLEAN, Boolean.class) + .withRule(Objects::nonNull, "Boolean property could not be null.") + .withRule(b -> b, "Boolean property must be true."); + + this.>checkValue(ROLE_LIST) + .withRule(CollectionTools::isNotEmpty, "角色列表不能为空!") + .withRule(l -> l.stream().allMatch(StringUtils::isNotBlank), + () -> new IllegalArgumentException("角色标识不能为空!")); + + } + }; + + @Test + void testValidateAndCopy() { + Map params = new HashMap<>(); + params.put("username", "ZhouXY"); + params.put("account", "zhouxy@code108.cn"); + params.put("password", "99Code108"); + params.put("password2", "99Code108"); + params.put("age", 18); + params.put("boolean", true); + params.put("roleList", Arrays.asList("admin", "")); + + Map validedParams = validator.validateAndCopy(params); + System.out.println(validedParams); + } +} diff --git a/src/test/java/xyz/zhouxy/plusone/validator/test/BaseValidatorTest.java b/src/test/java/xyz/zhouxy/plusone/validator/test/BaseValidatorTest.java index d9ffe81..fc4f5a0 100644 --- a/src/test/java/xyz/zhouxy/plusone/validator/test/BaseValidatorTest.java +++ b/src/test/java/xyz/zhouxy/plusone/validator/test/BaseValidatorTest.java @@ -4,129 +4,140 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; -import org.junit.Test; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Test; -import xyz.zhouxy.plusone.constant.RegexConsts; +import xyz.zhouxy.plusone.commons.constant.PatternConsts; +import xyz.zhouxy.plusone.commons.function.PredicateTools; +import xyz.zhouxy.plusone.commons.util.RegexTools; import xyz.zhouxy.plusone.validator.BaseValidator; import xyz.zhouxy.plusone.validator.ValidateUtil; -public class BaseValidatorTest { +class BaseValidatorTest { @Test - public void testValidate() { - RegisterCommand registerCommand = new RegisterCommand("zhouxy108", "luquanlion@outlook.com", "22336", "A1b2C3d4", + void testValidate() { + RegisterCommand registerCommand = new RegisterCommand("zhouxy108", "luquanlion@outlook.com", "22336", + "A1b2C3d4", "A1b2C3d4", Arrays.asList(new String[] { "admin", "editor" })); RegisterCommandValidator.INSTANCE.validate(registerCommand); ValidateUtil.validate(registerCommand, RegisterCommandValidator.INSTANCE); System.out.println(registerCommand); } -} -class RegisterCommandValidator extends BaseValidator { + static class RegisterCommandValidator extends BaseValidator { - static final RegisterCommandValidator INSTANCE = new RegisterCommandValidator(); + static final RegisterCommandValidator INSTANCE = new RegisterCommandValidator(); - private RegisterCommandValidator() { - ruleForString(RegisterCommand::getUsername) - .notNull("用户名不能为空") - .matches(RegexConsts.USERNAME, - username -> new IllegalArgumentException(String.format("用户名\"%s\"不符合规范", username))); - ruleForString(RegisterCommand::getAccount) - .notNull("请输入邮箱地址或手机号") - .matchesOr(new String[] { RegexConsts.EMAIL, RegexConsts.MOBILE_PHONE }, "请输入邮箱地址或手机号"); - ruleForString(RegisterCommand::getCode) - .notNull("验证码不能为空") - .matches(RegexConsts.CAPTCHA, "验证码不符合规范"); - ruleForString(RegisterCommand::getPassword) - .notEmpty("密码不能为空") - .matches(RegexConsts.PASSWORD, "密码不符合规范"); - ruleForCollection(RegisterCommand::getRoles) - .notEmpty(() -> new RuntimeException("角色列表不能为空")); + private RegisterCommandValidator() { + ruleForString(RegisterCommand::getUsername) + .isTrue(PredicateTools.from(Objects::nonNull) + .and(StringUtils::isNotEmpty) + .and(StringUtils::isNotBlank) + .and(username -> RegexTools.matches(username, PatternConsts.USERNAME)), + username -> new IllegalArgumentException(String.format("用户名【%s】不符合规范", username))); + ruleForString(RegisterCommand::getAccount) + .notNull("请输入邮箱地址或手机号") + .matchesOne(Arrays.asList(PatternConsts.EMAIL, PatternConsts.MOBILE_PHONE), "请输入邮箱地址或手机号"); + ruleForString(RegisterCommand::getCode) + .notNull("验证码不能为空") + .matches(PatternConsts.CAPTCHA, "验证码不符合规范"); + ruleForString(RegisterCommand::getPassword) + .notEmpty("密码不能为空") + .matches(PatternConsts.PASSWORD, "密码不符合规范"); + ruleForCollection(RegisterCommand::getRoles) + .notEmpty(() -> new RuntimeException("角色列表不能为空")); - withRule(registerCommand -> Objects.equals(registerCommand.getPassword(), registerCommand.getPassword2()), - "两次输入的密码不一致"); - } -} - -/** - * RegisterCommand - */ -class RegisterCommand { - - private String username; - private String account; - private String code; - private String password; - private String password2; - private List roles; - - public RegisterCommand() { - } - - public RegisterCommand(String username, String account, String code, String password, String password2, - List roles) { - this.username = username; - this.account = account; - this.code = code; - this.password = password; - this.password2 = password2; - this.roles = roles; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getAccount() { - return account; - } - - public void setAccount(String account) { - this.account = account; - } - - public String getCode() { - return code; - } - - public void setCode(String code) { - this.code = code; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getPassword2() { - return password2; - } - - public void setPassword2(String password2) { - this.password2 = password2; - } - - public List getRoles() { - return roles; - } - - public void setRoles(List roles) { - this.roles = roles; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("RegisterCommand [username=").append(username).append(", account=").append(account) - .append(", code=").append(code).append(", password=").append(password).append(", password2=") - .append(password2).append(", roles=").append(roles).append("]"); - return builder.toString(); + withRule(registerCommand -> Objects.equals(registerCommand.getPassword(), registerCommand.getPassword2()), + "两次输入的密码不一致"); + } + } + + /** + * RegisterCommand + */ + static class RegisterCommand { + + private String username; + private String account; + private String code; + private String password; + private String password2; + private List roles; + + public RegisterCommand() { + } + + public RegisterCommand(String username, String account, String code, String password, String password2, + List roles) { + this.username = username; + this.account = account; + this.code = code; + this.password = password; + this.password2 = password2; + this.roles = roles; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getPassword2() { + return password2; + } + + public void setPassword2(String password2) { + this.password2 = password2; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + @Override + public String toString() { + return new StringBuilder() + .append("RegisterCommand [") + .append("username=").append(username) + .append(", account=").append(account) + .append(", code=").append(code) + .append(", password=").append(password) + .append(", password2=").append(password2) + .append(", roles=").append(roles) + .append("]") + .toString(); + } } } diff --git a/src/test/java/xyz/zhouxy/plusone/validator/test/ValidatorTests.java b/src/test/java/xyz/zhouxy/plusone/validator/test/ValidatorTests.java new file mode 100644 index 0000000..b5cc7f4 --- /dev/null +++ b/src/test/java/xyz/zhouxy/plusone/validator/test/ValidatorTests.java @@ -0,0 +1,147 @@ +package xyz.zhouxy.plusone.validator.test; + +import static xyz.zhouxy.plusone.commons.constant.PatternConsts.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Test; + +import com.google.common.base.Preconditions; + +import xyz.zhouxy.plusone.commons.collection.CollectionTools; +import xyz.zhouxy.plusone.commons.function.PredicateTools; +import xyz.zhouxy.plusone.commons.util.RegexTools; +import xyz.zhouxy.plusone.validator.Validator; + +class ValidatorTests { + @Test + void testValidate() { + RegisterCommand registerCommand = new RegisterCommand( + "null", "luquanlion@outlook.com", "22336", + "A1b2C3d4", "A1b2C3d4", + Arrays.asList(new String[] { "admin", "editor" })); + + Validator registerCommandValidator = new Validator() + // 传入 predicate 和 Function + .addRule(command -> { + String username = command.getUsername(); + return Objects.nonNull(username) + && StringUtils.isNotEmpty(username) + && StringUtils.isNotBlank(username) + && RegexTools.matches(username, USERNAME); + }, command -> new IllegalArgumentException(String.format("用户名【%s】不符合规范", command.getUsername()))) + // 传入 predicate 和 error message + .addRule(command -> PredicateTools + .from(Objects::nonNull) + .and(account -> RegexTools.matchesOne(account, new Pattern[] { EMAIL, MOBILE_PHONE })) + .test(command.getAccount()), + "请输入邮箱地址或手机号") + // 传入 rule + .addRule(command -> { + String code = command.getCode(); + Preconditions.checkArgument(Objects.nonNull(code), "验证码不能为空"); + Preconditions.checkArgument(RegexTools.matches(code, CAPTCHA), "验证码不符合规范"); + }) + // 传入 rule + .addRule(command -> { + String password = command.getPassword(); + Preconditions.checkArgument(StringUtils.isNotEmpty(password), "密码不能为空"); + Preconditions.checkArgument(RegexTools.matches(password, PASSWORD), "密码不符合规范"); + }) + // 传入 predicate 和 Supplier + .addRule(command -> CollectionTools.isNotEmpty(command.getRoles()), + () -> new RuntimeException("角色列表不能为空")) + // 传入 predicate 和 error message + .addRule(command -> Objects.equals(command.getPassword(), command.getPassword2()), + "两次输入的密码不一致"); + registerCommandValidator.validate(registerCommand); + System.out.println(registerCommand); + } + + /** + * RegisterCommand + */ + static class RegisterCommand { + + private String username; + private String account; + private String code; + private String password; + private String password2; + private List roles; + + public RegisterCommand() { + } + + public RegisterCommand(String username, String account, String code, String password, String password2, + List roles) { + this.username = username; + this.account = account; + this.code = code; + this.password = password; + this.password2 = password2; + this.roles = roles; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getPassword2() { + return password2; + } + + public void setPassword2(String password2) { + this.password2 = password2; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("RegisterCommand [username=").append(username).append(", account=").append(account) + .append(", code=").append(code).append(", password=").append(password).append(", password2=") + .append(password2).append(", roles=").append(roles).append("]"); + return builder.toString(); + } + } +}