diff options
| author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-07-26 08:02:10 +0200 |
|---|---|---|
| committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-07-26 08:02:10 +0200 |
| commit | c86a73a6e1da7dd28076054936458b850eda2947 (patch) | |
| tree | 179aa5b90857750bbaf41b20a401985206b6817f /src/main/java/eu | |
| parent | 83ba9f0db3462a4c2d5122ba836f646b055156ca (diff) | |
Berlin column spike: stream order already reads correctly
The plan assumed the two-column Berlin gazette would need x-coordinate
column detection. It does not: the content stream already emits the left
column fully before the right one, so commands come out in reading order.
The test pins that finding.
What does go wrong there is different, and two parts of it are fixed:
* The article's amendment formula carries its own target ("§ 2 Satz 1
des Gesetzes zur Errichtung … wird wie folgt geändert"), but the
following items never inherited it. The pattern's leading \b could
never match before "§" — a non-word character needs a word character
in front of it — so the paragraph branch was dead code and only the
Bavarian "Art." branch ever fired.
* The running page header is not a line of its own; it sits inside the
body text of the following column, so it has to be cut out rather
than dropped as a column title.
Two blockers remain and are recorded rather than papered over: Article 1
amends an *Anlage* with its own numbering, which the model does not carry
(the same gap that shelved Baden-Württemberg), and full-width frames
(title block, imprint) sit elsewhere in the stream than on the page,
which would need a real XY-cut. Stems are unobtainable either way —
gesetze.berlin.de is an authenticated juris app like the Schleswig-
Holstein portal.
229 tests green; all pinned figures unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I4619dbe96a023d61104d7107a9a34533123d1bc5
Diffstat (limited to 'src/main/java/eu')
| -rw-r--r-- | src/main/java/eu/mulk/aendggner/aenderung/parse/AenderungsgesetzParser.java | 11 | ||||
| -rw-r--r-- | src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java | 10 |
2 files changed, 17 insertions, 4 deletions
diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/AenderungsgesetzParser.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/AenderungsgesetzParser.java index 976cd5e..407a4cb 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/AenderungsgesetzParser.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/AenderungsgesetzParser.java @@ -102,12 +102,15 @@ public final class AenderungsgesetzParser { } // Eingebettetes Rahmenziel in der Änderungsformel selbst: „Art. 7 Abs. 2 des Bayerischen - // Umweltinformationsgesetzes … wird wie folgt geändert:“. Der Lookbehind auf „durch “ schließt - // die Zitierkette der Änderungshistorie aus („das zuletzt durch § 5 des Gesetzes vom … geändert - // worden ist“) — dort ist die Norm nie das Subjekt der Formel. + // Umweltinformationsgesetzes … wird wie folgt geändert:“, „§ 2 Satz 1 des Gesetzes zur + // Errichtung … wird wie folgt geändert:“. Der Lookbehind auf „durch “ schließt die Zitierkette + // der Änderungshistorie aus („das zuletzt durch § 5 des Gesetzes vom … geändert worden ist“) — + // dort ist die Norm nie das Subjekt der Formel. Die vordere Grenze darf nicht \b sein: „§“ ist + // selbst kein Wortzeichen, ein \b davor verlangte also ein vorangehendes Wortzeichen und machte + // den §-Zweig unerreichbar. private static final Pattern EINGEBETTETES_ZIEL = Pattern.compile( - "(?<!durch )\\b((?:§|Art\\.)\\s*\\d+[a-z]?" + "(?<!durch )(?<![\\p{L}\\d])((?:§|Art\\.)\\s*\\d+[a-z]?" + "(?:\\s+(?:Absatz|Abs\\.|Satz|Nummer|Nr\\.|Buchstabe|Buchst\\.)\\s+\\d+[a-z]?)*)" + "\\s+(?:des|der)\\s+\\p{Lu}"); 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 f1b75e1..d0e37f8 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java @@ -190,6 +190,15 @@ public final class TextBereiniger { private static final Pattern INVERTIERTES_LISTEN_ZITAT = Pattern.compile("(?m)^(\\s*)(\\d+[a-z]?\\.|[a-z]{1,3}\\))[ \\t]+„[ \\t]+"); + // GVBl. für Berlin (Wolters-Kluwer-Satz): Der laufende Seitenkopf („235Gesetz- und Verordnungs- + // blatt für Berlin 82. Jahrgang Nr. 17 11. Juni 2026“) steht im Inhaltsstrom nicht als eigene + // Zeile, sondern klebt samt Seitenzahl mitten im Fließtext der folgenden Spalte. Er ist deshalb + // nicht als Kolumnentitel zu entfernen, sondern als Textstück herauszuschneiden. + private static final Pattern GVBL_BERLIN_KOPF = + Pattern.compile( + "\\s*\\d{0,4}\\s*Gesetz- und Verordnungsblatt für Berlin\\s+\\d+\\. Jahrgang" + + "\\s+Nr\\.\\s*\\d+\\s+\\d{1,2}\\. \\p{L}+ \\d{4}\\s*"); + // Sachnummern mit Leerzeichen: Niedersachsen zitiert eingeschobene Paragraphen als „§ 2 a“, der // Rest der Rechtssprache als „§ 2a“. Auf die kanonische Form ziehen, damit Stellen- und // Ebenenparser greifen. Ein folgendes „)“ oder „.“ schließt Aufzählungsmarker des @@ -209,6 +218,7 @@ public final class TextBereiniger { text = INVERTIERTES_LISTEN_ZITAT.matcher(text).replaceAll("$1„$2 "); text = trenneVerklebteZitatgrenzen(text); text = VORABFASSUNG.matcher(text).replaceAll("\n"); + text = GVBL_BERLIN_KOPF.matcher(text).replaceAll("\n"); var zeilen = entferneKolumnentitel(zerlegeInZeilen(text)); var verbunden = verbindeUmbrueche(zeilen); // Falsch-positive markerlose Zusammenzüge („durch“ + „die“ → „durchdie“) reparieren — die |
