From 4fe288d73f892a82b0b5f5f3684cf4aa87d8da1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Thu, 7 May 2020 09:41:13 +0200 Subject: [PATCH 01/13] Updated to latest junit. --- build.gradle | 14 +++++++++----- src/test/java/PersonnummerTest.java | 6 +++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 3eea1a1..152b80c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,15 +1,18 @@ -apply plugin: 'java-library' -apply plugin: 'java' +plugins { + id 'java-library' + id 'java' +} + sourceCompatibility = 1.8 targetCompatibility = 1.8 repositories { - jcenter() + mavenCentral() } dependencies { - testImplementation 'junit:junit:4.12' - testCompile "junit:junit:4.12" + testImplementation('org.junit.jupiter:junit-jupiter:5.6.2') + testCompile('org.junit.jupiter:junit-jupiter:5.6.2') } version = '0.1.0' @@ -22,6 +25,7 @@ jar { } test { + useJUnitPlatform() testLogging { events "passed", "skipped", "failed" exceptionFormat "full" diff --git a/src/test/java/PersonnummerTest.java b/src/test/java/PersonnummerTest.java index d0cf98e..e8b5843 100644 --- a/src/test/java/PersonnummerTest.java +++ b/src/test/java/PersonnummerTest.java @@ -1,6 +1,6 @@ -import org.junit.Test; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; public class PersonnummerTest { From 8c31e9c034a7fd5d157ed05218b6d4002f3642ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Wed, 17 Jun 2020 11:03:50 +0200 Subject: [PATCH 02/13] WIP. --- build.gradle | 4 +++- src/test/java/PersonnummerTest.java | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 152b80c..6bb4e70 100644 --- a/build.gradle +++ b/build.gradle @@ -10,12 +10,14 @@ repositories { mavenCentral() } + + dependencies { testImplementation('org.junit.jupiter:junit-jupiter:5.6.2') testCompile('org.junit.jupiter:junit-jupiter:5.6.2') } -version = '0.1.0' +version = '3.0.0' jar { manifest { diff --git a/src/test/java/PersonnummerTest.java b/src/test/java/PersonnummerTest.java index e8b5843..72e0b71 100644 --- a/src/test/java/PersonnummerTest.java +++ b/src/test/java/PersonnummerTest.java @@ -4,6 +4,8 @@ public class PersonnummerTest { + + @Test public void testWithControlDigit() { assertTrue(Personnummer.valid("6403273813")); From 55b27ce19fa2f89f598f4fd211ccae8f3e22407a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Wed, 17 Jun 2020 14:17:46 +0200 Subject: [PATCH 03/13] Updated tests to use parameterized tests. --- src/test/java/PersonnummerTest.java | 139 +++++++++++++++------------- 1 file changed, 73 insertions(+), 66 deletions(-) diff --git a/src/test/java/PersonnummerTest.java b/src/test/java/PersonnummerTest.java index 29c39d1..a51c429 100644 --- a/src/test/java/PersonnummerTest.java +++ b/src/test/java/PersonnummerTest.java @@ -1,6 +1,3 @@ -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -10,12 +7,51 @@ import java.util.ArrayList; import java.util.List; import org.json.*; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; public class PersonnummerTest { + //region Static setup stuff! private static Boolean fileLoaded = false; + public static List getValidSsnInt() { + return validSsnInt; + } + + public static List getValidSsnString() { + return validSsnString; + } + + public static List getInvalidSsnInt() { + return invalidSsnInt; + } + + public static List getInvalidSsnString() { + return invalidSsnString; + } + + public static List getValidConInt() { + return validConInt; + } + + public static List getValidConString() { + return validConString; + } + + public static List getInvalidConInt() { + return invalidConInt; + } + + public static List getInvalidConString() { + return invalidConString; + } + private static List validSsnInt = new ArrayList<>(); private static List validSsnString = new ArrayList<>(); private static List invalidSsnInt = new ArrayList<>(); @@ -25,12 +61,12 @@ public class PersonnummerTest { private static List invalidConInt = new ArrayList<>(); private static List invalidConString = new ArrayList<>(); - @AfterClass + @AfterAll public static void deleteTestData() throws IOException { Files.delete(Paths.get("temp.json")); } - @BeforeClass + @BeforeAll public static void loadTestData() throws IOException { if (fileLoaded) { return; @@ -48,19 +84,21 @@ public static void loadTestData() throws IOException { JSONObject ssn = json.getJSONObject("ssn"); JSONObject con = json.getJSONObject("con"); - validSsnInt = getIntList(ssn, "integer", "valid"); - invalidSsnInt = getIntList(ssn, "integer", "invalid"); - validSsnString = getStringList(ssn, "string", "valid"); - invalidSsnString = getStringList(ssn, "string", "invalid"); + validSsnInt = getIntList(ssn, "valid"); + invalidSsnInt = getIntList(ssn, "invalid"); + validSsnString = getStringList(ssn, "valid"); + invalidSsnString = getStringList(ssn, "invalid"); - validConInt = getIntList(con, "integer", "valid"); - invalidConInt = getIntList(con, "integer", "invalid"); - validConString = getStringList(con, "string", "valid"); - invalidConString = getStringList(con, "string", "invalid"); + validConInt = getIntList(con, "valid"); + invalidConInt = getIntList(con, "invalid"); + validConString = getStringList(con, "valid"); + invalidConString = getStringList(con, "invalid"); } - private static ArrayList getStringList(JSONObject root, String dataType, String valid) { - JSONArray arr = root.getJSONObject(dataType).getJSONArray(valid); + //endregion + + private static ArrayList getStringList(JSONObject root, String valid) { + JSONArray arr = root.getJSONObject("string").getJSONArray(valid); ArrayList result = new ArrayList<>(); for (int i=0; i getStringList(JSONObject root, String dataType, return result; } - private static ArrayList getIntList(JSONObject root, String dataType, String valid) { - JSONArray arr = root.getJSONObject(dataType).getJSONArray(valid); + private static ArrayList getIntList(JSONObject root, String valid) { + JSONArray arr = root.getJSONObject("integer").getJSONArray(valid); ArrayList result = new ArrayList<>(); for (int i=0; i getIntList(JSONObject root, String dataType, Stri return result; } - @Test - public void testPersonnNummerWithInvalidIntegerValues() { - for (Long ssn: invalidSsnInt) { - assertFalse(Personnummer.valid(ssn)); - } + @ParameterizedTest + @MethodSource({"getInvalidSsnInt", "getInvalidConInt"}) + public void testInvalidIntegerValues(long ssn) { + assertFalse(Personnummer.valid(ssn)); } - @Test - public void testCoordinationNummerWithInvalidIntegerValues() { - for (Long ssn: invalidConInt) { - assertFalse(Personnummer.valid(ssn)); - } + @ParameterizedTest + @MethodSource({"getInvalidConString", "getInvalidSsnString"}) + public void testInvalidStringValues(String ssn) { + assertFalse(Personnummer.valid(ssn)); } - @Test - public void testPersonnNummerWithInvalidStringValues() { - for (String ssn: invalidSsnString) { - assertFalse(Personnummer.valid(ssn)); - } + @ParameterizedTest + @MethodSource({"getValidConInt", "getValidSsnInt"}) + public void testValidIntegerValues(Long ssn) { + assertTrue(Personnummer.valid(ssn)); } - @Test - public void testCoordinationNummerWithInvalidStringValues() { - for (String ssn: invalidConString) { - assertFalse(Personnummer.valid(ssn)); - } - } - @Test - public void testPersonnNummerWithValidIntegerValues() { - for (Long ssn: validSsnInt) { - assertTrue(Personnummer.valid(ssn)); - } - } - - @Test - public void testCoordinationNummerVnvalidIntegerValues() { - for (Long ssn: validConInt) { - assertTrue(Personnummer.valid(ssn)); - } - } - - @Test - public void testPersonnNummerWithValidStringValues() { - for (String ssn: validSsnString) { - assertTrue(Personnummer.valid(ssn)); - } - } - - @Test - public void testCoordinationNummerWithValidStringValues() { - for (String ssn: validConString) { - assertTrue(Personnummer.valid(ssn)); - } + @ParameterizedTest + @MethodSource({"getValidConString", "getValidSsnString"}) + public void testValidStringValues(String ssn) { + assertTrue(Personnummer.valid(ssn)); } } From e7e5af7d6d971708717f62a2d9c5865603b307c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Wed, 17 Jun 2020 15:17:38 +0200 Subject: [PATCH 04/13] Added a data provider for all test data. --- src/main/java/Personnummer.java | 4 ++ src/test/java/DataProvider.java | 57 +++++++++++++++++++++++++++++ src/test/java/PersonnummerData.java | 25 +++++++++++++ src/test/java/PersonnummerTest.java | 4 ++ 4 files changed, 90 insertions(+) create mode 100644 src/test/java/DataProvider.java create mode 100644 src/test/java/PersonnummerData.java diff --git a/src/main/java/Personnummer.java b/src/main/java/Personnummer.java index ac717bb..be768d3 100644 --- a/src/main/java/Personnummer.java +++ b/src/main/java/Personnummer.java @@ -15,6 +15,10 @@ public final class Personnummer { regexPattern = Pattern.compile("^(\\d{2})?(\\d{2})(\\d{2})(\\d{2})([-|+]?)?((?!000)\\d{3})(\\d?)$"); } + private final Integer realDay; + + + private Personnummer() { throw new AssertionError("Class cannot be instantiated"); } diff --git a/src/test/java/DataProvider.java b/src/test/java/DataProvider.java new file mode 100644 index 0000000..71a0b2e --- /dev/null +++ b/src/test/java/DataProvider.java @@ -0,0 +1,57 @@ +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.*; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class DataProvider { + private static final List all = new ArrayList<>(); + + public static void Initialize() throws IOException { + InputStream in = new URL("https://raw.githubusercontent.com/personnummer/meta/master/testdata/list.json").openStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String json = ""; + String line; + while ((line = reader.readLine()) != null) { + json = json.concat(line); + } + JSONArray rootObject = new JSONArray(json); + for (int i = 0; i < rootObject.length(); i++) { + JSONObject current = rootObject.getJSONObject(i); + all.add(new PersonnummerData( + current.getLong("integer"), + current.getString("long_format"), + current.getString("short_format"), + current.getString("separated_format"), + current.getString("separated_long"), + current.getBoolean("valid"), + current.getString("type"), + current.getBoolean("isMale"), + current.getBoolean("isFemale") + )); + } + } + + public static List getCoordinationNumbers() { + return all.stream().filter(o -> !o.type.equals("ssn")).collect(Collectors.toList()); + } + public static List getPersonnummer() { + return all.stream().filter(o -> o.type.equals("ssn")).collect(Collectors.toList()); + } + public static List getInvalidCoordinationNumbers() { + return getCoordinationNumbers().stream().filter(o -> !o.valid).collect(Collectors.toList()); + } + public static List getInvalidPersonnummer() { + return getPersonnummer().stream().filter(o -> !o.valid).collect(Collectors.toList()); + } + public static List getValidCoordinationNumbers() { + return getCoordinationNumbers().stream().filter(o -> o.valid).collect(Collectors.toList()); + } + public static List getValidPersonnummer() { + return getPersonnummer().stream().filter(o -> o.valid).collect(Collectors.toList()); + } + +} diff --git a/src/test/java/PersonnummerData.java b/src/test/java/PersonnummerData.java new file mode 100644 index 0000000..b86e6b3 --- /dev/null +++ b/src/test/java/PersonnummerData.java @@ -0,0 +1,25 @@ +public class PersonnummerData { + + public PersonnummerData(Long integer, String longFormat, String shortFormat, String separatedFormat, String separatedLong, Boolean valid, String type, Boolean isMale, Boolean isFemale) { + this.integer = integer; + this.longFormat = longFormat; + this.shortFormat = shortFormat; + this.separatedFormat = separatedFormat; + this.separatedLong = separatedLong; + this.valid = valid; + this.type = type; + this.isMale = isMale; + this.isFemale = isFemale; + } + + public Long integer; + public String longFormat; + public String shortFormat; + public String separatedFormat; + public String separatedLong; + public Boolean valid; + public String type; + public Boolean isMale; + public boolean isFemale; + +} diff --git a/src/test/java/PersonnummerTest.java b/src/test/java/PersonnummerTest.java index a51c429..3c09da2 100644 --- a/src/test/java/PersonnummerTest.java +++ b/src/test/java/PersonnummerTest.java @@ -115,6 +115,10 @@ private static ArrayList getIntList(JSONObject root, String valid) { return result; } + public void testConstructor(String ssn) {} + + + @ParameterizedTest @MethodSource({"getInvalidSsnInt", "getInvalidConInt"}) public void testInvalidIntegerValues(long ssn) { From 8a500ec95f21c0a5060cc7a2e4e1b2353e05f00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Wed, 17 Jun 2020 18:52:42 +0200 Subject: [PATCH 05/13] Moved package to shared package name. Updated tests to use v3 tests. --- src/main/java/dev/personnummer/Options.java | 12 + .../{ => dev/personnummer}/Personnummer.java | 60 ++++- .../personnummer/PersonnummerException.java | 3 + src/test/java/DataProvider.java | 2 +- src/test/java/PersonnummerTest.java | 226 ++++++++++-------- 5 files changed, 201 insertions(+), 102 deletions(-) create mode 100644 src/main/java/dev/personnummer/Options.java rename src/main/java/{ => dev/personnummer}/Personnummer.java (69%) create mode 100644 src/main/java/dev/personnummer/PersonnummerException.java diff --git a/src/main/java/dev/personnummer/Options.java b/src/main/java/dev/personnummer/Options.java new file mode 100644 index 0000000..7d8fef1 --- /dev/null +++ b/src/main/java/dev/personnummer/Options.java @@ -0,0 +1,12 @@ +package dev.personnummer; + +public class Options { + public Options(boolean allowCoordinationNumber) { + this.allowCoordinationNumber = allowCoordinationNumber; + } + + public Options() { + } + + boolean allowCoordinationNumber = true; +} diff --git a/src/main/java/Personnummer.java b/src/main/java/dev/personnummer/Personnummer.java similarity index 69% rename from src/main/java/Personnummer.java rename to src/main/java/dev/personnummer/Personnummer.java index be768d3..0ab7b6a 100644 --- a/src/main/java/Personnummer.java +++ b/src/main/java/dev/personnummer/Personnummer.java @@ -1,28 +1,80 @@ +package dev.personnummer; + import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.regex.Matcher; import java.util.regex.Pattern; - /** * Class used to validate Swedish personal identity numbers. * * @author Johannes Tegnér */ public final class Personnummer { + + private static final Pattern regexPattern; static { regexPattern = Pattern.compile("^(\\d{2})?(\\d{2})(\\d{2})(\\d{2})([-|+]?)?((?!000)\\d{3})(\\d?)$"); } - private final Integer realDay; + public static Personnummer parse(String personnummer, Options options) { + return new Personnummer(personnummer, options); + } + public static Personnummer parse(String personnummer) { + return parse(personnummer, new Options()); + } + public static Personnummer parse(Long personnummer, Options options) { + return parse(Long.toString(personnummer), options); + } - private Personnummer() { - throw new AssertionError("Class cannot be instantiated"); + public static Personnummer parse(Long personnummer) { + return parse(personnummer, new Options()); } + public Personnummer(String personnummer, Options options) { + + } + + public Personnummer(String personnummer) { + this(personnummer, new Options()); + } + + public Personnummer(Long personnummer, Options options) { + this(Long.toString(personnummer), options); + } + + public Personnummer(Long personnummer) { + this(personnummer, new Options()); + } + + public int getAge() { + return -1; + } + + public String format() { + return format(false); + } + + public String format(boolean longFormat) { + return ""; + } + + public Boolean isMale() { + return null; + } + + public Boolean isFemale() { + return null; + } + + public String separator() { + return "+"; + } + + /** * Validate a Swedish personal identity number. * diff --git a/src/main/java/dev/personnummer/PersonnummerException.java b/src/main/java/dev/personnummer/PersonnummerException.java new file mode 100644 index 0000000..508c0c7 --- /dev/null +++ b/src/main/java/dev/personnummer/PersonnummerException.java @@ -0,0 +1,3 @@ +package dev.personnummer; + +public class PersonnummerException extends Exception { } diff --git a/src/test/java/DataProvider.java b/src/test/java/DataProvider.java index 71a0b2e..65302e0 100644 --- a/src/test/java/DataProvider.java +++ b/src/test/java/DataProvider.java @@ -10,7 +10,7 @@ public class DataProvider { private static final List all = new ArrayList<>(); - public static void Initialize() throws IOException { + public static void initialize() throws IOException { InputStream in = new URL("https://raw.githubusercontent.com/personnummer/meta/master/testdata/list.json").openStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String json = ""; diff --git a/src/test/java/PersonnummerTest.java b/src/test/java/PersonnummerTest.java index 3c09da2..5ad8e84 100644 --- a/src/test/java/PersonnummerTest.java +++ b/src/test/java/PersonnummerTest.java @@ -1,147 +1,179 @@ import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.List; -import org.json.*; - -import org.junit.jupiter.api.AfterAll; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import dev.personnummer.*; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.*; public class PersonnummerTest { - //region Static setup stuff! - private static Boolean fileLoaded = false; - public static List getValidSsnInt() { - return validSsnInt; + @BeforeAll + public static void setup() throws IOException { + DataProvider.initialize(); } - public static List getValidSsnString() { - return validSsnString; + @ParameterizedTest + @MethodSource("DataProvider#getValidPersonnummer") + public void testConstructor(PersonnummerData ssn) { + assertDoesNotThrow(() -> new Personnummer(ssn.longFormat, new Options(false))); + assertDoesNotThrow(() -> new Personnummer(ssn.shortFormat, new Options(false))); + assertDoesNotThrow(() -> new Personnummer(ssn.separatedFormat, new Options(false))); + assertDoesNotThrow(() -> new Personnummer(ssn.separatedFormat, new Options(false))); } - public static List getInvalidSsnInt() { - return invalidSsnInt; + @ParameterizedTest + @MethodSource("DataProvider#getValidCoordinationNumbers") + public void testConstructorCoord(PersonnummerData ssn) { + assertDoesNotThrow(() -> new Personnummer(ssn.longFormat, new Options(true))); + assertDoesNotThrow(() -> new Personnummer(ssn.shortFormat, new Options(true))); + assertDoesNotThrow(() -> new Personnummer(ssn.separatedFormat, new Options(true))); + assertDoesNotThrow(() -> new Personnummer(ssn.separatedFormat, new Options(true))); } - public static List getInvalidSsnString() { - return invalidSsnString; + @ParameterizedTest + @MethodSource({"DataProvider#getInvalidPersonnummer", "DataProvider#getValidCoordinationNumbers"}) + public void testConstructorInvalid(PersonnummerData ssn) { + assertThrows(PersonnummerException.class, () -> new Personnummer(ssn.longFormat, new Options(false))); + assertThrows(PersonnummerException.class, () -> new Personnummer(ssn.shortFormat, new Options(false))); + assertThrows(PersonnummerException.class, () -> new Personnummer(ssn.separatedFormat, new Options(false))); + assertThrows(PersonnummerException.class, () -> new Personnummer(ssn.separatedFormat, new Options(false))); } - public static List getValidConInt() { - return validConInt; + @ParameterizedTest + @MethodSource({"DataProvider#getInvalidCoordinationNumbers", "DataProvider#getValidPersonnummer"}) + public void testConstructorCoordInvalid(PersonnummerData ssn) { + assertThrows(PersonnummerException.class, () -> new Personnummer(ssn.longFormat, new Options(true))); + assertThrows(PersonnummerException.class, () -> new Personnummer(ssn.shortFormat, new Options(true))); + assertThrows(PersonnummerException.class, () -> new Personnummer(ssn.separatedFormat, new Options(true))); + assertThrows(PersonnummerException.class, () -> new Personnummer(ssn.separatedFormat, new Options(true))); } - public static List getValidConString() { - return validConString; + @ParameterizedTest + @MethodSource("DataProvider#getValidPersonnummer") + public void testParse(PersonnummerData ssn) { + assertDoesNotThrow(() -> Personnummer.parse(ssn.longFormat, new Options(false))); + assertDoesNotThrow(() -> Personnummer.parse(ssn.shortFormat, new Options(false))); + assertDoesNotThrow(() -> Personnummer.parse(ssn.separatedFormat, new Options(false))); + assertDoesNotThrow(() -> Personnummer.parse(ssn.separatedFormat, new Options(false))); } - public static List getInvalidConInt() { - return invalidConInt; + @ParameterizedTest + @MethodSource({"DataProvider#getValidCoordinationNumbers", "DataProvider#getValidPersonnummer"}) + public void testParseCoord(PersonnummerData ssn) { + assertDoesNotThrow(() -> Personnummer.parse(ssn.longFormat, new Options(true))); + assertDoesNotThrow(() -> Personnummer.parse(ssn.shortFormat, new Options(true))); + assertDoesNotThrow(() -> Personnummer.parse(ssn.separatedFormat, new Options(true))); + assertDoesNotThrow(() -> Personnummer.parse(ssn.separatedFormat, new Options(true))); } - public static List getInvalidConString() { - return invalidConString; + @ParameterizedTest + @MethodSource({"DataProvider#getInvalidPersonnummer", "DataProvider#getValidCoordinationNumbers"}) + public void testParseInvalid(PersonnummerData ssn) { + assertThrows(PersonnummerException.class, () -> Personnummer.parse(ssn.longFormat, new Options(false))); + assertThrows(PersonnummerException.class, () -> Personnummer.parse(ssn.shortFormat, new Options(false))); + assertThrows(PersonnummerException.class, () -> Personnummer.parse(ssn.separatedFormat, new Options(false))); + assertThrows(PersonnummerException.class, () -> Personnummer.parse(ssn.separatedFormat, new Options(false))); } - private static List validSsnInt = new ArrayList<>(); - private static List validSsnString = new ArrayList<>(); - private static List invalidSsnInt = new ArrayList<>(); - private static List invalidSsnString = new ArrayList<>(); - private static List validConInt = new ArrayList<>(); - private static List validConString = new ArrayList<>(); - private static List invalidConInt = new ArrayList<>(); - private static List invalidConString = new ArrayList<>(); - - @AfterAll - public static void deleteTestData() throws IOException { - Files.delete(Paths.get("temp.json")); + @ParameterizedTest + @MethodSource("DataProvider#getInvalidCoordinationNumbers") + public void testParseInvalidCoord(PersonnummerData ssn) { + assertThrows(PersonnummerException.class, () -> Personnummer.parse(ssn.longFormat, new Options(true))); + assertThrows(PersonnummerException.class, () -> Personnummer.parse(ssn.shortFormat, new Options(true))); + assertThrows(PersonnummerException.class, () -> Personnummer.parse(ssn.separatedFormat, new Options(true))); + assertThrows(PersonnummerException.class, () -> Personnummer.parse(ssn.separatedFormat, new Options(true))); } - @BeforeAll - public static void loadTestData() throws IOException { - if (fileLoaded) { - return; - } - if (!Files.exists(Paths.get("temp.json"))) { - InputStream in = new URL("https://raw.githubusercontent.com/personnummer/meta/master/testdata/structured.json").openStream(); - Files.copy(in, Paths.get("temp.json"), StandardCopyOption.REPLACE_EXISTING); - fileLoaded = true; - } + @ParameterizedTest + @MethodSource({"DataProvider#getValidPersonnummer"}) + public void testAge(PersonnummerData ssn) { + LocalDate date = LocalDate.parse(ssn.longFormat.substring(0, ssn.longFormat.length() - 4), DateTimeFormatter.ofPattern("yyyyMMdd")); + int years = (date.until(LocalDate.now())).getYears(); - String jsonString = new String(Files.readAllBytes(Paths.get("temp.json"))); - JSONObject json = new JSONObject(jsonString); + assertEquals(years, Personnummer.parse(ssn.separatedLong, new Options(false)).getAge()); + assertEquals(years, Personnummer.parse(ssn.separatedFormat, new Options(false)).getAge()); + assertEquals(years, Personnummer.parse(ssn.longFormat, new Options(false)).getAge()); + assertEquals(years > 99 ? years - 100 : years, Personnummer.parse(ssn.shortFormat, new Options(false)).getAge()); + } - JSONObject ssn = json.getJSONObject("ssn"); - JSONObject con = json.getJSONObject("con"); + @ParameterizedTest + @MethodSource({"DataProvider#getValidCoordinationNumbers"}) + public void testAgeCn(PersonnummerData ssn) { + String strDay = ssn.longFormat.substring(ssn.longFormat.length() - 6, ssn.longFormat.length() - 4); + int day = Integer.parseInt(strDay) - 60; + strDay = day < 10 ? "0" + Integer.toString(day) : Integer.toString(day); - validSsnInt = getIntList(ssn, "valid"); - invalidSsnInt = getIntList(ssn, "invalid"); - validSsnString = getStringList(ssn, "valid"); - invalidSsnString = getStringList(ssn, "invalid"); + LocalDate date = LocalDate.parse(ssn.longFormat.substring(0, ssn.longFormat.length() - 6) + strDay, DateTimeFormatter.ofPattern("yyyyMMdd")); + int years = (date.until(LocalDate.now())).getYears(); - validConInt = getIntList(con, "valid"); - invalidConInt = getIntList(con, "invalid"); - validConString = getStringList(con, "valid"); - invalidConString = getStringList(con, "invalid"); + assertEquals(years, Personnummer.parse(ssn.separatedLong, new Options(true)).getAge()); + assertEquals(years, Personnummer.parse(ssn.separatedFormat, new Options(true)).getAge()); + assertEquals(years, Personnummer.parse(ssn.longFormat, new Options(true)).getAge()); + assertEquals(years > 99 ? years - 100 : years, Personnummer.parse(ssn.shortFormat, new Options(true)).getAge()); } - //endregion - - private static ArrayList getStringList(JSONObject root, String valid) { - JSONArray arr = root.getJSONObject("string").getJSONArray(valid); - ArrayList result = new ArrayList<>(); - for (int i=0; i getIntList(JSONObject root, String valid) { - JSONArray arr = root.getJSONObject("integer").getJSONArray(valid); - ArrayList result = new ArrayList<>(); - for (int i=0; i Date: Wed, 17 Jun 2020 20:37:26 +0200 Subject: [PATCH 06/13] Implementation of functionality. --- .../java/dev/personnummer/Personnummer.java | 156 ++++++++++++------ src/test/java/PersonnummerTest.java | 21 ++- 2 files changed, 119 insertions(+), 58 deletions(-) diff --git a/src/main/java/dev/personnummer/Personnummer.java b/src/main/java/dev/personnummer/Personnummer.java index 0ab7b6a..41b57e8 100644 --- a/src/main/java/dev/personnummer/Personnummer.java +++ b/src/main/java/dev/personnummer/Personnummer.java @@ -1,57 +1,121 @@ package dev.personnummer; +import javax.swing.*; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.regex.Matcher; import java.util.regex.Pattern; + +import static java.time.format.DateTimeFormatter.ofPattern; + /** * Class used to validate Swedish personal identity numbers. * * @author Johannes Tegnér */ public final class Personnummer { - - private static final Pattern regexPattern; static { regexPattern = Pattern.compile("^(\\d{2})?(\\d{2})(\\d{2})(\\d{2})([-|+]?)?((?!000)\\d{3})(\\d?)$"); } - public static Personnummer parse(String personnummer, Options options) { + public static Personnummer parse(String personnummer, Options options) throws PersonnummerException { return new Personnummer(personnummer, options); } - public static Personnummer parse(String personnummer) { + public static Personnummer parse(String personnummer) throws PersonnummerException { return parse(personnummer, new Options()); } - public static Personnummer parse(Long personnummer, Options options) { + public static Personnummer parse(Long personnummer, Options options) throws PersonnummerException { return parse(Long.toString(personnummer), options); } - public static Personnummer parse(Long personnummer) { + public static Personnummer parse(Long personnummer) throws PersonnummerException { return parse(personnummer, new Options()); } - public Personnummer(String personnummer, Options options) { + private final int realDay; + private final String fullYear; + private final String century; + private final String year; + private final String month; + private final String day; + private final String numbers; + private final String controlNumber; + private final boolean isMale; + private final boolean isFemale; + + + + public Personnummer(String personnummer, Options options) throws PersonnummerException { + if (personnummer == null) { + throw new PersonnummerException(); + } + + Matcher matches = regexPattern.matcher(personnummer); + if (!matches.find()) { + throw new PersonnummerException(); + } + + String century; + String decade = matches.group(2); + if (matches.group(1) != null && !matches.group(1).isEmpty()) { + century = matches.group(1); + } else { + // LocalDate date = LocalDate.parse(ssn.longFormat.substring(0, ssn.longFormat.length() - 4), DateTimeFormatter.ofPattern("yyyyMMdd")); + // int years = (date.until(LocalDate.now())).getYears(); + int born = LocalDate.now().getYear() - Integer.parseInt(decade); + if (!matches.group(5).isEmpty() && matches.group(5).equals("+")) { + born -= 100; + } + + century = Integer.toString(born).substring(0, 2); + } + + int day = Integer.parseInt(matches.group(4)); + if (options.allowCoordinationNumber) { + day = day > 60 ? day - 60 : day; + } else if(day > 60) { + throw new PersonnummerException(); + } + this.realDay = day; + this.century = century; + this.year = decade; + this.fullYear = century + decade; + this.month = matches.group(3); + this.day = matches.group(4); + this.numbers = matches.group(6) + matches.group(7); + this.controlNumber = matches.group(7); + + this.isMale = Integer.parseInt(Character.toString(this.numbers.charAt(2))) % 2 == 1; + this.isFemale = !this.isMale; + + // The format passed to Luhn method is supposed to be YYmmDDNNN + // Hence all numbers that are less than 10 (or in last case 100) will have leading 0's added. + if (luhn(String.format("%s%s%s%s", this.year, this.month, this.day, matches.group(6))) != Integer.parseInt(this.controlNumber)) { + throw new PersonnummerException(); + } } - public Personnummer(String personnummer) { + public Personnummer(String personnummer) throws PersonnummerException { this(personnummer, new Options()); } - public Personnummer(Long personnummer, Options options) { + public Personnummer(Long personnummer, Options options) throws PersonnummerException { this(Long.toString(personnummer), options); } - public Personnummer(Long personnummer) { + public Personnummer(Long personnummer) throws PersonnummerException { this(personnummer, new Options()); } public int getAge() { - return -1; + return (LocalDate.of(Integer.parseInt(this.fullYear), Integer.parseInt(this.month), this.realDay).until(LocalDate.now())).getYears(); } public String format() { @@ -59,64 +123,62 @@ public String format() { } public String format(boolean longFormat) { - return ""; + return (longFormat ? this.fullYear : this.year) + this.month + this.day + separator() + numbers; } public Boolean isMale() { - return null; + return this.isMale; } public Boolean isFemale() { - return null; + return this.isFemale; } public String separator() { - return "+"; + return this.getAge() >= 100 ? "+" : "-"; } + public String getFullYear() { + return fullYear; + } - /** - * Validate a Swedish personal identity number. - * - * @param value personal identity number to validate, as string. - * @return True if valid. - */ - public static boolean valid(String value) { - if (value == null) { - return false; - } + public String getCentury() { + return century; + } - Matcher matches = regexPattern.matcher(value); - if (!matches.find()) { - return false; - } + public String getYear() { + return year; + } - int year, month, day, control, number; - try { - String y = matches.group(2); - year = Integer.parseInt((y.length() == 4 ? y.substring(2) : y)); - month = Integer.parseInt(matches.group(3)); - day = Integer.parseInt(matches.group(4)); - control = Integer.parseInt(matches.group(7)); - number = Integer.parseInt(matches.group(6)); - } catch (NumberFormatException e) { - return false; - } + public String getMonth() { + return month; + } - // The format passed to Luhn method is supposed to be YYmmDDNNN - // Hence all numbers that are less than 10 (or in last case 100) will have leading 0's added. - int luhn = luhn(String.format("%02d%02d%02d%03d0", year, month, day, number)); - return (luhn == control) && (testDate(year, month, day) || testDate(year, month, day - 60)); + public String getDay() { + return day; + } + + public String getNumbers() { + return numbers; + } + + public String getControlNumber() { + return controlNumber; } /** * Validate a Swedish personal identity number. * - * @param value personal identity number to validate, as long. + * @param personnummer personal identity number to validate, as string. * @return True if valid. */ - public static boolean valid(long value) { - return valid(Long.toString(value)); + public static boolean valid(String personnummer) { + try { + parse(personnummer); + return true; + } catch (PersonnummerException ex) { + return false; + } } private static int luhn(String value) { diff --git a/src/test/java/PersonnummerTest.java b/src/test/java/PersonnummerTest.java index 5ad8e84..268b69a 100644 --- a/src/test/java/PersonnummerTest.java +++ b/src/test/java/PersonnummerTest.java @@ -44,7 +44,7 @@ public void testConstructorInvalid(PersonnummerData ssn) { } @ParameterizedTest - @MethodSource({"DataProvider#getInvalidCoordinationNumbers", "DataProvider#getValidPersonnummer"}) + @MethodSource({"DataProvider#getInvalidCoordinationNumbers"}) public void testConstructorCoordInvalid(PersonnummerData ssn) { assertThrows(PersonnummerException.class, () -> new Personnummer(ssn.longFormat, new Options(true))); assertThrows(PersonnummerException.class, () -> new Personnummer(ssn.shortFormat, new Options(true))); @@ -91,7 +91,7 @@ public void testParseInvalidCoord(PersonnummerData ssn) { @ParameterizedTest @MethodSource({"DataProvider#getValidPersonnummer"}) - public void testAge(PersonnummerData ssn) { + public void testAge(PersonnummerData ssn) throws PersonnummerException { LocalDate date = LocalDate.parse(ssn.longFormat.substring(0, ssn.longFormat.length() - 4), DateTimeFormatter.ofPattern("yyyyMMdd")); int years = (date.until(LocalDate.now())).getYears(); @@ -103,7 +103,7 @@ public void testAge(PersonnummerData ssn) { @ParameterizedTest @MethodSource({"DataProvider#getValidCoordinationNumbers"}) - public void testAgeCn(PersonnummerData ssn) { + public void testAgeCn(PersonnummerData ssn) throws PersonnummerException { String strDay = ssn.longFormat.substring(ssn.longFormat.length() - 6, ssn.longFormat.length() - 4); int day = Integer.parseInt(strDay) - 60; strDay = day < 10 ? "0" + Integer.toString(day) : Integer.toString(day); @@ -119,15 +119,15 @@ public void testAgeCn(PersonnummerData ssn) { @ParameterizedTest @MethodSource({"DataProvider#getValidPersonnummer", "DataProvider#getValidCoordinationNumbers"}) - public void testFormat(PersonnummerData ssn) { - assertEquals(ssn.separatedLong, Personnummer.parse(ssn.separatedLong, new Options(true)).format()); - assertEquals(ssn.separatedLong, Personnummer.parse(ssn.separatedFormat, new Options(true)).format()); - assertEquals(ssn.separatedLong, Personnummer.parse(ssn.longFormat, new Options(true)).format()); + public void testFormat(PersonnummerData ssn) throws PersonnummerException { + assertEquals(ssn.separatedFormat, Personnummer.parse(ssn.separatedLong, new Options(true)).format()); + assertEquals(ssn.separatedFormat, Personnummer.parse(ssn.separatedFormat, new Options(true)).format()); + assertEquals(ssn.separatedFormat, Personnummer.parse(ssn.longFormat, new Options(true)).format()); } @ParameterizedTest @MethodSource({"DataProvider#getValidPersonnummer", "DataProvider#getValidCoordinationNumbers"}) - public void testFormatLong(PersonnummerData ssn) { + public void testFormatLong(PersonnummerData ssn) throws PersonnummerException { assertEquals(ssn.separatedLong, Personnummer.parse(ssn.separatedLong, new Options(true)).format(true)); assertEquals(ssn.separatedLong, Personnummer.parse(ssn.separatedFormat, new Options(true)).format(true)); assertEquals(ssn.separatedLong, Personnummer.parse(ssn.longFormat, new Options(true)).format(true)); @@ -153,7 +153,7 @@ public void testValidInvalid(PersonnummerData ssn) { @ParameterizedTest @MethodSource({"DataProvider#getValidPersonnummer", "DataProvider#getValidCoordinationNumbers"}) - public void testMaleFemale(PersonnummerData ssn) { + public void testMaleFemale(PersonnummerData ssn) throws PersonnummerException { assertEquals(ssn.isMale, Personnummer.parse(ssn.longFormat, new Options(true)).isMale()); assertEquals(ssn.isMale, Personnummer.parse(ssn.separatedLong, new Options(true)).isMale()); assertEquals(ssn.isMale, Personnummer.parse(ssn.separatedFormat, new Options(true)).isMale()); @@ -167,8 +167,7 @@ public void testMaleFemale(PersonnummerData ssn) { @ParameterizedTest @MethodSource({"DataProvider#getValidPersonnummer", "DataProvider#getValidCoordinationNumbers"}) - public void testSeparator(PersonnummerData ssn) - { + public void testSeparator(PersonnummerData ssn) throws PersonnummerException { String sep = ssn.separatedFormat.contains("+") ? "+" : "-"; assertEquals(sep, Personnummer.parse(ssn.longFormat, new Options(true)).separator()); assertEquals(sep, Personnummer.parse(ssn.separatedLong, new Options(true)).separator()); From ffd15acf1d5e3a260b8ed16a6fa007b3642bbd0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Wed, 17 Jun 2020 20:58:15 +0200 Subject: [PATCH 07/13] JavaDocs. --- .../java/dev/personnummer/Personnummer.java | 77 +++++++++++-------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/src/main/java/dev/personnummer/Personnummer.java b/src/main/java/dev/personnummer/Personnummer.java index 41b57e8..e1ba985 100644 --- a/src/main/java/dev/personnummer/Personnummer.java +++ b/src/main/java/dev/personnummer/Personnummer.java @@ -1,15 +1,9 @@ package dev.personnummer; -import javax.swing.*; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static java.time.format.DateTimeFormatter.ofPattern; - /** * Class used to validate Swedish personal identity numbers. * @@ -22,21 +16,29 @@ public final class Personnummer { regexPattern = Pattern.compile("^(\\d{2})?(\\d{2})(\\d{2})(\\d{2})([-|+]?)?((?!000)\\d{3})(\\d?)$"); } + /** + * Create a new Personnummber object from a string. + * In case options is not passed, they will default to accept any personal and coordination numbers. + * + * @param personnummer Personal identity number as a string to create the object from. + * @param options Options to use when creating the object. + * @throws PersonnummerException On parse error. + */ public static Personnummer parse(String personnummer, Options options) throws PersonnummerException { return new Personnummer(personnummer, options); } + /** + * Create a new Personnummber object from a string. + * In case options is not passed, they will default to accept any personal and coordination numbers. + * + * @param personnummer Personal identity number as a string to create the object from. + * @throws PersonnummerException On parse error. + */ public static Personnummer parse(String personnummer) throws PersonnummerException { return parse(personnummer, new Options()); } - public static Personnummer parse(Long personnummer, Options options) throws PersonnummerException { - return parse(Long.toString(personnummer), options); - } - - public static Personnummer parse(Long personnummer) throws PersonnummerException { - return parse(personnummer, new Options()); - } private final int realDay; private final String fullYear; @@ -49,8 +51,14 @@ public static Personnummer parse(Long personnummer) throws PersonnummerException private final boolean isMale; private final boolean isFemale; - - + /** + * Create a new Personnummber object from a string. + * In case options is not passed, they will default to accept any personal and coordination numbers. + * + * @param personnummer Personal identity number as a string to create the object from. + * @param options Options to use when creating the object. + * @throws PersonnummerException On parse error. + */ public Personnummer(String personnummer, Options options) throws PersonnummerException { if (personnummer == null) { throw new PersonnummerException(); @@ -102,26 +110,38 @@ public Personnummer(String personnummer, Options options) throws PersonnummerExc } } + /** + * Create a new Personnummber object from a string. + * In case options is not passed, they will default to accept any personal and coordination numbers. + * + * @param personnummer Personal identity number as a string to create the object from. + * @throws PersonnummerException On parse error. + */ public Personnummer(String personnummer) throws PersonnummerException { this(personnummer, new Options()); } - public Personnummer(Long personnummer, Options options) throws PersonnummerException { - this(Long.toString(personnummer), options); - } - - public Personnummer(Long personnummer) throws PersonnummerException { - this(personnummer, new Options()); - } - public int getAge() { return (LocalDate.of(Integer.parseInt(this.fullYear), Integer.parseInt(this.month), this.realDay).until(LocalDate.now())).getYears(); } + /** + * Format the personal identity number into a valid string (YYMMDD-/+XXXX) + * If longFormat is true, it will include the century (YYYYMMDD-/+XXXX) + * + * @return Formatted personal identity number. + */ public String format() { return format(false); } + /** + * Format the personal identity number into a valid string (YYMMDD-/+XXXX) + * If longFormat is true, it will include the century (YYYYMMDD-/+XXXX) + * + * @param longFormat If century should be included. + * @return Formatted personal identity number. + */ public String format(boolean longFormat) { return (longFormat ? this.fullYear : this.year) + this.month + this.day + separator() + numbers; } @@ -201,15 +221,4 @@ private static int luhn(String value) { return (int)(Math.ceil((double)sum / 10.0) * 10.0 - (double)sum); } - private static boolean testDate(int year, int month, int day) { - try { - DateFormat df = new SimpleDateFormat("yy-MM-dd"); - df.setLenient(false); - df.parse(String.format("%02d-%02d-%02d", year, month, day)); - return true; - } catch (Exception ex) { - return false; - } - } - } From a3fa2877e8b16de58ff007ec5632d7f6536ff611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Wed, 17 Jun 2020 21:02:40 +0200 Subject: [PATCH 08/13] Added message to exceptions. --- .../java/dev/personnummer/Personnummer.java | 96 +++++++++---------- .../personnummer/PersonnummerException.java | 6 +- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/src/main/java/dev/personnummer/Personnummer.java b/src/main/java/dev/personnummer/Personnummer.java index e1ba985..baae5bb 100644 --- a/src/main/java/dev/personnummer/Personnummer.java +++ b/src/main/java/dev/personnummer/Personnummer.java @@ -51,6 +51,50 @@ public static Personnummer parse(String personnummer) throws PersonnummerExcepti private final boolean isMale; private final boolean isFemale; + public Boolean isMale() { + return this.isMale; + } + + public Boolean isFemale() { + return this.isFemale; + } + + public String separator() { + return this.getAge() >= 100 ? "+" : "-"; + } + + public String getFullYear() { + return fullYear; + } + + public String getCentury() { + return century; + } + + public String getYear() { + return year; + } + + public String getMonth() { + return month; + } + + public String getDay() { + return day; + } + + public String getNumbers() { + return numbers; + } + + public String getControlNumber() { + return controlNumber; + } + + public int getAge() { + return (LocalDate.of(Integer.parseInt(this.fullYear), Integer.parseInt(this.month), this.realDay).until(LocalDate.now())).getYears(); + } + /** * Create a new Personnummber object from a string. * In case options is not passed, they will default to accept any personal and coordination numbers. @@ -61,12 +105,12 @@ public static Personnummer parse(String personnummer) throws PersonnummerExcepti */ public Personnummer(String personnummer, Options options) throws PersonnummerException { if (personnummer == null) { - throw new PersonnummerException(); + throw new PersonnummerException("Failed to parse personal identity number. Invalid input."); } Matcher matches = regexPattern.matcher(personnummer); if (!matches.find()) { - throw new PersonnummerException(); + throw new PersonnummerException("Failed to parse personal identity number. Invalid input."); } String century; @@ -88,7 +132,7 @@ public Personnummer(String personnummer, Options options) throws PersonnummerExc if (options.allowCoordinationNumber) { day = day > 60 ? day - 60 : day; } else if(day > 60) { - throw new PersonnummerException(); + throw new PersonnummerException("Invalid personal identity number."); } this.realDay = day; @@ -106,7 +150,7 @@ public Personnummer(String personnummer, Options options) throws PersonnummerExc // The format passed to Luhn method is supposed to be YYmmDDNNN // Hence all numbers that are less than 10 (or in last case 100) will have leading 0's added. if (luhn(String.format("%s%s%s%s", this.year, this.month, this.day, matches.group(6))) != Integer.parseInt(this.controlNumber)) { - throw new PersonnummerException(); + throw new PersonnummerException("Invalid personal identity number."); } } @@ -121,10 +165,6 @@ public Personnummer(String personnummer) throws PersonnummerException { this(personnummer, new Options()); } - public int getAge() { - return (LocalDate.of(Integer.parseInt(this.fullYear), Integer.parseInt(this.month), this.realDay).until(LocalDate.now())).getYears(); - } - /** * Format the personal identity number into a valid string (YYMMDD-/+XXXX) * If longFormat is true, it will include the century (YYYYMMDD-/+XXXX) @@ -146,46 +186,6 @@ public String format(boolean longFormat) { return (longFormat ? this.fullYear : this.year) + this.month + this.day + separator() + numbers; } - public Boolean isMale() { - return this.isMale; - } - - public Boolean isFemale() { - return this.isFemale; - } - - public String separator() { - return this.getAge() >= 100 ? "+" : "-"; - } - - public String getFullYear() { - return fullYear; - } - - public String getCentury() { - return century; - } - - public String getYear() { - return year; - } - - public String getMonth() { - return month; - } - - public String getDay() { - return day; - } - - public String getNumbers() { - return numbers; - } - - public String getControlNumber() { - return controlNumber; - } - /** * Validate a Swedish personal identity number. * diff --git a/src/main/java/dev/personnummer/PersonnummerException.java b/src/main/java/dev/personnummer/PersonnummerException.java index 508c0c7..be13965 100644 --- a/src/main/java/dev/personnummer/PersonnummerException.java +++ b/src/main/java/dev/personnummer/PersonnummerException.java @@ -1,3 +1,7 @@ package dev.personnummer; -public class PersonnummerException extends Exception { } +public class PersonnummerException extends Exception { + PersonnummerException(String message) { + super(message); + } +} From 00448c2094091f1a35a7eed5dbf47fbb23f667fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Wed, 17 Jun 2020 21:18:48 +0200 Subject: [PATCH 09/13] Updated readme and fixed license year. --- LICENSE | 2 +- README.md | 112 +++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 103 insertions(+), 11 deletions(-) diff --git a/LICENSE b/LICENSE index 2e08098..46a61ed 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017-2019 - Personnummer and Contributors +Copyright (c) 2017-2020 - Personnummer and Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index c3f39bb..59e2253 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,114 @@ # Personnummer -[![Build Status](https://travis-ci.org/personnummer/java.svg?branch=master)](https://travis-ci.org/personnummer/java) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/personnummer/java/Test)](https://github.com/personnummer/java/actions) -Validate Swedish personal identity numbers +Validate Swedish social security numbers. -## Example +## Installation -```java -class Test { - public void main(String[] args){ - Personnummer.valid(6403273813L); // => True - Personnummer.valid("19130401+2931"); // => True +Add the github repository as a Maven or Gradle repository: + +```xml + + dev.personnummer + personnummer + 3.*.* + +``` + +```groovy +plugins { + id 'maven' +} + +repositories { + maven { + url "https://github.com/personnummer/java:personnummer" } } + +dependencies { + configuration("dev.personnummer:personnummer") +} ``` -See [`src/test/java/PersonnummerTest.java`](src/test/java/PersonnummerTest.java) for more examples. +For more information on how to install and authenticate with github packages, check [this link](https://help.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-apache-maven-for-use-with-github-packages). + +## Examples + +### Validation + +```java +import dev.personnummer.*; + +class Test +{ + public void TestValidation() + { + Personnummer.valid("191212121212"); // => True + Personnummer.valid("12121+21212"); // => True + Personnummer.valid("2012121-21212"); // => True + } +} +``` + +### Format + +```java +// Short format (YYMMDD-XXXX) +(new Personnummer("1212121212")).format(); +// => 121212-1212 + +// Short format for 100+ years old +(new Personnummer("191212121212")).format(); +//=> 121212+1212 + +// Long format (YYYYMMDDXXXX) +Personnummer.parse("1212121212").format(true); +//=> 201212121212 +``` + +### Age + +```java +(new Personnummer("1212121212")).getAge(); +//=> 7 +``` + +### Get sex + +```java +(new Personnummer("1212121212")).isMale(); +//=> true +Personnummer.parse("1212121212").isFemale(); +//=> false +``` + +See `src/test//PersonnummerTest.java` for more examples. ## License -[MIT](LICENSE) +``` +MIT License + +Copyright (c) 2017-2020 - Personnummer and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` From 527546eab257d00abefd2acf1a520dfb90020dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Wed, 17 Jun 2020 23:24:29 +0200 Subject: [PATCH 10/13] Fixed format output on long format. --- src/main/java/dev/personnummer/Personnummer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dev/personnummer/Personnummer.java b/src/main/java/dev/personnummer/Personnummer.java index baae5bb..1b99d46 100644 --- a/src/main/java/dev/personnummer/Personnummer.java +++ b/src/main/java/dev/personnummer/Personnummer.java @@ -183,7 +183,7 @@ public String format() { * @return Formatted personal identity number. */ public String format(boolean longFormat) { - return (longFormat ? this.fullYear : this.year) + this.month + this.day + separator() + numbers; + return (longFormat ? this.fullYear : this.year) + this.month + this.day + (longFormat ? "" : separator()) + numbers; } /** From c981bf43344b4125762da719a2199ce93f54be50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Thu, 18 Jun 2020 09:23:41 +0200 Subject: [PATCH 11/13] Removed commented out code not in use. --- src/main/java/dev/personnummer/Personnummer.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/dev/personnummer/Personnummer.java b/src/main/java/dev/personnummer/Personnummer.java index 1b99d46..cc638ec 100644 --- a/src/main/java/dev/personnummer/Personnummer.java +++ b/src/main/java/dev/personnummer/Personnummer.java @@ -118,8 +118,6 @@ public Personnummer(String personnummer, Options options) throws PersonnummerExc if (matches.group(1) != null && !matches.group(1).isEmpty()) { century = matches.group(1); } else { - // LocalDate date = LocalDate.parse(ssn.longFormat.substring(0, ssn.longFormat.length() - 4), DateTimeFormatter.ofPattern("yyyyMMdd")); - // int years = (date.until(LocalDate.now())).getYears(); int born = LocalDate.now().getYear() - Integer.parseInt(decade); if (!matches.group(5).isEmpty() && matches.group(5).equals("+")) { born -= 100; From cee916318328e03db962ab58671b121d442d3640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Thu, 18 Jun 2020 09:25:04 +0200 Subject: [PATCH 12/13] Fixed readme slightly. --- README.md | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 59e2253..ea496da 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/personnummer/java/Test)](https://github.com/personnummer/java/actions) -Validate Swedish social security numbers. +Validate Swedish personal identity numbers. ## Installation @@ -88,27 +88,4 @@ See `src/test//PersonnummerTest.java` for more examples. ## License -``` -MIT License - -Copyright (c) 2017-2020 - Personnummer and Contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -``` +[MIT](https://github.com/personnummer/java/blob/master/LICENSE) From daa7ab2233ab1a430da58faaed323abb611d75d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Tegn=C3=A9r?= Date: Thu, 18 Jun 2020 09:27:09 +0200 Subject: [PATCH 13/13] Fixed broken test. --- src/test/java/PersonnummerTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/PersonnummerTest.java b/src/test/java/PersonnummerTest.java index 268b69a..4cd4598 100644 --- a/src/test/java/PersonnummerTest.java +++ b/src/test/java/PersonnummerTest.java @@ -128,9 +128,9 @@ public void testFormat(PersonnummerData ssn) throws PersonnummerException { @ParameterizedTest @MethodSource({"DataProvider#getValidPersonnummer", "DataProvider#getValidCoordinationNumbers"}) public void testFormatLong(PersonnummerData ssn) throws PersonnummerException { - assertEquals(ssn.separatedLong, Personnummer.parse(ssn.separatedLong, new Options(true)).format(true)); - assertEquals(ssn.separatedLong, Personnummer.parse(ssn.separatedFormat, new Options(true)).format(true)); - assertEquals(ssn.separatedLong, Personnummer.parse(ssn.longFormat, new Options(true)).format(true)); + assertEquals(ssn.longFormat, Personnummer.parse(ssn.separatedLong, new Options(true)).format(true)); + assertEquals(ssn.longFormat, Personnummer.parse(ssn.separatedFormat, new Options(true)).format(true)); + assertEquals(ssn.longFormat, Personnummer.parse(ssn.longFormat, new Options(true)).format(true)); } @ParameterizedTest