diff options
Diffstat (limited to 'src/main/java')
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; |
