aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktor.java34
-rw-r--r--src/main/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParser.java17
-rw-r--r--src/main/resources/sampledata/Landesrecht-Beispiele.adoc10
-rw-r--r--src/main/resources/sampledata/NRW/TMZ-Gesetz-alt.txt13
4 files changed, 70 insertions, 4 deletions
diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktor.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktor.java
index e116056..dc7dac8 100644
--- a/src/main/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktor.java
+++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktor.java
@@ -91,7 +91,23 @@ public final class ZitatExtraktor {
aktuellesZitat.append(c);
}
} else if (tiefe > 0) {
- aktuellesZitat.append(c);
+ if (c == '\n' && beginntArtikelUeberschrift(text, i + 1)) {
+ // Ein Änderungsgesetz zitiert nie über eine Artikel-Überschrift hinweg: Hier fehlt im
+ // amtlichen Satz ein schließendes Anführungszeichen (kommt vor, z.B. GV. NRW. 2026
+ // S. 202 Artikel 2 Nr. 11). Ohne diese Grenze verschlänge das offene Zitat alle
+ // folgenden Artikel und der Parser fände überhaupt keine Änderungsbefehle mehr.
+ warnungen.add(
+ "Zitat vor einer Artikel-Überschrift nicht geschlossen: …"
+ + kontextAuszug(text, i)
+ + "… — dort geschlossen.");
+ ausgabe.append('«').append(zitate.size()).append('»');
+ zitate.add(aktuellesZitat.toString());
+ aktuellesZitat.setLength(0);
+ tiefe = 0;
+ ausgabe.append(c);
+ } else {
+ aktuellesZitat.append(c);
+ }
} else {
ausgabe.append(c);
}
@@ -131,6 +147,22 @@ public final class ZitatExtraktor {
return FORTFUEHRUNGS_MARKER.matcher(fenster).find();
}
+ /**
+ * Strukturgrenze eines Änderungsgesetzes: eine Zeile, die nur aus „Artikel N“ besteht. Bayerische
+ * Stammgesetze zitieren ihre Normköpfe als „Art. N“, nicht als „Artikel N“ — zitierter Inhalt
+ * läuft hier also nicht versehentlich auf.
+ */
+ private static final Pattern ARTIKEL_GRENZE = Pattern.compile("^Artikel[ \\t]+\\d+[a-z]?[ \\t]*$");
+
+ /** Wahr, wenn die bei {@code von} beginnende Zeile eine Artikel-Überschrift ist. */
+ private static boolean beginntArtikelUeberschrift(String text, int von) {
+ if (von >= text.length()) {
+ return false;
+ }
+ int ende = text.indexOf('\n', von);
+ return ARTIKEL_GRENZE.matcher(text.substring(von, ende < 0 ? text.length() : ende)).matches();
+ }
+
private static String kontextAuszug(String text, int position) {
var von = Math.max(0, position - 60);
var bis = Math.min(text.length(), position + 20);
diff --git a/src/main/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParser.java b/src/main/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParser.java
index ada273c..a3397cf 100644
--- a/src/main/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParser.java
+++ b/src/main/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParser.java
@@ -41,6 +41,9 @@ final class LandesRechtTextParser {
// geprüft, sodass spätere Fundstellen-Klammern („(BayRS V S. 595)“) nicht getroffen werden.
private static final Pattern JURABK_ZEILE = Pattern.compile("^\\(([^()]+)\\)$");
+ /** Trennt im Klammerzusatz den Kurztitel von der Abkürzung („Kurztitel – Abk“). */
+ private static final Pattern KURZTITEL_TRENNER = Pattern.compile("\\s+[–—]\\s+");
+
// Kopf der Druckfassung („BayJG: Bayerisches Jagdgesetz … (Art. 1–64)“), ggf. mit
// umbrochenem Rest („1–64)“) auf der Folgezeile.
private static final Pattern DRUCKKOPF = Pattern.compile("^\\S{1,20}: .+$");
@@ -95,10 +98,20 @@ final class LandesRechtTextParser {
}
var langue = zeilen.get(i++).strip();
String jurabk = null;
+ String kurzue = null;
if (i < zeilen.size()) {
var m = JURABK_ZEILE.matcher(zeilen.get(i).strip());
if (m.matches()) {
- jurabk = m.group(1);
+ // Landesgesetze führen im Klammerzusatz häufig Kurztitel und Abkürzung nebeneinander
+ // („Telemedienzuständigkeitsgesetz – TMZ-Gesetz“, „Gemeindeordnung – GO –“). Das
+ // Änderungsgesetz zitiert dann den Kurztitel, nicht die Abkürzung.
+ var teile = KURZTITEL_TRENNER.split(m.group(1).strip(), 2);
+ if (teile.length == 2) {
+ kurzue = teile[0].strip();
+ jurabk = teile[1].strip().replaceAll("\\s*[–—-]\\s*$", "");
+ } else {
+ jurabk = m.group(1).strip();
+ }
i++;
}
}
@@ -190,7 +203,7 @@ final class LandesRechtTextParser {
"Kein „§ N“- oder „Art. N“-Normkopf gefunden — ist das eine konsolidierte Fassung im"
+ " kanonischen Klartextformat?");
}
- return new Gesetz(jurabk != null ? jurabk : langue, langue, null, normen, gliederungen);
+ return new Gesetz(jurabk != null ? jurabk : langue, langue, kurzue, normen, gliederungen);
}
/** Überspringt die Kopfzeile der Druckfassung samt umbrochenem Rest. */
diff --git a/src/main/resources/sampledata/Landesrecht-Beispiele.adoc b/src/main/resources/sampledata/Landesrecht-Beispiele.adoc
index 373f615..1d831ac 100644
--- a/src/main/resources/sampledata/Landesrecht-Beispiele.adoc
+++ b/src/main/resources/sampledata/Landesrecht-Beispiele.adoc
@@ -61,7 +61,10 @@ folgende Fassung"). Saubere einspaltige PDFs über die stabile API `verkuendung-
|`NRW/GV-NRW-2026-S202_22-Rundfunkaenderungsgesetz.pdf` — GV. NRW. 2026 Nr. 7 vom 31.03.2026,
S. 202 (22. Rundfunkänderungsgesetz)
|Umfangreiches Mehrgesetz-Artikelgesetz (17 S.); „Die Inhaltsübersicht wird wie folgt
-geändert", „§ … (weggefallen)". Bereitgestellt aus dem amtlichen recht.nrw.de-PDF.
+geändert", „§ … (weggefallen)". Bereitgestellt aus dem amtlichen recht.nrw.de-PDF. *Unbalancierte
+Anführungszeichen* im amtlichen Satz (Artikel 2 Nr. 7 b und Nr. 11). Konsolidierte Fassungen sind
+über `recht.nrw.de/lrgv/gesetz/<TTMMJJJJ>-<Kurztitel-Slug>` frei und serverseitig gerendert
+abrufbar; der Datumspräfix ist der Gültigkeitsbeginn der jeweiligen Fassung.
|Sachsen
|`Sachsen/SaechsGVBl-2026-S134_AendG-SaechsBeamtVG_revosax.pdf` — SächsGVBl. S. 134
@@ -92,6 +95,11 @@ folgt geändert").
(`EndToEndTest.kommunalrechtAendGSchleswigHolstein`): Artikel 1 trifft die Gemeindeordnung,
Artikel 2 die Kreisordnung, je ein Neufassungsbefehl, kein unbekannter Befehl. Ein voller
Akzeptanztest ist *nicht* möglich — siehe „Noch offen“.
+* *Nordrhein-Westfalen* ist umgesetzt: `NRW/TMZ-Gesetz-alt.txt` (Telemedienzuständigkeitsgesetz in
+ der Fassung vom 27.04.2022, aus der Fassungshistorie von recht.nrw.de) und
+ `EndToEndTest.tmzGesetzAcceptance` (Artikel 3 des 22. Rundfunkänderungsgesetzes; alle vier
+ Befehle angewandt, kein Rest). Das Ergebnis ist gegen die amtliche Fassung vom 01.04.2026
+ abgeglichen. Die übrigen Artikel (WDR-Gesetz, Landesmediengesetz, 17. RÄStV-AusfG) bleiben offen.
* Baden-Württemberg (GBl 2026 Nr. 26): großes Anlage-lastiges Änderungsgesetz (§§ 4–57a + Anlagen
1/3b/4b); Stammfassung/Akzeptanztest noch offen.
diff --git a/src/main/resources/sampledata/NRW/TMZ-Gesetz-alt.txt b/src/main/resources/sampledata/NRW/TMZ-Gesetz-alt.txt
new file mode 100644
index 0000000..74621d1
--- /dev/null
+++ b/src/main/resources/sampledata/NRW/TMZ-Gesetz-alt.txt
@@ -0,0 +1,13 @@
+Gesetz zur Regelung der Zuständigkeit für die Überwachung von Telemedien nach dem Telemediengesetz, dem Telekommunikation-Telemedien-Datenschutz-Gesetz und nach § 24 Absatz 3, § 104 Absatz 1, § 106 Absatz 3 und § 113 des Medienstaatsvertrages
+(Telemedienzuständigkeitsgesetz – TMZ-Gesetz)
+Vom 29. März 2007
+§ 1 Aufsicht bei Telemedien
+(1) Die Landesanstalt für Medien Nordrhein-Westfalen (im Folgenden LfM) ist die nach § 24 Absatz 3, § 104 Absatz 1 sowie § 106 Absatz 3 des Medienstaatsvertrages vom 14. bis 28. April 2020 (GV. NRW. S. 524) in der jeweils geltenden Fassung zuständige Aufsichtsbehörde für das gesamte Land Nordrhein-Westfalen, soweit Absatz 2 nichts Abweichendes bestimmt.
+(2) Die oder der Landesbeauftragte für Datenschutz und Informationsfreiheit, im Folgenden LDI, überwacht in ihrem oder seinem Bereich die Einhaltung der Bestimmungen des § 23 des Medienstaatsvertrages über den Datenschutz, der §§ 19 bis 25 des Telekommunikation-Telemedien-Datenschutz-Gesetzes vom 23. Juni 2021 (BGBl. I S. 1982) in der jeweils geltenden Fassung sowie sonstiger datenschutzrechtlicher Bestimmungen, soweit nicht die Zuständigkeit der oder des Datenschutzbeauftragten der LfM, der oder des Rundfunkbeauftragten für den Datenschutz des WDR, im Folgenden WDR-Rundfunkdatenschutzbeauftragte oder WDR-Rundfunkdatenschutzbeauftragter, oder einer anderen für die Datenschutzaufsicht beim öffentlich-rechtlichen Rundfunk zuständigen Stelle gesetzlich bestimmt ist oder ein Fall des § 113 Satz 3 des Medienstaatsvertrages vorliegt. Im Hinblick auf die Befugnisse der oder des LDI im Rahmen ihrer oder seiner Aufsichtstätigkeit über die Einhaltung der Bestimmungen nach dem Telekommunikation-Telemedien-Datenschutz-Gesetz findet Artikel 58 Absatz 1 bis 5 der Datenschutz-Grundverordnung vom 27. April 2016 (ABl. L 119 vom 4.5.2016, S. 1; L 314 vom 22.11.2016, S. 72; L 127 vom 23.5.2018, S. 2; L 74 vom 4.3.2021, S. 35) entsprechende Anwendung.
+(3) Die Bezirksregierung Düsseldorf ist für das gesamte Land Nordrhein-Westfalen zuständig für die Überwachung und Untersagung von Glücksspielen im Internet und der Werbung hierfür im Internet.
+§ 2 Zuständigkeit bei Ordnungswidrigkeiten
+Zuständige Verwaltungsbehörde im Sinne des § 36 Absatz 1 Nummer 1 des Gesetzes über Ordnungswidrigkeiten in der Fassung der Bekanntmachung vom 19. Februar 1987 (BGBl. I S. 602), das zuletzt durch Artikel 31 des Gesetzes vom 5. Oktober 2021 (BGBl. I S. 4607) geändert worden ist, ist in den Fällen des
+1. § 11 Absatz 1 und 2 des Telemediengesetzes die LfM,
+2. § 28 Absatz 1 Nummern 10 bis 13 des Telekommunikation-Telemedien-Datenschutz-Gesetzes die oder der Datenschutzbeauftragte der LfM, die oder der WDR-Rundfunkdatenschutzbeauftragte oder die oder der LDI, soweit die jeweilige Aufsichtszuständigkeit begründet ist.
+§ 3 Inkrafttreten
+Dieses Gesetz tritt am Tag nach der Verkündung in Kraft.