aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/eu
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/eu')
-rw-r--r--src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java123
1 files changed, 116 insertions, 7 deletions
diff --git a/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java b/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java
index 32350ff..73745a6 100644
--- a/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java
+++ b/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java
@@ -716,10 +716,11 @@ public final class BefehlAnwender {
return TextErgebnis.fehler(
"„" + befehl.woerter() + "“ kommt " + anzahl + "-mal vor.");
}
+ int naht = text.indexOf(befehl.woerter());
return TextErgebnis.ok(
- text.replace(befehl.woerter(), "")
- .replaceAll(" +", " ")
- .replaceAll(" ([,;.])", "$1"));
+ heileNaht(
+ text.substring(0, naht) + text.substring(naht + befehl.woerter().length()),
+ naht));
}));
}
@@ -833,7 +834,27 @@ public final class BefehlAnwender {
return angewandt(befehl, norm.enbez());
}
- // Neufassung eines Satzes / einer Nummer / eines Buchstabens: Bereich ersetzen.
+ // Neufassung einer Nummer / eines Buchstabens: Der neue Wortlaut tritt an die Stelle der
+ // alten Einheit — und zwar auf deren Einrückung. Ohne sie stünde die neugefasste Zeile in
+ // Spalte 0, und der Zeilenblock ihrer Marke reichte fortan bis ans Absatzende, weil er so
+ // weit reicht, wie tiefer eingerückt wird (StellenAufloeser.zeilenBlock): ein späterer
+ // Befehl „Nach Nr. 4 werden die folgenden Nrn. 5 bis 7 eingefügt“ träte dann hinter die
+ // letzte Nummer des Absatzes statt hinter die Nr. 4.
+ if (feinsteIstAufzaehlung(befehl.stelle())) {
+ return bearbeiteBereich(
+ normen,
+ befehl,
+ (text, bereich) ->
+ TextErgebnis.ok(
+ text.substring(0, bereich.von())
+ + rueckeZitatEin(
+ normalisiereZitatText(befehl.neuerText()),
+ einrueckungVon(text, bereich.von()))
+ + text.substring(bereich.bis())));
+ }
+
+ // Neufassung eines Satzes / Halbsatzes: Bereich ersetzen. Eine Einrückung gibt es hier
+ // nicht — der Bereich beginnt mitten in der Zeile.
return bearbeiteText(
normen, befehl, text -> TextErgebnis.ok(normalisiereZitatText(befehl.neuerText())));
}
@@ -1105,10 +1126,19 @@ public final class BefehlAnwender {
// Der Einfügeblock darf mehrere Einheiten enthalten („die Nummern 4a bis 4c“);
// jede Aufzählungszeile des Zitats bleibt eine eigene Zeile.
var block = rueckeZitatEin(normalisiereZitatText(befehl.text()), einrueckung);
- return TextErgebnis.ok(
+ var neuerText =
befehl.vorher()
? text.substring(0, position) + block + "\n" + text.substring(position)
- : text.substring(0, position) + "\n" + block + text.substring(position));
+ : text.substring(0, position) + "\n" + block + text.substring(position);
+ // Vergibt der Block eine Bezeichnung, die ein leerer Platzhalter der Altfassung
+ // noch hält („7. (aufgehoben)“), so weicht dieser: Zwei Einheiten gleicher
+ // Bezeichnung kann es nicht geben, und Inhalt geht dabei nicht verloren. Es ist
+ // dieselbe Regel, die schon bei der Umnummerierung auf eine weggefallene
+ // Bezeichnung gilt, und so zeigt es die amtliche Nachfassung.
+ for (var marke : platzhalterMarken(block, befehl.ebene())) {
+ neuerText = entferneWeggefallenenPlatzhalter(neuerText, marke);
+ }
+ return TextErgebnis.ok(neuerText);
});
};
}
@@ -1421,6 +1451,36 @@ public final class BefehlAnwender {
};
}
+ private static final Pattern LEERER_PLATZHALTER =
+ Pattern.compile("\\((?:weggefallen|gegenstandslos|aufgehoben)\\)");
+
+ /**
+ * Die Aufzählungsmarken, die ein eingefügter Block vergibt („5.“, „6.“, „7.“) — ohne solche, die
+ * er selbst als leeren Platzhalter führt, denn ein Platzhalter verdrängt keinen Platzhalter.
+ */
+ private static List<String> platzhalterMarken(String block, Ebene ebene) {
+ var muster =
+ switch (ebene) {
+ case NUMMER -> NUMMER_MARKER;
+ case BUCHSTABE -> BUCHSTABE_MARKER;
+ default -> null;
+ };
+ if (muster == null) {
+ return List.of();
+ }
+ var marken = new ArrayList<String>();
+ var marker = muster.matcher(block);
+ while (marker.find()) {
+ int zeilenEnde = block.indexOf('\n', marker.end());
+ var rest = block.substring(marker.end(), zeilenEnde < 0 ? block.length() : zeilenEnde);
+ if (LEERER_PLATZHALTER.matcher(rest.strip()).matches()) {
+ continue;
+ }
+ marken.add(marker.group(1) + (ebene == Ebene.BUCHSTABE ? ")" : "."));
+ }
+ return marken;
+ }
+
/**
* Entfernt die weggefallene Aufzählungszeile mit der gegebenen Marke — die soeben umbenannte
* Zeile bleibt stehen, weil nur ein leerer Platzhalter getroffen wird.
@@ -1812,6 +1872,50 @@ public final class BefehlAnwender {
|| k instanceof Stelle.BuchstabeNr);
}
+ /**
+ * Wahr, wenn die feinste Komponente der Stelle eine Aufzählungseinheit ist (Nummer/Buchstabe).
+ */
+ private static boolean feinsteIstAufzaehlung(Stelle stelle) {
+ var komponenten = stelle.komponenten();
+ if (komponenten.isEmpty()) {
+ return false;
+ }
+ var feinste = komponenten.get(komponenten.size() - 1);
+ return feinste instanceof Stelle.NummerNr || feinste instanceof Stelle.BuchstabeNr;
+ }
+
+ /**
+ * Heilt den Weißraum an der Stelle, an der eine Streichung Text herausgenommen hat: Aus zwei
+ * Leerzeichen wird eines, vor einem Satzzeichen bleibt keines. Geheilt wird ausdrücklich nur die
+ * Naht und nicht der ganze Zieltext — dieser ist bei einer Stelle ohne Fein-Komponente der
+ * gesamte Absatz, und eine Heilung über ihn hinweg fräße die Einrückung der Aufzählungszeilen:
+ * Aus den zwei Leerzeichen vor jeder Marke würde eines. Traf die Streichung den Zeilenanfang, so
+ * ist der Weißraum vor der Naht Einrückung und bleibt unangetastet.
+ */
+ private static String heileNaht(String text, int naht) {
+ int von = naht;
+ while (von > 0 && text.charAt(von - 1) == ' ') {
+ von--;
+ }
+ int bis = naht;
+ while (bis < text.length() && text.charAt(bis) == ' ') {
+ bis++;
+ }
+ if (bis == von) {
+ return text;
+ }
+ if (von == 0 || text.charAt(von - 1) == '\n') {
+ return text.substring(0, naht) + text.substring(bis);
+ }
+ if (bis < text.length() && ",;.".indexOf(text.charAt(bis)) >= 0) {
+ return text.substring(0, von) + text.substring(bis);
+ }
+ if (bis - von == 1) {
+ return text;
+ }
+ return text.substring(0, von) + " " + text.substring(bis);
+ }
+
private static @Nullable String labelVon(Stelle stelle) {
for (var komponente : stelle.komponenten().reversed()) {
if (komponente instanceof Stelle.NummerNr n) {
@@ -1969,6 +2073,11 @@ public final class BefehlAnwender {
private static final Pattern AUFZAEHLUNGSZEILE =
Pattern.compile("^(\\d+[a-z]?\\.|[a-z]{1,3}\\))\\s.*");
+ // Der Zwischenraum zwischen der Aufzählungsmarke und ihrem Text: im Gesetzblattsatz steht dort
+ // die Marken-Tabulatorspalte, im kanonischen Klartext ein einzelnes Leerzeichen.
+ private static final Pattern MARKEN_ZWISCHENRAUM =
+ Pattern.compile("^(\\d+[a-z]?\\.|[a-z]{1,3}\\))[ \\t]+");
+
private static String normalisiereZitatText(String text) {
var zeilen = text.split("\n");
var sb = new StringBuilder();
@@ -1978,7 +2087,7 @@ public final class BefehlAnwender {
// Stellenauflösung sie als Kindzeilen der Einheit erkennt.
var fortsetzungsEinzug = "";
for (var zeile : zeilen) {
- var gestutzt = zeile.strip();
+ var gestutzt = MARKEN_ZWISCHENRAUM.matcher(zeile.strip()).replaceFirst("$1 ");
if (gestutzt.isEmpty()) {
continue;
}