diff options
9 files changed, 229 insertions, 28 deletions
@@ -328,10 +328,18 @@ Streichen und Umnummerierung. soeben umnummerierte Einheit, weil ihr Zieltext nichts unterscheidet; eine Wortoperation dagegen löst zunächst norm-weit auf und fällt erst dann auf jene Einheit zurück, wenn die weite Suche mehrdeutig bleibt (§ 8 Absatz 5); -13. Verb-Rahmen, deren Unterpunkte allein die Fundstelle tragen („Es werden +13. der Halbsatz als Ebene der Anfügung („wird der Punkt durch einen Strichpunkt + ersetzt und folgender Halbsatz angefügt: „…““). Er beginnt keinen neuen Satz, + sondern setzt den bestehenden hinter dem Strichpunkt fort; angefügt wird er + deshalb nur, wenn der Zieltext tatsächlich auf einen Strichpunkt endet. + „Strichpunkt“ gilt dabei als Nebenform des Semikolons; +14. der bezugspunktlose Chapeau-Lokator („In der Einleitung“, „Im Eingangssatz“) — + er meint dasselbe wie „im Satzteil vor Nummer 1“ und trägt wie dieser keine + eigene Stellenkomponente; +15. Verb-Rahmen, deren Unterpunkte allein die Fundstelle tragen („Es werden ersetzt: … in § 35 Absatz 3 die Angabe „X“ jeweils durch die Angabe „Y“,“), sowie -14. die Neufassung der Gesetzesüberschrift. +16. die Neufassung der Gesetzesüberschrift. (3) Landesrechtliche Befehlsformen bestimmt ergänzend § 13. @@ -601,9 +609,13 @@ Berlin und Baden-Württemberg jede Fassung datierbar ausgeben, fehlt dort. Für Rheinland-Pfalz (Ausbildungs- und Prüfungsordnung der Unfallkasse) reicht die Prüfung deshalb nur bis zur Befehlserkennung — nicht weil das Werkzeug es nicht könnte, sondern weil die Vorfassung nicht zu beschaffen ist. Der Fall hat -gleichwohl zwei allgemeine Funde gebracht: die Nebenform „die Worte“ für „die -Wörter“ und den Seitenfuß des dortigen Gesetzblattes, der im Inhaltsstrom das -Zitat einer Neufassung zerschneidet. +gleichwohl vier allgemeine Funde gebracht: die Nebenform „die Worte“ für „die +Wörter“, den Seitenfuß des dortigen Gesetzblattes, der im Inhaltsstrom das +Zitat einer Neufassung zerschneidet, den Strichpunkt samt Halbsatz-Anfügung +(§ 7 Absatz 2 Nummer 13) und den bezugspunktlosen Chapeau-Lokator (§ 7 Absatz 2 +Nummer 14). Offen bleibt allein die Verweisung auf einen anderen Punkt desselben +Artikels („Die Inhaltsübersicht wird entsprechend der vorstehenden Nummer 8 +Buchst. a geändert“). (5) Welche Konvention welches Land beisteuert, welche Stammfassungen woher stammen und was noch offen ist, verzeichnet diff --git a/src/main/java/eu/mulk/aendggner/aenderung/Aenderungsbefehl.java b/src/main/java/eu/mulk/aendggner/aenderung/Aenderungsbefehl.java index a80e09a..47bca25 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/Aenderungsbefehl.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/Aenderungsbefehl.java @@ -22,6 +22,8 @@ public sealed interface Aenderungsbefehl { PARAGRAPH, ABSATZ, SATZ, + /** Die Hälfte eines am Strichpunkt geteilten Satzes; sie beginnt keinen neuen Satz. */ + HALBSATZ, NUMMER, BUCHSTABE } diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java index c825a64..ef49b7b 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java @@ -180,7 +180,7 @@ final class BefehlErkenner { // ersetzt: „…““ — der Ersatz steht als Zitatblock hinter dem Doppelpunkt. private static final Pattern PUNKT_DURCH_WORTLAUT = Pattern.compile( - "^(?:In )?(.+?) (?:wird|werden) (der Punkt|das Komma|das Semikolon)" + "^(?:In )?(.+?) (?:wird|werden) (der Punkt|das Komma|das Semikolon|der Strichpunkt)" + " am Ende(?: des Satzes)? durch " + "(?:die |den )?folgende[n]? (?:Wörter|Wortlaut) ersetzt: " + Z @@ -194,12 +194,12 @@ final class BefehlErkenner { // prüft, statt es an das Einheitsende zu heften. private static final Pattern SATZZEICHEN_ERSETZUNG_OHNE_STELLE = Pattern.compile( - "^(Der Punkt|Das Komma|Das Semikolon)(?: am Ende(?: des Satzes)?)? wird durch " + "^(Der Punkt|Das Komma|Das Semikolon|Der Strichpunkt)(?: am Ende(?: des Satzes)?)? wird durch " + "(ein Komma und " + WOERTER + " " + Z - + "|ein Komma|einen Punkt|ein Semikolon|" + + "|ein Komma|einen Punkt|ein Semikolon|einen Strichpunkt|" + WOERTER + " " + Z @@ -209,13 +209,13 @@ final class BefehlErkenner { // „am Ende“ ist wie oben entbehrlich. private static final Pattern SATZZEICHEN_ERSETZUNG = Pattern.compile( - "^(?:In )?(.+?) (?:wird|werden) (der Punkt|das Komma|das Semikolon)" + "^(?:In )?(.+?) (?:wird|werden) (der Punkt|das Komma|das Semikolon|der Strichpunkt)" + "(?: am Ende(?: des Satzes)?)? durch " + "(ein Komma und " + WOERTER + " " + Z - + "|ein Komma|einen Punkt|ein Semikolon|" + + "|ein Komma|einen Punkt|ein Semikolon|einen Strichpunkt|" + WOERTER + " " + Z @@ -473,7 +473,7 @@ final class BefehlErkenner { + WOERTER + " " + Z - + "(?: am Ende)? durch (ein Komma|ein Semikolon|einen Punkt) ersetzt\\.$"); + + "(?: am Ende)? durch (ein Komma|ein Semikolon|einen Strichpunkt|einen Punkt) ersetzt\\.$"); // „In Satz 2 wird nach dem Wort «1» ein Komma und werden die Wörter «2» eingefügt.“ private static final Pattern KOMMA_UND_WOERTER_EINFUEGUNG = @@ -518,6 +518,8 @@ final class BefehlErkenner { + Z + "\\.?$"); + private static final Pattern HALBSATZ = Pattern.compile("Halbsatz(?: \\d+[a-z]?)?"); + private static final Pattern EBENE_BEZEICHNUNG = Pattern.compile( "^(?:(?:§|Art\\.) (\\d+[a-z]?)|(?:Absatz|Abs\\.) (\\d+[a-z]?)|Satz(?: (\\d+[a-z]?))?|Sätze" @@ -583,7 +585,7 @@ final class BefehlErkenner { + WOERTER + " " + Z - + "|(ein Komma|ein Semikolon))$"); + + "|(ein Komma|ein Semikolon|einen Strichpunkt))$"); // „… ein Komma eingefügt und werden …“: Trennstellen eines Verbundbefehls sind „ und “ (ggf. mit // Komma) bzw. „, “ direkt vor „wird/werden“. Innerhalb von Zitaten steht „ und “ als «n» @@ -597,7 +599,7 @@ final class BefehlErkenner { + "|, (?=(?:die (?:Wörter|Worte)|das Wort|die Angabe|die Zahl) «)" // Ebenso vor einer Satzzeichen-Klausel („… ersetzt, der Punkt am Ende durch ein // Semikolon ersetzt und …“). - + "|, (?=(?:der Punkt|das Komma|das Semikolon) am Ende)"); + + "|, (?=(?:der Punkt|das Komma|das Semikolon|der Strichpunkt) am Ende)"); private static final Pattern WIRD_WERDEN = Pattern.compile(" (?:wird|werden) "); // Rechte Verbundklausel, die ihre Fundstelle als Struktureinheit nennt („in Satz 1 wird …“, @@ -622,7 +624,7 @@ final class BefehlErkenner { "^(?:In )?(.+?) (?:wird|werden) (?:jeweils )?(nach|vor) " + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung) " + Z - + " (ein Komma|ein Semikolon|einen Punkt) eingefügt\\.$"); + + " (ein Komma|ein Semikolon|einen Strichpunkt|einen Punkt) eingefügt\\.$"); // „Nach der Angabe «1» wird die Angabe «2» eingefügt.“ — Anker zuerst, ohne eigene Stelle (nutzt // den Kontext). Tritt vor allem als rechte Klausel eines Verbundbefehls auf. @@ -634,7 +636,7 @@ final class BefehlErkenner { + WOERTER + " " + Z - + "|(ein Komma|ein Semikolon)) eingefügt\\.$"); + + "|(ein Komma|ein Semikolon|einen Strichpunkt)) eingefügt\\.$"); private BefehlErkenner() {} @@ -1646,7 +1648,7 @@ final class BefehlErkenner { // Klausel, deren Subjekt ein Satzzeichen ist („das Komma wird durch … ersetzt“). private static final Pattern SATZZEICHEN_SUBJEKT = - Pattern.compile("(?i)(?:der Punkt|das Komma|das Semikolon)\\b"); + Pattern.compile("(?i)(?:der Punkt|das Komma|das Semikolon|der Strichpunkt)\\b"); private static Optional<Aenderungsbefehl> erkenneVerbund( String text, Stelle kontext, ZitatExtraktor.Ergebnis zitate, Provenienz provenienz) { @@ -2032,6 +2034,11 @@ final class BefehlErkenner { private record EbeneBezeichnung(Ebene ebene, String bezeichnung) {} private static Optional<EbeneBezeichnung> ebeneUndBezeichnung(String phrase) { + // Der Halbsatz steht außerhalb des Musters, damit dessen Gruppennummern unberührt bleiben; + // eine Bezeichnung führt er nicht, denn angefügt wird stets hinter dem Strichpunkt. + if (HALBSATZ.matcher(phrase.strip()).matches()) { + return Optional.of(new EbeneBezeichnung(Ebene.HALBSATZ, null)); + } var m = EBENE_BEZEICHNUNG.matcher(phrase.strip()); if (!m.matches()) { return Optional.empty(); @@ -2280,6 +2287,7 @@ final class BefehlErkenner { case PARAGRAPH -> new Stelle.Paragraph(nummer); case ABSATZ -> new Stelle.AbsatzNr(nummer); case SATZ -> new Stelle.SatzNr(nummer); + case HALBSATZ -> new Stelle.HalbsatzNr(nummer); case NUMMER -> new Stelle.NummerNr(nummer); case BUCHSTABE -> new Stelle.BuchstabeNr(nummer); }; @@ -2366,7 +2374,8 @@ final class BefehlErkenner { return switch (Character.toLowerCase(phrase.charAt(0)) + phrase.substring(1)) { case "der Punkt", "einen Punkt" -> "."; case "das Komma", "ein Komma" -> ","; - case "das Semikolon", "ein Semikolon" -> ";"; + // „Strichpunkt“ ist die deutsche Nebenform des Semikolons; Rheinland-Pfalz schreibt sie. + case "das Semikolon", "ein Semikolon", "der Strichpunkt", "einen Strichpunkt" -> ";"; default -> throw new IllegalArgumentException("Unbekanntes Satzzeichen: " + phrase); }; } diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/StellenParser.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/StellenParser.java index 9176f89..466adfe 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/StellenParser.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/StellenParser.java @@ -54,10 +54,18 @@ public final class StellenParser { // „in dem Satzteil vor Nummer 1“, „in der Angabe vor Nummer 1“, bayerisch auch „Satzteil nach // Nr. 3“ — verfeinernde Chapeau-Angaben ohne eigene Stelle-Komponente; die Ersetzung sucht // ohnehin im Text der umgebenden Stelle. + // + // Die zweite Alternative nennt keinen Bezugspunkt: „In der Einleitung“ (Rheinland-Pfalz) meint + // dasselbe wie „im Satzteil vor Nummer 1“, nämlich den Chapeau des Satzes, den der Rahmen + // bezeichnet. Auch sie trägt keine eigene Komponente. Kommt der Zieltext außer im Chapeau auch + // in einer Nummer vor, so meldet die Auflösung Mehrdeutigkeit — der richtige Ausgang; eine + // echte Verengung auf den Satzteil vor der ersten Marke wäre eine eigene Komponente und gehört + // nicht hierher. private static final Pattern CHAPEAU_QUALIFIER = Pattern.compile( - "(?i)(?:im |in dem |in der |dem |der )?(?:Satzteil|Angabe) (?:vor|nach) " - + "(?:Nummer|Nr\\.|Buchstabe|Buchst\\.|Satz|Absatz|Abs\\.) \\S+"); + "(?i)(?:im |in dem |in der |dem |der )?(?:(?:Satzteil|Angabe) (?:vor|nach) " + + "(?:Nummer|Nr\\.|Buchstabe|Buchst\\.|Satz|Absatz|Abs\\.) \\S+" + + "|(?:Einleitungssatz|Eingangssatz|Einleitung)(?!\\p{L}))"); /** * Wahr, wenn die Phrase ausschließlich aus einem Chapeau-Qualifier besteht (z.B. „im Satzteil vor diff --git a/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java b/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java index 56b610e..64c6564 100644 --- a/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java +++ b/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java @@ -591,6 +591,7 @@ public final class BefehlAnwender { bezeichnung, s.stelle().paragraph().map(Stelle.Paragraph::sigel).orElse("§")); case ABSATZ -> new Stelle.AbsatzNr(bezeichnung); case SATZ -> new Stelle.SatzNr(bezeichnung); + case HALBSATZ -> new Stelle.HalbsatzNr(bezeichnung); case NUMMER -> new Stelle.NummerNr(bezeichnung); case BUCHSTABE -> new Stelle.BuchstabeNr(bezeichnung); }; @@ -1276,6 +1277,15 @@ public final class BefehlAnwender { normen.addAll(vonIndex, neue); yield angewandt(befehl, neue.stream().map(Norm::enbez).toList()); } + // Ein Halbsatz ist keine selbständig bezeichnete Einheit: Er lässt sich anfügen + // (siehe wendeAnfuegungAn), aber nicht als Ziel ersetzen oder einfügen — welche Hälfte + // eines Satzes gemeint wäre, sagt keine Bezeichnung. + case HALBSATZ -> + manuell( + befehl, + Grund.NICHT_UNTERSTUETZT, + "Der Halbsatz ist keine selbständig bezeichnete Einheit; nur seine Anfügung ist" + + " umgesetzt."); }; } @@ -1474,6 +1484,15 @@ public final class BefehlAnwender { } return TextErgebnis.ok(neuerText); }); + // Ein Halbsatz ist keine selbständig bezeichnete Einheit: Er lässt sich anfügen + // (siehe wendeAnfuegungAn), aber nicht als Ziel ersetzen oder einfügen — welche Hälfte + // eines Satzes gemeint wäre, sagt keine Bezeichnung. + case HALBSATZ -> + manuell( + befehl, + Grund.NICHT_UNTERSTUETZT, + "Der Halbsatz ist keine selbständig bezeichnete Einheit; nur seine Anfügung ist" + + " umgesetzt."); }; } @@ -1636,6 +1655,21 @@ public final class BefehlAnwender { normen, befehl, text -> TextErgebnis.ok(text.stripTrailing() + " " + befehl.text().strip())); + // Ein Halbsatz beginnt keinen neuen Satz, sondern setzt den bestehenden hinter dem + // Strichpunkt fort. Angehängt wird deshalb wie beim Satz — aber nur, wenn der Zieltext + // tatsächlich auf einen Strichpunkt endet: Sonst stünde der Halbsatz hinter einem Punkt und + // wäre gerade kein Halbsatz. Regelmäßig setzt ihn der Verbund unmittelbar zuvor. + case HALBSATZ -> + bearbeiteText( + normen, + befehl, + text -> + text.stripTrailing().endsWith(";") + ? TextErgebnis.ok(text.stripTrailing() + " " + befehl.text().strip()) + : TextErgebnis.fehler( + "Der Halbsatz setzt einen Strichpunkt voraus; der Zieltext endet nicht" + + " darauf.", + Grund.BESTAND_WIDERSPRICHT)); case NUMMER, BUCHSTABE -> bearbeiteText( normen, diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java index 41f30e5..d0273ad 100644 --- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java +++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java @@ -6,6 +6,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assumptions.assumeTrue; import eu.mulk.aendggner.aenderung.Aenderungsbefehl; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ersetzung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Sammelbefehl; import eu.mulk.aendggner.aenderung.Aenderungsbefehl.StrukturEinfuegung; import eu.mulk.aendggner.aenderung.Aenderungsbefehl.UnbekannterBefehl; import eu.mulk.aendggner.aenderung.Aenderungsbefehl.WortAnker; @@ -1878,20 +1880,31 @@ class EndToEndTest { .filter(b -> b.provenienz().originalText().contains("die Worte")) .toList(); assertThat(wortbefehle).hasSizeGreaterThanOrEqualTo(7); - assertThat(wortbefehle).filteredOn(b -> b instanceof UnbekannterBefehl).hasSize(1); - - // Drei benannte Idiom-Grenzen, alle im Wortlaut festgehalten: - // 5. a) aa) — Satzzeichen-Ersetzung im Verbund mit einer Halbsatz-Anfügung („wird der Punkt - // durch einen Strichpunkt ersetzt und folgender Halbsatz angefügt“). - // 7. a) aa) — „In der Einleitung“ als Ziel, also der Chapeau eines Satzes mit Nummern. - // 12. — die Verweisung auf einen anderen Punkt desselben Artikels („Die Inhaltsübersicht wird - // entsprechend der vorstehenden Nummer 8 Buchst. a geändert“). + assertThat(wortbefehle).noneMatch(b -> b instanceof UnbekannterBefehl); + + // Der Strichpunkt-Verbund („wird der Punkt durch einen Strichpunkt ersetzt und folgender + // Halbsatz angefügt“) und der bezugspunktlose Chapeau („In der Einleitung“) werden gelesen; + // dass beide gleichwohl nicht greifen, liegt an der Quelle und nicht am Werkzeug: Der Stamm + // ist hier die Nachfassung, ihr Wortlaut trägt die Änderung bereits. + assertThat(befehlAn(parseErgebnis, "5. a) aa)")).isInstanceOf(Sammelbefehl.class); + assertThat(befehlAn(parseErgebnis, "7. a) aa)")).isInstanceOf(Ersetzung.class); + + // Eine benannte Grenze bleibt: die Verweisung auf einen anderen Punkt desselben Artikels + // („Die Inhaltsübersicht wird entsprechend der vorstehenden Nummer 8 Buchst. a geändert“). var unerkannt = parseErgebnis.befehle().stream() .filter(b -> b instanceof UnbekannterBefehl) .map(b -> b.provenienz().gliederungsPfad()) .toList(); - assertThat(unerkannt).containsExactly("5. a) aa)", "7. a) aa)", "12."); + assertThat(unerkannt).containsExactly("12."); + } + + private static Aenderungsbefehl befehlAn( + AenderungsgesetzParser.ParseErgebnis ergebnis, String gliederungsPfad) { + return ergebnis.befehle().stream() + .filter(b -> gliederungsPfad.equals(b.provenienz().gliederungsPfad())) + .findFirst() + .orElseThrow(); } /** diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java index 7742a3a..80234b8 100644 --- a/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java @@ -1473,4 +1473,45 @@ class BefehlErkennerTest { assertThat(BefehlErkenner.erkenne(text, Stelle.LEER, zitate, PROV)) .containsInstanceOf(Ersetzung.class); } + + /** + * Rheinland-Pfalz: „In Satz 2 wird der Punkt durch einen Strichpunkt ersetzt und folgender + * Halbsatz angefügt: „…““. Zwei Neuerungen in einem Satz — der Strichpunkt als deutsche Nebenform + * des Semikolons und der Halbsatz als Ebene der Anfügung. Die Verbundmechanik selbst bestand + * bereits. + */ + @Test + void erkenntStrichpunktErsetzungMitHalbsatzAnfuegung() { + var befehl = + erkenne( + "In Satz 2 wird der Punkt durch einen Strichpunkt ersetzt und folgender Halbsatz" + + " angefügt: „die Bearbeitungszeit der Abschlussarbeit zählt zu den" + + " Fachstudien.“", + new Stelle(List.of(new Stelle.Paragraph("8"), new Stelle.AbsatzNr("1")))); + + assertThat(befehl).containsInstanceOf(Sammelbefehl.class); + var teile = ((Sammelbefehl) befehl.orElseThrow()).teilbefehle(); + assertThat(teile).hasSize(2); + + assertThat(teile.get(0)).isInstanceOf(Ersetzung.class); + var ersetzung = (Ersetzung) teile.get(0); + assertThat(ersetzung.alt()).isEqualTo("."); + assertThat(ersetzung.neu()).isEqualTo(";"); + assertThat(ersetzung.stelle().anzeigeText()).isEqualTo("§ 8 Absatz 1 Satz 2"); + + assertThat(teile.get(1)).isInstanceOf(Anfuegung.class); + var anfuegung = (Anfuegung) teile.get(1); + assertThat(anfuegung.ebene()).isEqualTo(Ebene.HALBSATZ); + assertThat(anfuegung.text()).contains("die Bearbeitungszeit der Abschlussarbeit"); + } + + /** Der Strichpunkt gilt auch als Subjekt („Der Strichpunkt wird durch ein Komma ersetzt“). */ + @Test + void erkenntDenStrichpunktAlsSubjekt() { + var befehl = erkenne("Der Strichpunkt am Ende wird durch ein Komma ersetzt.", PARAGRAPH_5); + assertThat(befehl).containsInstanceOf(Ersetzung.class); + var ersetzung = (Ersetzung) befehl.orElseThrow(); + assertThat(ersetzung.alt()).isEqualTo(";"); + assertThat(ersetzung.neu()).isEqualTo(","); + } } diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/StellenParserTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/StellenParserTest.java index ee7e5c7..13e4442 100644 --- a/src/test/java/eu/mulk/aendggner/aenderung/parse/StellenParserTest.java +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/StellenParserTest.java @@ -138,4 +138,18 @@ class StellenParserTest { StellenParser.parse("Absatz 1 in der Angabe vor Nummer 1").orElseThrow().anzeigeText()) .isEqualTo("Absatz 1"); } + + /** + * „In der Einleitung“ (Rheinland-Pfalz) ist ein Chapeau-Lokator wie „im Satzteil vor Nummer 1“: + * Er nennt keinen Bezugspunkt und trägt deshalb keine eigene Komponente — die Operation läuft auf + * der Stelle, die der Rahmen bezeichnet. + */ + @Test + void einleitungIstEinChapeauLokator() { + assertThat(StellenParser.istNurChapeau("In der Einleitung")).isTrue(); + assertThat(StellenParser.istNurChapeau("Im Eingangssatz")).isTrue(); + assertThat(StellenParser.istNurChapeau("in dem Einleitungssatz")).isTrue(); + // Ein Wort, das bloß so anfängt, ist keiner. + assertThat(StellenParser.istNurChapeau("In der Einleitungsformel")).isFalse(); + } } diff --git a/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java b/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java index a1f5cff..39c71cc 100644 --- a/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java +++ b/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java @@ -1891,4 +1891,72 @@ class BefehlAnwenderTest { assertThat(ergebnis.neu().norm("§ 108").orElseThrow().absaetze().get(0).text()) .doesNotContain("oder Absatz 4"); } + + /** + * Ein Halbsatz setzt den Satz hinter dem Strichpunkt fort; er beginnt keinen neuen. Angefügt wird + * er deshalb wie ein Satz — aber nur hinter einem Strichpunkt. Endet der Zieltext auf einen + * Punkt, so wäre das Angefügte gerade kein Halbsatz, und der Befehl bleibt liegen. + */ + @Test + void halbsatzWirdHinterDemStrichpunktAngefuegt() { + var gesetz = + new Gesetz( + "TestG", + "Testgesetz", + "TestG", + List.of( + new Norm( + "§ 8", + null, + null, + List.of(new Absatz("1", "Erster Satz. Der zweite Satz endet hier;")), + false))); + + var befehl = + new Anfuegung( + stelle(new Stelle.Paragraph("8"), new Stelle.AbsatzNr("1")), + Ebene.HALBSATZ, + null, + "die Bearbeitungszeit zählt zu den Fachstudien.", + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz, List.of(befehl)); + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(ergebnis.neu().norm("§ 8").orElseThrow().absaetze().get(0).text()) + .isEqualTo( + "Erster Satz. Der zweite Satz endet hier; die Bearbeitungszeit zählt zu den" + + " Fachstudien."); + } + + @Test + void halbsatzOhneStrichpunktBleibtLiegen() { + var gesetz = + new Gesetz( + "TestG", + "Testgesetz", + "TestG", + List.of( + new Norm( + "§ 8", + null, + null, + List.of(new Absatz("1", "Erster Satz. Der zweite Satz endet hier.")), + false))); + + var befehl = + new Anfuegung( + stelle(new Stelle.Paragraph("8"), new Stelle.AbsatzNr("1")), + Ebene.HALBSATZ, + null, + "die Bearbeitungszeit zählt zu den Fachstudien.", + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz, List.of(befehl)); + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.MANUELL_PRUEFEN); + assertThat(ergebnis.protokoll().get(0).grund()).isEqualTo(Grund.BESTAND_WIDERSPRICHT); + assertThat(ergebnis.protokoll().get(0).begruendung()) + .contains("setzt einen Strichpunkt voraus"); + assertThat(ergebnis.neu().norm("§ 8").orElseThrow().absaetze().get(0).text()) + .isEqualTo("Erster Satz. Der zweite Satz endet hier."); + } } |
