Skip to content

Commit

Permalink
#1214 [refactoring] load country-specific ID number providers by coun…
Browse files Browse the repository at this point in the history
…try code

Thus, we can add ID numbers for new countries just by implementing "IdNumbers" without need to add public methods like "validZhCNSsn()", "validEstonianPersonalCode()", "validBulgarianPersonalCode()" to class "IdNumber".

Now all country-specific ID number generators are loaded from file "src/main/resources/META-INF/services/net.datafaker.idnumbers.IdNumbers" using a standard Java mechanism "service loader".

+ rename existing "*IdNumber" classes to human-readable, e.g. "ZhCnIdNumber" to "ChineseIdNumber".
  • Loading branch information
asolntsev committed May 28, 2024
1 parent bfba030 commit 7cc9929
Show file tree
Hide file tree
Showing 45 changed files with 611 additions and 465 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ target
!.idea/icon.png
*.iml
/dependency-reduced-pom.xml
META-INF/
bin/
site
.mvn/wrapper/maven-wrapper.jar
11 changes: 8 additions & 3 deletions src/main/java/net/datafaker/idnumbers/AlbanianIdNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ public class AlbanianIdNumber implements IdNumbers {
private static final String FIRST_CHAR = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String CHECKSUM_CHAR = "WABCDEFGHIJKLMNOPQRSTUV";

public String getInvalid(BaseProviders faker) {
String pin = getValid(faker);
@Override
public String country() {
return "AL";
}

public String generateInvalid(BaseProviders faker) {
String pin = generateValid(faker);
int invalidMonth = faker.number().numberBetween(93, 99);
return pin.substring(0, 2) + invalidMonth + pin.substring(4);
}

public String getValid(BaseProviders faker) {
public String generateValid(BaseProviders faker) {
LocalDate birthDate = faker.timeAndDate().birthday(0, 200);
boolean female = faker.bool().bool();
String basePart = yy(birthDate.getYear()) + mm(birthDate.getMonthValue(), female) + dd(birthDate.getDayOfMonth()) + sss(faker);
Expand Down
43 changes: 43 additions & 0 deletions src/main/java/net/datafaker/idnumbers/AmericanIdNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package net.datafaker.idnumbers;

import net.datafaker.providers.base.BaseProviders;

import java.util.List;
import java.util.regex.Pattern;

public class AmericanIdNumber implements IdNumbers {
@Override
public String country() {
return "US";
}

private static final List<String> INVALID_SSNS = List.of(
"0{3}-\\d{2}-\\d{4}",
"\\d{3}-0{2}-\\d{4}",
"\\d{3}-\\d{2}-0{4}",
"666-\\d{2}-\\d{4}",
"9\\d{2}-\\d{2}-\\d{4}");

private static final List<Pattern> INVALID_SSN_PATTERNS = INVALID_SSNS.stream()
.map(Pattern::compile)
.toList();

@Deprecated
public String getValidSsn(BaseProviders f) {
return generateValid(f);
}

@Override
public String generateValid(BaseProviders f) {
final String ssn = f.regexify("[0-8]\\d{2}-\\d{2}-\\d{4}");

boolean isValid = INVALID_SSN_PATTERNS.stream()
.noneMatch(invalidSSNPattern -> invalidSSNPattern.matcher(ssn).matches());
return isValid ? ssn : generateValid(f);
}

@Override
public String generateInvalid(BaseProviders faker) {
return faker.regexify(faker.options().option(INVALID_SSNS));
}
}
9 changes: 7 additions & 2 deletions src/main/java/net/datafaker/idnumbers/BulgarianIdNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@ public class BulgarianIdNumber implements IdNumbers {
private static final int[] EVEN_DIGITS = {0, 2, 4, 6, 8};
private static final int[] ODD_DIGITS = {1, 3, 5, 7, 9};

public String getValid(BaseProviders faker) {
@Override
public String country() {
return "BG";
}

public String generateValid(BaseProviders faker) {
String basePart = basePart(faker);
return basePart + checksum(basePart);
}

public String getInvalid(BaseProviders faker) {
public String generateInvalid(BaseProviders faker) {
String basePart = basePart(faker);
return basePart + (checksum(basePart) + 1) % 10;
}
Expand Down

Large diffs are not rendered by default.

30 changes: 0 additions & 30 deletions src/main/java/net/datafaker/idnumbers/EnIdNumber.java

This file was deleted.

9 changes: 7 additions & 2 deletions src/main/java/net/datafaker/idnumbers/EstonianIdNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ public class EstonianIdNumber implements IdNumbers {
private static final int[] CHECKSUM_COEFFICIENTS = {1, 2, 3, 4, 5, 6, 7, 8, 9, 1};
private static final int[] CHECKSUM_COEFFICIENTS2 = {3, 4, 5, 6, 7, 8, 9, 1, 2, 3};

public String getInvalid(final BaseProviders faker) {
@Override
public String country() {
return "EE";
}

public String generateInvalid(final BaseProviders faker) {
String digits = basePart(faker);
return digits + (checksum(digits) + 1) % 10;
}

public String getValid(final BaseProviders faker) {
public String generateValid(final BaseProviders faker) {
String digits = basePart(faker);
return digits + checksum(digits);
}
Expand Down
23 changes: 23 additions & 0 deletions src/main/java/net/datafaker/idnumbers/GeorgianIdNumber.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package net.datafaker.idnumbers;

import net.datafaker.providers.base.BaseProviders;

/**
* Generates ID numbers for Georgian citizens and Residents
*/
public class GeorgianIdNumber implements IdNumbers {
@Override
public String country() {
return "GE";
}

@Override
public String generateValid(BaseProviders faker) {
return faker.numerify("###########");
}

@Override
public String generateInvalid(BaseProviders faker) {
return faker.numerify("###########42");
}
}
17 changes: 17 additions & 0 deletions src/main/java/net/datafaker/idnumbers/IdNumbers.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
package net.datafaker.idnumbers;

import net.datafaker.providers.base.BaseProviders;

import java.time.format.DateTimeFormatter;

public interface IdNumbers {
DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd");

/**
* @return ISO-2 code of the country ("US" for America, "EE" for Estonia etc.)
*/
String country();

/**
* Generates a valid ID number for given country (a.k.a. "SSN", "Personal code" etc.)
*/
String generateValid(BaseProviders faker);

/**
* Generates an invalid ID number for given country (a.k.a. "SSN", "Personal code" etc.)
*/
String generateInvalid(BaseProviders faker);
}
9 changes: 7 additions & 2 deletions src/main/java/net/datafaker/idnumbers/MacedonianIdNumber.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
public class MacedonianIdNumber implements IdNumbers {
private static final List<String> REGIONS = List.of("41", "42", "43", "44", "45", "46", "47", "48", "49");

public String getValid(BaseProviders faker) {
@Override
public String country() {
return "MK";
}

public String generateValid(BaseProviders faker) {
String basePart = basePart(faker);
return basePart + checksum(basePart);
}

public String getInvalid(BaseProviders faker) {
public String generateInvalid(BaseProviders faker) {
String basePart = basePart(faker);
return basePart + (checksum(basePart) + 1) % 10;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
import net.datafaker.providers.base.BaseProviders;
import net.datafaker.providers.base.Options;

import java.util.Set;
import java.time.LocalDate;

/**
* Implementation based on the definition at
* <a href="https://en.wikipedia.org/wiki/Unique_Population_Registry_Code">https://en.wikipedia.org/wiki/Unique_Population_Registry_Code</a>
*/
public class EsMXIdNumber implements IdNumbers {
public class MexicanIdNumber implements IdNumbers {

@Override
public String country() {
return "MX";
}

private static final String[] CHA = {
"HEFA560427MVZRRL04",
Expand Down Expand Up @@ -39,15 +44,19 @@ public class EsMXIdNumber implements IdNumbers {
"TB", "TM", "TL", "VE", "YU", "ZA", "NE",
};
private static final char[] SEX = {'H', 'M'};
private static final Set<Integer> BIG_MONTHS = Set.of(1, 3, 5, 7, 8, 10, 12);

@Deprecated
public String get(BaseProviders faker) {
return generateValid(faker);
}

/**
* Get A valid MEX CURP.
*
* @param faker faker
* @return A valid MEX CURP.
*/
public String get(BaseProviders faker) {
public String generateValid(BaseProviders faker) {
final Options options = faker.options();
char[] birthDay = getBirthday(faker).toCharArray();
final char[] ssn = new char[18];
Expand All @@ -67,13 +76,18 @@ public String get(BaseProviders faker) {
return String.valueOf(ssn);
}

@Deprecated
public String getWrong(BaseProviders faker) {
return generateInvalid(faker);
}

/**
* Get A invalid MEX CURP.
*
* @param faker faker
* @return A invalid MEX CURP.
*/
public String getWrong(BaseProviders faker) {
public String generateInvalid(BaseProviders faker) {
return faker.options().option(CHA);
}

Expand All @@ -84,29 +98,8 @@ public String getWrong(BaseProviders faker) {
* @return A valid date.
*/
private String getBirthday(BaseProviders f) {
int year = f.random().nextInt(1900, 2021);
int month = f.random().nextInt(1, 12);
int day = validDay(year, month, f);
return String.valueOf(year * 10000 + month * 100 + day);
}


/**
* Gets a valid day according to year and month.
*
* @param year A specific year.
* @param month A specific month.
* @param f A specific instance of Faker.
* @return A valid day.
*/
private int validDay(int year, int month, BaseProviders f) {
if (month == 2) {
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) {
return f.random().nextInt(1, 29);
} else return f.random().nextInt(1, 28);
} else if (BIG_MONTHS.contains(month)) {
return f.random().nextInt(1, 31);
} else return f.random().nextInt(1, 30);
LocalDate birthday = f.timeAndDate().birthday(0, 120);
return String.valueOf(birthday.getYear() * 10000 + birthday.getMonthValue() * 100 + birthday.getDayOfMonth());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,21 @@
* <a href="https://taxid.pro/docs/countries/moldova">Overview</a>
* <a href="https://taxid.pro/?example=moldova-tin-for-individuals">Online generator</a>
*/
public class MoldovaIdNumber implements IdNumbers {
public class MoldovanIdNumber implements IdNumbers {

private static final int[] CHECKSUM_MASK = new int[]{7, 3, 1, 7, 3, 1, 7, 3, 1, 7, 3, 1};

public String getValid(BaseProviders faker) {
@Override
public String country() {
return "MD";
}

public String generateValid(BaseProviders faker) {
String basePart = basePart(faker);
return basePart + checksum(basePart);
}

public String getInvalid(BaseProviders faker) {
public String generateInvalid(BaseProviders faker) {
String basePart = basePart(faker);
return basePart + (checksum(basePart) + 1) % 10;
}
Expand Down
Loading

0 comments on commit 7cc9929

Please sign in to comment.