shade.com.alibaba.fastjson2.schema.StringSchema Maven / Gradle / Ivy
package com.alibaba.fastjson2.schema;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.alibaba.fastjson2.util.DateUtils;
import com.alibaba.fastjson2.util.TypeUtils;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class StringSchema
extends JSONSchema {
static final Pattern EMAIL_PATTERN = Pattern.compile("^\\s*?(.+)@(.+?)\\s*$");
static final Pattern IP_DOMAIN_PATTERN = Pattern.compile("^\\[(.*)\\]$");
static final Pattern USER_PATTERN = Pattern.compile("^\\s*(((\\\\.)|[^\\s\\p{Cntrl}\\(\\)<>@,;:'\\\\\\\"\\.\\[\\]]|')+|(\"[^\"]*\"))(\\.(((\\\\.)|[^\\s\\p{Cntrl}\\(\\)<>@,;:'\\\\\\\"\\.\\[\\]]|')+|(\"[^\"]*\")))*$");
final int maxLength;
final int minLength;
final String format;
final String patternFormat;
final Pattern pattern;
final boolean typed;
final AnyOf anyOf;
final OneOf oneOf;
final String constValue;
final Set enumValues;
final Predicate formatValidator;
StringSchema(JSONObject input) {
super(input);
this.typed = "string".equalsIgnoreCase(input.getString("type"));
this.minLength = input.getIntValue("minLength", -1);
this.maxLength = input.getIntValue("maxLength", -1);
this.patternFormat = input.getString("pattern");
this.pattern = patternFormat == null ? null : Pattern.compile(patternFormat);
this.format = input.getString("format");
Object anyOf = input.get("anyOf");
if (anyOf instanceof JSONArray) {
this.anyOf = anyOf((JSONArray) anyOf, String.class);
} else {
this.anyOf = null;
}
Object oneOf = input.get("oneOf");
if (oneOf instanceof JSONArray) {
this.oneOf = oneOf((JSONArray) oneOf, String.class);
} else {
this.oneOf = null;
}
this.constValue = input.getString("const");
{
Set enumValues = null;
Object property = input.get("enum");
if (property instanceof Collection) {
Collection enums = (Collection) property;
enumValues = new LinkedHashSet<>(enums.size());
enumValues.addAll((Collection) enums);
} else if (property instanceof Object[]) {
enumValues = input.getObject("enum", TypeReference.collectionType(LinkedHashSet.class, String.class));
}
this.enumValues = enumValues;
}
if (format == null) {
formatValidator = null;
} else {
switch (format) {
case "email":
formatValidator = StringSchema::isEmail;
break;
case "ipv4":
formatValidator = TypeUtils::validateIPv4;
break;
case "ipv6":
formatValidator = TypeUtils::validateIPv6;
break;
case "uri":
formatValidator = url -> {
if (url == null || url.isEmpty()) {
return false;
}
try {
new URI(url);
return true;
} catch (URISyntaxException ignored) {
return false;
}
};
break;
case "date-time":
formatValidator = DateUtils::isDate;
break;
case "date":
formatValidator = DateUtils::isLocalDate;
break;
case "time":
formatValidator = DateUtils::isLocalTime;
break;
case "duration":
formatValidator = str -> {
if (str == null || str.isEmpty()) {
return false;
}
try {
Duration.parse(str);
return true;
} catch (DateTimeParseException ignored) {
return false;
}
};
break;
case "uuid":
formatValidator = TypeUtils::isUUID;
break;
default:
formatValidator = null;
break;
}
}
}
@Override
public Type getType() {
return Type.String;
}
@Override
public ValidateResult validate(Object value) {
if (value == null) {
if (typed) {
return REQUIRED_NOT_MATCH;
}
return SUCCESS;
}
if (value instanceof String) {
String str = (String) value;
if (minLength >= 0 || maxLength >= 0) {
int count = str.codePointCount(0, str.length());
if (minLength >= 0 && count < minLength) {
return new ValidateResult(false, "minLength not match, expect >= %s, but %s", minLength, str.length());
}
if (maxLength >= 0 && count > maxLength) {
return new ValidateResult(false, "maxLength not match, expect <= %s, but %s", maxLength, str.length());
}
}
if (pattern != null) {
if (!pattern.matcher(str).find()) {
return new ValidateResult(false, "pattern not match, expect %s, but %s", patternFormat, str);
}
}
if (formatValidator != null) {
if (!formatValidator.test(str)) {
return new ValidateResult(false, "format not match, expect %s, but %s", format, str);
}
}
if (anyOf != null) {
ValidateResult result = anyOf.validate(str);
if (!result.isSuccess()) {
return result;
}
}
if (oneOf != null) {
ValidateResult result = oneOf.validate(str);
if (!result.isSuccess()) {
return result;
}
}
if (constValue != null) {
if (!constValue.equals(str)) {
return new ValidateResult(false, "must be const %s, but %s", constValue, str);
}
}
if (enumValues != null) {
if (!enumValues.contains(str)) {
return new ValidateResult(false, "not in enum values, %s", str);
}
}
return SUCCESS;
}
if (!typed) {
return SUCCESS;
}
return new ValidateResult(false, "expect type %s, but %s", Type.String, value.getClass());
}
public static boolean isEmail(String email) {
if (email == null) {
return false;
}
if (email.endsWith(".")) { // check this first - it's cheap!
return false;
}
// Check the whole email address structure
Matcher emailMatcher = EMAIL_PATTERN.matcher(email);
if (!emailMatcher.matches()) {
return false;
}
String user = emailMatcher.group(1);
if (user.length() > 64) {
return false;
}
if (!USER_PATTERN.matcher(user).matches()) {
return false;
}
String domain = emailMatcher.group(2);
Matcher ipDomainMatcher = IP_DOMAIN_PATTERN.matcher(domain);
boolean validDomain;
if (ipDomainMatcher.matches()) {
String inetAddress = ipDomainMatcher.group(1);
validDomain = TypeUtils.validateIPv4(inetAddress) || TypeUtils.validateIPv6(inetAddress);
} else {
validDomain = DomainValidator.isValid(domain) || DomainValidator.isValidTld(domain);
}
return validDomain;
}
@Override
public JSONObject toJSONObject() {
JSONObject object = new JSONObject();
object.put("type", "string");
if (minLength != -1) {
object.put("minLength", minLength);
}
if (format != null) {
object.put("format", format);
}
if (patternFormat != null) {
object.put("pattern", pattern);
}
if (anyOf != null) {
object.put("anyOf", anyOf);
}
if (oneOf != null) {
object.put("oneOf", oneOf);
}
if (constValue != null) {
object.put("const", constValue);
}
if (enumValues != null && !enumValues.isEmpty()) {
object.put("enum", enumValues);
}
return object;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy