| Matthias Andreas Benkard | 279fca6 | 2026-08-23 08:32:08 +0200 | [diff] [blame] | 1 | // SPDX-FileCopyrightText: 2020 Matthias Andreas Benkard <code@mail.matthias.benkard.de> |
| 2 | // SPDX-License-Identifier: AGPL-3.0-or-later |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 3 | package eu.mulk.aendggner; |
| 4 | |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 5 | import eu.mulk.aendggner.aenderung.DokumentArt; |
| 6 | import eu.mulk.aendggner.aenderung.DokumentKopf; |
| 7 | import eu.mulk.aendggner.aenderung.parse.AenderungsantragParser; |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 8 | import eu.mulk.aendggner.aenderung.parse.AenderungsgesetzParser; |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 9 | import eu.mulk.aendggner.aenderung.parse.DokumentErkenner; |
| 10 | import eu.mulk.aendggner.aenderung.parse.EntwurfsPatcher; |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 11 | import eu.mulk.aendggner.aenderung.parse.PatchTextExtraktor; |
| 12 | import eu.mulk.aendggner.aenderung.parse.SuperskriptModus; |
| 13 | import eu.mulk.aendggner.aenderung.parse.TextBereiniger; |
| 14 | import eu.mulk.aendggner.anwendung.BefehlAnwender; |
| 15 | import eu.mulk.aendggner.gesetz.Gesetz; |
| 16 | import eu.mulk.aendggner.gesetz.Superskript; |
| 17 | import eu.mulk.aendggner.gesetz.gii.GiiXmlLoader; |
| 18 | import eu.mulk.aendggner.gesetz.land.LandesRechtLoader; |
| 19 | import eu.mulk.aendggner.synopse.HtmlRenderer; |
| 20 | import eu.mulk.aendggner.synopse.SynopseBuilder; |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 21 | import java.nio.file.Path; |
| 22 | import java.util.ArrayList; |
| 23 | import java.util.List; |
| Matthias Andreas Benkard | ffc553b | 2026-08-15 12:18:32 +0200 | [diff] [blame] | 24 | import org.jspecify.annotations.Nullable; |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 25 | |
| 26 | /** |
| 27 | * Kernpipeline: Stammgesetz laden → Änderungsgesetze parsen und anwenden → Synopse rendern. |
| 28 | * |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 29 | * <p>Wird sowohl von der Befehlszeile ({@link AendGgner}) als auch von der Browserfassung ({@code |
| 30 | * eu.mulk.aendggner.wasm.BrowserMain}) verwendet, damit die Anwendungslogik nur an einer Stelle |
| 31 | * existiert. Sie kennt kein Dateisystem: Eingaben kommen als {@link Quelle} (Name und Bytes). |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 32 | */ |
| 33 | public final class Pipeline { |
| 34 | |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 35 | private static final org.jboss.logging.Logger log = |
| 36 | org.jboss.logging.Logger.getLogger(Pipeline.class); |
| 37 | |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 38 | private Pipeline() {} |
| 39 | |
| 40 | public record Ergebnis( |
| 41 | String html, |
| 42 | long anzahlAngewandt, |
| 43 | long anzahlManuell, |
| 44 | int anzahlGeaenderteNormen, |
| 45 | int anzahlProtokollEintraege) {} |
| 46 | |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 47 | /** |
| 48 | * Ein eingespeistes Änderungsdokument samt erkannter Art und aufbereitetem Text. |
| 49 | * |
| 50 | * @param eingearbeitet die Dokumente, die vor der Anwendung in {@code text} eingearbeitet wurden |
| 51 | * — bei einem Entwurf die Änderungsanträge, die ihn geändert haben. Sie gehen in die |
| 52 | * Quellenzeile ein, denn die gezeigte Fassung ist ohne sie nicht nachvollziehbar. |
| Matthias Andreas Benkard | ffc553b | 2026-08-15 12:18:32 +0200 | [diff] [blame] | 53 | * @param fassung welche von mehreren Fassungen des Dokuments gilt, sofern es mehrere trägt — die |
| 54 | * Beschlussempfehlung stellt Entwurf und Ausschussfassung nebeneinander. Sonst {@code null}. |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 55 | */ |
| Matthias Andreas Benkard | ffc553b | 2026-08-15 12:18:32 +0200 | [diff] [blame] | 56 | record Quelldokument( |
| 57 | Quelle quelle, |
| 58 | DokumentKopf kopf, |
| 59 | String text, |
| 60 | List<String> eingearbeitet, |
| 61 | @Nullable String fassung) { |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 62 | |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 63 | Quelldokument(Quelle quelle, DokumentKopf kopf, String text) { |
| Matthias Andreas Benkard | ffc553b | 2026-08-15 12:18:32 +0200 | [diff] [blame] | 64 | this(quelle, kopf, text, List.of(), null); |
| 65 | } |
| 66 | |
| 67 | Quelldokument(Quelle quelle, DokumentKopf kopf, String text, List<String> eingearbeitet) { |
| 68 | this(quelle, kopf, text, eingearbeitet, null); |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 69 | } |
| 70 | |
| 71 | String quellenAngabe(List<String> artikel) { |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 72 | var sb = new StringBuilder(quelle.name()); |
| Matthias Andreas Benkard | ffc553b | 2026-08-15 12:18:32 +0200 | [diff] [blame] | 73 | sb.append(" [").append(kopf.anzeigeName()); |
| 74 | if (fassung != null) { |
| 75 | sb.append(", ").append(fassung); |
| 76 | } |
| 77 | sb.append("]"); |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 78 | for (var zusatz : eingearbeitet) { |
| 79 | sb.append(" + ").append(zusatz); |
| 80 | } |
| 81 | return sb.append(" (Artikel ").append(String.join(", ", artikel)).append(")").toString(); |
| 82 | } |
| 83 | } |
| 84 | |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 85 | /** Bequemlichkeit für Befehlszeile und Tests; im Browser gibt es keine {@link Path}e. */ |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 86 | public static Ergebnis erzeugeSynopse( |
| 87 | Path baseFile, List<Path> patches, String artikel, boolean vollstaendig) throws Exception { |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 88 | var patchQuellen = new ArrayList<Quelle>(); |
| 89 | for (var patch : patches) { |
| 90 | patchQuellen.add(Quelle.lies(patch)); |
| 91 | } |
| 92 | return erzeugeSynopse(Quelle.lies(baseFile), patchQuellen, artikel, vollstaendig); |
| 93 | } |
| 94 | |
| 95 | public static Ergebnis erzeugeSynopse( |
| 96 | Quelle baseFile, List<Quelle> patches, String artikel, boolean vollstaendig) |
| 97 | throws Exception { |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 98 | var altesGesetz = ladeStammgesetz(baseFile); |
| 99 | var extraktor = new PatchTextExtraktor(superskriptModus(altesGesetz)); |
| 100 | var parser = new AenderungsgesetzParser(); |
| 101 | |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 102 | var warnungen = new ArrayList<String>(); |
| 103 | var dokumente = wendeAntraegeAn(leseDokumente(patches, extraktor, warnungen), warnungen); |
| 104 | |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 105 | var gesetz = altesGesetz; |
| 106 | var protokoll = new ArrayList<BefehlAnwender.AngewandteAenderung>(); |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 107 | var quellen = new ArrayList<String>(); |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 108 | boolean entwurfsfassung = false; |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 109 | |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 110 | for (var dokument : dokumente) { |
| 111 | var parseErgebnis = parser.parse(dokument.text(), gesetz, artikel, entwurfsGrenzen(dokument)); |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 112 | if (parseErgebnis.befehle().isEmpty()) { |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 113 | warnungen.add( |
| 114 | "In %s (%s) wurde kein auf %s anwendbarer Artikel gefunden." |
| 115 | .formatted( |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 116 | dokument.quelle().name(), dokument.kopf().anzeigeName(), gesetz.jurabk())); |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 117 | } |
| 118 | var anwendung = BefehlAnwender.anwenden(gesetz, parseErgebnis.befehle()); |
| 119 | gesetz = anwendung.neu(); |
| 120 | protokoll.addAll(anwendung.protokoll()); |
| 121 | warnungen.addAll(parseErgebnis.warnungen()); |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 122 | entwurfsfassung |= dokument.kopf().art().istEntwurfsfassung(); |
| 123 | quellen.add(dokument.quellenAngabe(parseErgebnis.artikel())); |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 124 | } |
| 125 | |
| 126 | var gesamtErgebnis = new BefehlAnwender.AnwendungsErgebnis(gesetz, protokoll); |
| 127 | var synopse = SynopseBuilder.baue(altesGesetz, gesamtErgebnis, warnungen, vollstaendig); |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 128 | var quellenZeile = baseFile.name() + " + " + String.join(" + ", quellen); |
| 129 | var html = HtmlRenderer.rendere(synopse, quellenZeile, entwurfsfassung); |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 130 | |
| 131 | return new Ergebnis( |
| 132 | html, |
| 133 | gesamtErgebnis.anzahlAngewandt(), |
| 134 | gesamtErgebnis.anzahlManuell(), |
| 135 | synopse.eintraege().size(), |
| 136 | protokoll.size()); |
| 137 | } |
| 138 | |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 139 | /** |
| 140 | * Liest die Änderungsdokumente ein, bestimmt ihre Art und sortiert die aus, aus denen keine |
| 141 | * Synopse zu gewinnen ist. Verworfen wird nichts stillschweigend: Jedes ausgesonderte Dokument |
| 142 | * hinterlässt eine Warnung, die in der Synopse erscheint. |
| 143 | */ |
| 144 | private static List<Quelldokument> leseDokumente( |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 145 | List<Quelle> patches, PatchTextExtraktor extraktor, List<String> warnungen) throws Exception { |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 146 | var dokumente = new ArrayList<Quelldokument>(); |
| 147 | for (var datei : patches) { |
| 148 | var rohText = extraktor.extrahiere(datei); |
| 149 | // Die Erkennung arbeitet auf dem Rohtext: Der Bereiniger entfernt genau die |
| 150 | // Drucksachenköpfe, aus denen Art und Nummer hervorgehen. |
| 151 | var kopf = DokumentErkenner.erkenne(rohText); |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 152 | log.infof("Datei %s erkannt als %s.", datei.name(), kopf.anzeigeName()); |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 153 | if (kopf.art() == DokumentArt.OHNE_BEFEHLE) { |
| 154 | warnungen.add( |
| 155 | "%s ist ein %s und enthält keine Änderungsbefehle; die Datei wurde übergangen." |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 156 | .formatted(datei.name(), kopf.art().anzeigeName())); |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 157 | continue; |
| 158 | } |
| 159 | if (kopf.art() == DokumentArt.BESCHLUSSEMPFEHLUNG) { |
| Matthias Andreas Benkard | ffc553b | 2026-08-15 12:18:32 +0200 | [diff] [blame] | 160 | // Die maßgebliche Fassung steht nicht im Fließtext, sondern in der zweispaltigen |
| 161 | // Zusammenstellung: links der Entwurf, rechts die Beschlüsse des Ausschusses. |
| 162 | var fassung = extraktor.leseZusammenstellung(datei); |
| 163 | warnungen.addAll(fassung.warnungen()); |
| 164 | if (fassung.text() == null) { |
| 165 | // Eine halb aufgelöste Fassung auszugeben wäre schlimmer als keine. |
| 166 | warnungen.add( |
| 167 | ("%s ist eine Beschlussempfehlung, deren Zusammenstellung sich nicht auflösen ließ;" |
| 168 | + " die Datei wurde übergangen. Für eine Synopse eignet sich der zugrunde" |
| 169 | + " liegende Gesetzentwurf%s.") |
| 170 | .formatted( |
| 171 | datei.name(), |
| 172 | kopf.bezugsDrucksachen().isEmpty() |
| 173 | ? "" |
| 174 | : " (Drs. " + kopf.bezugsDrucksachen().get(0) + ")")); |
| 175 | continue; |
| 176 | } |
| 177 | dokumente.add( |
| 178 | new Quelldokument( |
| 179 | datei, |
| 180 | kopf, |
| 181 | TextBereiniger.bereinige(fassung.text()), |
| 182 | List.of(), |
| 183 | "Ausschussfassung")); |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 184 | continue; |
| 185 | } |
| 186 | dokumente.add(new Quelldokument(datei, kopf, TextBereiniger.bereinige(rohText))); |
| 187 | } |
| 188 | return dokumente; |
| 189 | } |
| 190 | |
| 191 | /** |
| 192 | * Wendet jeden Änderungsantrag auf den Entwurf an, den er ändern will, und nimmt ihn aus der |
| 193 | * Liste: Was danach bleibt, sind lauter Dokumente, die unmittelbar das Stammgesetz ändern. |
| 194 | * |
| 195 | * <p>Zugeordnet wird über die Drucksachennummer, die der Antrag selbst nennt („(Drs. 19/9707)“). |
| 196 | * Nur wo die fehlt, entscheidet die Reihenfolge der Argumente — der zuletzt genannte Entwurf |
| 197 | * davor. Findet sich gar kein Entwurf, bleibt der Antrag unangewandt und wird gemeldet; ihn |
| 198 | * ersatzweise auf das Stammgesetz loszulassen wäre falsch, denn seine Stellenangaben zielen auf |
| 199 | * die Drucksache. |
| 200 | */ |
| 201 | private static List<Quelldokument> wendeAntraegeAn( |
| 202 | List<Quelldokument> dokumente, List<String> warnungen) { |
| 203 | if (dokumente.stream().noneMatch(d -> d.kopf().art() == DokumentArt.AENDERUNGSANTRAG)) { |
| 204 | return dokumente; |
| 205 | } |
| 206 | var ergebnis = new ArrayList<>(dokumente); |
| 207 | for (var antrag : dokumente) { |
| 208 | if (antrag.kopf().art() != DokumentArt.AENDERUNGSANTRAG) { |
| 209 | continue; |
| 210 | } |
| 211 | ergebnis.remove(antrag); |
| 212 | int zielIndex = findeEntwurf(ergebnis, antrag, dokumente.indexOf(antrag)); |
| 213 | if (zielIndex < 0) { |
| 214 | warnungen.add( |
| 215 | ("%s ist ein Änderungsantrag zu %s; der zugehörige Gesetzentwurf wurde nicht" |
| 216 | + " mitgegeben, der Antrag blieb daher unberücksichtigt.") |
| 217 | .formatted( |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 218 | antrag.quelle().name(), |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 219 | antrag.kopf().bezugsDrucksachen().isEmpty() |
| 220 | ? "einer Drucksache" |
| 221 | : "Drs. " + String.join(", ", antrag.kopf().bezugsDrucksachen()))); |
| 222 | continue; |
| 223 | } |
| 224 | var ziel = ergebnis.get(zielIndex); |
| 225 | var parseErgebnis = AenderungsantragParser.parse(antrag.text()); |
| 226 | warnungen.addAll(parseErgebnis.warnungen()); |
| 227 | var patch = EntwurfsPatcher.wendeAn(ziel.text(), parseErgebnis.befehle()); |
| 228 | warnungen.addAll(patch.warnungen()); |
| 229 | log.infof( |
| 230 | "%s: %d von %d Antragsbefehlen auf %s angewandt.", |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 231 | antrag.quelle().name(), |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 232 | patch.angewandt(), |
| 233 | parseErgebnis.befehle().size(), |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 234 | ziel.quelle().name()); |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 235 | var eingearbeitet = new ArrayList<>(ziel.eingearbeitet()); |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 236 | eingearbeitet.add(antrag.quelle().name() + " [" + antrag.kopf().anzeigeName() + "]"); |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 237 | ergebnis.set( |
| 238 | zielIndex, |
| Matthias Andreas Benkard | ffc553b | 2026-08-15 12:18:32 +0200 | [diff] [blame] | 239 | new Quelldokument( |
| 240 | ziel.quelle(), |
| 241 | ziel.kopf(), |
| 242 | patch.text(), |
| 243 | List.copyOf(eingearbeitet), |
| 244 | ziel.fassung())); |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 245 | } |
| 246 | return ergebnis; |
| 247 | } |
| 248 | |
| 249 | /** Der Index des Entwurfs, den {@code antrag} ändert; {@code -1}, wenn keiner dabei ist. */ |
| 250 | private static int findeEntwurf( |
| 251 | List<Quelldokument> dokumente, Quelldokument antrag, int antragsPosition) { |
| 252 | for (int i = 0; i < dokumente.size(); i++) { |
| 253 | var kopf = dokumente.get(i).kopf(); |
| 254 | if (kopf.eigeneDrucksache() != null |
| 255 | && antrag.kopf().bezugsDrucksachen().contains(kopf.eigeneDrucksache())) { |
| 256 | return i; |
| 257 | } |
| 258 | } |
| 259 | int letzter = -1; |
| 260 | for (int i = 0; i < dokumente.size() && i < antragsPosition; i++) { |
| 261 | if (dokumente.get(i).kopf().art() == DokumentArt.GESETZENTWURF) { |
| 262 | letzter = i; |
| 263 | } |
| 264 | } |
| 265 | return letzter; |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * Entwürfe tragen hinter dem Regelungstext einen Begründungsteil, dessen Freitext keine Befehle |
| 270 | * enthält und den letzten Artikel nicht verunreinigen darf. Verkündete Gesetze haben ihn nicht — |
| 271 | * dort bliebe die Suche nach Begründungsmarken folgenlos, aber sie unterbleibt trotzdem, damit |
| 272 | * ein Gesetzblatt nicht an einem gleichlautenden Wort abbricht. |
| 273 | */ |
| 274 | private static boolean entwurfsGrenzen(Quelldokument dokument) { |
| 275 | return dokument.kopf().art().istEntwurfsfassung(); |
| 276 | } |
| 277 | |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 278 | /** Bequemlichkeit für Befehlszeile und Tests; im Browser gibt es keine {@link Path}e. */ |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 279 | static Gesetz ladeStammgesetz(Path baseFile) throws Exception { |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 280 | return ladeStammgesetz(Quelle.lies(baseFile)); |
| 281 | } |
| 282 | |
| Matthias Andreas Benkard | 06da70e | 2026-08-16 07:34:12 +0200 | [diff] [blame] | 283 | /** |
| 284 | * Gii-XML → {@link GiiXmlLoader}; PDF/Klartext (Landesrecht) → {@link LandesRechtLoader}. |
| 285 | * |
| 286 | * <p>Vorweg wird ausgepackt: gesetze-im-internet.de gibt das Norm-XML nur als {@code xml.zip} |
| 287 | * aus, und das soll unentpackt taugen (siehe {@link ZipAuspacker}). Die Änderungsdokumente |
| 288 | * bleiben davon unberührt — Gesetzblätter und Drucksachen kommen nirgends als Archiv. |
| 289 | */ |
| Matthias Andreas Benkard | 2b1841e | 2026-08-15 10:51:11 +0200 | [diff] [blame] | 290 | static Gesetz ladeStammgesetz(Quelle baseFile) throws Exception { |
| Matthias Andreas Benkard | 06da70e | 2026-08-16 07:34:12 +0200 | [diff] [blame] | 291 | var quelle = ZipAuspacker.auspacken(baseFile); |
| 292 | return DateiTyp.erkenne(quelle.inhalt()) == DateiTyp.XML |
| 293 | ? new GiiXmlLoader().load(quelle) |
| 294 | : new LandesRechtLoader().load(quelle); |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 295 | } |
| 296 | |
| 297 | /** |
| 298 | * Der Superskriptmodus folgt der Schreibweise des Stammgesetzes: Trägt es amtliche Satznummern |
| 299 | * (bayerisches Landesrecht, Niedersachsen u.a.), werden auch die Änderungsgesetze mit |
| 300 | * Superskript-Erhalt extrahiert, damit Zitate und Stammtext dieselbe Schreibweise tragen; sonst |
| Matthias Andreas Benkard | 7235b34 | 2026-08-14 18:58:25 +0200 | [diff] [blame] | 301 | * (Bundesrecht, Länder ohne amtliche Satzzählung) sind hochgestellte Ziffern bloße Fußnotenmarker |
| 302 | * und werden verworfen. |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 303 | */ |
| 304 | static SuperskriptModus superskriptModus(Gesetz gesetz) { |
| 305 | for (var norm : gesetz.normen()) { |
| 306 | for (var absatz : norm.absaetze()) { |
| 307 | if (Superskript.traegtSatznummern(absatz.text())) { |
| 308 | return SuperskriptModus.BEHALTEN; |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | return SuperskriptModus.ENTFERNEN; |
| 313 | } |
| Matthias Andreas Benkard | 7b455cd | 2026-08-09 08:33:37 +0200 | [diff] [blame] | 314 | } |