Skip to content

Instantly share code, notes, and snippets.

@edurbs
Last active January 13, 2024 22:56
Show Gist options
  • Save edurbs/dc8c2fef15b168bbd0e220ea18444160 to your computer and use it in GitHub Desktop.
Save edurbs/dc8c2fef15b168bbd0e220ea18444160 to your computer and use it in GitHub Desktop.
Validação de números telefone fixo ou móvel (celular) do Brasil
public interface DomainEntity {
public default void validate() {
new EntityValidatorService().validate(this);
}
}
import java.util.Set;
import java.util.stream.Collectors;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
public class EntityValidatorService {
private final Validator validator;
public EntityValidatorService() {
this.validator = Validation.buildDefaultValidatorFactory().getValidator();
}
public <T> void validate(T object) {
Set<ConstraintViolation<T>> violations = this.validator.validate(object);
if (!violations.isEmpty()) {
throw new DomainEntityValidationException(violations.stream()
.map(violation ->
violation.getRootBeanClass().getSimpleName()
+ "." + violation.getPropertyPath()
+ ": " + violation.getMessage()
)
.collect(Collectors.joining(", ")));
}
}
}
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
public record Phone(@NotBlank String number) implements DomainEntity {
public Phone(String number)
this.number = number;
validate();
validatePhone(number);
}
private void validatePhone(String phoneNumber) {
String onlyNumbers = phoneNumber.replaceAll("\\D", "");
if (onlyNumbers.isBlank()
|| isInvalidLength(onlyNumbers)
|| isInvalidMobileStartNumber(onlyNumbers)
|| isAllTheSameNumber(onlyNumbers)
|| isInvalidDddBrazilCode(onlyNumbers)
|| isInvalidBrazilCngNumber(onlyNumbers)
|| isInvalidSpecialBrazilMobileNumber(onlyNumbers)
|| isInvalidNonMobileNumber(onlyNumbers)){
throw new DomainEntityValidationException("Invalid phone number");
}
}
private boolean isInvalidBrazilCngNumber(String onlyNumbers) {
String prefix = onlyNumbers.substring(0, 4);
// https://www.gov.br/anatel/pt-br/regulado/numeracao/tabela-servico-telefonico-fixo-comutado-codigos-nao-geograficos-cng
Set<String> prefixCng = new HashSet<>(
Arrays.asList("0800", "0300", "0500", "0900"));
return onlyNumbers.length() == 11 && onlyNumbers.startsWith("0") && !prefixCng.contains(prefix);
}
private boolean isInvalidSpecialBrazilMobileNumber(String onlyNumbers) {
// https://www.gov.br/anatel/pt-br/regulado/numeracao/tabela-servico-movel-especializado
int areaCode = Integer.parseInt(onlyNumbers.substring(0, 2));
int prefix = Integer.parseInt(onlyNumbers.substring(2, 4));
return (prefix == 70 || (prefix >= 77 && prefix <= 79))
&& !(
((areaCode >= 11 && areaCode <= 19) && (prefix == 70 || (prefix >= 77 && prefix <= 79)))
|| ((areaCode==21 || areaCode==22 || areaCode==24) && (prefix==70 || prefix == 77 || prefix == 78))
|| ((areaCode == 31 || areaCode == 34 || areaCode == 37) && (prefix == 77 || prefix == 78))
|| (((areaCode == 27 || areaCode==47 || areaCode==48 || areaCode==51 || areaCode==54 || areaCode == 61 || areaCode==62 || areaCode==65 || areaCode==71 || areaCode==73 || areaCode==75 || areaCode==81 || areaCode==85) || (areaCode >= 41 && areaCode <= 44)) && prefix == 78));
}
private boolean isInvalidNonMobileNumber(String onlyNumbers) {
Set<Integer> nonMobilePrefix = new HashSet<>(Arrays.asList(2, 3, 4, 5, 7));
return (onlyNumbers.length() == 10
&& !nonMobilePrefix.contains(Integer.parseInt(onlyNumbers.substring(2, 3))));
}
private boolean isInvalidDddBrazilCode(String onlyNumbers) {
Set<Integer> validDddBrazilCodes = new HashSet<>(
Arrays.asList(11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 24, 27, 28, 31, 32, 33,
34, 35, 37, 38, 41, 42, 43, 44, 45, 46, 47, 48, 49, 51, 53, 54, 55, 61, 62, 64, 63,
65, 66, 67, 68, 69, 71, 73, 74, 75, 77, 79, 81, 82, 83, 84, 85, 86, 87, 88, 89, 91,
92, 93, 94, 95, 96, 97, 98, 99));
return isInvalidBrazilCngNumber(onlyNumbers) && !(validDddBrazilCodes.contains(Integer.parseInt(onlyNumbers.substring(0, 2))));
}
private boolean isAllTheSameNumber(String onlyNumbers) {
Pattern regexPattern =
Pattern.compile(onlyNumbers.charAt(0) + "{" + onlyNumbers.length() + "}");
Matcher matchAllSameNumbers = regexPattern.matcher(onlyNumbers);
return matchAllSameNumbers.find();
}
private boolean isInvalidMobileStartNumber(String onlyNumbers) {
String subprefix = onlyNumbers.substring(2, 3);
return onlyNumbers.length() == 11 && !subprefix.equals("9") && !subprefix.equals("0");
}
private boolean isInvalidLength(String onlyNumbers) {
return !(onlyNumbers.length() >= 10 && onlyNumbers.length() <= 11);
}
}
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class PhoneTest {
@ParameterizedTest
@ValueSource(strings = {"35-3221-1000", "66-98899-8899", "11-2000-0000", "11-3000-0000", "11-4000-0000", "11-5000-9999", "11-7777-8888", "51-7800-0000", "0800 892 5063", "11-4003-4004", "0900-123-4235", "0500 12345 20"})
void givenValidNumber_whenInstantiate_thenNotThrows(String number) {
assertDoesNotThrow(() -> new Phone(number));
}
@ParameterizedTest
@ValueSource(strings = {"35-9221-1000", "66-78899-8899", "23-7777-8888", "21-7977-8888", "35-7000-1234", "900-123-1234", "4003-9004", "", "abc", "0", "1111111111"})
void givenInvalidNumber_whenInstantiate_thenThrows(String number) {
assertThrows(DomainEntityValidationException.class, () -> new Phone(number));
}
@Test
void givenNullNumber_whenInstantiate_thenThrows() {
assertThrows(DomainEntityValidationException.class, () -> new Phone(null));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment