|
@@ -36,41 +36,39 @@ import static java.util.regex.Pattern.CASE_INSENSITIVE;
|
|
|
* @since 2020/2/15
|
|
|
*/
|
|
|
@Slf4j
|
|
|
-public class Verification {
|
|
|
- private final Class<? extends Annotation> verifyMethodAnnotation = Verifier.class;
|
|
|
- private final Class<? extends Annotation> verifyParamAnnotation = Ver.class;
|
|
|
+public class VerifyProcessor {
|
|
|
/**
|
|
|
* the name of the verify method.
|
|
|
*/
|
|
|
- private final String verifyMethod = "verify";
|
|
|
+ private final String verifyMethod = "verify";
|
|
|
/**
|
|
|
* the list of validation annotation & method can be work in {@code javax.validation.constraints.*}
|
|
|
*/
|
|
|
- private Map<Class<?>, Method> verifyMethodMap = new ConcurrentHashMap<>();
|
|
|
+ private final Map<Class<?>, Method> verifyMethodMap = new ConcurrentHashMap<>();
|
|
|
/**
|
|
|
* whether write log to file. it's {@code true} by default.
|
|
|
*/
|
|
|
- private boolean logWrite = true;
|
|
|
+ private boolean logWrite = true;
|
|
|
/**
|
|
|
* the message to complete verify.
|
|
|
*/
|
|
|
- private ThreadLocal<String> message = new ThreadLocal<>();
|
|
|
+ private final ThreadLocal<String> message = new ThreadLocal<>();
|
|
|
/**
|
|
|
* the type of message ,1: verify ,2: illegal argument
|
|
|
*/
|
|
|
- private ThreadLocal<Integer> code = new ThreadLocal<>();
|
|
|
+ private final ThreadLocal<Integer> code = new ThreadLocal<>();
|
|
|
/**
|
|
|
* the max length for a valid email address local part.
|
|
|
*/
|
|
|
- private final int MAX_LOCAL_PART_LENGTH = 64;
|
|
|
+ private final int MAX_LOCAL_PART_LENGTH = 64;
|
|
|
/**
|
|
|
* the regular expression for local part of a valid email address.
|
|
|
*/
|
|
|
- private final String LOCAL_PART_ATOM = "[a-z0-9!#$%&'*+/=?^_`{|}~\u0080-\uFFFF-]";
|
|
|
+ private final String LOCAL_PART_ATOM = "[a-z0-9!#$%&'*+/=?^_`{|}~\u0080-\uFFFF-]";
|
|
|
/**
|
|
|
* the regular expression for the local part of an email address.
|
|
|
*/
|
|
|
- private final String LOCAL_PART_INSIDE_QUOTES_ATOM = "([a-z0-9!#$%&'*.(),<>\\[\\]:; @+/=?^_`{|}~\u0080-\uFFFF-]|\\\\\\\\|\\\\\\\")";
|
|
|
+ private final String LOCAL_PART_INSIDE_QUOTES_ATOM = "([a-z0-9!#$%&'*.(),<>\\[\\]:; @+/=?^_`{|}~\u0080-\uFFFF-]|\\\\\\\\|\\\\\\\")";
|
|
|
/**
|
|
|
* Regular expression for the local part of an email address (everything before '@')
|
|
|
*/
|
|
@@ -133,17 +131,16 @@ public class Verification {
|
|
|
/**
|
|
|
* to private the constrctor.
|
|
|
*/
|
|
|
- private Verification() {
|
|
|
+ private VerifyProcessor() {
|
|
|
collectVerifyAnnotation();
|
|
|
}
|
|
|
-
|
|
|
/**
|
|
|
* collect all vaild validation annotation from the methods.
|
|
|
*/
|
|
|
private void collectVerifyAnnotation() {
|
|
|
Method[] declaredMethods = this.getClass().getDeclaredMethods();
|
|
|
List<Method> verifyMethods = Arrays.stream(declaredMethods).filter(method -> verifyMethod.equals(method.getName())).collect(Collectors.toList());
|
|
|
- isTrue(verifyMethods.isEmpty(), "There is no method named 'verify' in Verification.");
|
|
|
+ isTrue(verifyMethods.isEmpty(), "There is no method named 'verify' in VerifyProcessor.");
|
|
|
for (Method method : verifyMethods) {
|
|
|
Optional<Class<?>> classOptional = Arrays.stream(method.getParameterTypes()).filter(type -> Annotation.class.isAssignableFrom(type)).findFirst();
|
|
|
classOptional.ifPresent(verifyClass -> {
|
|
@@ -154,22 +151,27 @@ public class Verification {
|
|
|
log.info("@interface: {}", verifyClass.getCanonicalName());
|
|
|
});
|
|
|
}
|
|
|
- isTrue(verifyMethodMap.isEmpty(), "No valid validation annotation was resolved in Verification.");
|
|
|
+ isTrue(verifyMethodMap.isEmpty(), "No valid validation annotation was resolved in VerifyProcessor.");
|
|
|
}
|
|
|
/**
|
|
|
* the static inner class to builder a singleton instance.
|
|
|
*/
|
|
|
private static class Builder {
|
|
|
- private static Verification INSTANCE = new Verification();
|
|
|
+ private static VerifyProcessor INSTANCE = new VerifyProcessor();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* get the singleton instance of this
|
|
|
*/
|
|
|
- public static Verification getInstance() {
|
|
|
+ private static VerifyProcessor getInstance() {
|
|
|
return Builder.INSTANCE;
|
|
|
}
|
|
|
-
|
|
|
+ public static void perform(final boolean logWrite, final Object object, final String... fields) {
|
|
|
+ Builder.INSTANCE.logWrite(logWrite).action(object, fields);
|
|
|
+ }
|
|
|
+ public static void perform(final Object object, final String... fields) {
|
|
|
+ Builder.INSTANCE.action(object, fields);
|
|
|
+ }
|
|
|
/**
|
|
|
* 因为单例模式虽然能保证线程安全,但在序列化和反序列化的情况下会出现生成多个对象的情况.
|
|
|
* readResolve()方法,其实当JVM从内存中反序列化地"组装"一个新对象时,
|
|
@@ -185,7 +187,7 @@ public class Verification {
|
|
|
* @param logWrite {@code true} to write log, {@code false} to no.
|
|
|
* @return this instance.
|
|
|
*/
|
|
|
- public Verification logWrite(boolean logWrite) {
|
|
|
+ public VerifyProcessor logWrite(boolean logWrite) {
|
|
|
this.logWrite = logWrite;
|
|
|
return this;
|
|
|
}
|
|
@@ -204,7 +206,7 @@ public class Verification {
|
|
|
this.message.set(format(message, values));
|
|
|
}
|
|
|
/**
|
|
|
- * get the systemDefaultZone {@link Clock.systemDefaultZone()}. for the date compare.
|
|
|
+ * get the systemDefaultZone {@link Clock#systemDefaultZone()}. for the date compare.
|
|
|
*/
|
|
|
private Clock systemDefaultClock() {
|
|
|
return Clock.offset(Clock.systemDefaultZone(), Duration.ZERO.abs().negated());
|
|
@@ -658,7 +660,7 @@ public class Verification {
|
|
|
return false;
|
|
|
}
|
|
|
if (!isBoolean(value)) {
|
|
|
- setIllegalArg("The @AssertTrue is only for Boolean.");
|
|
|
+ setIllegalArg("The @AssertTrue only supports Boolean.");
|
|
|
return false;
|
|
|
}
|
|
|
return ((Boolean) value);
|
|
@@ -680,7 +682,7 @@ public class Verification {
|
|
|
return false;
|
|
|
}
|
|
|
if (!isBoolean(value)) {
|
|
|
- setIllegalArg("The @AssertFalse is only for Boolean.");
|
|
|
+ setIllegalArg("The @AssertFalse only supports Boolean.");
|
|
|
return false;
|
|
|
}
|
|
|
return !((Boolean) value);
|
|
@@ -704,10 +706,10 @@ public class Verification {
|
|
|
final boolean isNumber = isNumber(value);
|
|
|
final boolean isCharSequence = isCharSequence(value);
|
|
|
if (!isNumber && !isCharSequence) {
|
|
|
- setIllegalArg("The @DecimalMax is only for Number & CharSequence.");
|
|
|
+ setIllegalArg("The @DecimalMax only supports Number & CharSequence.");
|
|
|
return false;
|
|
|
}
|
|
|
- String maxValue = annotation.value();
|
|
|
+ final String maxValue = annotation.value();
|
|
|
if (isNull(maxValue)) {
|
|
|
setIllegalArg("The value of @DecimalMax is null, a invalid BigDecimal format.");
|
|
|
return false;
|
|
@@ -753,10 +755,10 @@ public class Verification {
|
|
|
final boolean isNumber = isNumber(value);
|
|
|
final boolean isCharSequence = isCharSequence(value);
|
|
|
if (!isNumber && !isCharSequence) {
|
|
|
- setIllegalArg("The @DecimalMin is only for Number & CharSequence.");
|
|
|
+ setIllegalArg("The @DecimalMin only supports Number & CharSequence.");
|
|
|
return false;
|
|
|
}
|
|
|
- String minValue = annotation.value();
|
|
|
+ final String minValue = annotation.value();
|
|
|
if (isNull(minValue)) {
|
|
|
setIllegalArg("The value of @DecimalMin is null, a invalid BigDecimal format.");
|
|
|
return false;
|
|
@@ -836,7 +838,7 @@ public class Verification {
|
|
|
}
|
|
|
int length = length(value);
|
|
|
if(-1 == length){
|
|
|
- setIllegalArg("The @NotEmpty is only for CharSequence & Collection & Map & Array & Iterator & Enumeration.");
|
|
|
+ setIllegalArg("The @NotEmpty only supports CharSequence & Collection & Map & Array & Iterator & Enumeration.");
|
|
|
return false;
|
|
|
}
|
|
|
// length > 0
|
|
@@ -898,8 +900,8 @@ public class Verification {
|
|
|
if (isNull(annotation)) {
|
|
|
return true;
|
|
|
}
|
|
|
- int min = annotation.min();
|
|
|
- int max = annotation.max();
|
|
|
+ final int min = annotation.min();
|
|
|
+ final int max = annotation.max();
|
|
|
String message = annotation.message();
|
|
|
if(null != message && !message.trim().isEmpty()){
|
|
|
if(message.contains("{min}")){
|
|
@@ -927,7 +929,7 @@ public class Verification {
|
|
|
}
|
|
|
int length = length(value);
|
|
|
if(-1 == length){
|
|
|
- setIllegalArg("The @Size is only for CharSequence & Collection & Map & Array & Iterator & Enumeration.");
|
|
|
+ setIllegalArg("The @Size only supports CharSequence & Collection & Map & Array & Iterator & Enumeration.");
|
|
|
return false;
|
|
|
}
|
|
|
//size >= min && size <= max
|
|
@@ -945,8 +947,8 @@ public class Verification {
|
|
|
if (isNull(annotation)) {
|
|
|
return true;
|
|
|
}
|
|
|
- int maxInteger = annotation.integer();
|
|
|
- int maxFraction = annotation.fraction();
|
|
|
+ final int maxInteger = annotation.integer();
|
|
|
+ final int maxFraction = annotation.fraction();
|
|
|
String message = annotation.message();
|
|
|
if(null != message && !message.trim().isEmpty()){
|
|
|
if(message.contains("{integer}")){
|
|
@@ -963,7 +965,7 @@ public class Verification {
|
|
|
final boolean isNumber = isNumber(value);
|
|
|
final boolean isCharSequence = isCharSequence(value);
|
|
|
if (!isNumber && !isCharSequence) {
|
|
|
- setIllegalArg("The @Digits is only for Number & CharSequence.");
|
|
|
+ setIllegalArg("The @Digits only supports Number & CharSequence.");
|
|
|
return false;
|
|
|
}
|
|
|
if (lt0(maxInteger)) {
|
|
@@ -1013,7 +1015,7 @@ public class Verification {
|
|
|
return false;
|
|
|
}
|
|
|
if (!isCharSequence(value)) {
|
|
|
- setIllegalArg("The @NotBlank is only for CharSequence.");
|
|
|
+ setIllegalArg("The @NotBlank only supports CharSequence.");
|
|
|
return false;
|
|
|
}
|
|
|
return ((CharSequence) value).toString().trim().isEmpty();
|
|
@@ -1035,11 +1037,11 @@ public class Verification {
|
|
|
return false;
|
|
|
}
|
|
|
if (!isCharSequence(value)) {
|
|
|
- setIllegalArg("The @Email is only for CharSequence.");
|
|
|
+ setIllegalArg("The @Email only supports CharSequence.");
|
|
|
return false;
|
|
|
}
|
|
|
//@Email need trim
|
|
|
- String val = ((CharSequence) value).toString().trim();
|
|
|
+ final String val = ((CharSequence) value).toString().trim();
|
|
|
if (val.isEmpty()) {
|
|
|
return false;
|
|
|
}
|
|
@@ -1062,8 +1064,8 @@ public class Verification {
|
|
|
if (!isValidEmailDomainAddress(domainPart)) {
|
|
|
return false;
|
|
|
}
|
|
|
- Pattern.Flag[] flags = annotation.flags();
|
|
|
- String regexp = annotation.regexp();
|
|
|
+ final Pattern.Flag[] flags = annotation.flags();
|
|
|
+ final String regexp = annotation.regexp();
|
|
|
int intFlag = 0;
|
|
|
for (Pattern.Flag flag : flags) {
|
|
|
intFlag = intFlag | flag.getValue();
|
|
@@ -1074,7 +1076,7 @@ public class Verification {
|
|
|
try {
|
|
|
pattern = java.util.regex.Pattern.compile(regexp, intFlag);
|
|
|
} catch (PatternSyntaxException e) {
|
|
|
- setIllegalArg("The regexp for @Email is Invalid regular expression. Exception: %s", e.getMessage());
|
|
|
+ setIllegalArg("Failed to Compile the regexp and flag for @Email. Exception: %s", e.getMessage());
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
@@ -1101,17 +1103,17 @@ public class Verification {
|
|
|
return false;
|
|
|
}
|
|
|
if (!isCharSequence(value)) {
|
|
|
- setIllegalArg("The @Pattern is only for CharSequence.");
|
|
|
+ setIllegalArg("The @Pattern only supports CharSequence.");
|
|
|
return false;
|
|
|
}
|
|
|
//@Pattern no trim
|
|
|
- String val = ((CharSequence) value).toString();
|
|
|
+ final String val = ((CharSequence) value).toString();
|
|
|
if (val.isEmpty()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- Pattern.Flag[] flags = annotation.flags();
|
|
|
- String regexp = annotation.regexp();
|
|
|
+ final Pattern.Flag[] flags = annotation.flags();
|
|
|
+ final String regexp = annotation.regexp();
|
|
|
int intFlag = 0;
|
|
|
for (Pattern.Flag flag : flags) {
|
|
|
intFlag = intFlag | flag.getValue();
|
|
@@ -1148,10 +1150,10 @@ public class Verification {
|
|
|
final boolean isNumber = isNumber(value);
|
|
|
final boolean isCharSequence = isCharSequence(value);
|
|
|
if (!isCharSequence && !isNumber) {
|
|
|
- setIllegalArg("The @Max is only for Number & CharSequence.");
|
|
|
+ setIllegalArg("The @Max only supports Number & CharSequence.");
|
|
|
return false;
|
|
|
}
|
|
|
- long max = annotation.value();
|
|
|
+ final long max = annotation.value();
|
|
|
int compare;
|
|
|
if (isNumber) {
|
|
|
compare = numberComparator((Number) value, max, GREATER_THAN);
|
|
@@ -1188,10 +1190,10 @@ public class Verification {
|
|
|
final boolean isNumber = isNumber(value);
|
|
|
final boolean isCharSequence = isCharSequence(value);
|
|
|
if (!isCharSequence && !isNumber) {
|
|
|
- setIllegalArg("The @Min is only for Number & CharSequence.");
|
|
|
+ setIllegalArg("The @Min only supports Number & CharSequence.");
|
|
|
return false;
|
|
|
}
|
|
|
- long min = annotation.value();
|
|
|
+ final long min = annotation.value();
|
|
|
int compare;
|
|
|
if (isNumber) {
|
|
|
compare = numberComparator((Number) value, min, LESS_THAN);
|
|
@@ -1307,7 +1309,7 @@ public class Verification {
|
|
|
return false;
|
|
|
}
|
|
|
if (!isNumber(value)) {
|
|
|
- setIllegalArg("The @Negative is only for Number.");
|
|
|
+ setIllegalArg("The @Negative only supports Number.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -1328,7 +1330,7 @@ public class Verification {
|
|
|
return false;
|
|
|
}
|
|
|
if (!isNumber(value)) {
|
|
|
- setIllegalArg("The @NegativeOrZero is only for Number.");
|
|
|
+ setIllegalArg("The @NegativeOrZero only supports Number.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -1349,7 +1351,7 @@ public class Verification {
|
|
|
return false;
|
|
|
}
|
|
|
if (!isNumber(value)) {
|
|
|
- setIllegalArg("The @Positive is only for Number.");
|
|
|
+ setIllegalArg("The @Positive only supports Number.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -1370,7 +1372,7 @@ public class Verification {
|
|
|
return false;
|
|
|
}
|
|
|
if (!isNumber(value)) {
|
|
|
- setIllegalArg("The @PositiveOrZero is only for Number.");
|
|
|
+ setIllegalArg("The @PositiveOrZero only supports Number.");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -1418,7 +1420,7 @@ public class Verification {
|
|
|
} else if (value instanceof ZonedDateTime) {
|
|
|
compare = ((ZonedDateTime) value).compareTo(ZonedDateTime.now(clock));
|
|
|
}else{
|
|
|
- setIllegalArg("The %s is not a supported date class temporarily.", value.getClass().getCanonicalName());
|
|
|
+ setIllegalArg("The '%s' is not a supported TemporalAccessor class temporarily.", value.getClass().getCanonicalName());
|
|
|
compare = Integer.MAX_VALUE;
|
|
|
}
|
|
|
} else if (isDate) {
|
|
@@ -1450,7 +1452,7 @@ public class Verification {
|
|
|
final boolean isDate = isDate(value);
|
|
|
final boolean isCalendar = isCalendar(value);
|
|
|
if (!isTemporalAccessor && !isDate && !isCalendar) {
|
|
|
- setIllegalArg("The @Future is only for TemporalAccessor & Calendar & Date.");
|
|
|
+ setIllegalArg("The @Future only supports TemporalAccessor & Calendar & Date.");
|
|
|
return false;
|
|
|
}
|
|
|
int compare = dateComparator(value, isTemporalAccessor, isDate);
|
|
@@ -1480,7 +1482,7 @@ public class Verification {
|
|
|
final boolean isDate = isDate(value);
|
|
|
final boolean isCalendar = isCalendar(value);
|
|
|
if (!isTemporalAccessor && !isDate && !isCalendar) {
|
|
|
- setIllegalArg("The @FutureOrPresent is only for TemporalAccessor & Calendar & Date.");
|
|
|
+ setIllegalArg("The @FutureOrPresent only supports TemporalAccessor & Calendar & Date.");
|
|
|
return false;
|
|
|
}
|
|
|
int compare = dateComparator(value, isTemporalAccessor, isDate);
|
|
@@ -1510,7 +1512,7 @@ public class Verification {
|
|
|
final boolean isDate = isDate(value);
|
|
|
final boolean isCalendar = isCalendar(value);
|
|
|
if (!isTemporalAccessor && !isDate && !isCalendar) {
|
|
|
- setIllegalArg("The @Past is only for TemporalAccessor & Calendar & Date.");
|
|
|
+ setIllegalArg("The @Past only supports TemporalAccessor & Calendar & Date.");
|
|
|
return false;
|
|
|
}
|
|
|
int compare = dateComparator(value, isTemporalAccessor, isDate);
|
|
@@ -1540,7 +1542,7 @@ public class Verification {
|
|
|
final boolean isDate = isDate(value);
|
|
|
final boolean isCalendar = isCalendar(value);
|
|
|
if (!isTemporalAccessor && !isDate && !isCalendar) {
|
|
|
- setIllegalArg("The @PastOrPresent is only for TemporalAccessor & Calendar & Date.");
|
|
|
+ setIllegalArg("The @PastOrPresent only supports TemporalAccessor & Calendar & Date.");
|
|
|
return false;
|
|
|
}
|
|
|
int compare = dateComparator(value, isTemporalAccessor, isDate);
|
|
@@ -1550,7 +1552,6 @@ public class Verification {
|
|
|
//compare <= 0
|
|
|
return lte0(compare);
|
|
|
}
|
|
|
-
|
|
|
/**
|
|
|
* get field by the names
|
|
|
*
|
|
@@ -1642,7 +1643,7 @@ public class Verification {
|
|
|
try {
|
|
|
methodHandle = lookup.findVirtual(lookup.lookupClass(), methodName, methodType);
|
|
|
} catch (NoSuchMethodException | IllegalAccessException e) {
|
|
|
- throw illegalArgException("No such method [%s(%s)] in Verification.", methodName, methodType);
|
|
|
+ throw illegalArgException("No such method [%s(%s)] in VerifyProcessor.", methodName, methodType);
|
|
|
}
|
|
|
return methodHandle;
|
|
|
}
|
|
@@ -1673,32 +1674,6 @@ public class Verification {
|
|
|
}
|
|
|
return invoke;
|
|
|
}
|
|
|
-
|
|
|
- public void action(final Method method, final Object... arguments){
|
|
|
- //check whether the method is present with verifier annotation.
|
|
|
- boolean annotationPresent = method.isAnnotationPresent(verifyMethodAnnotation);
|
|
|
- if(!annotationPresent){
|
|
|
- return;
|
|
|
- }
|
|
|
- //check the list of parameters.
|
|
|
- Parameter[] parameters = method.getParameters();
|
|
|
- if(null == parameters || parameters.length == 0){
|
|
|
- return;
|
|
|
- }
|
|
|
- //if the parameter is present with Ver. return the index.
|
|
|
- Object object = null;
|
|
|
- for (int i = 0; i < parameters.length; i++) {
|
|
|
- if(parameters[i].isAnnotationPresent(verifyParamAnnotation)){
|
|
|
- object = arguments[i];
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if(null == object){
|
|
|
- return;
|
|
|
- }
|
|
|
- Verifier verifier = method.getDeclaredAnnotation(Verifier.class);
|
|
|
- action(object, verifier.fields());
|
|
|
- }
|
|
|
/**
|
|
|
* verify the fields of the object base on the annotation present on it.
|
|
|
* if it is not valid that will throw a {@link VerifyException}.
|
|
@@ -1707,7 +1682,7 @@ public class Verification {
|
|
|
* @param object any object value. with some validation annotation in {@code javax.validation.constraints.*}
|
|
|
* @param fields the list of fields to verify. if no, that will verify all fields in the object.
|
|
|
*/
|
|
|
- public void action(final Object object, final String... fields) {
|
|
|
+ private void action(final Object object, final String... fields) {
|
|
|
isNull(object, "The object to verify must not be null.");
|
|
|
List<Field> verifyFields = fieldFilter(object, fields);
|
|
|
for (Field verifyField : verifyFields) {
|