aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/eu
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-07-25 09:11:56 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-07-25 09:11:56 +0200
commit519c1e7b769c37769d1b5d1ffd6e13566b26c86d (patch)
tree59c8deaa1e34c3464801e9337fc2a92a15af307d /src/main/java/eu
parent43e91612875de3cc6e7466eee56023ab55cefd89 (diff)
Add Schleswig-Holstein extraction case and three cleanup fixes
The GVOBl. Schl.-H. amendment act (Kommunalrecht, 2026/27) brings three conventions the tool had not seen: * a running two-line page header that sits between articles and glued itself onto the following article heading, * superscript footnote markers on article headings ("Artikel 1 ¹)"), which defeated the article split, and * ragged-right typesetting, where the geometric margin detection finds no alignment cluster and reports full lines as hard line ends, so a trailing hyphen was never joined. All three are fixed in TextBereiniger; the hyphen rule now trusts the extractor's trailing-space signal over the geometric class. A full acceptance test is not possible: the Schleswig-Holstein law portal serves document bodies only through an authenticated API, and the last freely archived consolidated Gemeindeordnung (14 Nov 2022) predates the amended § 34a. The new test therefore covers everything up to and including command recognition; the blocker is recorded in Landesrecht-Beispiele.adoc. 219 tests green; Bund/Bayern/Sachsen/Niedersachsen figures unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Change-Id: I9b3ab8a756372504b51c4d410b491fded255da96
Diffstat (limited to 'src/main/java/eu')
-rw-r--r--src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java43
1 files changed, 40 insertions, 3 deletions
diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java
index 9c1edaa..7460b15 100644
--- a/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java
+++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java
@@ -1,5 +1,6 @@
package eu.mulk.aendggner.aenderung.parse;
+import eu.mulk.aendggner.gesetz.Superskript;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
@@ -131,6 +132,19 @@ public final class TextBereiniger {
private static final Pattern NDS_HERAUSGEBER =
Pattern.compile("^\\s*Herausgeber: Niedersächsische Staatskanzlei\\s*$");
+ // GVOBl. Schleswig-Holstein: zweizeiliger Titelkopf („Gesetz- und Verordnungsblatt“ / „für
+ // Schleswig-Holstein“, auf Folgeseiten in einer Zeile) und die Heftangabe („Nummer 2026/27“ auf
+ // dem Titelblatt, „2026/27 vom 30. März“ als zweite Zeile des laufenden Seitenkopfs). Der
+ // laufende Kopf steht mitten im Text zwischen zwei Artikeln und klebte sonst an der folgenden
+ // Artikel-Überschrift.
+ private static final Pattern GVOBL_SH_KOPF =
+ Pattern.compile("^\\s*Gesetz- und Verordnungsblatt(?: für Schleswig-Holstein)?\\s*$");
+ private static final Pattern GVOBL_SH_LAND =
+ Pattern.compile("^\\s*für Schleswig-Holstein\\s*$");
+ private static final Pattern GVOBL_SH_HEFT =
+ Pattern.compile(
+ "^\\s*(?:Nummer \\d{4}/\\d{1,3}|\\d{4}/\\d{1,3} vom \\d{1,2}\\. \\p{L}+)\\s*$");
+
/** Konjunktionen, die typischerweise auf einen Suspensivstrich folgen („Wirk- und …“). */
private static final Pattern KONJUNKTION =
Pattern.compile("^(und|oder|sowie|bzw\\.|beziehungsweise)\\b.*");
@@ -192,7 +206,7 @@ public final class TextBereiniger {
var verbunden = verbindeUmbrueche(zeilen);
// Falsch-positive markerlose Zusammenzüge („durch“ + „die“ → „durchdie“) reparieren — die
// Befehlsvokabeln sind nie Kompositum-Bestandteile.
- return trenneVerklebteZitatgrenzen(reflowUndStrippe(verbunden));
+ return loeseArtikelFussnoten(trenneVerklebteZitatgrenzen(reflowUndStrippe(verbunden)));
}
/**
@@ -262,6 +276,20 @@ public final class TextBereiniger {
.replaceAll("(wie folgt geändert:?)(?=[a-z]{1,3}\\)|\\d+[a-z]?\\.)", "$1\n");
}
+ /**
+ * Streift hochgestellte Fußnotenmarker von Artikel-Überschriften ab („Artikel 1 ¹)“ → „Artikel
+ * 1“). Schleswig-Holstein verweist damit auf die Gl.-Nr.-Fußnoten am Seitenfuß. Der Marker
+ * verhindert sonst die Artikel-Teilung; steht die Einleitung in derselben Zeile, wird sie dabei
+ * wieder abgetrennt.
+ */
+ private static String loeseArtikelFussnoten(String text) {
+ return ARTIKEL_FUSSNOTE.matcher(text).replaceAll("$1\n");
+ }
+
+ private static final Pattern ARTIKEL_FUSSNOTE =
+ Pattern.compile(
+ "(?m)^(Artikel\\s+\\d+[a-z]?)\\s*" + Superskript.LAUF.pattern() + "\\)[ \\t]*\\n?");
+
/** Entfernt Seitenkopf-/Fußzeilen. Trailing-Whitespace der übrigen Zeilen bleibt erhalten! */
private static ArrayList<Zeile> entferneKolumnentitel(List<Zeile> roh) {
var kolumnentitel = new boolean[roh.size()];
@@ -302,7 +330,10 @@ public final class TextBereiniger {
|| LANDTAG_MARKE.matcher(zeile).matches()
|| REVOSAX_FUSS.matcher(zeile).matches()
|| NDS_GVBL_FUSS.matcher(zeile).matches()
- || NDS_HERAUSGEBER.matcher(zeile).matches();
+ || NDS_HERAUSGEBER.matcher(zeile).matches()
+ || GVOBL_SH_KOPF.matcher(zeile).matches()
+ || GVOBL_SH_LAND.matcher(zeile).matches()
+ || GVOBL_SH_HEFT.matcher(zeile).matches();
}
/**
@@ -342,7 +373,13 @@ public final class TextBereiniger {
while (true) {
var gestutzt = zeile.stripTrailing();
// Ein geometrisch hartes Zeilenende ist ein bewusster Umbruch — nie zusammenziehen.
- var mitTrennstrich = umbruch != Umbruch.HART && endetMitSilbentrennung(gestutzt);
+ // Ausnahme: Klebt der Trennstrich ohne Trailing-Whitespace am Zeilenende, ist er eine
+ // echte Silbentrennung. Im Flattersatz (GVOBl. Schl.-H.) findet die Randerkennung keinen
+ // Ausrichtungs-Cluster und stuft auch volle Zeilen als hart ein; das Trailing-Space-Signal
+ // des Extraktors ist dort das verlässlichere Kriterium.
+ var mitTrennstrich =
+ endetMitSilbentrennung(gestutzt)
+ && (umbruch != Umbruch.HART || gestutzt.length() == zeile.length());
var markerlos =
markerlosAktiv
&& endetMarkerlos(zeile)