diff options
| author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-08-23 21:52:20 +0200 |
|---|---|---|
| committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-08-23 21:52:20 +0200 |
| commit | 2ada4600cbb8dbd0dc8ac2fa0537f6853ba62359 (patch) | |
| tree | 81b3c22e9c4f64070e219de2db3e8e98d2667cac /src/test/java | |
| parent | 2febdfd49776c473377ec08c0d4920ede38a242a (diff) | |
Die Nummer einer Anlage ist eine Norm, kein Aufzählungsglied
Der Zuständigkeitskatalog des Berliner Sicherheits- und Ordnungsgesetzes zählt
seine Einheiten nicht als Aufzählungsglieder, sondern führt sie als „Nummer 6“,
„Nummer 23“, „Nummer 31“ — jede mit eigener Absatzzählung, jede im Portal eine
eigene Einzelnorm. Der Stellenauflöser suchte dagegen eine Marke „6.“ im
Wortlaut der Anlage und fand dort vielerlei; von sechs Befehlen des Artikels 1
wurden zwei angewandt.
Sie werden nunmehr geführt, wie sie sind: als eigene Normen, deren Bezeichnung
die Anlage voranstellt. Damit trägt die vorhandene Absatz-Maschinerie ihre
Befehle unverändert, und es braucht keinen neuen Rang im Modell. Wo ein Gesetz
solche Normen nicht führt — im gii-XML des Bundes stehen die Nummern einer
Anlage als Marken im Wortlaut —, bleibt es beim bisherigen Weg.
Dabei kamen drei Mängel ans Licht. Ob die feinste Komponente einer Fundstelle
ein Absatz ist, entschied das bloße Vorkommen einer feineren Komponente; die
Nummer einer Anlage steht aber vor der Absatzangabe und gehört zur Bezeichnung
der Norm. Die Umnummerierung eines solchen Absatzes galt deshalb als die einer
Aufzählungseinheit, tauschte eine Marke, die es nicht gab, hieß angewandt und
bewirkte nichts. Ein Absatz, der „vor den Wörtern …“ eingefügt wird, wurde eine
Zeile im Nachbarabsatz statt eines Absatzes. Und die Bezeichnung eines so
eingefügten Absatzes verlor die Nummer der Anlage, weshalb die Schritt-Ordnung
nicht sah, dass eine Umnummerierung ihm den Platz erst räumt.
Beigelegt sind die beiden Fassungen des ASOG, über die Browsersteuerung
beschafft. Der Auszug ist dort bei fünfzigtausend Zeichen gekappt und verschluckt
einzelne Leerzeichen; base64-kodierte Stücke kamen zeichengenau herüber.
Fünf der sechs Befehle werden angewandt, und 168 der 171 Normen gleichen danach
der amtlichen Nachfassung. Die drei Abweichungen sind benannt und liegen nicht
in der Anwendung — eine von ihnen belegt erstmals, was die geometrische
Lesereihenfolge kosten würde: Der Titelblock des Änderungsgesetzes steht im
Inhaltsstrom mitten in einem Zitat.
Geprüft mit „mvnw verify“: 333 Prüfungen, kein Fehlschlag; keine gepinnte Zahl
eines anderen Belegfalls hat sich geändert. REUSE 171/171.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I24b94cb96e6728b1ff1b1fcb5bb4b8433594e240
Diffstat (limited to 'src/test/java')
| -rw-r--r-- | src/test/java/eu/mulk/aendggner/EndToEndTest.java | 72 | ||||
| -rw-r--r-- | src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParserTest.java | 37 |
2 files changed, 107 insertions, 2 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java index 97bcffb..d7e19f3 100644 --- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java +++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java @@ -17,6 +17,7 @@ import eu.mulk.aendggner.aenderung.parse.SuperskriptModus; import eu.mulk.aendggner.aenderung.parse.TextBereiniger; import eu.mulk.aendggner.aenderung.parse.ZitatExtraktor; import eu.mulk.aendggner.anwendung.BefehlAnwender; +import eu.mulk.aendggner.gesetz.Absatz; import eu.mulk.aendggner.gesetz.Gesetz; import eu.mulk.aendggner.gesetz.gii.GiiXmlLoader; import eu.mulk.aendggner.synopse.HtmlRenderer; @@ -940,8 +941,13 @@ class EndToEndTest { // Artikel 1 ändert die unnummerierte, nach ihrer Vorschrift benannte Anlage („Die Anlage zu // § 2 Absatz 4 Satz 1 wird wie folgt geändert“). Der Zusatz benennt die Anlage nur — er darf // nicht als Ziel „§ 2“ gelesen werden; die Punkte erben „Anlage“ als Kontext. - var asog = - new Gesetz("ASOG Bln", "Allgemeines Sicherheits- und Ordnungsgesetz", null, List.of()); + var altAsog = SAMPLEDATA.resolve("Berlin/ASOG-Bln-alt.txt"); + assumeTrue(Files.exists(altAsog), "ASOG-Stammfassung fehlt"); + var asog = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(altAsog); + // 129 Paragraphen, die Anlage und ihre 40 Nummern — letztere sind eigene Normen mit eigener + // Absatzzählung, wie das Landesrechtsportal sie führt. + assertThat(asog.normen()).hasSize(171); + assertThat(asog.norm("Anlage Nummer 23")).isPresent(); var artikel1 = new AenderungsgesetzParser().parse(text, asog, null); assertThat(artikel1.artikel()).containsExactly("1"); assertThat(artikel1.befehle()) @@ -976,6 +982,68 @@ class EndToEndTest { // Artikel 2 wird vollständig angewandt und gleicht danach der amtlichen Nachfassung. pruefeGegenNachfassung(laf, ergebnis, "Berlin/LAF-ErrG-neu.txt"); + + // Artikel 1: fünf der sechs Befehle werden angewandt. + var anwendung = BefehlAnwender.anwenden(asog, artikel1.befehle()); + var manuellPfade = + anwendung.protokoll().stream() + .filter(x -> x.status() == BefehlAnwender.Status.MANUELL_PRUEFEN) + .map(x -> x.befehl().provenienz().gliederungsPfad()) + .toList(); + // Einziger Rest, und er benennt eine Grenze des Klartextformats: „In Absatz 4a wird der Punkt + // am Ende durch ein Semikolon ersetzt.“ Auf den Absatz 4a folgt der Zwischentitel „Aus dem + // Bereich Verkehr:“, der keine eigene Absatzbezeichnung trägt und deshalb dem vorangehenden + // Absatz zugeschlagen wird. Dessen Text endet damit nicht auf den Punkt, den der Befehl meint. + assertThat(manuellPfade).containsExactly("2. b) aa)"); + assertThat(anwendung.anzahlAngewandt()).isEqualTo(5); + + // Die Nummer 23 der Anlage trägt danach die Absatzfolge der amtlichen Nachfassung: Der neue + // Absatz 5 ist ein Absatz geworden (nicht eine Zeile im Absatz 4a), und die bisherigen + // Absätze 5 bis 9 sind zu 6 bis 10 aufgerückt. + var nummer23 = anwendung.neu().norm("Anlage Nummer 23").orElseThrow(); + assertThat(nummer23.absaetze()) + .extracting(Absatz::nummer) + .containsExactly(null, "1", "2", "3", "4", "4a", "5", "6", "7", "8", "9", "10"); + + // Norm für Norm gegen die amtliche Nachfassung vom 12. Juni 2026. Es bleiben genau zwei + // benannte Abweichungen — beide nicht in der Anwendung begründet: + var sollAsog = SAMPLEDATA.resolve("Berlin/ASOG-Bln-neu.txt"); + assumeTrue(Files.exists(sollAsog), "ASOG-Nachfassung fehlt"); + var soll = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(sollAsog); + var abweichend = new java.util.ArrayList<String>(); + for (var normSoll : soll.normen()) { + var normIst = anwendung.neu().norm(normSoll.enbez()).orElseThrow(); + if (!normIst + .gesamtText() + .replaceAll("\\s+", " ") + .strip() + .equals(normSoll.gesamtText().replaceAll("\\s+", " ").strip())) { + abweichend.add(normSoll.enbez()); + } + } + // Drei benannte Abweichungen, keine davon in der Anwendung begründet: + // + // § 67 — Das Portal setzt in der neuen Fassung amtliche Satznummern („(2) ¹Gegen einen …“); + // das Gesetzblatt, aus dem der Wortlaut stammt, setzt keine. + // Anlage Nummer 23 — der oben benannte Rest: dort steht der Punkt, wo die Nachfassung ein + // Semikolon führt. + // Anlage Nummer 31 — das Zitat läuft im Gesetzblatt über einen Seitenwechsel, und der + // ganzseitenbreite Titelblock des Änderungsgesetzes steht im Inhaltsstrom mitten darin + // („… zur Sicherung des Be-“ / Titelblock / „triebs von Unterkünften …“). Das ist die + // bekannte Berliner Layout-Frage, die erst eine geometrische Lesereihenfolge löst; sie + // betrifft die Extraktion, nicht die Anwendung. + assertThat(abweichend).containsExactly("§ 67", "Anlage Nummer 23", "Anlage Nummer 31"); + assertThat( + anwendung + .neu() + .norm("Anlage Nummer 31") + .orElseThrow() + .gesamtText() + .replaceAll("\\s+", " ")) + .as("der Titelblock steht im Zitat — Beleg der offenen Layout-Frage") + .contains("Gesetz zur Änderung des Allgemeinen Sicherheits- und Ordnungsgesetzes"); + assertThat(anwendung.neu().norm("§ 67").orElseThrow().gesamtText()) + .contains("Gegen einen straßenverkehrsrechtlichen Verwaltungsakt"); } /** diff --git a/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParserTest.java b/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParserTest.java index bed8f9e..e2a6086 100644 --- a/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParserTest.java +++ b/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParserTest.java @@ -4,6 +4,7 @@ package eu.mulk.aendggner.gesetz.land; import static org.assertj.core.api.Assertions.assertThat; +import eu.mulk.aendggner.gesetz.Absatz; import eu.mulk.aendggner.gesetz.Gliederung; import org.junit.jupiter.api.Test; @@ -299,4 +300,40 @@ class LandesRechtTextParserTest { .contains("§ 1 bleibt unberührt"); assertThat(gesetz.norm("Anlage 2").orElseThrow().gesamtText()).contains("Das Muster"); } + + /** + * Trägt eine Anlage Nummern als eigene Einheiten — so der Zuständigkeitskatalog des Berliner ASOG + * —, so ist jede eine eigene Norm mit eigener Absatzzählung. Ihre Bezeichnung stellt die Anlage + * voran, damit sie im Gesetz eindeutig bleibt: „Anlage Nummer 6“. + */ + @Test + void nummernEinerAnlageSindEigeneNormen() { + var gesetz = + LandesRechtTextParser.parse( + """ + Beispielgesetz + (BeispG) + Vom 1. Januar 2020 + § 1 Zweck + (1) Dieses Gesetz dient der Erprobung. + Anlage + (zu § 1 Absatz 1) + Nummer 6 + Bezirksamt + (1) Die erste Aufgabe. + (2) Die zweite Aufgabe. + Nummer 23 + Polizeipräsident + (1) Die dritte Aufgabe. + """); + + assertThat(gesetz.normen()) + .extracting(eu.mulk.aendggner.gesetz.Norm::enbez) + .containsExactly("§ 1", "Anlage", "Anlage Nummer 6", "Anlage Nummer 23"); + var nummer6 = gesetz.norm("Anlage Nummer 6").orElseThrow(); + assertThat(nummer6.absaetze()).extracting(Absatz::nummer).containsExactly(null, "1", "2"); + // Der Vorspann der Anlage bleibt bei ihr; die Nummern nehmen ihn nicht mit. + assertThat(gesetz.norm("Anlage").orElseThrow().gesamtText()).contains("(zu § 1 Absatz 1)"); + assertThat(nummer6.gesamtText()).contains("Bezirksamt").doesNotContain("zu § 1 Absatz 1"); + } } |
