refactor: address PR review concerns

- Remove Architekt from WORD_PREFIXES (classifier handles it)
- Use Objects.equals for null-safe firstName/lastName comparison
- Remove unused trimmed variable in PersonTypeClassifier
- Fix containsWord to loop through all occurrences (finds
  "Eltern" in "Nachbareltern Eltern")
- Extract DisplayNameFormatter utility shared by Person and
  PersonSummaryDTO to eliminate display logic duplication

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-08 13:25:06 +02:00
parent c0cf8d7952
commit ac545ecdaa
6 changed files with 34 additions and 20 deletions

View File

@@ -20,10 +20,7 @@ public interface PersonSummaryDTO {
long getDocumentCount();
default String getDisplayName() {
StringBuilder sb = new StringBuilder();
if (getTitle() != null) sb.append(getTitle()).append(" ");
if (getFirstName() != null) sb.append(getFirstName()).append(" ");
sb.append(getLastName());
return sb.toString().trim();
return org.raddatz.familienarchiv.model.DisplayNameFormatter.format(
getTitle(), getFirstName(), getLastName());
}
}

View File

@@ -0,0 +1,12 @@
package org.raddatz.familienarchiv.model;
public class DisplayNameFormatter {
public static String format(String title, String firstName, String lastName) {
StringBuilder sb = new StringBuilder();
if (title != null) sb.append(title).append(" ");
if (firstName != null) sb.append(firstName).append(" ");
sb.append(lastName);
return sb.toString().trim();
}
}

View File

@@ -58,10 +58,6 @@ public class Person {
@Transient
@Schema(accessMode = Schema.AccessMode.READ_ONLY, requiredMode = Schema.RequiredMode.REQUIRED)
public String getDisplayName() {
StringBuilder sb = new StringBuilder();
if (title != null) sb.append(title).append(" ");
if (firstName != null) sb.append(firstName).append(" ");
sb.append(lastName);
return sb.toString().trim();
return DisplayNameFormatter.format(title, firstName, lastName);
}
}

View File

@@ -3,6 +3,7 @@ package org.raddatz.familienarchiv.service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -159,7 +160,7 @@ public class PersonNameParser {
if ("?".equals(lastName) && !cleaned.contains(" ")) {
lastName = firstName;
firstName = null;
} else if (firstName.equals(lastName)) {
} else if (Objects.equals(firstName, lastName)) {
firstName = null;
}
}
@@ -217,7 +218,7 @@ public class PersonNameParser {
"Frau", "Herr", "Freifrau", "Freiherr",
"Tante", "Onkel", "Schwester", "Bruder",
"Cousine", "Cousin", "Freundin", "Freund",
"Mutter", "Vater", "Pastor", "Architekt");
"Mutter", "Vater", "Pastor");
/** Strips known title/relationship prefixes, looping for stacked titles. */
public static TitleResult stripTitle(String input) {

View File

@@ -24,8 +24,7 @@ public class PersonTypeClassifier {
public static PersonType classify(String rawName) {
if (rawName == null || rawName.isBlank()) return PersonType.PERSON;
String trimmed = rawName.trim();
String lower = trimmed.toLowerCase();
String lower = rawName.trim().toLowerCase();
for (String keyword : SKIP_KEYWORDS) {
if (lower.startsWith(keyword.toLowerCase())) return PersonType.SKIP;
@@ -50,11 +49,15 @@ public class PersonTypeClassifier {
}
private static boolean containsWord(String text, String word) {
int idx = text.indexOf(word);
if (idx < 0) return false;
boolean startOk = idx == 0 || !Character.isLetter(text.charAt(idx - 1));
int end = idx + word.length();
boolean endOk = end >= text.length() || !Character.isLetter(text.charAt(end));
return startOk && endOk;
int fromIndex = 0;
while (true) {
int idx = text.indexOf(word, fromIndex);
if (idx < 0) return false;
boolean startOk = idx == 0 || !Character.isLetter(text.charAt(idx - 1));
int end = idx + word.length();
boolean endOk = end >= text.length() || !Character.isLetter(text.charAt(end));
if (startOk && endOk) return true;
fromIndex = idx + 1;
}
}
}

View File

@@ -86,4 +86,9 @@ class PersonTypeClassifierTest {
void classify_caseInsensitive() {
assertThat(PersonTypeClassifier.classify("firma auschrath")).isEqualTo(PersonType.INSTITUTION);
}
@Test
void classify_containsWord_findsSecondOccurrence() {
assertThat(PersonTypeClassifier.classify("Nachbareltern Eltern")).isEqualTo(PersonType.GROUP);
}
}