diff --git a/pom.xml b/pom.xml index 5c25ec3..4f88683 100644 --- a/pom.xml +++ b/pom.xml @@ -26,5 +26,10 @@ jackson-annotations 2.13.4 + + cn.hutool + hutool-core + 5.8.9 + \ No newline at end of file diff --git a/src/main/java/xyz/zhouxy/plusone/constant/RegexConsts.java b/src/main/java/xyz/zhouxy/plusone/constant/RegexConsts.java new file mode 100644 index 0000000..a34c436 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/constant/RegexConsts.java @@ -0,0 +1,27 @@ +package xyz.zhouxy.plusone.constant; + +/** + * 正则表达式常量 + * + * @author ZhouXY + */ +public final class RegexConsts { + + public static final String DATE = "^\\d{4}-\\d{2}-\\d{2}"; + + public static final String PASSWORD = "^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])[\\w\\\\!#$%&'*\\+\\-/=?^`{|}~@\\(\\)\\[\\]\",\\.;':><]{8,32}$"; + + public static final String CAPTCHA = "^[0-9A-Za-z]{4,6}$"; + + public static final String EMAIL = "^\\w+([-+.]\\w+)*@[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})*(\\.(?![0-9]+$)[a-zA-Z0-9][-0-9A-Za-z]{0,62})$"; + + public static final String MOBILE_PHONE = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$"; + + public static final String USERNAME = "^[\\da-zA-Z_.@\\\\]{4,36}$"; + + public static final String NICKNAME = "^[\\da-zA-Z_.@\\\\]{4,36}$"; + + private RegexConsts() { + throw new IllegalStateException("Utility class"); + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/util/RegexUtil.java b/src/main/java/xyz/zhouxy/plusone/util/RegexUtil.java new file mode 100644 index 0000000..023160c --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/util/RegexUtil.java @@ -0,0 +1,36 @@ +package xyz.zhouxy.plusone.util; + +import java.util.regex.Pattern; + +public class RegexUtil { + + private RegexUtil() { + throw new IllegalStateException("Utility class"); + } + + public static boolean matches(CharSequence input, String regex) { + return Pattern.matches(regex, input); + } + + public static boolean matchesOr(CharSequence input, String... regexs) { + boolean isMatched; + for (String regex : regexs) { + isMatched = Pattern.matches(regex, input); + if (isMatched) { + return true; + } + } + return false; + } + + public static boolean matchesAnd(CharSequence input, String... regexs) { + boolean isMatched; + for (String regex : regexs) { + isMatched = Pattern.matches(regex, input); + if (!isMatched) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/validator/BaseValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/BaseValidator.java new file mode 100644 index 0000000..08f13d0 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/validator/BaseValidator.java @@ -0,0 +1,86 @@ +package xyz.zhouxy.plusone.validator; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/** + * 校验器 + * + *

+ * 可以使用以下方式初始化一个校验器: + *

+ * + *
+ * BaseValidator<Integer> validator = new BaseValidator<>() {
+ *     {
+ *         withRule(value -> Objects.nonNull(value), "value 不能为空");
+ *         withRule(value -> (value >= 0 && value <= 500), "value 应在 [0, 500] 内");
+ *     }
+ * };
+ * 
+ * + *

+ * 也可以通过继承本类,定义一个校验器(可使用单例模式)。 + *

+ * + *

+ * 然后通过校验器的 {@link #validate} 方法,或 + * {@link ValidateUtil#validate(Object, Validator)} 对指定对象进行校验。 + *

+ * + *
+ * ValidateUtil.validate(255, validator);
+ * 
+ * + *
+ * validator.validate(666);
+ * 
+ *

+ * + * @author ZhouXY + * @see IValidateRequired + * @see ValidateUtil + * @see Validator + */ +public abstract class BaseValidator { + + private final List> rules = new ArrayList<>(); + + protected BaseValidator() { + } + + protected final void withRule(Predicate rule, String errorMessage) { + withRule(rule, () -> new InvalidInputException(errorMessage)); + } + + protected final void withRule(Predicate rule, Supplier exceptionCreator) { + withRule(rule, value -> exceptionCreator.get()); + } + + protected final void withRule(Predicate rule, Function exceptionCreator) { + this.rules.add(new RuleInfo<>(rule, exceptionCreator)); + } + + public void validate(T obj) { + this.rules.forEach(ruleInfo -> ruleInfo.validate(obj)); + } + + protected static class RuleInfo { + private final Predicate rule; + private final Function exceptionCreator; + + private RuleInfo(Predicate rule, Function exceptionCreator) { + this.rule = rule; + this.exceptionCreator = exceptionCreator; + } + + private void validate(T obj) { + if (!rule.test(obj)) { + throw exceptionCreator.apply(obj); + } + } + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/validator/InvalidInputException.java b/src/main/java/xyz/zhouxy/plusone/validator/InvalidInputException.java new file mode 100644 index 0000000..5a23431 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/validator/InvalidInputException.java @@ -0,0 +1,53 @@ +package xyz.zhouxy.plusone.validator; + +import xyz.zhouxy.plusone.exception.PlusoneException; + +/** + * 4040200 - 无效的用户输入 + * + * @author ZhouXY + */ +public class InvalidInputException extends PlusoneException { + + 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/ValidateUtil.java b/src/main/java/xyz/zhouxy/plusone/validator/ValidateUtil.java new file mode 100644 index 0000000..438b197 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/validator/ValidateUtil.java @@ -0,0 +1,23 @@ +package xyz.zhouxy.plusone.validator; + +/** + * 校验工具类 + *

+ * 对 {@link IValidateRequired} 的实现类对象进行校验 + *

+ * + * @author ZhouXY + * + * @see BaseValidator + * @see Validator + * @see IValidateRequired + */ +public class ValidateUtil { + private ValidateUtil() { + throw new IllegalStateException("Utility class"); + } + + public static void validate(T obj, BaseValidator validator) { + validator.validate(obj); + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/validator/Validator.java b/src/main/java/xyz/zhouxy/plusone/validator/Validator.java new file mode 100644 index 0000000..b56ff50 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/validator/Validator.java @@ -0,0 +1,42 @@ +package xyz.zhouxy.plusone.validator; + +import java.util.function.Predicate; + +/** + * 校验器 + * + *

+ * 可以使用以下方式初始化一个校验器: + *

+ * + *
+ * var validator = new Validator<Integer>()
+ *         .addRule(value -> Objects.nonNull(value), "value 不能为空")
+ *         .addRule(value -> (value >= 0 && value <= 500), "value 应在 [0, 500] 内");
+ * 
+ * + *

+ * 然后通过校验器的 {@link #validate} 方法,或 + * {@link ValidateUtil#validate(Object, Validator)} 对指定对象进行校验。 + *

+ * + *
+ * validator.validate(666);
+ * 
+ * + *
+ * ValidateUtil.validate(255, validator);
+ * 
+ *

+ * + * @author ZhouXY + * @see IValidateRequired + * @see ValidateUtil + * @see BaseValidator + */ +public final class Validator extends BaseValidator { + public final Validator addRule(final Predicate rule, final String errorMessage) { + withRule(rule, errorMessage); + return this; + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/validator/validator2/BaseValidator2.java b/src/main/java/xyz/zhouxy/plusone/validator/validator2/BaseValidator2.java new file mode 100644 index 0000000..257af16 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/validator/validator2/BaseValidator2.java @@ -0,0 +1,22 @@ +package xyz.zhouxy.plusone.validator.validator2; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +public abstract class BaseValidator2 { + + private List> valueValidators = new ArrayList<>(); + + protected final ValueValidator ruleFor(Function getter) { + ValueValidator validValueHolder = new ValueValidator<>(getter); + valueValidators.add(validValueHolder); + return validValueHolder; + } + + public void validate(T obj) { + for (ValueValidator valueValidator : this.valueValidators) { + valueValidator.validateProperty(obj); + } + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/validator/validator2/ValueValidator.java b/src/main/java/xyz/zhouxy/plusone/validator/validator2/ValueValidator.java new file mode 100644 index 0000000..e80b136 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/validator/validator2/ValueValidator.java @@ -0,0 +1,379 @@ +package xyz.zhouxy.plusone.validator.validator2; + +import java.util.ArrayList; +import java.util.Collection; +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; + +import cn.hutool.core.util.StrUtil; +import xyz.zhouxy.plusone.constant.RegexConsts; +import xyz.zhouxy.plusone.util.RegexUtil; +import xyz.zhouxy.plusone.validator.InvalidInputException; + +public class ValueValidator { + Function getter; + List> rules = new ArrayList<>(); + + public ValueValidator(Function getter) { + this.getter = getter; + } + + private void withRule(Predicate condition, + Function exceptionCreator) { + withRule(value -> { + if (!condition.test(value)) { + throw exceptionCreator.apply(value); + } + }); + } + + private void withRule(Consumer rule) { + this.rules.add(rule); + } + + // ==================== + // ====== Object ====== + // ==================== + + // ====== notNull ===== + + public ValueValidator notNull() { + return notNull("Value could not be null."); + } + + public ValueValidator notNull(String errMsg) { + return notNull(convertExceptionCreator(errMsg)); + } + + public ValueValidator notNull(Supplier exceptionCreator) { + return notNull(convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator notNull(Function exceptionCreator) { + withRule(Objects::nonNull, exceptionCreator); + return this; + } + + // ====== isNull ===== + + public ValueValidator isNull(String errMsg) { + return isNull(convertExceptionCreator(errMsg)); + } + + public ValueValidator isNull(Supplier exceptionCreator) { + return isNull(convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator isNull(Function exceptionCreator) { + withRule(Objects::isNull, exceptionCreator); + return this; + } + + // ===== equals ===== + + public ValueValidator equalsThat(Object that) { + return equalsThat(that, value -> new InvalidInputException(String.format("(%s) 必须与 (%s) 相等", value, that))); + } + + public ValueValidator equalsThat(Object that, String errMsg) { + return equalsThat(that, convertExceptionCreator(errMsg)); + } + + public ValueValidator equalsThat( + Object that, Supplier exceptionCreator) { + return equalsThat(that, convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator equalsThat( + Object that, Function exceptionCreator) { + withRule(value -> Objects.equals(value, that), exceptionCreator); + return this; + } + + // ===== state ===== + + public ValueValidator state(Predicate condition) { + return state(condition, "无效的用户输入"); + } + + public ValueValidator state(Predicate condition, String errMsg) { + return state(condition, convertExceptionCreator(errMsg)); + } + + public ValueValidator state( + Predicate condition, + Supplier exceptionCreator) { + return state(condition, convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator state( + Predicate condition, + Function exceptionCreator) { + withRule(condition, exceptionCreator); + return this; + } + + // ================= + // ====== int ====== + // ================= + + public ValueValidator between(int min, int max) { + return between(min, max, String.format("数值不在 %s 和 %s 之间", String.valueOf(min), String.valueOf(max))); + } + + public ValueValidator between(int min, int max, String errMsg) { + return between(min, max, convertExceptionCreator(errMsg)); + } + + public ValueValidator between(int min, int max, + Supplier exceptionCreator) { + return between(min, max, convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator between(int min, int max, + Function exceptionCreator) { + withRule(value -> ((int) value >= min && (int) value < max), exceptionCreator); + return this; + } + + // ==================== + // ====== double ====== + // ==================== + + public ValueValidator between(double min, double max) { + return between(min, max, String.format("数值不在 %s 和 %s 之间", String.valueOf(min), String.valueOf(max))); + } + + public ValueValidator between(double min, double max, String errMsg) { + return between(min, max, convertExceptionCreator(errMsg)); + } + + public ValueValidator between(double min, double max, + Supplier exceptionCreator) { + return between(min, max, convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator between(double min, double max, + Function exceptionCreator) { + withRule(value -> ((double) value >= min && (double) value < max), exceptionCreator); + return this; + } + + // ================================ + // ====== Collection, String ====== + // ================================ + + // ====== notEmpty ===== + + public ValueValidator notEmpty(String errMsg) { + return notEmpty(convertExceptionCreator(errMsg)); + } + + public ValueValidator notEmpty(Supplier exceptionCreator) { + return notEmpty(convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator notEmpty(Function exceptionCreator) { + withRule(value -> { + if (value == null) { + return false; + } + if (value instanceof Collection) { + return !((Collection) value).isEmpty(); + } + if (value instanceof String) { + return !((String) value).isEmpty(); + } + return false; + }, exceptionCreator); + return this; + } + + // ====== isEmpty ===== + + public ValueValidator isEmpty(String errMsg) { + return isEmpty(convertExceptionCreator(errMsg)); + } + + public ValueValidator isEmpty(Supplier exceptionCreator) { + return isEmpty(convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator isEmpty(Function exceptionCreator) { + withRule(value -> { + if (value == null) { + return false; + } + if (value instanceof Collection) { + return ((Collection) value).isEmpty(); + } + if (value instanceof String) { + return ((String) value).isEmpty(); + } + return false; + }, exceptionCreator); + return this; + } + + // ===================== + // ====== boolean ====== + // ===================== + + // ====== isTrue ====== + + public ValueValidator isTrue() { + return isTrue("The value must be true."); + } + + public ValueValidator isTrue(String errMsg) { + return isTrue(convertExceptionCreator(errMsg)); + } + + public ValueValidator isTrue(Supplier exceptionCreator) { + return isTrue(convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator isTrue(Function exceptionCreator) { + withRule(Boolean.TRUE::equals, exceptionCreator); + return this; + } + + // ====== isFalse ====== + + public ValueValidator isFalse() { + return isFalse("The value must be false."); + } + + public ValueValidator isFalse(String errMsg) { + return isFalse(convertExceptionCreator(errMsg)); + } + + public ValueValidator isFalse(Supplier exceptionCreator) { + return isFalse(convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator isFalse(Function exceptionCreator) { + withRule(Boolean.FALSE::equals, exceptionCreator); + return this; + } + + // ==================== + // ====== String ====== + // ==================== + + // ===== matches ===== + + public ValueValidator matches(String regex, String errMsg) { + return matches(regex, convertExceptionCreator(errMsg)); + } + + public ValueValidator matches( + String regex, + Supplier exceptionCreator) { + return matches(regex, convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator matches( + String regex, + Function exceptionCreator) { + withRule(input -> RegexUtil.matches((String) input, regex), exceptionCreator); + return this; + } + + // ===== matchesOr ===== + + public ValueValidator matchesOr(String[] regexs, String errMsg) { + return matchesOr(regexs, convertExceptionCreator(errMsg)); + } + + public ValueValidator matchesOr( + String[] regexs, + Supplier exceptionCreator) { + return matchesOr(regexs, convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator matchesOr( + String[] regexs, + Function exceptionCreator) { + withRule(input -> RegexUtil.matchesOr((String) input, regexs), exceptionCreator); + return this; + } + + // ===== matchesAnd ===== + + public ValueValidator matchesAnd(String[] regexs, String errMsg) { + return matchesAnd(regexs, convertExceptionCreator(errMsg)); + } + + public ValueValidator matchesAnd( + String[] regexs, + Supplier exceptionCreator) { + return matchesAnd(regexs, convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator matchesAnd( + String[] regexs, + Function exceptionCreator) { + withRule(input -> RegexUtil.matchesAnd((String) input, regexs), exceptionCreator); + return this; + } + + // ===== notBlank ===== + + public ValueValidator notBlank() { + return notBlank("This String argument must have text; it must not be null, empty, or blank"); + } + + public ValueValidator notBlank(String errMsg) { + return notBlank(convertExceptionCreator(errMsg)); + } + + public ValueValidator notBlank(Supplier exceptionCreator) { + return notBlank(convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator notBlank(Function exceptionCreator) { + withRule(input -> StrUtil.isNotBlank((String) input), exceptionCreator); + return this; + } + + // ===== email ===== + + public ValueValidator email() { + return email("The value is not an email address."); + } + + public ValueValidator email(String errMsg) { + return email(convertExceptionCreator(errMsg)); + } + + public ValueValidator email(Supplier exceptionCreator) { + return email(convertExceptionCreator(exceptionCreator)); + } + + public ValueValidator email(Function exceptionCreator) { + return matches(RegexConsts.EMAIL, exceptionCreator); + } + + // ======================================================================== + + void validateProperty(DTO obj) { + PROPERTY value = this.getter.apply(obj); + for (Consumer rule : this.rules) { + rule.accept(value); + } + } + + private static Function convertExceptionCreator(String errMsg) { + return convertExceptionCreator(errMsg); + } + + private static Function convertExceptionCreator( + Supplier exceptionSupplier) { + return value -> exceptionSupplier.get(); + } +}