Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
321 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,6 +67,7 @@ Fakers | |
* Beer | ||
* Book | ||
* Bool | ||
* Brazilian CPF identifier | ||
* Business | ||
* ChuckNorris | ||
* Cat | ||
|
205 changes: 205 additions & 0 deletions
205
src/main/java/com/github/javafaker/idnumbers/BrCpfIdNumber.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
package com.github.javafaker.idnumbers; | ||
|
||
import java.util.Arrays; | ||
import java.util.regex.Pattern; | ||
|
||
import org.apache.commons.lang3.ArrayUtils; | ||
|
||
import com.github.javafaker.Faker; | ||
|
||
/** | ||
* Generate both valid and invalid Brazilian CPF identification numbers. | ||
* | ||
* For more information about CPF please check | ||
* https://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas | ||
* | ||
* The validation numbers algorithm was implemented following instructions | ||
* provided by the Brazilian portuguese Wikipedia page at | ||
* https://pt.wikipedia.org/wiki/Cadastro_de_pessoas_f%C3%ADsicas#Algoritmo | ||
* | ||
* @author MarceloLeite2604 | ||
* @author EullerLisowski | ||
* | ||
*/ | ||
public class BrCpfIdNumber { | ||
|
||
private static final int INPUT_DIGITS_LENGTH = 9; | ||
|
||
private static final int VERIFIER_DIGITS_LENGTH = 2; | ||
|
||
private static final int CPF_DIGITS_LENGTH = INPUT_DIGITS_LENGTH + VERIFIER_DIGITS_LENGTH; | ||
|
||
private static final String CPF_FORMAT_PATTERN = "###.###.###-##"; | ||
|
||
protected static final String FORMATTED_CPF_REGEX_PATTERN = "^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$"; | ||
|
||
static final Pattern FORMATTED_CPF_PATTERN = Pattern.compile(FORMATTED_CPF_REGEX_PATTERN); | ||
|
||
protected static final String UNFORMATTED_CPF_REGEX_PATTERN = "^\\d{11}$"; | ||
|
||
static final Pattern UNFORMATTED_CPF_PATTERN = Pattern.compile(UNFORMATTED_CPF_REGEX_PATTERN); | ||
|
||
public String getValidFormattedCpf(Faker faker) { | ||
|
||
int[] cpfDigits = createCpfDigits(faker); | ||
|
||
return convertAsFormattedString(cpfDigits); | ||
} | ||
|
||
public String getValidUnformattedCpf(Faker faker) { | ||
|
||
int[] cpfDigits = createCpfDigits(faker); | ||
|
||
return convertAsUnformattedString(cpfDigits); | ||
} | ||
|
||
public String getInvalidFormattedCpf(Faker faker) { | ||
|
||
int[] cpfDigits = createInvalidCpfDigits(faker); | ||
|
||
return convertAsFormattedString(cpfDigits); | ||
} | ||
|
||
public String getInvalidUnformattedCpf(Faker faker) { | ||
|
||
int[] cpfDigits = createInvalidCpfDigits(faker); | ||
|
||
return convertAsUnformattedString(cpfDigits); | ||
} | ||
|
||
private int[] createCpfDigits(Faker faker) { | ||
int[] inputDigits = createInputDigits(faker); | ||
|
||
int[] verifierDigits = calculateVerifierDigits(inputDigits); | ||
|
||
return ArrayUtils.addAll(inputDigits, verifierDigits); | ||
} | ||
|
||
private int[] createInvalidCpfDigits(Faker faker) { | ||
int[] inputDigits = createInputDigits(faker); | ||
|
||
int[] verifierDigits = calculateInvalidVerifierDigits(inputDigits, faker); | ||
|
||
return ArrayUtils.addAll(inputDigits, verifierDigits); | ||
} | ||
|
||
protected boolean isValid(String cpf) { | ||
|
||
if (!isFormatted(cpf) && !isUnformatted(cpf)) { | ||
return false; | ||
} | ||
|
||
int[] cpfDigits = convertAsIntegerArray(cpf); | ||
|
||
return isValid(cpfDigits); | ||
} | ||
|
||
private int[] convertAsIntegerArray(String cpf) { | ||
char[] cpfChars = cpf.toCharArray(); | ||
int[] result = new int[CPF_DIGITS_LENGTH]; | ||
int index = 0; | ||
|
||
for (char cpfChar : cpfChars) { | ||
if (Character.isDigit(cpfChar)) { | ||
result[index++] = Character.getNumericValue(cpfChar); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
private boolean isFormatted(String cpf) { | ||
return FORMATTED_CPF_PATTERN.matcher(cpf).matches(); | ||
} | ||
|
||
private boolean isUnformatted(String cpf) { | ||
return UNFORMATTED_CPF_PATTERN.matcher(cpf).matches(); | ||
} | ||
|
||
private int[] calculateInvalidVerifierDigits(int[] inputDigits, Faker faker) { | ||
int[] verifierDigits = calculateVerifierDigits(inputDigits); | ||
|
||
for (int index = 0; index < verifierDigits.length; index++) { | ||
verifierDigits[index] = retrieveRandomIntDifferentFrom(verifierDigits[index], faker); | ||
} | ||
|
||
return verifierDigits; | ||
} | ||
|
||
private int retrieveRandomIntDifferentFrom(int integer, Faker faker) { | ||
int result; | ||
|
||
do { | ||
result = retrieveRandomDigit(faker); | ||
} while (result == integer); | ||
|
||
return result; | ||
} | ||
|
||
private String convertAsFormattedString(int[] cpfDigits) { | ||
String cpf = CPF_FORMAT_PATTERN; | ||
|
||
for (int cpfDigit : cpfDigits) { | ||
cpf = cpf.replaceFirst("#", Integer.toString(cpfDigit)); | ||
} | ||
return cpf; | ||
} | ||
|
||
private String convertAsUnformattedString(int[] cpfDigits) { | ||
StringBuilder cpfStringBuilder = new StringBuilder(); | ||
|
||
for (int cpfDigit : cpfDigits) { | ||
cpfStringBuilder.append(cpfDigit); | ||
} | ||
|
||
return cpfStringBuilder.toString(); | ||
} | ||
|
||
private int[] calculateVerifierDigits(int[] inputDigits) { | ||
|
||
int[] invertedInputDigits = Arrays.copyOf(inputDigits, inputDigits.length); | ||
|
||
ArrayUtils.reverse(invertedInputDigits); | ||
|
||
int[] verifierDigits = new int[VERIFIER_DIGITS_LENGTH]; | ||
|
||
for (int index = 0; index < invertedInputDigits.length; index++) { | ||
verifierDigits[0] += invertedInputDigits[index] * (9 - (index % 10)); | ||
verifierDigits[1] += invertedInputDigits[index] * (9 - ((index + 1) % 10)); | ||
} | ||
|
||
verifierDigits[0] = (verifierDigits[0] % 11) % 10; | ||
|
||
verifierDigits[1] += verifierDigits[0] * 9; | ||
verifierDigits[1] = (verifierDigits[1] % 11) % 10; | ||
|
||
return verifierDigits; | ||
} | ||
|
||
private boolean isValid(int[] cpfDigits) { | ||
int[] inputDigits = retrieveInputDigits(cpfDigits); | ||
|
||
int[] cpfVerifierDigits = Arrays.copyOfRange(cpfDigits, INPUT_DIGITS_LENGTH, | ||
INPUT_DIGITS_LENGTH + VERIFIER_DIGITS_LENGTH); | ||
|
||
int[] calculatedVerifierDigits = calculateVerifierDigits(inputDigits); | ||
|
||
return Arrays.equals(cpfVerifierDigits, calculatedVerifierDigits); | ||
} | ||
|
||
private int[] retrieveInputDigits(int[] cpfDigits) { | ||
return Arrays.copyOfRange(cpfDigits, 0, INPUT_DIGITS_LENGTH); | ||
} | ||
|
||
private int[] createInputDigits(Faker faker) { | ||
int[] inputDigits = new int[INPUT_DIGITS_LENGTH]; | ||
for (int index = 0; index < inputDigits.length; index++) { | ||
inputDigits[index] = retrieveRandomDigit(faker); | ||
} | ||
return inputDigits; | ||
} | ||
|
||
private int retrieveRandomDigit(Faker faker) { | ||
return faker.random().nextInt(10); | ||
} | ||
} |
115 changes: 115 additions & 0 deletions
115
src/test/java/com/github/javafaker/idnumbers/BrCpfIdNumberTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package com.github.javafaker.idnumbers; | ||
|
||
import static org.junit.Assert.assertFalse; | ||
import static org.junit.Assert.assertTrue; | ||
|
||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
import com.github.javafaker.AbstractFakerTest; | ||
|
||
public class BrCpfIdNumberTest extends AbstractFakerTest { | ||
|
||
private BrCpfIdNumber brCpfIdNumber; | ||
|
||
@Before | ||
public void setUp() { | ||
brCpfIdNumber = new BrCpfIdNumber(); | ||
} | ||
|
||
@Test | ||
public void testGetValidFormattedCpf() throws Exception { | ||
String cpf = brCpfIdNumber.getValidFormattedCpf(faker); | ||
assertTrue(BrCpfIdNumber.FORMATTED_CPF_PATTERN.matcher(cpf).matches()); | ||
} | ||
|
||
@Test | ||
public void testGetValidUnformattedCpf() throws Exception { | ||
String cpf = brCpfIdNumber.getValidUnformattedCpf(faker); | ||
assertTrue(BrCpfIdNumber.UNFORMATTED_CPF_PATTERN.matcher(cpf).matches()); | ||
} | ||
|
||
@Test | ||
public void testGetInvalidFormattedCpf() throws Exception { | ||
String cpf = brCpfIdNumber.getInvalidFormattedCpf(faker); | ||
assertTrue(BrCpfIdNumber.FORMATTED_CPF_PATTERN.matcher(cpf).matches()); | ||
} | ||
|
||
@Test | ||
public void testGetInvalidUnformattedCpf() throws Exception { | ||
String cpf = brCpfIdNumber.getInvalidUnformattedCpf(faker); | ||
assertTrue(BrCpfIdNumber.UNFORMATTED_CPF_PATTERN.matcher(cpf).matches()); | ||
} | ||
|
||
@Test | ||
public void testIsValidShouldReturnTrueForValidFormattedCpfStartingWithZero() throws Exception { | ||
String cpf = "065.154.070-41"; | ||
assertTrue(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testIsValidShouldReturnTrueForValidUnformattedCpfStartingWithZero() throws Exception { | ||
String cpf = "04893747002"; | ||
assertTrue(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testIsValidShouldReturnFalseForInvalidFormattedCpfStartingWithZero() throws Exception { | ||
String cpf = "099.972.740-59"; | ||
assertFalse(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testIsValidShouldReturnFalseForInvalidUnformattedCpfStartingWithZero() throws Exception { | ||
String cpf = "03360109025"; | ||
assertFalse(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testIsValidShouldReturnTrueForValidFormattedCpfStartingWithGreaterThanZero() throws Exception { | ||
String cpf = "919.766.290-98"; | ||
assertTrue(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testIsValidShouldReturnTrueForValidUnformattedCpfStartingWithGreaterThanZero() throws Exception { | ||
String cpf = "49764225004"; | ||
assertTrue(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testIsValidShouldReturnFalseForInvalidFormattedCpfStartingWithGreaterThanZero() throws Exception { | ||
String cpf = "144.970.180-88"; | ||
assertFalse(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testIsValidShouldReturnFalseForInvalidUnformattedCpfStartingWithGreaterThanZero() throws Exception { | ||
String cpf = "56803652090"; | ||
assertFalse(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testGetValidFormattedCpfShouldReturnValidFormattedCpf() throws Exception { | ||
String cpf = brCpfIdNumber.getValidFormattedCpf(faker); | ||
assertTrue(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testGetInvalidFormattedCpfShouldReturnInvalidFormattedCpf() throws Exception { | ||
String cpf = brCpfIdNumber.getInvalidFormattedCpf(faker); | ||
assertFalse(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testGetValidUnformattedCpfShouldReturnValidFormattedCpf() throws Exception { | ||
String cpf = brCpfIdNumber.getValidUnformattedCpf(faker); | ||
assertTrue(brCpfIdNumber.isValid(cpf)); | ||
} | ||
|
||
@Test | ||
public void testGetInvalidUnformattedCpfShouldReturnInvalidFormattedCpf() throws Exception { | ||
String cpf = brCpfIdNumber.getInvalidUnformattedCpf(faker); | ||
assertFalse(brCpfIdNumber.isValid(cpf)); | ||
} | ||
} |