Rheinland-Pfalz kennt den Strichpunkt und die Einleitung
Von den drei benannten Idiom-Grenzen des rheinland-pfälzischen Falles fallen zwei.
Die erste: „In Satz 2 wird der Punkt durch einen Strichpunkt ersetzt und
folgender Halbsatz angefügt: „…““. Zwei Neuerungen in einem Satz. „Strichpunkt“
ist die deutsche Nebenform des Semikolons und tritt zu den Satzzeichen-Mustern
hinzu — als Subjekt wie als Ziel, ohne eine einzige neue Gruppe, damit die
hartkodierten Gruppennummern des Erkenners unberührt bleiben. Der Halbsatz wird
eine eigene Ebene der Anfügung. 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. Sonst stünde er hinter einem
Punkt und wäre gerade kein Halbsatz. Als Ziel einer Struktur-Einfügung oder
-Ersetzung bleibt er ausgeschlossen: Welche Hälfte eines Satzes gemeint wäre,
sagt keine Bezeichnung. Die Verbundmechanik selbst brauchte keinen Eingriff.
Die zweite: „In der Einleitung werden die Worte „…“ durch die Angabe „…“
ersetzt.“ Das ist ein Chapeau-Lokator wie „im Satzteil vor Nummer 1“, nur ohne
Bezugspunkt; er tritt als zweite Alternative zum vorhandenen Muster. Eine echte
Verengung auf den Satzteil vor der ersten Marke wäre eine eigene
Stellenkomponente und ist es nicht: Kommt der Zieltext außer im Chapeau auch in
einer Nummer vor, so meldet die Auflösung Mehrdeutigkeit — der richtige Ausgang.
Dass beide Befehle am rheinland-pfälzischen Stamm gleichwohl nicht greifen, liegt
an der Quelle und nicht am Werkzeug: Das Portal führt keine früheren
Gesamtausgaben, der Stamm ist hier die Nachfassung, und ihr Wortlaut trägt die
Änderung bereits.
Offen bleibt die dritte Grenze, die Verweisung auf einen anderen Punkt desselben
Artikels.
Geprüft: 386 Tests (zuvor 381).
Change-Id: I993d8645fba1c933ad8497ca175cef265df2c0b0
diff --git a/README.md b/README.md
index 84889b9..538f10a 100644
--- a/README.md
+++ b/README.md
@@ -328,10 +328,18 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
// „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 @@
+ 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 @@
+ 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 @@
+ 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 @@
+ "|, (?=(?: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 @@
"^(?: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 @@
+ WOERTER
+ " "
+ Z
- + "|(ein Komma|ein Semikolon)) eingefügt\\.$");
+ + "|(ein Komma|ein Semikolon|einen Strichpunkt)) eingefügt\\.$");
private BefehlErkenner() {}
@@ -1646,7 +1648,7 @@
// 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 @@
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 @@
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 @@
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 @@
// „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 @@
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 @@
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 @@
}
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 @@
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.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 @@
.filter(b -> b.provenienz().originalText().contains("die Worte"))
.toList();
assertThat(wortbefehle).hasSizeGreaterThanOrEqualTo(7);
- assertThat(wortbefehle).filteredOn(b -> b instanceof UnbekannterBefehl).hasSize(1);
+ assertThat(wortbefehle).noneMatch(b -> b instanceof UnbekannterBefehl);
- // 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“).
+ // 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 @@
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 @@
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 @@
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.");
+ }
}