package cn.com.ty.lift.common.verify; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Array; import java.math.BigDecimal; import java.math.BigInteger; import java.net.IDN; import java.time.*; import java.time.chrono.HijrahDate; import java.time.chrono.JapaneseDate; import java.time.chrono.MinguoDate; import java.time.chrono.ThaiBuddhistDate; import java.time.temporal.TemporalAccessor; import java.util.*; import java.util.regex.Matcher; import java.util.regex.PatternSyntaxException; import static java.util.regex.Pattern.CASE_INSENSITIVE; /** * the validation for parameter implements javax.validation.constraints.*, * reform from hibernate validator (v6.0.16.Final) * * @author wcz * @since 2020/2/15 */ @Slf4j public class Verifies { private static final int MAX_LOCAL_PART_LENGTH = 64; private static final String LOCAL_PART_ATOM = "[a-z0-9!#$%&'*+/=?^_`{|}~\u0080-\uFFFF-]"; private static final String LOCAL_PART_INSIDE_QUOTES_ATOM = "([a-z0-9!#$%&'*.(),<>\\[\\]:; @+/=?^_`{|}~\u0080-\uFFFF-]|\\\\\\\\|\\\\\\\")"; /** * Regular expression for the local part of an email address (everything before '@') */ private static final java.util.regex.Pattern LOCAL_PART_PATTERN = java.util.regex.Pattern.compile( "(" + LOCAL_PART_ATOM + "+|\"" + LOCAL_PART_INSIDE_QUOTES_ATOM + "+\")" + "(\\." + "(" + LOCAL_PART_ATOM + "+|\"" + LOCAL_PART_INSIDE_QUOTES_ATOM + "+\")" + ")*", CASE_INSENSITIVE ); /** * This is the maximum length of a domain name. But be aware that each label (parts separated by a dot) of the * domain name must be at most 63 characters long. This is verified by {@link IDN#toASCII(String)}. */ private static final int MAX_DOMAIN_PART_LENGTH = 255; private static final String DOMAIN_CHARS_WITHOUT_DASH = "[a-z\u0080-\uFFFF0-9!#$%&'*+/=?^_`{|}~]"; private static final String DOMAIN_LABEL = "(" + DOMAIN_CHARS_WITHOUT_DASH + "-*)*" + DOMAIN_CHARS_WITHOUT_DASH + "+"; private static final String DOMAIN = DOMAIN_LABEL + "+(\\." + DOMAIN_LABEL + "+)*"; private static final String IP_DOMAIN = "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"; //IP v6 regex taken from http://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses private static final String IP_V6_DOMAIN = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))"; /** * Regular expression for the domain part of an URL *
* A host string must be a domain string, an IPv4 address string, or "[", followed by an IPv6 address string,
* followed by "]".
*/
private static final java.util.regex.Pattern DOMAIN_PATTERN = java.util.regex.Pattern.compile(
DOMAIN + "|\\[" + IP_V6_DOMAIN + "\\]", CASE_INSENSITIVE
);
/**
* Regular expression for the domain part of an email address (everything after '@')
*/
private static final java.util.regex.Pattern EMAIL_DOMAIN_PATTERN = java.util.regex.Pattern.compile(
DOMAIN + "|\\[" + IP_DOMAIN + "\\]|" + "\\[IPv6:" + IP_V6_DOMAIN + "\\]", CASE_INSENSITIVE
);
private static final OptionalInt LESS_THAN = OptionalInt.of(-1);
private static final OptionalInt FINITE_VALUE = OptionalInt.empty();
private static final OptionalInt GREATER_THAN = OptionalInt.of(1);
private static final short SHORT_ZERO = (short) 0;
private static final byte BYTE_ZERO = (byte) 0;
private static final Clock SYSTEM_DEFAULT_CLOCK = Clock.offset(Clock.systemDefaultZone(), Duration.ZERO.abs().negated());
private Verifies(){}
public static boolean isNull(Object value) {
return null == value;
}
public static boolean notNull(Object value) {
return null != value;
}
private static class VerifyUtils{
private static VerifyException exception(String message){
return new VerifyException(message);
}
private static void isTrue(boolean expression, String message) {
if (expression) {
throw exception(message);
}
}
private static void notTrue(boolean expression, String message) {
if (!expression) {
throw exception(message);
}
}
private static void isNull(Object value, String message) {
isTrue(null == value, message);
}
}
private static class IllegalArg{
private static IllegalArgumentException exception(String message) {
return new IllegalArgumentException(message);
}
private static void isTrue(boolean expression, String message) {
if (expression) {
throw exception(message);
}
}
private static void isNull(Object value, String message) {
isTrue(null == value, message);
}
}
public static boolean gt0(int value) {
return value > 0;
}
public static boolean lt0(int value) {
return value < 0;
}
public static boolean gte0(int value) {
return value >= 0;
}
public static boolean lte0(int value) {
return value <= 0;
}
public static boolean isCharSequence(Object value) {
return (value instanceof CharSequence);
}
public static boolean isBigDecimal(Object value) {
return (value instanceof BigDecimal);
}
public static boolean isBigInteger(Object value) {
return (value instanceof BigInteger);
}
public static boolean isLong(Object value) {
return (value instanceof Long);
}
public static boolean isInteger(Object value) {
return (value instanceof Integer);
}
public static boolean isDouble(Object value) {
return (value instanceof Double);
}
public static boolean isFloat(Object value) {
return (value instanceof Float);
}
public static boolean isByte(Object value) {
return (value instanceof Byte);
}
public static boolean isShort(Object value) {
return (value instanceof Short);
}
public static boolean isString(Object value) {
return (value instanceof String);
}
public static boolean isCollection(Object value) {
return (value instanceof Collection);
}
public static boolean isMap(Object value) {
return (value instanceof Map);
}
public static boolean isArray(Object value) {
return value.getClass().isArray();
}
public static boolean isIterator(Object value) {
return (value instanceof Iterator);
}
public static boolean isEnumeration(Object value) {
return (value instanceof Enumeration);
}
private static BigDecimal newBigDecimal(CharSequence value) {
try {
BigDecimal bd;
if (isString(value)) {
bd = new BigDecimal((String) value);
} else {
bd = new BigDecimal(value.toString());
}
return bd;
} catch (Exception e) {
return null;
}
}
private static BigDecimal newBigDecimal(Number value) {
try {
BigDecimal bd;
if (isLong(value)) {
bd = BigDecimal.valueOf((Long) value);
} else if (isBigDecimal(value)) {
bd = ((BigDecimal) value);
} else if (isBigInteger(value)) {
bd = new BigDecimal((BigInteger) value);
} else if (isDouble(value)) {
bd = BigDecimal.valueOf((Double) value);
} else if (isFloat(value)) {
bd = BigDecimal.valueOf((Float) value);
} else {
bd = BigDecimal.valueOf(value.doubleValue());
}
return bd;
} catch (Exception e) {
return null;
}
}
private static OptionalInt infinityCheck(Double number, OptionalInt treatNanAs) {
OptionalInt result = FINITE_VALUE;
if (number == Double.NEGATIVE_INFINITY) {
result = LESS_THAN;
} else if (number.isNaN()) {
result = treatNanAs;
} else if (number == Double.POSITIVE_INFINITY) {
result = GREATER_THAN;
}
return result;
}
private static OptionalInt infinityCheck(Float number, OptionalInt treatNanAs) {
OptionalInt result = FINITE_VALUE;
if (number == Float.NEGATIVE_INFINITY) {
result = LESS_THAN;
} else if (number.isNaN()) {
result = treatNanAs;
} else if (number == Float.POSITIVE_INFINITY) {
result = GREATER_THAN;
}
return result;
}
/**
* Checks the validity of the domain name used in an email. To be valid it should be either a valid host name, or an
* IP address wrapped in [].
*
* @param domain domain to check for validity
* @return {@code true} if the provided string is a valid domain, {@code false} otherwise
*/
public static boolean isValidEmailDomainAddress(String domain) {
return isValidDomainAddress(domain, EMAIL_DOMAIN_PATTERN);
}
/**
* Checks validity of a domain name.
*
* @param domain the domain to check for validity
* @return {@code true} if the provided string is a valid domain, {@code false} otherwise
*/
public static boolean isValidDomainAddress(String domain) {
return isValidDomainAddress(domain, DOMAIN_PATTERN);
}
private static boolean isValidDomainAddress(String domain, java.util.regex.Pattern pattern) {
// if we have a trailing dot the domain part we have an invalid email address.
// the regular expression match would take care of this, but IDN.toASCII drops the trailing '.'
if (domain.endsWith(".")) {
return false;
}
Matcher matcher = pattern.matcher(domain);
if (!matcher.matches()) {
return false;
}
String asciiString;
try {
asciiString = IDN.toASCII(domain);
} catch (IllegalArgumentException e) {
return false;
}
if (asciiString.length() > MAX_DOMAIN_PART_LENGTH) {
return false;
}
return true;
}
//NotNull : Object
public static void notNull(Object value, String message) {
VerifyUtils.isTrue(isNull(value), message);
}
//Null : Object
public static void isNull(Object value, String message) {
VerifyUtils.isTrue(notNull(value), message);
}
//AssertTrue : Boolean
public static void assertTrue(Boolean value, String message) {
VerifyUtils.isNull(value, message);
VerifyUtils.isTrue(!value, message);
}
//AssertFalse : Boolean
public static void assertFalse(Boolean value, String message) {
VerifyUtils.isNull(value, message);
VerifyUtils.isTrue(value, message);
}
//decimalMax : number
public static void decimalMax(Number value, BigDecimal maxValue, boolean inclusive, String message) {
VerifyUtils.isNull(value, message);
IllegalArg.isNull(maxValue, maxValue + " does not represent a valid BigDecimal format.");
BigDecimal val = newBigDecimal(value);
VerifyUtils.isNull(val, message);
int compare = decimalComparator(value, val, maxValue, GREATER_THAN);
//inclusive ? comparisonResult <= 0 : comparisonResult < 0;
if (inclusive) {
VerifyUtils.notTrue(lte0(compare), message);
} else {
VerifyUtils.notTrue(lt0(compare), message);
}
}
public static void decimalMax(Number value, BigDecimal maxValue, String message) {
decimalMax(value, maxValue, true, message);
}
//decimalMax : CharSequence
public static void decimalMax(CharSequence value, BigDecimal maxValue, boolean inclusive, String message) {
VerifyUtils.isNull(value, message);
IllegalArg.isNull(maxValue, maxValue + " does not represent a valid BigDecimal format.");
BigDecimal val = newBigDecimal(value.toString().trim());
VerifyUtils.isNull(val, message);
int compare = decimalComparator(value, val, maxValue, GREATER_THAN);
//inclusive ? comparisonResult <= 0 : comparisonResult < 0;
if (inclusive) {
VerifyUtils.notTrue(lte0(compare), message);
} else {
VerifyUtils.notTrue(lt0(compare), message);
}
}
public static void decimalMax(CharSequence value, BigDecimal maxValue, String message) {
decimalMax(value, maxValue, true, message);
}
//decimalMin : number
public static void decimalMin(Number value, BigDecimal minValue, boolean inclusive, String message) {
VerifyUtils.isNull(value, message);
IllegalArg.isNull(minValue, minValue + " does not represent a valid BigDecimal format.");
BigDecimal val = newBigDecimal(value);
VerifyUtils.isNull(val, message);
int compare = decimalComparator(value, val, minValue, LESS_THAN);
//inclusive ? comparisonResult >= 0 : comparisonResult > 0;
if (inclusive) {
VerifyUtils.notTrue(gte0(compare), message);
} else {
VerifyUtils.notTrue(gt0(compare), message);
}
}
public static void decimalMin(Number value, BigDecimal minValue, String message) {
decimalMin(value, minValue, true, message);
}
//decimalMin : CharSequence
public static void decimalMin(CharSequence value, BigDecimal minValue, boolean inclusive, String message) {
VerifyUtils.isNull(value, message);
IllegalArg.isNull(minValue, minValue + " does not represent a valid BigDecimal format.");
BigDecimal val = newBigDecimal(value.toString().trim());
VerifyUtils.isNull(val, message);
int compare = decimalComparator(value, val, minValue, LESS_THAN);
//inclusive ? comparisonResult >= 0 : comparisonResult > 0;
if (inclusive) {
VerifyUtils.notTrue(gte0(compare), message);
} else {
VerifyUtils.notTrue(gt0(compare), message);
}
}
public static void decimalMin(CharSequence value, BigDecimal minValue, String message) {
decimalMin(value, minValue, true, message);
}
private static int decimalComparator(Object value, BigDecimal val, BigDecimal boundary, OptionalInt treatNanAs) {
int compare;
if (isLong(value) || isBigInteger(value) || isBigDecimal(value)) {
compare = val.compareTo(boundary);
} else if (isDouble(value)) {
Double v = (Double) value;
OptionalInt infinity = infinityCheck(v, treatNanAs);
if (infinity.isPresent()) {
compare = infinity.getAsInt();
} else {
compare = val.compareTo(boundary);
}
} else if (isFloat(value)) {
Float v = (Float) value;
OptionalInt infinity = infinityCheck(v, treatNanAs);
if (infinity.isPresent()) {
compare = infinity.getAsInt();
} else {
compare = val.compareTo(boundary);
}
} else {
compare = val.compareTo(boundary);
}
return compare;
}
//length : CharSequence,Collection,Map,Array,Iterator,Enumeration
public static int length(Object value) {
if (isNull(value)) {
return 0;
}
if (isCharSequence(value)) {
return ((CharSequence) value).length();
}
if (isCollection(value)) {
return ((Collection>) value).size();
}
if (isMap(value)) {
return ((Map, ?>) value).size();
}
if (isArray(value)) {
return Array.getLength(value);
}
int count;
if (isIterator(value)) {
Iterator> iter = (Iterator>) value;
count = 0;
while (iter.hasNext()) {
count++;
iter.next();
}
return count;
}
if (isEnumeration(value)) {
Enumeration> enumeration = (Enumeration>) value;
count = 0;
while (enumeration.hasMoreElements()) {
count++;
enumeration.nextElement();
}
return count;
}
return -1;
}
//NotEmpty : CharSequence,Collection,Map,Array,Iterator,Enumeration
public static void notEmpty(CharSequence value, String message) {
VerifyUtils.isNull(value, message);
VerifyUtils.notTrue(gt0(value.length()), message);
}
public static void notEmpty(Collection> value, String message) {
VerifyUtils.isNull(value, message);
VerifyUtils.isTrue(value.isEmpty(), message);
}
public static void notEmpty(Map, ?> value, String message) {
VerifyUtils.isNull(value, message);
VerifyUtils.isTrue(value.isEmpty(), message);
}
public static void notEmpty(Iterator> value, String message) {
VerifyUtils.isNull(value, message);
VerifyUtils.notTrue(value.hasNext(), message);
}
public static void notEmpty(Enumeration> value, String message) {
VerifyUtils.isNull(value, message);
VerifyUtils.notTrue(value.hasMoreElements(), message);
}
public static