From 0b577175867a607e046877ac164b283c982f1c9c Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 7 Apr 2026 17:34:56 +0200 Subject: [PATCH] feat(parser): normalize dot-compressed names in split() Inserts spaces after dots when the cleaned name has no spaces but contains dots, so the existing last-space fallback handles names like "E.Rockstroh" and "Dr.Fr.Zarncke" correctly. Co-Authored-By: Claude Sonnet 4.6 --- .../service/PersonNameParser.java | 5 +++ .../service/PersonNameParserTest.java | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/backend/src/main/java/org/raddatz/familienarchiv/service/PersonNameParser.java b/backend/src/main/java/org/raddatz/familienarchiv/service/PersonNameParser.java index 99585e9a..2706d630 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/service/PersonNameParser.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/service/PersonNameParser.java @@ -123,6 +123,11 @@ public class PersonNameParser { String cleaned = GEB_PATTERN.matcher(rawName).replaceAll("").trim(); + // Normalize dot-compressed names: "Dr.Fr.Zarncke" -> "Dr. Fr. Zarncke" + if (!cleaned.contains(" ") && cleaned.contains(".")) { + cleaned = cleaned.replace(".", ". ").trim(); + } + String lastName = findKnownLastName(cleaned); if (lastName != null) { String firstName = cleaned.substring(0, cleaned.length() - lastName.length()).trim(); diff --git a/backend/src/test/java/org/raddatz/familienarchiv/service/PersonNameParserTest.java b/backend/src/test/java/org/raddatz/familienarchiv/service/PersonNameParserTest.java index 45911728..6139a60c 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/service/PersonNameParserTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/service/PersonNameParserTest.java @@ -133,6 +133,42 @@ class PersonNameParserTest { assertThat(result.lastName()).isEqualTo("de Gruyter"); } + // --- split — dot-compressed names --- + + @Test + void split_dotCompressed_initialAndLastName() { + PersonNameParser.SplitName result = PersonNameParser.split("E.Rockstroh"); + assertThat(result.firstName()).isEqualTo("E."); + assertThat(result.lastName()).isEqualTo("Rockstroh"); + } + + @Test + void split_dotCompressed_twoInitials() { + PersonNameParser.SplitName result = PersonNameParser.split("E.M."); + assertThat(result.firstName()).isEqualTo("E."); + assertThat(result.lastName()).isEqualTo("M."); + } + + @Test + void split_dotCompressed_titleFirstNameLastName() { + PersonNameParser.SplitName result = PersonNameParser.split("Dr.Fr.Zarncke"); + assertThat(result.firstName()).isEqualTo("Dr. Fr."); + assertThat(result.lastName()).isEqualTo("Zarncke"); + } + + @Test + void split_dotCompressed_titleAndLastName() { + PersonNameParser.SplitName result = PersonNameParser.split("Dr.Zarnke"); + assertThat(result.firstName()).isEqualTo("Dr."); + assertThat(result.lastName()).isEqualTo("Zarnke"); + } + + @Test + void parseReceivers_dotCompressedName_passthrough() { + assertThat(PersonNameParser.parseReceivers("Dr.Fr.Zarncke")) + .containsExactly("Dr.Fr.Zarncke"); + } + // --- parseReceivers — shared last name with full-name part ───────────────── @Test