blob: 24083c78f9ae00e6ad1816c4132abb7bf0265db5 [file] [log] [blame]
Matthias Andreas Benkardb291c362020-11-22 10:22:27 +01001= ÄndGgner -- Programmbibliothek zur Konsolidierung von Änderungsgesetzen des Bundes und der Länder
2Matthias Andreas Benkard
3// Meta
4:experimental:
5:data-uri:
6:sectnums:
7:toc:
8:stem:
9:toclevels: 2
10:description: ÄndGgner Manual
11:keywords: mulk
12// Settings
13:icons: font
14:source-highlighter: pygments
15
16
17== Resources
18
19|===
20|Resource |Links
21
22|Public Artifact
23|https://bintray.com/mulk/maven/aendggner[Bintray]
24
25|Bug Tracker
26|MantisBT
27|===
28
29
30[[building]]
31== Building a JAR
32
Matthias Andreas Benkardc7f11362020-11-22 15:01:07 +010033To build an executable JAR at `target/aendggner-${REVISION}.jar`:
Matthias Andreas Benkardb291c362020-11-22 10:22:27 +010034
35[source,shell script]
36----
37./mvnw package
38----
39
40`${REVISION}` defaults to `0.1.0-SNAPSHOT` unless you override it by
41passing the `-Drevision` flag to `mvnw`.
42
43
44== Running the Command Line Application
45
46To run the command line application after <<building,building>> it:
47
48[source,shell script]
49----
50./mvnw exec:java
51----
52
53
Matthias Andreas Benkard58e19982026-07-13 07:09:19 +020054[[usage]]
55== Usage
56
57ÄndGgner erzeugt aus einem Stammgesetz und einem Änderungsgesetz eine
58zweispaltige HTML-Synopse (alte Fassung links, neue rechts, Änderungen
59wortweise hervorgehoben).
60
61Eingaben:
62
Matthias Andreas Benkard65eff082026-07-18 12:24:32 +020063* Stammgesetz (Bundesrecht): XML im gii-norm-Format von
Matthias Andreas Benkard58e19982026-07-13 07:09:19 +020064 https://www.gesetze-im-internet.de/[gesetze-im-internet.de]
Matthias Andreas Benkard2cfea002026-07-26 13:32:26 +020065* Stammgesetz (Landesrecht): konsolidierte Fassung als PDF oder als
Matthias Andreas Benkardbac648b2026-07-28 07:51:09 +020066 kanonischer Klartext im Format der `--extract-only`-Ausgabe. Eine Zeile
67 Inhaltsübersicht eröffnet darin die gleichnamige Norm, auf die die
68 Angabe-Befehle zielen; ihre Zeilen tragen das Übersichtsformat
69 „§ N | Titel“. Amtliche
Matthias Andreas Benkard65eff082026-07-18 12:24:32 +020070 Satznummern und Fußnotenmarker stehen als Unicode-Superskripte im
Matthias Andreas Benkard2cfea002026-07-26 13:32:26 +020071 Text („¹Die freilebende Tierwelt …“, Enteignung⁶)“); ob sie erhalten
72 bleiben, ergibt sich aus der geladenen Stammfassung, nicht aus einer
73 Länderkennung. Ebenso folgt das Zitiersigel oder Art.) aus den
74 Normköpfen der Stammfassung.
Matthias Andreas Benkard7235b342026-08-14 18:58:25 +020075* Änderungsdokument: BGBl-, GVBl- oder Drucksachen-PDF (Bundestag,
76 Bundesrat, Landtage) oder Klartext. Die Dokumentart wird aus dem Text
77 erschlossen, nicht aus dem Dateinamen (siehe <<quellformate>>);
78 Änderungsanträge dürfen zusammen mit dem Entwurf angegeben werden, den
79 sie ändern.
Matthias Andreas Benkard58e19982026-07-13 07:09:19 +020080
81[source,shell script]
82----
83java -jar target/aendggner-0.1.0-SNAPSHOT.jar \
84 stammgesetz.xml aenderungsgesetz.pdf -o synopse.html
85----
86
87Wichtige Optionen:
88
89`-o, --output <file>`::
90 Ausgabedatei (Default `synopse.html`; `-` = Standardausgabe).
91`--vollstaendig`::
92 Auch unveränderte Normen in die Synopse aufnehmen.
93`--artikel <n>`::
94 Nur diesen Artikel des Änderungsgesetzes anwenden (Default: alle
95 Artikel, deren Einleitung das Stammgesetz nennt).
96`--extract-only`::
97 Nur den bereinigten Lineartext des Änderungsgesetzes ausgeben. Nützlich,
98 wenn die PDF-Extraktion fehlerhaft ist: Text prüfen, von Hand
99 korrigieren und als Klartextdatei wieder einspeisen.
100
101Erkannt werden die gebräuchlichsten Änderungsbefehle des Handbuchs der
102Rechtsförmlichkeit (Ersetzen, Neufassung, Einfügen, Anfügen, Aufheben,
Matthias Andreas Benkardebd5a7b2026-07-15 20:54:27 +0200103Streichen, Umnummerierung) einschließlich zahlreicher Sonderformen:
104Bereichs- und Koordinationsziele („Die Absätze 8 und 9 werden durch die
105folgenden Absätze 8 bis 10 ersetzt“), strukturelle Streichungen ganzer
106Einheiten („§ 9 wird gestrichen“), §- und Gliederungs-Umnummerierungen
107(„§ 9a wird zu § 9“, Der bisherige Abschnitt 2 wird zu Abschnitt 3“),
Matthias Andreas Benkardb7e9f0f2026-07-16 21:12:08 +0200108das Einfügen/Ersetzen ganzer §-Blöcke, Chapeau-Lokatoren („Im
109Satzteil vor Nummer 1 …“), Änderungen an Anhängen und Anlagen („Der
110Anhang wird wie folgt geändert: Nach Nummer 2 wird die folgende
111Nummer 2a eingefügt“), Angabe-Befehle auf die Inhaltsübersicht (gefasst,
112ersetzt, eingefügt, gestrichen sie werden auf die
113Inhaltsübersichts-Norm angewandt), das Einfügen und Ersetzen von
114Gliederungsberschriften („Nach § 33 werden die folgenden Überschriften
115zu Teil 3 eingefügt“), Voranstellungen, Mehrfach-Ersetzungs- und
Matthias Andreas Benkard5f30f562026-07-28 06:00:16 +0200116-Einfügepaare, Einfügungen, deren Position ein Wortanker statt einer
117Stellenangabe bestimmt („Vor den Wörtern Aus dem Bereich Verkehr:“ wird
118folgender Absatz 5 eingefügt“), Verbünde aus Umnummerierung und
Matthias Andreas Benkard5b2e6ef2026-08-02 20:01:37 +0200119Folgeänderung („§ 50 wird zu § 38 und wird wie folgt geändert“, Die
120bisherige Nr. 7 wird Nr. 5 und das Komma wird durch das Wort und
121ersetzt eine Satzzeichen-Operation meint dabei stets die soeben
122umnummerierte Einheit, weil ihr Zieltext nichts unterscheidet),
Matthias Andreas Benkardbac648b2026-07-28 07:51:09 +0200123Verb-Rahmen, deren Unterpunkte allein die Fundstelle tragen („Es werden
124ersetzt: in § 35 Absatz 3 die Angabe X jeweils durch die Angabe
125Y“,“) sowie die Neufassung der Gesetzesüberschrift.
126
127Angewandt wird nicht stur in Dokumentreihenfolge: Umnummerierungen
128beziehen sich stets auf die ursprüngliche Zählung, nicht auf den Stand
129nach den vorangegangenen Punkten. Wer eine Bezeichnung räumt, kommt
130deshalb vor den, der sie neu besetzt. Aus dieser einen Regel folgt die
131absteigende Abarbeitung einer aufsteigenden Kaskade („Der bisherige
132Absatz 3 wird Absatz 4“, Der bisherige Absatz 4 wird Absatz 5“, …)
133ebenso wie der Vorrang einer Umnummerierung vor der Einfügung, die deren
134Bezeichnung neu vergibt. Verschoben wird dabei nur nach vorn, sodass
135jede Folgeänderung hinter der Umnummerierung bleibt, auf deren neue
136Bezeichnung sie zeigt. Die PDF-Aufbereitung toleriert dabei
Matthias Andreas Benkardb7e9f0f2026-07-16 21:12:08 +0200137Drucksachen-Artefakte (Seitenköpfe und -füße, Vorabfassungs-Wasserzeichen,
Matthias Andreas Benkard5f30f562026-07-28 06:00:16 +0200138vertauschte oder gerade Anführungszeichen, verklebte Wortgrenzen, zerlegt
139kodierte Umlaute) und bestimmt die Brotschrift seitenweise, sodass auch
140Ministeriumsentwürfe mit gemischten Layouts vollständig extrahiert
141werden. Fehlt im amtlichen Satz ein schließendes Anführungszeichen, endet
142das Zitat an der nächsten Strukturgrenze an einer Artikelberschrift
143oder, wo die Anführungszeichen eines Artikels nachweislich nicht
144aufgehen, am nächsten Aufzählungspunkt des Änderungsgesetzes; gemeldet
145wird das als Warnung. Auf den
Matthias Andreas Benkardb7e9f0f2026-07-16 21:12:08 +0200146Beispieldaten werden damit alle Befehle der BGBl-Fassungen und der
147aktuellen Entwürfe angewandt (UWG/AGG/ProdHaftG: 0 manuell); was
148unsicher bleibt etwa Befehle gegen eine ältere Gesetzesfassung, deren
149Zieltext nicht mehr existiert landet mit Begründung im Abschnitt
150*Manuell prüfen* der Synopse und wird niemals stillschweigend verworfen.
Matthias Andreas Benkard58e19982026-07-13 07:09:19 +0200151
Matthias Andreas Benkard65eff082026-07-18 12:24:32 +0200152Für das bayerische Landesrecht versteht ÄndGgner die dortigen
153Konventionen: Stammgesetze gliedern sich in Artikel („Art. 6 Abs. 2
154Satz 1 Nr. 2“, durchgängig abgekürzt zitiert), Änderungsgesetze dagegen
155in Paragraphen auch mehrere Gesetze in einem GVBl-Heft, aus denen die
156auf das Stammgesetz zielenden §§ (einschließlich Weitere Änderung“)
157anhand des Einleitungssatzes ausgewählt werden. Amtliche Satznummern
158bleiben als Superskripte erhalten und dienen als exakte Satzgrenzen;
159zusätzlich erkannt werden die bayerischen Befehlsformen („Fußnote 1
160wird aufgehoben“, In Satz 1 wird die Satznummerierung 1 gestrichen“,
161Dem Wortlaut werden die folgenden Abs. 1 bis 4 vorangestellt“, Der
162bisherige Wortlaut wird Abs. 5“, Halbsatz-Ziele, Klauselketten mit
163gemeinsamem Schlussverb) sowie das Fortführungszeichen des GVBl (jedes
Matthias Andreas Benkard7235b342026-08-14 18:58:25 +0200164neugefasste Aufzählungsglied öffnet erneut mit „).
Matthias Andreas Benkard65eff082026-07-18 12:24:32 +0200165
Matthias Andreas Benkard2cfea002026-07-26 13:32:26 +0200166Die übrigen Länder gliedern ihre Stammgesetze wie der Bund in
167Paragraphen; die Unterschiede liegen im Gesetzblatt-Satz und in
168Befehlsidiomen. Belegt sind Sachsen (SächsBeamtVG), Niedersachsen
Matthias Andreas Benkarda201e832026-08-02 19:45:34 +0200169(NEFG) und Nordrhein-Westfalen dort alle vier ändernden Artikel eines
170Heftes: Telemedienzuständigkeitsgesetz, Landesmediengesetz,
171Ausführungsgesetz zum 17. Rundfunkänderungsstaatsvertrag und, mit 101
172Befehlen an 31 Normen der größte Landesrechtsfall, das WDR-Gesetz
173mit Akzeptanztests gegen die
Matthias Andreas Benkard5b2e6ef2026-08-02 20:01:37 +0200174amtlichen Nachfassungen; für Schleswig-Holstein, Berlin und Hessen
Matthias Andreas Benkard5f30f562026-07-28 06:00:16 +0200175reicht die Prüfung bis zur Befehlserkennung dort vollständig, aber ohne
176Anwendung —, weil deren Landesportale ihre Stammfassungen nur über eine
177anmeldepflichtige Schnittstelle ausgeben. Welche Konvention welches Land beisteuert, welche
Matthias Andreas Benkard2cfea002026-07-26 13:32:26 +0200178Stammfassungen woher stammen und was noch offen ist, verzeichnet
179`src/main/resources/sampledata/Landesrecht-Beispiele.adoc`.
180
Matthias Andreas Benkard58e19982026-07-13 07:09:19 +0200181
Matthias Andreas Benkard7235b342026-08-14 18:58:25 +0200182[[quellformate]]
183== Quellformate: Gesetz, Entwurf, Antrag
184
185Ein Änderungsbefehl steht nicht nur im verkündeten Gesetzblatt. Dasselbe
186Vorhaben durchläuft als Referenten-, Regierungs- und Fraktionsentwurf,
187als Änderungsantrag und als Beschlussempfehlung mehrere Fassungen, und
188die Frage was gälte, wenn das durchkommt?“ stellt sich in jeder davon.
189ÄndGgner erschließt die Art eines Dokuments deshalb aus seinem Kopf
190nie aus dem Dateinamen, der lügen kann (im Beispielkorpus heißt ein
191Entschließungsantrag `BT-Drs-21-7071_Beschlussempfehlung.pdf`) und nie
192aus einer Kennung, die von außen mitzugeben wäre.
193
194Unterschieden werden:
195
196Änderungsgesetz::
197 Das verkündete Artikelgesetz aus BGBl, GVBl oder GVOBl. Der Regelfall.
198Gesetzentwurf::
199 Referenten-, Regierungs- und Fraktionsentwürfe, auch als Drucksache von
200 Bundestag, Bundesrat oder Landtag. Der Begründungsteil hinter dem
201 Regelungstext erzeugt keine Befehle; erkannt wird er an Begründung
202 ebenso wie an den Entwurfsvarianten („A. Allgemeiner Teil“, Zu
203 Artikel 1“).
204Änderungsantrag::
205 Ändert nicht das Stammgesetz, sondern eine *Drucksache* siehe unten.
206Beschlussempfehlung::
207 Trägt ihre Fassung in einer zweispaltigen Zusammenstellung. Erkannt,
208 aber noch nicht angewandt (siehe unten).
209Dokument ohne Änderungsbefehle::
210 Entschließungs- und schlichter Antrag, Plenarprotokoll, Bericht. Sie
211 werden übergangen und gemeldet nicht stillschweigend zu null
212 Befehlen verarbeitet.
213
214Sobald ein Entwurf, ein Antrag oder eine Beschlussempfehlung beteiligt
215ist, trägt die Synopse den Hinweis *Entwurfsfassung nicht geltendes
216Recht*; die Quellenzeile nennt je Datei die erkannte Art.
217
218=== Änderungsanträge
219
220Ein Änderungsantrag ist eine Metanderung: Er ändert den Entwurf, nicht
221das Gesetz. Sein Rahmensatz adressiert deshalb zwei Ebenen zugleich
222In § 3 Nr. 22 wird § 18 Nr. 1 wie folgt geändert:“ nennt erst die
223Stelle _in der Drucksache_ (den 22. Änderungsbefehl ihres dritten
224Paragraphen) und dann die Stelle _in dem Text, den dieser Befehl
225zitiert_. Angegeben wird der Antrag zusammen mit seinem Entwurf:
226
227[source,shell script]
228----
229java -jar target/aendggner-0.1.0-SNAPSHOT.jar \
230 BayJG-alt.txt Ltg-Drs-19-9707_Gesetzentwurf.pdf \
231 Ltg-Drs-19-10365_Aenderungsantrag-Gruene.pdf -o synopse.html
232----
233
234ÄndGgner wendet dann erst den Antrag auf den Entwurf an und danach den
235so geänderten Entwurf auf das Stammgesetz; die Synopse zeigt also, was
236gälte, wenn Entwurf _und_ Antrag durchkämen. Welcher Entwurf gemeint
237ist, entscheidet die Drucksachennummer, die der Antrag selbst nennt
238(„(Drs. 19/9707)“), nicht die Reihenfolge der Argumente. Fehlt der
239Entwurf, bleibt der Antrag unangewandt und wird gemeldet ihn
240ersatzweise auf das Stammgesetz loszulassen wäre falsch, denn seine
241Stellenangaben zielen auf die Drucksache. Erkannt wird auch die
242elliptische Antragsform, die das Hilfsverb nur einmal in der
243Beschlussformel führt („1. In Nr. 1.29 die Angabe „,“ am Ende durch die
244Angabe „;“ ersetzt.“).
245
246=== Beschlussempfehlungen: erkannt, noch nicht angewandt
247
248Die maßgebliche Fassung einer Beschlussempfehlung steht in einer
249zweispaltigen Zusammenstellung: links der Entwurf, rechts die Beschlüsse
250des Ausschusses. Die Spaltentrennung ist erledigt und belegt anders
251als beim alten BGBl und beim Berliner GVBl stehen die Spalten *nicht*
252nacheinander im Inhaltsstrom, sondern zeilenweise verschränkt, weshalb
253sie erstmals koordinatenbasiert getrennt werden (`PatchTextExtraktor.
254extrahiereSpalten`, Schnitt an der Blattmitte, aber nur an einem
255tatsächlichen Spaltensteg, damit ganzseitenbreite Zeilen ungeschnitten
256bleiben). Der Akzeptanztest hält fest, dass die linke Spalte Befehl für
257Befehl den Regierungsentwurf ergibt, aus dem die Zusammenstellung gebaut
258ist.
259
260Was fehlt, ist die Auflösung der rechten Spalte. Sie druckt Unverändertes
261nicht ab, sondern vermerkt bloß unverändert und zwar nicht nur je
262Gliederungspunkt, sondern auch zeilenweise innerhalb zitierter Blöcke.
263Dadurch ist die rechte Spalte für sich genommen kein vollständiges
264Dokument: Ihre Anführungszeichen gehen nicht auf. Eine Auflösung über
265Gliederungspfade allein scheitert daran nachweislich; nötig ist die
266zeilenweise Zuordnung beider Spalten über ihre gemeinsame Grundlinie im
267PDF. Bis dahin wird eine Beschlussempfehlung erkannt und mit Begründung
268übergangen samt Hinweis auf die Drucksachennummer des Entwurfs, der
269sich stattdessen eignet. Eine halb aufgelöste Fassung auszugeben wäre
270schlimmer als keine.
271
Matthias Andreas Benkard7b455cd2026-08-09 08:33:37 +0200272[[web]]
273== Web-App
274
Matthias Andreas Benkard2b1841e2026-08-15 10:51:11 +0200275Neben der CLI gibt es eine Browserfassung, die dieselbe Pipeline
276(`eu.mulk.aendggner.Pipeline`) über ein Upload-Formular zugänglich macht:
277Stammgesetz- und Änderungsgesetz-Datei(en) wählen, Synopse erhalten.
Matthias Andreas Benkard7b455cd2026-08-09 08:33:37 +0200278
Matthias Andreas Benkard2b1841e2026-08-15 10:51:11 +0200279Sie braucht keinen Server: Die vollständige Verarbeitung PDF-Textgewinnung
280mit PDFBox eingeschlossen läuft als WebAssembly-Modul im Browser, übersetzt
281mit GraalVM Web Image aus demselben Java-Quelltext. Ausgeliefert werden nur
282statische Dateien; die gewählten Dokumente verlassen den Rechner der
283Nutzer:innen nicht.
284
285Bauen (verlangt Oracle GraalVM 25.1 oder neuer Web Image ist dort enthalten,
286in der Community Edition nicht):
Matthias Andreas Benkard7b455cd2026-08-09 08:33:37 +0200287
288[source,shell script]
289----
Matthias Andreas Benkard2b1841e2026-08-15 10:51:11 +0200290JAVA_HOME=/pfad/zu/oracle-graalvm ./mvnw -Pwasm package
Matthias Andreas Benkard7b455cd2026-08-09 08:33:37 +0200291----
292
Matthias Andreas Benkard2b1841e2026-08-15 10:51:11 +0200293Ergebnis ist `target/web/` mit `index.html`, `app.js`, `worker.js`,
294`style.css`, `aendggner.js` und `aendggner.js.wasm` (rund 20 MB, komprimiert
295etwa 6 MB). Die daneben liegende `aendggner.js.wat` ist ein Zwischenschritt des
296Übersetzers und gehört nicht auf den Server.
297
298Lokal ansehen `file://` genügt nicht, Browser laden Wasm-Module und Worker
299nur über HTTP:
300
301[source,shell script]
302----
303python3 -m http.server --directory target/web 8000
304----
305
306Der öffentliche Betrieb braucht nur einen Webserver für statische Dateien;
307eine `nginx`-Vorlage liegt unter `deploy/nginx-aendggner.conf`. Die
308Befehlszeilenfassung bleibt davon unberührt und ist weiterhin der Weg für
309Massenläufe.
310
311=== Warum WebAssembly und nicht ein Java-Server
312
313Der Kern ist reines Java ohne Dateisystem- oder Netzzugriff; nur vier Stellen
314berührten die Plattform (PDFBox, MIME-Erkennung, XML-Parser, Dateizugriff).
315Sie sind hinter `eu.mulk.aendggner.Quelle` (Name + Bytes) und
316`eu.mulk.aendggner.DateiTyp` (Signaturbytes statt Tika) gebündelt, sodass
317Befehlszeile und Browser dieselbe Pipeline speisen.
318
319Zwei Eigenheiten von Web Image sind dabei zu beachten und im Quelltext
320vermerkt:
321
322* Die nativen zlib-Bindungen des JDK fehlen (`java.util.zip.Inflater`), ohne
323 die kein PDF lesbar ist. `src/wasm/java/.../InflaterErsatz.java` ersetzt sie
324 durch die reine Java-Umsetzung von jzlib.
325* Typisierte Felder lassen sich derzeit nicht nach `byte[]` umsetzen; der
326 Dateiinhalt wandert deshalb als Base64-Text über die JS-Grenze.
327
Matthias Andreas Benkard7b455cd2026-08-09 08:33:37 +0200328
329Vor dem produktiven, öffentlichen Betrieb sind zwingend zu erledigen:
330
331* `impressum.html` und `datenschutz.html`
332 (`src/main/resources/eu/mulk/aendggner/web/`) mit den tatsächlich
333 zutreffenden Angaben statt der `TODO`-Platzhalter füllen (Pflicht
334 nach § 5 DDG bzw. DSGVO für öffentlich erreichbare Dienste).
335* Den Quellcode-Link im Footer von `index.html` auf das tatsächlich
336 öffentlich erreichbare Repository setzen das Projekt steht unter
337 AGPLv3 (`COPYING`), dessen §13 bei Netzwerkbetrieb einen
338 Quellcode-Zugriff für Nutzer:innen verlangt.
339
340
Matthias Andreas Benkardb291c362020-11-22 10:22:27 +0100341== Running the Tests
342
343To build and run the tests:
344
345[source,shell script]
346----
347./mvnw verify
348----