package io.micronaut.validation.validator.constraints;
import io.micronaut.core.annotation.AnnotationValue;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import javax.inject.Singleton;
import javax.validation.constraints.Email;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
@Singleton
public class EmailValidator extends AbstractPatternValidator<Email> {
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-]|\\\\\\\\|\\\\\\\")";
private static final Pattern LOCAL_PART_PATTERN = Pattern.compile(
"(" + LOCAL_PART_ATOM + "+|\"" + LOCAL_PART_INSIDE_QUOTES_ATOM + "+\")" +
"(\\." + "(" + LOCAL_PART_ATOM + "+|\"" + LOCAL_PART_INSIDE_QUOTES_ATOM + "+\")" + ")*", CASE_INSENSITIVE
);
@Override
public boolean isValid(
@Nullable CharSequence value,
@NonNull AnnotationValue<Email> annotationMetadata,
@NonNull ConstraintValidatorContext context) {
if (value == null) {
return true;
}
String stringValue = value.toString();
int i = stringValue.lastIndexOf('@');
if (i < 0) {
return false;
}
String localPart = stringValue.substring(0, i);
String domainPart = stringValue.substring(i + 1);
boolean isValid;
if (!isValidEmailLocalPart(localPart)) {
isValid = false;
} else {
isValid = DomainNameUtil.isValidEmailDomainAddress(domainPart);
}
final Pattern pattern = getPattern(annotationMetadata, true);
if (pattern == null || !isValid) {
return isValid;
}
Matcher m = pattern.matcher(value);
return m.matches();
}
private boolean isValidEmailLocalPart(String localPart) {
if (localPart.length() > MAX_LOCAL_PART_LENGTH) {
return false;
}
Matcher matcher = LOCAL_PART_PATTERN.matcher(localPart);
return matcher.matches();
}
}