| package eu.mulk.aendggner.anwendung; |
| |
| import static org.assertj.core.api.Assertions.assertThat; |
| |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Anfuegung; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Aufhebung; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ebene; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ersetzung; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.GliederungsUeberschriften; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Neufassung; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Sammelbefehl; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Streichung; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.StrukturEinfuegung; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.StrukturErsetzung; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Umnummerierung; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.UnbekannterBefehl; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.WoerterEinfuegung; |
| import eu.mulk.aendggner.aenderung.Aenderungsbefehl.WortAnker; |
| import eu.mulk.aendggner.aenderung.Provenienz; |
| import eu.mulk.aendggner.aenderung.Stelle; |
| import eu.mulk.aendggner.anwendung.BefehlAnwender.Status; |
| import eu.mulk.aendggner.gesetz.Absatz; |
| import eu.mulk.aendggner.gesetz.Gesetz; |
| import eu.mulk.aendggner.gesetz.Gliederung; |
| import eu.mulk.aendggner.gesetz.Norm; |
| import java.util.List; |
| import org.junit.jupiter.api.Test; |
| |
| class BefehlAnwenderTest { |
| |
| private static final Provenienz PROV = new Provenienz("1", "1.", "(Test)"); |
| |
| private static Stelle stelle(Stelle.Komponente... komponenten) { |
| return new Stelle(List.of(komponenten)); |
| } |
| |
| @Test |
| void wendetErsetzungAn() { |
| var befehl = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| "die Erprobung", |
| "die gründliche Erprobung", |
| false, |
| false, |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) |
| .isEqualTo("Zweck dieses Gesetzes ist die gründliche Erprobung."); |
| } |
| |
| @Test |
| void meldetFehlendenAltTextAlsManuellPruefen() { |
| var befehl = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| "gibt es nicht", |
| "egal", |
| false, |
| false, |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| var eintrag = ergebnis.protokoll().get(0); |
| assertThat(eintrag.status()).isEqualTo(Status.MANUELL_PRUEFEN); |
| assertThat(eintrag.begruendung()).contains("kommt im Zieltext nicht vor"); |
| // Das Gesetz bleibt unverändert. |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) |
| .isEqualTo("Zweck dieses Gesetzes ist die Erprobung."); |
| } |
| |
| @Test |
| void meldetMehrdeutigeErsetzungOhneJeweils() { |
| var befehl = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("2")), "Prüfung", "Untersuchung", false, false, PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.MANUELL_PRUEFEN); |
| assertThat(ergebnis.protokoll().get(0).begruendung()).contains("mehrdeutig"); |
| } |
| |
| @Test |
| void wendetJeweilsErsetzungAufAlleVorkommenAn() { |
| var befehl = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("2")), "Prüfung", "Untersuchung", true, false, PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 2", 0)) |
| .contains("die Untersuchung der Tauglichkeit") |
| .contains("Die Untersuchung erfolgt"); |
| } |
| |
| @Test |
| void wendetErsetzungInSatzAn() { |
| var befehl = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("2"), new Stelle.SatzNr("2")), |
| "sorgfältig", |
| "gewissenhaft", |
| false, |
| false, |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 2", 0)).contains("erfolgt gewissenhaft"); |
| } |
| |
| @Test |
| void wendetErsetzungInNummerAn() { |
| var befehl = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2"), new Stelle.NummerNr("2")), |
| "Befehlen", |
| "Änderungsbefehlen", |
| false, |
| false, |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 1)) |
| .contains("2. die Anwendung von Änderungsbefehlen und"); |
| } |
| |
| @Test |
| void wendetPunktZuKommaErsetzungAmEndeAn() { |
| var befehl = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2"), new Stelle.NummerNr("3")), |
| ".", |
| ",", |
| false, |
| true, |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 1)).contains("3. die Ausgabe von Synopsen,"); |
| } |
| |
| @Test |
| void wendetNeufassungEinesAbsatzesAn() { |
| var befehl = |
| new Neufassung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| "(1) Zweck dieses Gesetzes ist die umfassende Erprobung.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) |
| .isEqualTo("Zweck dieses Gesetzes ist die umfassende Erprobung."); |
| } |
| |
| @Test |
| void wendetNeufassungEinesParagraphenAn() { |
| var befehl = |
| new Neufassung( |
| stelle(new Stelle.Paragraph("2")), |
| "§ 2 Begriffsbestimmungen (1) Erprobung ist die Untersuchung. (2) Bericht ist das" |
| + " Ergebnis.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var norm = ergebnis.neu().norm("§ 2").orElseThrow(); |
| assertThat(norm.titel()).isEqualTo("Begriffsbestimmungen"); |
| assertThat(norm.absaetze()).hasSize(2); |
| assertThat(norm.absaetze().get(0).nummer()).isEqualTo("1"); |
| assertThat(norm.absaetze().get(1).text()).isEqualTo("Bericht ist das Ergebnis."); |
| } |
| |
| @Test |
| void fuegtParagraphEin() { |
| var befehl = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("2")), |
| false, |
| Ebene.PARAGRAPH, |
| "2a", |
| "§ 2a Verfahren (1) Das Verfahren ist einfach.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var enbezListe = ergebnis.neu().normen().stream().map(Norm::enbez).toList(); |
| assertThat(enbezListe).containsExactly("§ 1", "§ 2", "§ 2a", "§ 3"); |
| var neueNorm = ergebnis.neu().norm("§ 2a").orElseThrow(); |
| assertThat(neueNorm.titel()).isEqualTo("Verfahren"); |
| assertThat(neueNorm.absaetze().get(0).text()).isEqualTo("Das Verfahren ist einfach."); |
| } |
| |
| @Test |
| void meldetBereitsVorhandenenParagraphen() { |
| var befehl = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("1")), false, Ebene.PARAGRAPH, "2", "§ 2 Doppelt", PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.MANUELL_PRUEFEN); |
| assertThat(ergebnis.protokoll().get(0).begruendung()).contains("existiert bereits"); |
| } |
| |
| @Test |
| void fuegtEinheitVorWortankerEin() { |
| // „Vor den Wörtern „…“ wird folgende Nummer 1a eingefügt: „…““ — die Position bestimmt der |
| // Wortanker, nicht die Struktur (GVBl. für Berlin 17/2026, Artikel 1 Nr. 2 b) bb)). |
| var befehl = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), |
| true, |
| Ebene.NUMMER, |
| "1a", |
| "1a. die Prüfung von Zitaten,", |
| new WortAnker.VorWoertern("die Anwendung von Befehlen"), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 1)) |
| .isEqualTo( |
| "Die Erprobung umfasst\n" |
| + " 1. das Einlesen von Gesetzen,\n" |
| + " 1a. die Prüfung von Zitaten,\n" |
| + " 2. die Anwendung von Befehlen und\n" |
| + " 3. die Ausgabe von Synopsen."); |
| } |
| |
| @Test |
| void fuegtEinheitNachWortankerEin() { |
| var befehl = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), |
| false, |
| Ebene.NUMMER, |
| "1a", |
| "1a. die Prüfung von Zitaten,", |
| new WortAnker.NachWoertern("das Einlesen von Gesetzen"), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 1)) |
| .contains( |
| " 1. das Einlesen von Gesetzen,\n 1a. die Prüfung von Zitaten,\n 2. die" |
| + " Anwendung von Befehlen und"); |
| } |
| |
| @Test |
| void meldetFehlendenWortankerEinerEinfuegung() { |
| var befehl = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), |
| true, |
| Ebene.NUMMER, |
| "1a", |
| "1a. die Prüfung von Zitaten,", |
| new WortAnker.VorWoertern("gibt es nicht"), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| var eintrag = ergebnis.protokoll().get(0); |
| assertThat(eintrag.status()).isEqualTo(Status.MANUELL_PRUEFEN); |
| assertThat(eintrag.begruendung()).contains("kommt im Zieltext nicht vor"); |
| } |
| |
| @Test |
| void meldetMehrdeutigenWortankerEinerEinfuegung() { |
| // „Prüfung“ steht zweimal im Wortlaut des § 2 — die Position wäre nicht bestimmt. |
| var befehl = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("2")), |
| true, |
| Ebene.SATZ, |
| null, |
| "Die Prüfung ist zu dokumentieren.", |
| new WortAnker.VorWoertern("Prüfung"), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| var eintrag = ergebnis.protokoll().get(0); |
| assertThat(eintrag.status()).isEqualTo(Status.MANUELL_PRUEFEN); |
| assertThat(eintrag.begruendung()).contains("mehrdeutig"); |
| } |
| |
| @Test |
| void fuegtWoerterNachAnkerEin() { |
| var befehl = |
| new WoerterEinfuegung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| new WortAnker.NachWoertern("Gesetzes"), |
| "und dieser Verordnung", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) |
| .isEqualTo("Zweck dieses Gesetzes und dieser Verordnung ist die Erprobung."); |
| } |
| |
| @Test |
| void fuegtAbsatzAn() { |
| var befehl = |
| new Anfuegung( |
| stelle(new Stelle.Paragraph("3")), |
| Ebene.ABSATZ, |
| "2", |
| "(2) Es tritt nie außer Kraft.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var norm = ergebnis.neu().norm("§ 3").orElseThrow(); |
| assertThat(norm.absaetze()).hasSize(2); |
| assertThat(norm.absaetze().get(1).nummer()).isEqualTo("2"); |
| } |
| |
| @Test |
| void hebtParagraphenAuf() { |
| var befehl = new Aufhebung(stelle(new Stelle.Paragraph("2")), PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var norm = ergebnis.neu().norm("§ 2").orElseThrow(); |
| assertThat(norm.weggefallen()).isTrue(); |
| assertThat(norm.gesamtText()).isEqualTo("(weggefallen)"); |
| } |
| |
| @Test |
| void hebtAbsatzAuf() { |
| var befehl = new Aufhebung(stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 1)).isEqualTo("(weggefallen)"); |
| } |
| |
| @Test |
| void streichtWoerter() { |
| var befehl = |
| new Streichung( |
| stelle(new Stelle.Paragraph("2"), new Stelle.SatzNr("2")), "sorgfältig", PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 2", 0)).contains("Die Prüfung erfolgt."); |
| } |
| |
| @Test |
| void nummeriertAbsatzUm() { |
| var befehl = |
| new Umnummerierung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("3")), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var norm = ergebnis.neu().norm("§ 1").orElseThrow(); |
| assertThat(norm.absaetze().get(1).nummer()).isEqualTo("3"); |
| } |
| |
| @Test |
| void meldetUnbekanntenBefehlAlsManuellPruefen() { |
| var befehl = new UnbekannterBefehl(Stelle.LEER, "Die Nummern 1 bis 3 werden aufgehoben.", PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.MANUELL_PRUEFEN); |
| assertThat(ergebnis.anzahlManuell()).isEqualTo(1); |
| assertThat(ergebnis.anzahlAngewandt()).isEqualTo(0); |
| } |
| |
| @Test |
| void ersetztAbsatzDurchMehrereAbsaetze() { |
| var befehl = |
| new StrukturErsetzung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| Ebene.ABSATZ, |
| "(1) Zweck ist die Erprobung. (1a) Die Erprobung ist wichtig.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var norm = ergebnis.neu().norm("§ 1").orElseThrow(); |
| assertThat(norm.absaetze()).hasSize(3); |
| assertThat(norm.absaetze().get(0).nummer()).isEqualTo("1"); |
| assertThat(norm.absaetze().get(1).nummer()).isEqualTo("1a"); |
| assertThat(norm.absaetze().get(1).text()).isEqualTo("Die Erprobung ist wichtig."); |
| assertThat(norm.absaetze().get(2).nummer()).isEqualTo("2"); |
| } |
| |
| @Test |
| void ersetztSatzDurchMehrereSaetze() { |
| var befehl = |
| new StrukturErsetzung( |
| stelle(new Stelle.Paragraph("2"), new Stelle.SatzNr("2")), |
| Ebene.SATZ, |
| "Die Prüfung erfolgt gewissenhaft. Sie wird dokumentiert.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 2", 0)) |
| .contains( |
| "Erprobung ist die Prüfung der Tauglichkeit. Die Prüfung erfolgt gewissenhaft." |
| + " Sie wird dokumentiert. Sie endet mit einem Bericht."); |
| } |
| |
| @Test |
| void ersetztAbsatzBereichDurchBlock() { |
| // „Die Absätze 1 und 2 werden durch die folgenden Absätze 1 bis 3 ersetzt: „…““ |
| var befehl = |
| new StrukturErsetzung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), |
| Ebene.ABSATZ, |
| "(1) Neu eins. (2) Neu zwei. (3) Neu drei.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var norm = ergebnis.neu().norm("§ 1").orElseThrow(); |
| assertThat(norm.absaetze()).hasSize(3); |
| assertThat(norm.absaetze()).extracting(Absatz::nummer).containsExactly("1", "2", "3"); |
| assertThat(norm.absaetze().get(0).text()).isEqualTo("Neu eins."); |
| } |
| |
| @Test |
| void ersetztSatzBereichDurchBlock() { |
| // „Die Sätze 1 und 2 werden wie folgt gefasst: „…““ in § 2 (3 Sätze). |
| var befehl = |
| new StrukturErsetzung( |
| stelle(new Stelle.Paragraph("2"), new Stelle.SatzNr("1")), |
| stelle(new Stelle.Paragraph("2"), new Stelle.SatzNr("2")), |
| Ebene.SATZ, |
| "Neuer Satz eins. Neuer Satz zwei.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 2", 0)) |
| .isEqualTo("Neuer Satz eins. Neuer Satz zwei. Sie endet mit einem Bericht."); |
| } |
| |
| @Test |
| void fuegtSatzNachSatzEin() { |
| var befehl = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("2"), new Stelle.SatzNr("1")), |
| false, |
| Ebene.SATZ, |
| null, |
| "Die Tauglichkeit ist zu dokumentieren.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 2", 0)) |
| .contains( |
| "Erprobung ist die Prüfung der Tauglichkeit. Die Tauglichkeit ist zu dokumentieren." |
| + " Die Prüfung erfolgt sorgfältig."); |
| } |
| |
| // --- Helfer -------------------------------------------------------------------------------- |
| |
| @Test |
| void wendetSammelbefehlAlsEinenEintragAn() { |
| var teil1 = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| "Erprobung", |
| "Prüfung", |
| false, |
| false, |
| PROV); |
| var teil2 = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("2")), "Erprobung", "Prüfung", false, false, PROV); |
| var sammel = new Sammelbefehl(List.of(teil1, teil2)); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(sammel)); |
| |
| // Ein Befehl → genau ein Protokolleintrag, aber beide Teiledits wirken. |
| assertThat(ergebnis.protokoll()).hasSize(1); |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(ergebnis.protokoll().get(0).betroffeneEnbez()) |
| .containsExactlyInAnyOrder("§ 1", "§ 2"); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) |
| .isEqualTo("Zweck dieses Gesetzes ist die Prüfung."); |
| assertThat(absatzText(ergebnis.neu(), "§ 2", 0)).startsWith("Prüfung ist die Prüfung"); |
| } |
| |
| @Test |
| void sammelbefehlMitFehlschlagendemTeilWirdManuell() { |
| var teil1 = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| "Erprobung", |
| "Prüfung", |
| false, |
| false, |
| PROV); |
| var teil2 = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("2")), "gibt es nicht", "egal", false, false, PROV); |
| var sammel = new Sammelbefehl(List.of(teil1, teil2)); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(sammel)); |
| |
| assertThat(ergebnis.protokoll()).hasSize(1); |
| var eintrag = ergebnis.protokoll().get(0); |
| assertThat(eintrag.status()).isEqualTo(Status.MANUELL_PRUEFEN); |
| assertThat(eintrag.begruendung()).contains("Teil 2"); |
| // Der gelungene Teil bleibt trotzdem wirksam. |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) |
| .isEqualTo("Zweck dieses Gesetzes ist die Prüfung."); |
| } |
| |
| @Test |
| void nummeriertParagraphUm() { |
| var befehl = |
| new Umnummerierung( |
| stelle(new Stelle.Paragraph("3")), stelle(new Stelle.Paragraph("4")), PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var enbezListe = ergebnis.neu().normen().stream().map(Norm::enbez).toList(); |
| assertThat(enbezListe).containsExactly("§ 1", "§ 2", "§ 4"); |
| assertThat(ergebnis.neu().norm("§ 4").orElseThrow().titel()).isEqualTo("Schlussvorschriften"); |
| } |
| |
| @Test |
| void paragraphUmnummerierungUeberschreibtWeggefalleneZielnorm() { |
| // § 2 aufheben, dann § 3 → § 2: die weggefallene Zielnorm wird ersetzt. |
| var befehle = |
| List.<eu.mulk.aendggner.aenderung.Aenderungsbefehl>of( |
| new Aufhebung(stelle(new Stelle.Paragraph("2")), PROV), |
| new Umnummerierung( |
| stelle(new Stelle.Paragraph("3")), stelle(new Stelle.Paragraph("2")), PROV)); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), befehle); |
| |
| assertThat(ergebnis.protokoll()).allMatch(a -> a.status() == Status.ANGEWANDT); |
| var enbezListe = ergebnis.neu().normen().stream().map(Norm::enbez).toList(); |
| assertThat(enbezListe).containsExactly("§ 1", "§ 2"); |
| assertThat(ergebnis.neu().norm("§ 2").orElseThrow().titel()).isEqualTo("Schlussvorschriften"); |
| } |
| |
| @Test |
| void meldetKonfliktBeiParagraphUmnummerierung() { |
| var befehl = |
| new Umnummerierung( |
| stelle(new Stelle.Paragraph("3")), stelle(new Stelle.Paragraph("1")), PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.MANUELL_PRUEFEN); |
| assertThat(ergebnis.protokoll().get(0).begruendung()).contains("existiert bereits"); |
| } |
| |
| @Test |
| void fuegtParagraphenBlockEin() { |
| // „Nach § 1 werden die folgenden §§ 1a und 1b eingefügt: „…““ (bezeichnung == null). |
| var befehl = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("1")), |
| false, |
| Ebene.PARAGRAPH, |
| null, |
| "§ 1a Erstes Neu (1) Inhalt eins. § 1b Zweites Neu (1) Inhalt zwei.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var enbezListe = ergebnis.neu().normen().stream().map(Norm::enbez).toList(); |
| assertThat(enbezListe).containsExactly("§ 1", "§ 1a", "§ 1b", "§ 2", "§ 3"); |
| assertThat(ergebnis.neu().norm("§ 1a").orElseThrow().titel()).isEqualTo("Erstes Neu"); |
| assertThat(ergebnis.neu().norm("§ 1b").orElseThrow().titel()).isEqualTo("Zweites Neu"); |
| } |
| |
| @Test |
| void ersetztParagraphBlock() { |
| // „§ 2 wird durch die folgenden §§ 2 und 2a ersetzt: „…““ — § 2 wird durch zwei §§ ersetzt. |
| // Der Querverweis „§ 1 Absatz 1“ im Text darf NICHT als Grenze zerteilt werden. |
| var befehl = |
| new StrukturErsetzung( |
| stelle(new Stelle.Paragraph("2")), |
| null, |
| Ebene.PARAGRAPH, |
| "§ 2 Begriffe (1) Erprobung nach § 1 Absatz 1 ist die Prüfung. § 2a Weiteres (1) Mehr.", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var enbezListe = ergebnis.neu().normen().stream().map(Norm::enbez).toList(); |
| assertThat(enbezListe).containsExactly("§ 1", "§ 2", "§ 2a", "§ 3"); |
| assertThat(ergebnis.neu().norm("§ 2").orElseThrow().absaetze().get(0).text()) |
| .isEqualTo("Erprobung nach § 1 Absatz 1 ist die Prüfung."); |
| assertThat(ergebnis.neu().norm("§ 2a").orElseThrow().titel()).isEqualTo("Weiteres"); |
| } |
| |
| @Test |
| void nummeriertGliederungUm() { |
| var gesetz = |
| new Gesetz( |
| "TestG", |
| "Gesetz", |
| "Test", |
| List.of(new Norm("§ 1", "Zweck", null, List.of(new Absatz("1", "Text.")), false)), |
| List.of(new Gliederung("010020", "Abschnitt 2", "Früherkennung"))); |
| var befehl = |
| new Umnummerierung( |
| stelle(new Stelle.Gliederungseinheit("Abschnitt", "2")), |
| stelle(new Stelle.Gliederungseinheit("Abschnitt", "3")), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz, List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(ergebnis.neu().gliederungen().get(0).bezeichnung()).isEqualTo("Abschnitt 3"); |
| assertThat(ergebnis.neu().gliederungen().get(0).titel()).isEqualTo("Früherkennung"); |
| } |
| |
| private static Gesetz gesetz() { |
| return new Gesetz( |
| "TestG", |
| "Gesetz zur Erprobung", |
| "Testgesetz", |
| List.of( |
| new Norm( |
| "§ 1", |
| "Zweck", |
| null, |
| List.of( |
| new Absatz("1", "Zweck dieses Gesetzes ist die Erprobung."), |
| new Absatz( |
| "2", |
| "Die Erprobung umfasst\n" |
| + " 1. das Einlesen von Gesetzen,\n" |
| + " 2. die Anwendung von Befehlen und\n" |
| + " 3. die Ausgabe von Synopsen.")), |
| false), |
| new Norm( |
| "§ 2", |
| "Begriffe", |
| null, |
| List.of( |
| new Absatz( |
| null, |
| "Erprobung ist die Prüfung der Tauglichkeit. Die Prüfung erfolgt" |
| + " sorgfältig. Sie endet mit einem Bericht.")), |
| false), |
| new Norm( |
| "§ 3", |
| "Schlussvorschriften", |
| null, |
| List.of(new Absatz("1", "Dieses Gesetz tritt am 1. Januar 2021 in Kraft.")), |
| false))); |
| } |
| |
| @Test |
| void wendetWortlautZuAbsatzAn() { |
| // § 2 hat einen unnummerierten Wortlaut, der zu Absatz 1 wird. |
| var befehl = |
| new eu.mulk.aendggner.aenderung.Aenderungsbefehl.WortlautZuAbsatz( |
| stelle(new Stelle.Paragraph("2")), "1", PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var norm = ergebnis.neu().norm("§ 2").orElseThrow(); |
| assertThat(norm.absaetze()).hasSize(1); |
| assertThat(norm.absaetze().get(0).nummer()).isEqualTo("1"); |
| assertThat(norm.absaetze().get(0).anzeigeText()).startsWith("(1) Erprobung ist"); |
| } |
| |
| @Test |
| void wendetBereichsAufhebungAn() { |
| // „Die Nummern 1 bis 3 werden aufgehoben.“ innerhalb von § 1 Absatz 2. |
| var teile = |
| List.<eu.mulk.aendggner.aenderung.Aenderungsbefehl>of( |
| new Aufhebung( |
| stelle( |
| new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2"), new Stelle.NummerNr("1")), |
| PROV), |
| new Aufhebung( |
| stelle( |
| new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2"), new Stelle.NummerNr("2")), |
| PROV), |
| new Aufhebung( |
| stelle( |
| new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2"), new Stelle.NummerNr("3")), |
| PROV)); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(new Sammelbefehl(teile))); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var text = absatzText(ergebnis.neu(), "§ 1", 1); |
| assertThat(text) |
| .contains("1. (weggefallen)") |
| .contains("2. (weggefallen)") |
| .contains("3. (weggefallen)"); |
| } |
| |
| // --- Anhang/Anlage als Norm-Ziel ----------------------------------------------------------- |
| |
| /** Ein Gesetz mit Anhang-Norm nach dem Muster des UWG (mehrere Absätze, Nummern mit Kindern). */ |
| private static Gesetz gesetzMitAnhang() { |
| return new Gesetz( |
| "TestG", |
| null, |
| null, |
| List.of( |
| new Norm("§ 1", "Zweck", null, List.of(new Absatz("1", "Es gilt der Anhang.")), false), |
| new Norm( |
| "Anhang", |
| "(zu § 1)", |
| null, |
| List.of( |
| new Absatz(null, "Folgende Handlungen sind stets unzulässig:"), |
| new Absatz( |
| null, |
| " 1. die erste Handlung;\n" |
| + " 2. die zweite Handlung,\n" |
| + " a) wenn sie morgens geschieht, oder\n" |
| + " b) wenn sie abends geschieht;\n" |
| + " 3. die dritte Handlung;"), |
| new Absatz( |
| null, |
| " 31. die aggressive Handlung,\n" |
| + " a) wenn sie laut geschieht, oder\n" |
| + " b) wenn sie leise gemacht wird.\n" |
| + " 32. die letzte Handlung;")), |
| false))); |
| } |
| |
| private static Stelle anhangStelle(Stelle.Komponente... feinere) { |
| var komponenten = new java.util.ArrayList<Stelle.Komponente>(); |
| komponenten.add(new Stelle.Gliederungseinheit("Anhang", "")); |
| komponenten.addAll(List.of(feinere)); |
| return new Stelle(komponenten); |
| } |
| |
| @Test |
| void fuegtNummerImAnhangNachNummerMitKindernEin() { |
| // „Nach Nummer 2 wird die folgende Nummer 2a eingefügt“ — Nummer 2 hat Buchstaben a)/b); |
| // die neue Nummer muss hinter deren Block landen, nicht zwischen Nummer und Buchstaben. |
| var befehl = |
| new StrukturEinfuegung( |
| anhangStelle(new Stelle.NummerNr("2")), |
| false, |
| Ebene.NUMMER, |
| "2a", |
| "2a. die eingeschobene Handlung;", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetzMitAnhang(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Anhang", 1)) |
| .isEqualTo( |
| " 1. die erste Handlung;\n" |
| + " 2. die zweite Handlung,\n" |
| + " a) wenn sie morgens geschieht, oder\n" |
| + " b) wenn sie abends geschieht;\n" |
| + " 2a. die eingeschobene Handlung;\n" |
| + " 3. die dritte Handlung;"); |
| } |
| |
| @Test |
| void fuegtNummernBlockImAnhangEin() { |
| // Mehrere Einheiten in einem Einfügeblock bleiben eigene Zeilen. |
| var befehl = |
| new StrukturEinfuegung( |
| anhangStelle(new Stelle.NummerNr("3")), |
| false, |
| Ebene.NUMMER, |
| null, |
| "3a. die vierte Handlung;\n3b. die fünfte Handlung;", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetzMitAnhang(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Anhang", 1)) |
| .endsWith( |
| " 3. die dritte Handlung;\n" |
| + " 3a. die vierte Handlung;\n" |
| + " 3b. die fünfte Handlung;"); |
| } |
| |
| @Test |
| void loestNummerBuchstabeKetteImAnhangAuf() { |
| // „b)“ existiert in Nummer 2 und Nummer 31 — die Kette „Nummer 31 Buchstabe b“ ist trotzdem |
| // eindeutig, weil der Buchstabe im Block der Nummer 31 gesucht wird. |
| var befehl = |
| new Ersetzung( |
| anhangStelle(new Stelle.NummerNr("31"), new Stelle.BuchstabeNr("b")), |
| "gemacht wird.", |
| "gemacht wird;", |
| false, |
| false, |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetzMitAnhang(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Anhang", 2)) |
| .contains("wenn sie leise gemacht wird;") |
| .contains(" 32. die letzte Handlung;"); |
| // Nummer 2 Buchstabe b bleibt unangetastet. |
| assertThat(absatzText(ergebnis.neu(), "Anhang", 1)).contains("wenn sie abends geschieht;"); |
| } |
| |
| @Test |
| void streichtWoerterInDerUeberschriftEinerNorm() { |
| var befehl = |
| new Streichung( |
| stelle(new Stelle.Paragraph("3"), new Stelle.Ueberschrift()), |
| "Schlussvorschriften", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(ergebnis.neu().norm("§ 3").orElseThrow().titel()).isEmpty(); |
| } |
| |
| @Test |
| void meldetMehrdeutigeNummerOhneAbsatzangabeImAnhang() { |
| // Gäbe es dieselbe Nummer in mehreren Absätzen, bliebe der Befehl manuell. |
| var gesetz = |
| new Gesetz( |
| "TestG", |
| null, |
| null, |
| List.of( |
| new Norm( |
| "Anhang", |
| null, |
| null, |
| List.of( |
| new Absatz(null, " 1. erstens;"), |
| new Absatz(null, " 1. nochmal erstens;")), |
| false))); |
| var befehl = |
| new Ersetzung( |
| anhangStelle(new Stelle.NummerNr("1")), "erstens", "zuerst", false, false, PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz, List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.MANUELL_PRUEFEN); |
| assertThat(ergebnis.protokoll().get(0).begruendung()).contains("nicht eindeutig"); |
| } |
| |
| // --- Inhaltsübersicht ------------------------------------------------------------------------ |
| |
| private static Gesetz gesetzMitInhaltsuebersicht() { |
| return new Gesetz( |
| "TestG", |
| null, |
| null, |
| List.of( |
| new Norm( |
| "Inhaltsübersicht", |
| null, |
| null, |
| List.of( |
| new Absatz( |
| null, |
| "Teil 1 | Allgemeines\n" |
| + "§ 1 | Zweck\n" |
| + "§ 2 | Begriffe\n" |
| + "Teil 2 | Verfahren\n" |
| + "Abschnitt 1 | Grundsätze\n" |
| + "§ 3 | Ablauf\n" |
| + "§ 4 | Fristen\n" |
| + "§ 5 | Schluss")), |
| false), |
| new Norm("§ 1", "Zweck", null, List.of(new Absatz(null, "Text.")), false))); |
| } |
| |
| private static Stelle iuStelle(Stelle.Komponente... feinere) { |
| var komponenten = new java.util.ArrayList<Stelle.Komponente>(); |
| komponenten.add(new Stelle.Inhaltsuebersicht()); |
| komponenten.addAll(List.of(feinere)); |
| return new Stelle(komponenten); |
| } |
| |
| @Test |
| void fasstAngabeInDerInhaltsuebersichtNeu() { |
| var befehl = |
| new Neufassung(iuStelle(new Stelle.Paragraph("2")), "§ 2 Begriffsbestimmungen", PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetzMitInhaltsuebersicht(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Inhaltsübersicht", 0)) |
| .contains("§ 2 | Begriffsbestimmungen") |
| .doesNotContain("§ 2 | Begriffe\n"); |
| } |
| |
| @Test |
| void fuegtAngabeInDerInhaltsuebersichtEin() { |
| var befehl = |
| new StrukturEinfuegung( |
| iuStelle(new Stelle.Paragraph("2")), |
| false, |
| Ebene.PARAGRAPH, |
| null, |
| "§ 2a Anwendungsbereich", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetzMitInhaltsuebersicht(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Inhaltsübersicht", 0)) |
| .contains("§ 2 | Begriffe\n§ 2a | Anwendungsbereich\nTeil 2 | Verfahren"); |
| } |
| |
| @Test |
| void ersetztAngabenBereichInDerInhaltsuebersicht() { |
| // „Die Angaben zu den §§ 3 bis 4 werden durch die folgenden Angaben ersetzt: …“ |
| var befehl = |
| new StrukturErsetzung( |
| iuStelle(new Stelle.Paragraph("3")), |
| iuStelle(new Stelle.Paragraph("4")), |
| Ebene.PARAGRAPH, |
| "§ 3 (weggefallen) § 4 (weggefallen)", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetzMitInhaltsuebersicht(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Inhaltsübersicht", 0)) |
| .contains("§ 3 | (weggefallen)\n§ 4 | (weggefallen)") |
| .doesNotContain("Ablauf"); |
| } |
| |
| @Test |
| void streichtAngabeUndLoestGliederungsKetteAuf() { |
| // „Die Angabe zu Teil 2 Abschnitt 1 wird gestrichen.“ — Kette grenzt das Fenster ein. |
| var befehl = |
| new Aufhebung( |
| iuStelle( |
| new Stelle.Gliederungseinheit("Teil", "2"), |
| new Stelle.Gliederungseinheit("Abschnitt", "1")), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetzMitInhaltsuebersicht(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Inhaltsübersicht", 0)) |
| .doesNotContain("Abschnitt 1 | Grundsätze") |
| .contains("Teil 2 | Verfahren\n§ 3 | Ablauf"); |
| } |
| |
| // --- Gliederungs-Überschriften --------------------------------------------------------------- |
| |
| private static Gesetz gesetzMitGliederungen() { |
| var teil1 = new Gliederung("010", "Teil 1", "Allgemeines"); |
| var teil2 = new Gliederung("020", "Teil 2", "Anforderungen"); |
| return new Gesetz( |
| "TestG", |
| null, |
| null, |
| List.of( |
| new Norm("§ 1", "Zweck", teil1, List.of(new Absatz(null, "Eins.")), false), |
| new Norm("§ 2", "Begriffe", teil1, List.of(new Absatz(null, "Zwei.")), false), |
| new Norm("§ 3", "Pflichten", teil2, List.of(new Absatz(null, "Drei.")), false), |
| new Norm("§ 4", "Nachweise", teil2, List.of(new Absatz(null, "Vier.")), false)), |
| List.of(teil1, teil2)); |
| } |
| |
| @Test |
| void fuegtGliederungsUeberschriftenEin() { |
| // „Nach § 2 werden die folgenden Überschriften zu Teil 3 und zu Teil 3 Abschnitt 1 |
| // eingefügt: „Teil 3 Modernisierung Abschnitt 1 Grundpflichten“.“ |
| var befehl = |
| new GliederungsUeberschriften( |
| stelle(new Stelle.Paragraph("2")), |
| List.of( |
| new Stelle.Gliederungseinheit("Teil", "3"), |
| new Stelle.Gliederungseinheit("Abschnitt", "1")), |
| List.of(), |
| "Teil 3 Modernisierung Abschnitt 1 Grundpflichten", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetzMitGliederungen(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(ergebnis.neu().gliederungen()) |
| .extracting(Gliederung::bezeichnung) |
| .containsExactly("Teil 1", "Teil 3", "Abschnitt 1", "Teil 2"); |
| var abschnitt1 = ergebnis.neu().gliederungen().get(2); |
| assertThat(abschnitt1.titel()).isEqualTo("Grundpflichten"); |
| // §§ 3 und 4 (der zusammenhängende Block nach dem Anker) hängen jetzt unter Abschnitt 1. |
| assertThat(ergebnis.neu().norm("§ 3").orElseThrow().gliederung()).isEqualTo(abschnitt1); |
| assertThat(ergebnis.neu().norm("§ 4").orElseThrow().gliederung()).isEqualTo(abschnitt1); |
| assertThat(ergebnis.neu().norm("§ 2").orElseThrow().gliederung().bezeichnung()) |
| .isEqualTo("Teil 1"); |
| } |
| |
| @Test |
| void ersetztGliederungsUeberschriften() { |
| // „Die bisherigen Überschriften zu Teil 2 werden durch die folgende Überschrift zu |
| // Abschnitt 2 ersetzt: „Abschnitt 2 Neue Anforderungen“.“ |
| var befehl = |
| new GliederungsUeberschriften( |
| Stelle.LEER, |
| List.of(new Stelle.Gliederungseinheit("Abschnitt", "2")), |
| List.of(List.of(new Stelle.Gliederungseinheit("Teil", "2"))), |
| "Abschnitt 2 Neue Anforderungen", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetzMitGliederungen(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(ergebnis.neu().gliederungen()) |
| .extracting(Gliederung::bezeichnung) |
| .containsExactly("Teil 1", "Abschnitt 2"); |
| assertThat(ergebnis.neu().norm("§ 3").orElseThrow().gliederung().titel()) |
| .isEqualTo("Neue Anforderungen"); |
| } |
| |
| @Test |
| void ersetztGesetzesUeberschrift() { |
| var befehl = |
| new Neufassung(stelle(new Stelle.Ueberschrift()), "Gesetz zur gründlichen Erprobung", PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(ergebnis.neu().langue()).isEqualTo("Gesetz zur gründlichen Erprobung"); |
| } |
| |
| /** |
| * Aufsteigende Umnummerierungs-Kaskade: Beide Befehle meinen die ursprüngliche Zählung. In |
| * Dokumentreihenfolge angewandt entstünde ein zweiter Absatz 2; der Anwender zieht deshalb den |
| * räumenden Befehl vor. |
| */ |
| @Test |
| void loestAufsteigendeUmnummerierungsKaskadeAuf() { |
| var eins = |
| new Umnummerierung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), |
| PROV); |
| var zwei = |
| new Umnummerierung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("3")), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(eins, zwei)); |
| |
| assertThat(ergebnis.anzahlManuell()).isZero(); |
| assertThat(ergebnis.neu().norm("§ 1").orElseThrow().absaetze().stream().map(Absatz::nummer)) |
| .containsExactly("2", "3"); |
| // Das Protokoll bleibt in Dokumentreihenfolge, unabhängig von der Anwendungsreihenfolge. |
| assertThat(ergebnis.protokoll().get(0).befehl()).isSameAs(eins); |
| } |
| |
| /** Eine Umnummerierung läuft vor der Einfügung, die ihre bisherige Bezeichnung neu vergibt. */ |
| @Test |
| void raeumtBezeichnungVorEinfuegung() { |
| var einfuegung = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| false, |
| Ebene.ABSATZ, |
| "2", |
| "(2) Neuer Absatz.", |
| PROV); |
| var umnummerierung = |
| new Umnummerierung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("3")), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(einfuegung, umnummerierung)); |
| |
| assertThat(ergebnis.anzahlManuell()).isZero(); |
| assertThat(ergebnis.neu().norm("§ 1").orElseThrow().absaetze().stream().map(Absatz::nummer)) |
| .containsExactly("1", "2", "3"); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 1)).isEqualTo("Neuer Absatz."); |
| } |
| |
| /** |
| * Anders als ein Absatz trägt eine Aufzählungsnummer ihre Bezeichnung als Marke im Text; die |
| * Umnummerierung muss sie dort austauschen. |
| */ |
| @Test |
| void nummeriertAufzaehlungsNummerImTextUm() { |
| var befehl = |
| new Umnummerierung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2"), new Stelle.NummerNr("2")), |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2"), new Stelle.NummerNr("4")), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 1)) |
| .contains(" 4. die Anwendung von Befehlen und") |
| .doesNotContain(" 2. die Anwendung"); |
| } |
| |
| /** Der weggefallene Platzhalter mit der Zielbezeichnung weicht der Umnummerierung. */ |
| @Test |
| void ueberschreibtWeggefalleneNummer() { |
| var mitLuecke = |
| new Gesetz( |
| "TestG", |
| "Gesetz zur Erprobung", |
| "Testgesetz", |
| List.of( |
| new Norm( |
| "§ 1", |
| "Zweck", |
| null, |
| List.of( |
| new Absatz( |
| "1", |
| "Die Erprobung umfasst\n" |
| + " 1. (weggefallen)\n" |
| + " 2. die Anwendung von Befehlen.")), |
| false)), |
| List.of()); |
| var befehl = |
| new Umnummerierung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1"), new Stelle.NummerNr("2")), |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1"), new Stelle.NummerNr("1")), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(mitLuecke, List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) |
| .isEqualTo("Die Erprobung umfasst\n 1. die Anwendung von Befehlen."); |
| } |
| |
| /** Beginnt der Einschub mit einem Satzzeichen, entfällt das Leerzeichen hinter dem Anker. */ |
| @Test |
| void fuegtSatzzeichenOhneVorangehendesLeerzeichenEin() { |
| var befehl = |
| new WoerterEinfuegung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| new WortAnker.NachWoertern("die Erprobung"), |
| ", auch in Teilen,", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); |
| |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) |
| .isEqualTo("Zweck dieses Gesetzes ist die Erprobung, auch in Teilen,."); |
| } |
| |
| private static String absatzText(Gesetz gesetz, String enbez, int index) { |
| return gesetz.norm(enbez).orElseThrow().absaetze().get(index).text(); |
| } |
| |
| // --- Bayerisches Landesrecht --------------------------------------------------------------- |
| |
| /** Ein Gesetz nach bayerischem Muster: Art.-Normen, amtliche Satznummern, Fußnoten. */ |
| private static Gesetz bayGesetz() { |
| return new Gesetz( |
| "BayTestG", |
| "Bayerisches Testgesetz", |
| null, |
| List.of( |
| new Norm( |
| "Art. 1", |
| "Zweck", |
| null, |
| List.of( |
| new Absatz( |
| "1", |
| "¹Die Erprobung ist Zweck dieses Gesetzes⁶). ²Sie erfolgt sorgfältig.\n" |
| + "⁶) [Amtl. Anm.:] BayRS 0-0-T"), |
| new Absatz("2", "¹Erster Satz. ²Zweiter Satz.")), |
| false), |
| new Norm( |
| "Art. 2", |
| "Begriffe", |
| null, |
| List.of( |
| new Absatz( |
| null, |
| "Erprobung ist die Prüfung der Tauglichkeit; sie endet mit einem" |
| + " Bericht.")), |
| false)), |
| List.of()); |
| } |
| |
| @Test |
| void wendetFussnotenAufhebungAn() { |
| var befehl = |
| new eu.mulk.aendggner.aenderung.Aenderungsbefehl.FussnotenAufhebung( |
| stelle(new Stelle.Paragraph("1", "Art.")), List.of("6"), PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(bayGesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| var text = absatzText(ergebnis.neu(), "Art. 1", 0); |
| assertThat(text).doesNotContain("⁶").doesNotContain("[Amtl. Anm.:]"); |
| assertThat(text).startsWith("¹Die Erprobung ist Zweck dieses Gesetzes."); |
| } |
| |
| @Test |
| void meldetFehlendeFussnoteAlsManuellPruefen() { |
| var befehl = |
| new eu.mulk.aendggner.aenderung.Aenderungsbefehl.FussnotenAufhebung( |
| stelle(new Stelle.Paragraph("1", "Art.")), List.of("7"), PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(bayGesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.MANUELL_PRUEFEN); |
| assertThat(ergebnis.protokoll().get(0).begruendung()).contains("Fußnote 7"); |
| } |
| |
| @Test |
| void wendetSatznummerierungsStreichungAn() { |
| var befehl = |
| new eu.mulk.aendggner.aenderung.Aenderungsbefehl.SatznummerierungStreichung( |
| stelle( |
| new Stelle.Paragraph("1", "Art."), |
| new Stelle.AbsatzNr("2"), |
| new Stelle.SatzNr("1")), |
| "1", |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(bayGesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Art. 1", 1)).isEqualTo("Erster Satz. ²Zweiter Satz."); |
| } |
| |
| @Test |
| void satzUmnummerierungSchreibtSuperskriptUm() { |
| // „Satz 2 wird Satz 3.“ — die amtliche Satznummer im Text wird umgeschrieben. |
| var befehl = |
| new Umnummerierung( |
| stelle( |
| new Stelle.Paragraph("1", "Art."), |
| new Stelle.AbsatzNr("2"), |
| new Stelle.SatzNr("2")), |
| stelle( |
| new Stelle.Paragraph("1", "Art."), |
| new Stelle.AbsatzNr("2"), |
| new Stelle.SatzNr("3")), |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(bayGesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Art. 1", 1)).isEqualTo("¹Erster Satz. ³Zweiter Satz."); |
| } |
| |
| @Test |
| void wendetWortlautZuSatzAn() { |
| var befehl = |
| new eu.mulk.aendggner.aenderung.Aenderungsbefehl.WortlautZuSatz( |
| stelle(new Stelle.Paragraph("2", "Art.")), "1", PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(bayGesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Art. 2", 0)).startsWith("¹Erprobung ist die Prüfung"); |
| } |
| |
| @Test |
| void wendetWortlautVoranstellungUndNummerierungAn() { |
| // Bayerische Folge: erst „Dem Wortlaut werden die folgenden Abs. 1 und 2 vorangestellt“, |
| // dann „Der bisherige Wortlaut wird Abs. 3“ — nur der unnummerierte Absatz erhält die Nummer. |
| var voranstellung = |
| new eu.mulk.aendggner.aenderung.Aenderungsbefehl.WortlautVoranstellung( |
| stelle(new Stelle.Paragraph("2", "Art.")), "(1) Erstens. (2) Zweitens.", PROV); |
| var nummerierung = |
| new eu.mulk.aendggner.aenderung.Aenderungsbefehl.WortlautZuAbsatz( |
| stelle(new Stelle.Paragraph("2", "Art.")), "3", PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(bayGesetz(), List.of(voranstellung, nummerierung)); |
| |
| assertThat(ergebnis.protokoll()) |
| .allSatisfy(eintrag -> assertThat(eintrag.status()).isEqualTo(Status.ANGEWANDT)); |
| var norm = ergebnis.neu().norm("Art. 2").orElseThrow(); |
| assertThat(norm.absaetze()).hasSize(3); |
| assertThat(norm.absaetze().get(0).nummer()).isEqualTo("1"); |
| assertThat(norm.absaetze().get(1).nummer()).isEqualTo("2"); |
| assertThat(norm.absaetze().get(2).nummer()).isEqualTo("3"); |
| assertThat(norm.absaetze().get(2).text()).startsWith("Erprobung ist"); |
| } |
| |
| @Test |
| void wendetHalbsatzErsetzungAn() { |
| var befehl = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("2", "Art."), new Stelle.HalbsatzNr("2")), |
| "sie endet", |
| "sie schließt", |
| false, |
| false, |
| PROV); |
| |
| var ergebnis = BefehlAnwender.anwenden(bayGesetz(), List.of(befehl)); |
| |
| assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); |
| assertThat(absatzText(ergebnis.neu(), "Art. 2", 0)) |
| .isEqualTo("Erprobung ist die Prüfung der Tauglichkeit; sie schließt mit einem Bericht."); |
| } |
| |
| @Test |
| void ziehtUmnummerierungVorDieNeubesetzendeEinfuegung() { |
| // „Nach § 2 wird der folgende neue § 3 eingefügt“ + „Der bisherige § 3 wird § 4“: In der |
| // Textreihenfolge angewandt kollidierten beide Befehle auf „§ 3“. Die Umnummerierung |
| // beschreibt den Stand vor der Änderung und geht der Neubesetzung sachlich voraus. |
| var einfuegung = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("2")), |
| false, |
| Ebene.PARAGRAPH, |
| "3", |
| "§ 3 Zwischennorm (1) Der neue Text.", |
| PROV); |
| var umnummerierung = |
| new Umnummerierung( |
| stelle(new Stelle.Paragraph("3")), |
| stelle(new Stelle.Paragraph("4")), |
| new Provenienz("1", "2.", "(Test)")); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(einfuegung, umnummerierung)); |
| |
| assertThat(ergebnis.anzahlAngewandt()).isEqualTo(2); |
| // Protokolliert wird weiterhin in der Reihenfolge des Änderungsgesetzes. |
| assertThat(ergebnis.protokoll()) |
| .extracting(a -> a.befehl().provenienz().gliederungsPfad()) |
| .containsExactly("1.", "2."); |
| assertThat(ergebnis.neu().normen()) |
| .extracting(n -> n.enbez()) |
| .containsExactly("§ 1", "§ 2", "§ 3", "§ 4"); |
| assertThat(ergebnis.neu().norm("§ 3").orElseThrow().titel()).isEqualTo("Zwischennorm"); |
| assertThat(ergebnis.neu().norm("§ 4").orElseThrow().titel()).isEqualTo("Schlussvorschriften"); |
| } |
| |
| @Test |
| void zieehtEineGanzeUmnummerierungsketteVorDieEinfuegung() { |
| // Eine Einfügung besetzt die Nr. 2, die eine Kette von Umnummerierungen erst räumen muss: |
| // „Nr. 3 wird Nr. 4“ hält „Nr. 2 wird Nr. 3“ auf, und diese hält die Einfügung auf. Vorgezogen |
| // werden muss deshalb nicht nur das letzte Glied der Kette, sondern die ganze Kette — sonst |
| // träfe „Nr. 2 wird Nr. 3“ auf eine noch besetzte Nr. 3 (BayJG Art. 56 Abs. 1 Buchst. hh/ii). |
| // Solange der Verbund als Ganzes vorrückte, war das umsonst zu haben; seit die Schritte |
| // einzeln geordnet werden, hängt es an der Mitnahme der Vorgänger. |
| var einfuegung = |
| new StrukturEinfuegung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2"), new Stelle.NummerNr("1")), |
| false, |
| Ebene.NUMMER, |
| "2", |
| "2. die Zulassung von Anträgen,", |
| PROV); |
| // So liefert der Erkenner eine Bereichs-Umnummerierung: das höhere Paar zuerst. |
| var kette = |
| new Sammelbefehl( |
| List.of(umnummerierungNummer("2", "3", "4"), umnummerierungNummer("2", "2", "3"))); |
| |
| var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(einfuegung, kette)); |
| |
| assertThat(ergebnis.anzahlManuell()).isZero(); |
| assertThat(absatzText(ergebnis.neu(), "§ 1", 1)) |
| .isEqualTo( |
| "Die Erprobung umfasst\n" |
| + " 1. das Einlesen von Gesetzen,\n" |
| + " 2. die Zulassung von Anträgen,\n" |
| + " 3. die Anwendung von Befehlen und\n" |
| + " 4. die Ausgabe von Synopsen."); |
| } |
| |
| @Test |
| void laesstDieBegleitaenderungAnIhrerDokumentstelle() { |
| // „Die bisherige Nr. 3 wird Nr. 4 und die Angabe „schriftliche “ wird gestrichen“: Die |
| // Umnummerierung muss vor den Befehl rücken, der die Nr. 3 neu besetzt — ihre Begleitänderung |
| // aber nicht. Vorgezogen träfe die Streichung noch zwei Fundstellen und bliebe mehrdeutig; an |
| // ihrem Platz trifft sie genau eine, weil der Punkt davor die andere längst getilgt hat |
| // (BayJG Art. 56 Abs. 1 Buchst. gg). |
| var neuBesetzung = umnummerierungNummer("1", "2", "3"); |
| var tilgtDieErsteFundstelle = |
| new Ersetzung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), |
| "die schriftliche Anmeldung", |
| "die Anmeldung", |
| false, |
| false, |
| PROV); |
| var verbund = |
| new Sammelbefehl( |
| List.of( |
| umnummerierungNummer("1", "3", "4"), |
| new Streichung(stelle(new Stelle.Paragraph("1")), "schriftliche ", PROV))); |
| |
| var ergebnis = |
| BefehlAnwender.anwenden( |
| kaskadenGesetz(), List.of(neuBesetzung, tilgtDieErsteFundstelle, verbund)); |
| |
| // Beide Teile des Verbunds greifen — die Streichung ist an ihrer Dokumentstelle eindeutig. |
| assertThat(ergebnis.anzahlManuell()).isZero(); |
| var text = absatzText(ergebnis.neu(), "§ 1", 0); |
| assertThat(text).contains("1. die Anmeldung,").contains("3. die Prüfung und"); |
| // Die Umnummerierung ist vorgerückt, ihre Begleitänderung nicht: Nr. 4 trägt den Text der |
| // bisherigen Nr. 3, und zwar ohne „schriftliche“. |
| assertThat(text).contains("4. die Bestätigung.").doesNotContain("schriftliche"); |
| } |
| |
| private static Umnummerierung umnummerierungNummer(String absatz, String alt, String neu) { |
| return new Umnummerierung( |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr(absatz), new Stelle.NummerNr(alt)), |
| stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr(absatz), new Stelle.NummerNr(neu)), |
| PROV); |
| } |
| |
| /** |
| * Ein Gesetz für die Kaskadenprobe: Das Wort „schriftliche“ steht zweimal in derselben Norm, in |
| * der ersten und in der letzten Nummer. Die Nummer 2, die die Kette frei macht, bleibt hier |
| * unbesetzt — die Einfügung, die sie im echten Fall füllt, gehört nicht zur Ordnungsfrage. |
| */ |
| private static Gesetz kaskadenGesetz() { |
| return new Gesetz( |
| "TestG", |
| "Gesetz zur Erprobung", |
| "Testgesetz", |
| List.of( |
| new Norm( |
| "§ 1", |
| "Verfahren", |
| null, |
| List.of( |
| new Absatz( |
| "1", |
| "Die Erprobung umfasst\n" |
| + " 1. die schriftliche Anmeldung,\n" |
| + " 2. die Prüfung und\n" |
| + " 3. die schriftliche Bestätigung.")), |
| false))); |
| } |
| } |