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