aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java19
-rw-r--r--src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java10
-rw-r--r--src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java53
3 files changed, 75 insertions, 7 deletions
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 fb578cc..80206f6 100644
--- a/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java
+++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java
@@ -200,7 +200,19 @@ final class BefehlErkenner {
private static final Pattern STRUKTUR_EINFUEGUNG =
Pattern.compile(
- "^(Nach|Vor) (.+?) (?:wird|werden) (?:der |die |das )?folgende[nrs]? (.+?) "
+ "^(Nach|Vor) (.+?) (?:wird|werden) (?:der |die |das )?folgende[nrs]? (?:neue[nrs]? )?(.+?) "
+ + "(?:ein|an)gefügt: "
+ + ENUM
+ + Z
+ + "\\.?$");
+
+ // „In Kapitel 4 wird nach § 12 der folgende neue § 13 angefügt: „…““ — gliederungsbezogene
+ // Einfügung. Die Gliederungsangabe nennt nur den Abschnitt, in dem die neue Einheit landet;
+ // maßgeblich für die Position ist der Anker („nach § 12“), der ohnehin eindeutig ist.
+ private static final Pattern STRUKTUR_EINFUEGUNG_IN_GLIEDERUNG =
+ Pattern.compile(
+ "^In (?:Buch|Teil|Kapitel|Abschnitt|Unterabschnitt|Titel) \\S+ (?:wird|werden) "
+ + "(?i:(nach|vor)) (.+?) (?:der |die |das )?folgende[nrs]? (?:neue[nrs]? )?(.+?) "
+ "(?:ein|an)gefügt: "
+ ENUM
+ Z
@@ -897,8 +909,9 @@ final class BefehlErkenner {
provenienz));
}
- if ((m = STRUKTUR_EINFUEGUNG.matcher(text)).matches()) {
- var vorher = m.group(1).equals("Vor");
+ if ((m = STRUKTUR_EINFUEGUNG.matcher(text)).matches()
+ || (m = STRUKTUR_EINFUEGUNG_IN_GLIEDERUNG.matcher(text)).matches()) {
+ var vorher = m.group(1).equalsIgnoreCase("vor");
var stelle = StellenParser.parse(m.group(2));
if (stelle.isEmpty()) {
return Optional.empty();
diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java
index 7460b15..f1b75e1 100644
--- a/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java
+++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java
@@ -190,6 +190,13 @@ public final class TextBereiniger {
private static final Pattern INVERTIERTES_LISTEN_ZITAT =
Pattern.compile("(?m)^(\\s*)(\\d+[a-z]?\\.|[a-z]{1,3}\\))[ \\t]+„[ \\t]+");
+ // Sachnummern mit Leerzeichen: Niedersachsen zitiert eingeschobene Paragraphen als „§ 2 a“, der
+ // Rest der Rechtssprache als „§ 2a“. Auf die kanonische Form ziehen, damit Stellen- und
+ // Ebenenparser greifen. Ein folgendes „)“ oder „.“ schließt Aufzählungsmarker des
+ // Änderungsgesetzes aus („… nach § 8 c) In Absatz 2 …“).
+ private static final Pattern SACHNUMMER_MIT_LEERZEICHEN =
+ Pattern.compile("(§|Art\\.) (\\d+) ([a-z])(?![a-zäöüß).])");
+
private TextBereiniger() {}
public static String bereinige(String rohText) {
@@ -206,7 +213,8 @@ public final class TextBereiniger {
var verbunden = verbindeUmbrueche(zeilen);
// Falsch-positive markerlose Zusammenzüge („durch“ + „die“ → „durchdie“) reparieren — die
// Befehlsvokabeln sind nie Kompositum-Bestandteile.
- return loeseArtikelFussnoten(trenneVerklebteZitatgrenzen(reflowUndStrippe(verbunden)));
+ var ergebnis = loeseArtikelFussnoten(trenneVerklebteZitatgrenzen(reflowUndStrippe(verbunden)));
+ return SACHNUMMER_MIT_LEERZEICHEN.matcher(ergebnis).replaceAll("$1 $2$3");
}
/**
diff --git a/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java b/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java
index b6bee1b..b2e855d 100644
--- a/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java
+++ b/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java
@@ -2,6 +2,7 @@ package eu.mulk.aendggner.anwendung;
import eu.mulk.aendggner.aenderung.Aenderungsbefehl;
import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Anfuegung;
+import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ebene;
import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Aufhebung;
import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ersetzung;
import eu.mulk.aendggner.aenderung.Aenderungsbefehl.FussnotenAufhebung;
@@ -26,6 +27,7 @@ import eu.mulk.aendggner.gesetz.Gliederung;
import eu.mulk.aendggner.gesetz.Norm;
import eu.mulk.aendggner.gesetz.Superskript;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@@ -71,16 +73,20 @@ public final class BefehlAnwender {
var protokoll = new ArrayList<AngewandteAenderung>();
String neuerLangtitel = null;
- for (var befehl : befehle) {
+ // Angewandt wird in Sachreihenfolge, protokolliert in der Reihenfolge des Änderungsgesetzes.
+ var ergebnisse = new AngewandteAenderung[befehle.size()];
+ for (int index : anwendungsReihenfolge(befehle)) {
+ var befehl = befehle.get(index);
// „Die Überschrift wird wie folgt gefasst / durch die folgende Überschrift ersetzt“ auf
// oberster Ebene meint die Überschrift des Gesetzes selbst.
if (befehl instanceof Neufassung n && istNurUeberschrift(n.stelle())) {
neuerLangtitel = n.neuerText().replaceAll("\\s+", " ").strip();
- protokoll.add(angewandt(befehl, "(Gesetzesüberschrift)"));
+ ergebnisse[index] = angewandt(befehl, "(Gesetzesüberschrift)");
continue;
}
- protokoll.add(wendeAn(normen, gliederungen, befehl));
+ ergebnisse[index] = wendeAn(normen, gliederungen, befehl);
}
+ protokoll.addAll(Arrays.asList(ergebnisse));
var neu = alt.mitNormen(normen).mitGliederungen(gliederungen);
if (neuerLangtitel != null) {
@@ -89,6 +95,47 @@ public final class BefehlAnwender {
return new AnwendungsErgebnis(neu, protokoll);
}
+ /**
+ * Reihenfolge, in der die Befehle anzuwenden sind: grundsätzlich die des Änderungsgesetzes.
+ *
+ * <p>Eine Umnummerierung „Der bisherige § 13 wird § 14“ wird jedoch vor eine vorangehende
+ * Einfügung gezogen, die denselben Paragraphen neu besetzt („In Kapitel 4 wird nach § 12 der
+ * folgende neue § 13 angefügt“). „Bisherig“ bezeichnet den Stand vor der Änderung; die
+ * Umnummerierung geht der Neubesetzung sachlich also voraus. In der Textreihenfolge angewandt
+ * liefen beide Befehle dagegen auf zwei Paragraphen gleicher Bezeichnung hinaus.
+ */
+ private static List<Integer> anwendungsReihenfolge(List<Aenderungsbefehl> befehle) {
+ var reihenfolge = new ArrayList<Integer>(befehle.size());
+ for (int i = 0; i < befehle.size(); i++) {
+ reihenfolge.add(i);
+ }
+ for (int j = 0; j < befehle.size(); j++) {
+ if (!(befehle.get(j) instanceof Umnummerierung u) || !nurParagraph(u.stelle())) {
+ continue;
+ }
+ var quelle = u.stelle().paragraph().get().enbez();
+ for (int i = 0; i < j; i++) {
+ if (quelle.equals(neuerParagraph(befehle.get(i)))) {
+ reihenfolge.remove(Integer.valueOf(j));
+ reihenfolge.add(reihenfolge.indexOf(i), j);
+ break;
+ }
+ }
+ }
+ return reihenfolge;
+ }
+
+ /** Bezeichnung des Paragraphen, den {@code befehl} neu anlegt — sonst {@code null}. */
+ private static @Nullable String neuerParagraph(Aenderungsbefehl befehl) {
+ if (!(befehl instanceof StrukturEinfuegung s)
+ || s.ebene() != Ebene.PARAGRAPH
+ || s.bezeichnung() == null) {
+ return null;
+ }
+ var sigel = s.stelle().paragraph().map(Stelle.Paragraph::sigel).orElse("§");
+ return sigel + " " + s.bezeichnung();
+ }
+
private static boolean istNurUeberschrift(Stelle stelle) {
return stelle.komponenten().size() == 1
&& stelle.komponenten().get(0) instanceof Stelle.Ueberschrift;