diff options
16 files changed, 847 insertions, 53 deletions
diff --git a/FASSUNGEN.txt b/FASSUNGEN.txt index a497e28..cfe31bc 100644 --- a/FASSUNGEN.txt +++ b/FASSUNGEN.txt @@ -21,6 +21,112 @@ Vorbemerkung zur Führung dieses Verzeichnisses ════════════════════════════════════════════════════════════════════════════════ + Fassung vom 26. August 2026 (vierte Fassung des Tages), + zuletzt geändert durch die am 26. August 2026 vorgenommenen Änderungen +════════════════════════════════════════════════════════════════════════════════ + +Hamburg tritt als vierzehntes Land hinzu. Das dortige Gesetzblatt bringt zwei +Gliederungen, die im übrigen Landesrecht nicht vorkommen: Das Änderungsdokument +selbst teilt sich in Paragraphen, obgleich dasselbe Heft davor eine Verkündung +führt, die sich in Artikel teilt; und seine Befehle sind dezimal gegliedert, wo +die übrigen Blätter Buchstaben setzen. Der Fall hat überdies vier Idiome, drei +Aufbereitungs- und drei Anwendungsfunde gebracht. + + +Artikel 1 +Aufbereitung des hamburgischen Satzes + +(1) Der Textbereiniger (TextBereiniger) normalisiert fortan sämtlichen Leerraum, + den Javas Begriff des Leerzeichens nicht kennt (schmal, halbgeviert, geviert + und dergleichen). Das Blatt setzt zwischen Paragraphenzeichen und Nummer ein + schmales Leerzeichen; ohne dies ist „§ 1“ dort kein Normkopf, und ein ganzes + Heft bliebe ungelesen. + +(2) Der Kolumnentitel des Blattes („Dienstag, den 26. Mai 2026 149HmbGVBl. + Nr. 17“) steht im Inhaltsstrom und fällt hinter das Zitat eines Befehls; er + wird wie der Berliner und der thüringische herausgeschnitten. + +(3) Die Lesereihenfolge (Lesereihenfolge) ordnet die Zeilen einer Spalte fortan + nach ihrer Grundlinie und nicht mehr nach dem Inhaltsstrom. Die Sortierung ist + stabil und ändert am Regelfall nichts; im hamburgischen Satz bewahrt sie davor, + dass das Schlusswort der Eingangsformel („verordnet:“) mitten in einen Absatz + der rechten Spalte fällt und dort ein Wort zerschneidet. + +(4) Der Wortbestand, der über Trennstriche vor Großbuchstaben entscheidet, nimmt + fortan auch die von Punkten befreite Form eines Wortes auf. Ohne sie fände + „(Immo-“ + „WertV)“ seinen Beweis nicht, obgleich das Dokument „ImmoWertV.“ + wenige Zeilen weiter führt. + + +Artikel 2 +Gliederung und Erkennung + +(1) Der Gliederungsscanner (GliederungsScanner) erkennt die Dezimalgliederung + („6.1“, „7.1.1“). Die Ebene steht in der Zahl selbst; eröffnet wird eine + Unterebene nur unter demjenigen Punkt, dessen Bezeichnung das Label + voranstellt. Der Gliederungspfad der Provenienz wiederholt sie nicht. + +(2) Die Artikelteilung (AenderungsgesetzParser) versucht die §-Teilung fortan + nicht erst dann, wenn ein Heft überhaupt keinen Artikel führt, sondern schon + dann, wenn kein Artikel das Stammgesetz betrifft. Ein Sammelheft trägt beide + Gliederungen nebeneinander. + +(3) Der Befehlserkenner (BefehlErkenner) nimmt vier Nebenformen an: „die + Textstelle“ neben „die Wörter“ und „die Angabe“; „hinter“ neben „nach“ (als + Ort der Einfügung wie als Wortanker); die subjektlose Anfügung („Es wird + folgender Absatz 3 angefügt: „…““), deren Ziel allein aus dem Rahmen folgt; + und die Neufassung einer Paragraphenfolge auch mit „und“ statt „bis“ und ohne + vorangestelltes „Die“. + + +Artikel 3 +Anwendung + +(1) Tritt ein Satzzeichen an die Stelle eines Wortes, so nimmt es dessen + Zwischenraum mit („…Aufgaben und“ wird „…Aufgaben,“ und nicht „…Aufgaben ,“). + Das ist die Umkehrung der bereits geregelten Fuge. + +(2) Ein Satz, der an einen Absatz mit Aufzählung angefügt wird, tritt auf eine + eigene Zeile: Er gehört dem Absatz und nicht dem letzten Aufzählungsglied. Er + erbt dabei die Einrückung des Blockes, denn die Einrückung trägt die + Gliederung; bündig gesetzt beendete er den Block, dem er zugehören soll. + +(3) Eine Neufassung, deren Bezeichnung eine aufsteigende Umnummerierung räumt, + tritt hinter diese zurück. „Nummer 3 erhält folgende Fassung“ neben „Die + bisherige Nummer 3 wird Nummer 4“ meint die ursprüngliche Zählung; liefe die + Neufassung zuerst, so verlöre die bisherige Nummer 3 ihren Wortlaut. Die + Neufassung findet ihre Bezeichnung alsdann geräumt und bleibt zu Recht liegen; + sie als Einfügung zu vollziehen, bleibt einer späteren Welle vorbehalten. + + +Artikel 4 +Beispieldaten und Handbuch + +(1) Unter `Hamburg/` treten das Heft Nr. 17/2026, die Ursprungsfassung der + Verordnung über den Gutachterausschuss für Grundstückswerte und die amtliche + Nachfassung hinzu, nebst Herkunftsnachweis. Die Auszeichnung nach REUSE ist + entsprechend ergänzt. + +(2) Das Handbuch (README.md) wird geändert: § 7 Absatz 2 um die Nummern 18 und 19 + (hamburgische Nebenformen, Neufassung einer Paragraphenfolge), § 9 Absatz 1 + und 2 (unsichtbarer Leerraum, Grundlinien-Ordnung innerhalb der Spalte) und + § 13 um einen Absatz 2a. Das Beispieldaten-Verzeichnis + (`Landesrecht-Beispiele.adoc`) führt Hamburg als umgesetzt. + + +Schlussbestimmung + +Die vorstehenden Änderungen sind durch die Prüfung sämtlicher Testfälle +(vierhundertfünfundzwanzig an der Zahl, zuvor vierhundertfünfzehn) sowie durch die +vollständige Erstellung (mvnw verify) bestätigt worden. Der hamburgische Fall +steht auf einundzwanzig gelesenen und siebzehn angewandten Befehlen; zehn von +vierzehn Normen gleichen der amtlichen Nachfassung. Von den vier Abweichungen +liegen drei an der Vorlage: Die Nummern 2 und 3 des Heftes schreiben „wir“ statt +„wird“, und in § 6 setzt das Gesetzblatt ein Komma nicht, das das Portal führt. +Die Auszeichnung nach REUSE ist vollständig (200 von 200 Dateien). + + +════════════════════════════════════════════════════════════════════════════════ Fassung vom 26. August 2026 (dritte Fassung des Tages), zuletzt geändert durch die am 26. August 2026 vorgenommenen Änderungen ════════════════════════════════════════════════════════════════════════════════ @@ -438,6 +438,13 @@ Streichen und Umnummerierung. führt allein Bezeichnung und Überschrift, und was im Absatz eines Paragraphen geschieht, hat in ihr kein Gegenstück. +18. die hamburgischen Nebenformen: „die Textstelle „…““ neben „die Wörter“ und + „die Angabe“, „hinter“ neben „nach“ (als Ort der Einfügung wie als Wortanker) + sowie die subjektlose Anfügung „Es wird folgender Absatz 3 angefügt: „…““, + deren Ziel allein aus dem Rahmen folgt; ferner +19. die Neufassung einer Paragraphenfolge („§§ 6 und 7 erhalten folgende + Fassung: „…““) — sie wird in Einzel-Neufassungen zerlegt. + (3) Landesrechtliche Befehlsformen bestimmt ergänzend § 13. ## § 8 Reihenfolge der Anwendung @@ -504,7 +511,11 @@ steht nicht im Gesetzestext. (1) Die PDF-Aufbereitung toleriert die üblichen Drucksachen-Artefakte, nämlich Seitenköpfe und -füße, Vorabfassungs-Wasserzeichen, vertauschte oder gerade -Anführungszeichen, verklebte Wortgrenzen und zerlegt kodierte Umlaute. Die +Anführungszeichen, verklebte Wortgrenzen und zerlegt kodierte Umlaute. Zum +Leerraum zählen dabei auch die typographischen Ausschlüsse, die Javas Begriff +des Leerzeichens nicht kennt: Das hamburgische Gesetzblatt setzt zwischen +Paragraphenzeichen und Nummer ein schmales Leerzeichen (U+2009), und ohne dessen +Normalisierung wäre „§ 1“ dort kein Normkopf. Die Brotschrift wird seitenweise bestimmt, sodass auch Ministeriumsentwürfe mit gemischten Layouts vollständig ausgelesen werden. @@ -515,9 +526,15 @@ Mitte des Satzspiegels liegt und zu deren beiden Seiten je eine Spalte von nennenswerter Breite steht. Die wenigen Zeilen, die sie gleichwohl überschreiten, sind die ganzseitenbreiten — Kolumnentitel, Seitenfuß, Titelblock —; sie zerlegen die Seite in Bänder. Gelesen wird Band für Band von -oben nach unten, in jedem Band erst die linke, dann die rechte Spalte. Findet -sich keine Rinne, so bleibt es beim Inhaltsstrom; innerhalb einer Spalte -ohnehin. +oben nach unten, in jedem Band erst die linke, dann die rechte Spalte, jede von +oben nach unten. Findet sich keine Rinne, so bleibt es beim Inhaltsstrom. + +Innerhalb einer Spalte folgt die Lesung der Grundlinie und nicht dem Strom. Wo +dieser ohnehin von oben nach unten läuft — der Regelfall —, ändert das nichts, +denn die Sortierung ist stabil; wo er es nicht tut, bewahrt sie vor dem +Schlimmsten. Im hamburgischen Gesetzblatt fällt das Schlusswort der +Eingangsformel („verordnet:“) mitten in einen Absatz der rechten Spalte und +zerschneidet dort ein Wort. (3) Absatz 2 ist keine Förmelei. Das Berliner Gesetz- und Verordnungsblatt zeichnet den Titelblock des Gesetzes **zuletzt**, obgleich er über beiden @@ -686,6 +703,21 @@ Telemedienzuständigkeitsgesetz, Landesmediengesetz, Ausführungsgesetz zum Fall des Heftes, das WDR-Gesetz —, jeweils mit Akzeptanztests gegen die amtlichen Nachfassungen. +(2a) Hamburg bringt zwei Eigenheiten, die im übrigen Landesrecht nicht vorkommen. +Zum einen gliedert sich dort das *Änderungsdokument* in Paragraphen (§ 1 Änderung, +§ 2 Inkrafttreten), während dasselbe Heft davor eine Verkündung führt, die sich in +Artikel gliedert; ein Sammelheft trägt also beide Gliederungen nebeneinander, und +die §-Teilung greift deshalb nicht erst dann, wenn ein Heft überhaupt keinen +Artikel führt, sondern schon dann, wenn kein Artikel das Stammgesetz betrifft. Zum +anderen sind die Befehle **dezimal** gegliedert („6.1“, „7.1.1“, „7.2.3“), wo die +übrigen Blätter a)/aa)/aaa) setzen; die Ebene steht dabei in der Zahl selbst, und +der Gliederungspfad wiederholt sie nicht. Der Fall (Verordnung über den +Gutachterausschuss für Grundstückswerte, HmbGVBl. Nr. 17/2026) ist gegen die +amtliche Nachfassung belegt: 21 Befehle gelesen, 17 angewandt, zehn von vierzehn +Normen gleich. Von den vier Abweichungen liegen drei an der Vorlage — zwei Befehle +schreiben „wir“ statt „wird“, und in einem dritten Fall setzt das Gesetzblatt ein +Komma, das das Portal führt, nicht. + (3) Die meisten Landesportale geben ihre Stammfassungen einer Skriptabfrage nicht aus, sondern nur ihre leere Anwendungshülle; einem Browser dagegen schon, und auf diesem Wege sind die hessische, die beiden schleswig-holsteinischen und die @@ -190,6 +190,11 @@ SPDX-FileCopyrightText = "Land Brandenburg" SPDX-License-Identifier = "LicenseRef-AmtlichesWerk" [[annotations]] +path = ["src/test/resources/sampledata/Hamburg/HmbGVBl-2026-17_GutachterausschussVO-AendVO.pdf"] +SPDX-FileCopyrightText = "Freie und Hansestadt Hamburg" +SPDX-License-Identifier = "LicenseRef-AmtlichesWerk" + +[[annotations]] path = ["src/test/resources/sampledata/RheinlandPfalz/GVBl-2026-21_UKAPOGS-E3-AendVO.pdf"] SPDX-FileCopyrightText = "Land Rheinland-Pfalz" SPDX-License-Identifier = "LicenseRef-AmtlichesWerk" @@ -210,6 +215,8 @@ path = [ "src/test/resources/sampledata/Berlin/ASOG-Bln-alt.txt", "src/test/resources/sampledata/Brandenburg/FraktG-alt.txt", "src/test/resources/sampledata/Brandenburg/FraktG-neu.txt", + "src/test/resources/sampledata/Hamburg/GAusschV-HH-alt.txt", + "src/test/resources/sampledata/Hamburg/GAusschV-HH-neu.txt", "src/test/resources/sampledata/RheinlandPfalz/UKAPOGS-E3-neu.txt", "src/test/resources/sampledata/Berlin/ASOG-Bln-neu.txt", "src/test/resources/sampledata/Berlin/LAF-ErrG-alt.txt", @@ -239,6 +246,7 @@ path = [ "src/test/resources/sampledata/GEG/SOURCES", "src/test/resources/sampledata/Berlin/SOURCES", "src/test/resources/sampledata/Brandenburg/SOURCES", + "src/test/resources/sampledata/Hamburg/SOURCES", "src/test/resources/sampledata/RheinlandPfalz/SOURCES", "src/test/resources/sampledata/Hessen/SOURCES", "src/test/resources/sampledata/SchleswigHolstein/SOURCES", diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/AenderungsgesetzParser.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/AenderungsgesetzParser.java index 2d6f0d6..7b8e4a5 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/AenderungsgesetzParser.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/AenderungsgesetzParser.java @@ -73,10 +73,20 @@ public final class AenderungsgesetzParser { var zitate = ZitatExtraktor.extrahiere(text); var artikelBloecke = teileInArtikel(zitate.text(), ARTIKEL_UEBERSCHRIFT, entwurfsGrenzen); boolean paragraphenModus = false; - if (artikelBloecke.isEmpty()) { - artikelBloecke = + // Ein Sammelheft führt beide Gliederungen nebeneinander: Die eine Verkündung teilt sich in + // Artikel, die nächste in Paragraphen (so das hamburgische GVBl. Nr. 17/2026). Es genügt + // deshalb nicht, die §-Teilung erst dann zu versuchen, wenn das Heft überhaupt keinen Artikel + // führt — sie ist auch dann zu versuchen, wenn kein Artikel das Stammgesetz betrifft. + if (artikelBloecke.isEmpty() + || artikelBloecke.stream() + .noneMatch(b -> istRelevant(b, ziel, zitate, artikelFilter, false))) { + var nachParagraphen = teileInArtikel(zitate.text(), PARAGRAPH_UEBERSCHRIFT_AUSSEN, entwurfsGrenzen); - paragraphenModus = !artikelBloecke.isEmpty(); + if (nachParagraphen.stream() + .anyMatch(b -> istRelevant(b, ziel, zitate, artikelFilter, true))) { + artikelBloecke = nachParagraphen; + paragraphenModus = true; + } } var befehle = new ArrayList<Aenderungsbefehl>(); @@ -86,12 +96,7 @@ public final class AenderungsgesetzParser { for (int artikelIndex = 0; artikelIndex < artikelBloecke.size(); artikelIndex++) { var artikel = artikelBloecke.get(artikelIndex); - var relevant = - artikelFilter != null - ? artikel.label.equals(artikelFilter) - && (!paragraphenModus || hatAenderungsformel(artikel)) - : betrifft(artikel, ziel, zitate); - if (!relevant) { + if (!istRelevant(artikel, ziel, zitate, artikelFilter, paragraphenModus)) { continue; } log.infof("Artikel %s betrifft %s.", artikel.label, ziel.jurabk()); @@ -139,6 +144,22 @@ public final class AenderungsgesetzParser { return new ParseErgebnis(befehle, betroffeneArtikel, warnungen, inkrafttreten); } + /** + * Ob dieser Block anzuwenden ist: der benannte, wenn ein Filter gesetzt ist, sonst jeder, dessen + * Einleitung das Stammgesetz nennt. Im §-Modus muss der benannte Block überdies eine + * Änderungsformel tragen — ein Heft führt mehrere Verkündungen mit je eigener §-Zählung. + */ + private static boolean istRelevant( + ArtikelBlock artikel, + Gesetz ziel, + ZitatExtraktor.Ergebnis zitate, + @Nullable String artikelFilter, + boolean paragraphenModus) { + return artikelFilter != null + ? artikel.label.equals(artikelFilter) && (!paragraphenModus || hatAenderungsformel(artikel)) + : betrifft(artikel, ziel, zitate); + } + private static final String AENDERUNGSFORMEL = "wird wie folgt geändert:"; private static boolean hatAenderungsformel(ArtikelBlock artikel) { @@ -231,7 +252,12 @@ public final class AenderungsgesetzParser { ZitatExtraktor.Ergebnis zitate, List<Aenderungsbefehl> befehle) { - var eigenerPfad = pfad.isEmpty() ? markerText(punkt) : pfad + " " + markerText(punkt); + // Die Dezimalgliederung trägt ihre Herkunft im Label selbst („6.“ → „6.1“); ihn dem Pfad des + // Elternpunktes noch einmal anzuhängen ergäbe „6. 6.1“. + var eigenerPfad = + pfad.isEmpty() || punkt.label().startsWith(pfad.replaceAll("\\.$", "") + ".") + ? markerText(punkt) + : pfad + " " + markerText(punkt); var text = punkt.text().replaceAll("\\s+", " ").strip(); var provenienz = new Provenienz(artikelLabel, eigenerPfad, zitate.stelleZitateWiederHer(text)); @@ -303,6 +329,11 @@ public final class AenderungsgesetzParser { } private static String markerText(GliederungsScanner.GliederungsPunkt punkt) { + if (punkt.label().contains(".")) { + // Dezimalgliederung: Das Label ist schon vollständig („7.1.1“); ein Klammerzeichen + // dahinter wäre eine Erfindung. + return punkt.label(); + } return punkt.label().matches("\\d+[a-z]?") ? punkt.label() + "." : punkt.label() + ")"; } 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 f1cece5..c60bbaf 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/BefehlErkenner.java @@ -47,7 +47,7 @@ final class BefehlErkenner { // Wiederkehrende Bausteine. private static final String WOERTER = - "(?:die (?:Wörter|Worte)|das Wort|die Angabe|die Zahl|die Verweisung)"; + "(?:die (?:Wörter|Worte)|das Wort|die Angabe|die Zahl|die Verweisung|die Textstelle)"; private static final String Z = "«(\\d+)»"; // Der Doppelpunkt fehlt gelegentlich (Seitenumbruch-Artefakt); für einen Punkt mit Unterpunkten @@ -139,7 +139,7 @@ final class BefehlErkenner { // darf auch hier verkürzt sein („Die Angabe „X“ wird durch „Y“ ersetzt“, hessisches GVBl). private static final Pattern ERSETZUNG_OHNE_STELLE = Pattern.compile( - "^(?:Die (?:Wörter|Worte)|Das Wort|Die Angabe|Die Zahl|Die Verweisung) " + "^(?:Die (?:Wörter|Worte)|Das Wort|Die Angabe|Die Zahl|Die Verweisung|Die Textstelle) " + Z + " (?:wird|werden) (jeweils )?durch (?:" + WOERTER @@ -153,7 +153,7 @@ final class BefehlErkenner { private static final Pattern ERSETZUNG_MIT_ANKER = Pattern.compile( "^(?:In )?(.+?) (?:wird|werden) (?:jeweils )?nach " - + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung) " + + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung|der Textstelle) " + Z + " " + WOERTER @@ -169,7 +169,7 @@ final class BefehlErkenner { private static final Pattern WORT_VORANSTELLUNG = Pattern.compile( "^(?:In )?(.+?) (?:wird|werden) (?:jeweils )?" - + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung) " + + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung|der Textstelle) " + Z + " " + WOERTER @@ -224,8 +224,8 @@ final class BefehlErkenner { private static final Pattern WOERTER_EINFUEGUNG = Pattern.compile( - "^(?:In )?(.+?) (?:wird|werden) (?:jeweils )?(nach|vor) " - + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung) " + "^(?:In )?(.+?) (?:wird|werden) (?:jeweils )?(nach|hinter|vor) " + + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung|der Textstelle) " + Z + " " + WOERTER @@ -247,7 +247,7 @@ final class BefehlErkenner { private static final Pattern STRUKTUR_EINFUEGUNG = Pattern.compile( - "^(Nach|Vor) (.+?) (?:wird|werden) (?:der |die |das )?folgende[nrs]? (?:neue[nrs]? )?(.+?) " + "^(Nach|Hinter|Vor) (.+?) (?:wird|werden) (?:der |die |das )?folgende[nrs]? (?:neue[nrs]? )?(.+?) " + "(?:ein|an)gefügt: " + ENUM + Z @@ -258,7 +258,7 @@ final class BefehlErkenner { // statt einer Stellenangabe; das Ziel selbst erbt der Befehl aus dem Kontextrahmen. private static final Pattern STRUKTUR_EINFUEGUNG_WORTANKER = Pattern.compile( - "^(Nach|Vor) (?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl) " + "^(Nach|Hinter|Vor) (?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Textstelle) " + Z + " (?:wird|werden) (?:der |die |das )?folgende[nrs]? (?:neue[nrs]? )?(.+?) " + "(?:ein|an)gefügt: " @@ -272,7 +272,7 @@ final class BefehlErkenner { 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]? )?(.+?) " + + "(?i:(nach|hinter|vor)) (.+?) (?:der |die |das )?folgende[nrs]? (?:neue[nrs]? )?(.+?) " + "(?:ein|an)gefügt: " + ENUM + Z @@ -284,7 +284,7 @@ final class BefehlErkenner { // („nach dem Satz 1“) gehört zum Satzbau, nicht zur Stelle. private static final Pattern STRUKTUR_EINFUEGUNG_MIT_STELLE = Pattern.compile( - "^In (.+?) (?:wird|werden) (?i:(nach|vor)) (?:dem |der |den )?(.+?) " + "^In (.+?) (?:wird|werden) (?i:(nach|hinter|vor)) (?:dem |der |den )?(.+?) " + "(?:der |die |das )?folgende[nrs]? (?:neue[nrs]? )?(.+?) " + "(?:ein|an)gefügt: " + ENUM @@ -300,7 +300,7 @@ final class BefehlErkenner { // selbst und wird von diesem Muster nicht getroffen. private static final Pattern STRUKTUR_ANFUEGUNG_MIT_STELLE = Pattern.compile( - "^(?:Dem |Der |In |)(.+?) (?:wird|werden) (?:der |die |das )?folgende[nrs]? (.+?)" + "^(?:Dem |Der |In |)(?!Es )(.+?) (?:wird|werden) (?:der |die |das )?folgende[nrs]? (.+?)" + "(?: wird| werden)? angefügt ?: " + ENUM + Z @@ -308,7 +308,7 @@ final class BefehlErkenner { private static final Pattern STRUKTUR_ANFUEGUNG = Pattern.compile( - "^(?:Der |Die |Das )?[Ff]olgende[nrs]? (.+?) (?:wird|werden) angefügt ?: " + "^(?:Es (?:wird|werden) )?(?:Der |Die |Das )?[Ff]olgende[nrs]? (.+?) (?:(?:wird|werden) )?angefügt ?: " + ENUM + Z + "\\.?$"); @@ -403,7 +403,7 @@ final class BefehlErkenner { // allem als rechte Klausel eines Verbunds auf („… ersetzt und die Angabe „X“ wird gestrichen“). private static final Pattern STREICHUNG_OHNE_STELLE = Pattern.compile( - "^(?:Die (?:Wörter|Worte)|Das Wort|Die Angabe|Die Zahl|Die Verweisung) " + "^(?:Die (?:Wörter|Worte)|Das Wort|Die Angabe|Die Zahl|Die Verweisung|Die Textstelle) " + Z + " (?:wird|werden) " + "(?:jeweils )?gestrichen\\.$"); @@ -456,7 +456,7 @@ final class BefehlErkenner { // #paragraphBereichNeufassung}). private static final Pattern PARAGRAPH_BEREICH_NEUFASSUNG = Pattern.compile( - "^Die (§§|Artt?\\.) (\\d+[a-z]?) bis (\\d+[a-z]?) " + "^(?:Die )?(§§|Artt?\\.) (\\d+[a-z]?) (?:bis|und) (\\d+[a-z]?) " + NEUFASSUNG_VERB + ": " + Z @@ -479,8 +479,8 @@ final class BefehlErkenner { // „In Satz 2 wird nach dem Wort «1» ein Komma und werden die Wörter «2» eingefügt.“ private static final Pattern KOMMA_UND_WOERTER_EINFUEGUNG = Pattern.compile( - "^(?:In )?(.+?) (?:wird|werden) (?:jeweils )?(nach|vor) " - + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung) " + "^(?:In )?(.+?) (?:wird|werden) (?:jeweils )?(nach|hinter|vor) " + + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung|der Textstelle) " + Z // Das wiederholte Verb fehlt im amtlichen Satz oft („… ein Komma und die Angabe „…“ // eingefügt“, GV. NRW.). @@ -631,8 +631,8 @@ final class BefehlErkenner { // einfügen) private static final Pattern KOMMA_EINFUEGUNG = Pattern.compile( - "^(?:In )?(.+?) (?:wird|werden) (?:jeweils )?(nach|vor) " - + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung) " + "^(?:In )?(.+?) (?:wird|werden) (?:jeweils )?(nach|hinter|vor) " + + "(?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Verweisung|der Textstelle) " + Z + " (ein Komma|ein Semikolon|einen Strichpunkt|einen Punkt) eingefügt\\.$"); @@ -640,7 +640,7 @@ final class BefehlErkenner { // den Kontext). Tritt vor allem als rechte Klausel eines Verbundbefehls auf. private static final Pattern EINFUEGUNG_ANKER_ZUERST = Pattern.compile( - "^(Nach|Vor) (?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl) " + "^(Nach|Hinter|Vor) (?:dem Wort|den (?:Wörtern|Worten)|der Angabe|der Zahl|der Textstelle) " + Z + " (?:wird|werden) (?:" + WOERTER @@ -1219,7 +1219,7 @@ final class BefehlErkenner { if ((m = EINFUEGUNG_ANKER_ZUERST.matcher(text)).matches()) { var ankerWoerter = wortZitat(zitate, m.group(2)); var anker = - m.group(1).equalsIgnoreCase("nach") + !m.group(1).equalsIgnoreCase("vor") ? new WortAnker.NachWoertern(ankerWoerter) : new WortAnker.VorWoertern(ankerWoerter); var woerter = m.group(3) != null ? wortZitat(zitate, m.group(3)) : satzzeichen(m.group(4)); @@ -1254,7 +1254,7 @@ final class BefehlErkenner { } var ankerWoerter = wortZitat(zitate, m.group(2)); var anker = - m.group(1).equalsIgnoreCase("nach") + !m.group(1).equalsIgnoreCase("vor") ? new WortAnker.NachWoertern(ankerWoerter) : new WortAnker.VorWoertern(ankerWoerter); var textInhalt = diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/GliederungsScanner.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/GliederungsScanner.java index 3d6b29d..39bffe5 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/GliederungsScanner.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/GliederungsScanner.java @@ -6,6 +6,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; +import org.jspecify.annotations.Nullable; /** * Zerlegt den Rumpf eines Artikels in einen Baum von Gliederungspunkten (1. → a) → aa)). @@ -24,6 +25,15 @@ final class GliederungsScanner { /** Ergebnis: Text vor dem ersten Gliederungspunkt (Einleitungssatz) und die Punkte selbst. */ record ScanErgebnis(String vorspann, List<GliederungsPunkt> punkte) {} + /** + * Dezimalgliederung: „6.1“, „7.1.1“ — die Ebene steht in der Zahl selbst. Das hamburgische + * Gesetzblatt gliedert seine Änderungsbefehle so, wo andere Blätter a)/aa) setzen. Der Punkt + * hinter dem letzten Glied ist wahlfrei; ein Leerzeichen muss folgen, sonst wäre „6.1“ aus + * „Nummer 6.1“ ein Marker. + */ + private static final Pattern DEZIMAL_MARKER = + Pattern.compile("^(\\d+(?:\\.\\d+[a-z]?)+)\\.?\\s+(\\S.*)$"); + // Eingeschobene Punkte tragen Suffixe: „2a.“, „a1)“, „aa1)“. private static final Pattern NUMMER_MARKER = Pattern.compile("^(\\d+[a-z]?)\\.\\s+(.*)$"); private static final Pattern BUCHSTABE_MARKER = Pattern.compile("^([a-z]\\d*)\\)\\s+(.*)$"); @@ -70,6 +80,11 @@ final class GliederungsScanner { private record Marker(String label, int ebene, String rest) {} private static Marker erkenneMarker(String zeile) { + var dezimal = DEZIMAL_MARKER.matcher(zeile); + if (dezimal.matches()) { + var label = dezimal.group(1); + return new Marker(label, label.split("\\.").length, dezimal.group(2)); + } var dreifach = DREIFACHBUCHSTABE_MARKER.matcher(zeile); if (dreifach.matches()) { return new Marker(dreifach.group(1), 4, dreifach.group(3)); @@ -98,10 +113,20 @@ final class GliederungsScanner { } // Sonst: nur ein Eröffnungslabel einer tieferen Ebene ist zulässig. int aktuelleTiefe = stapel.isEmpty() ? 0 : stapel.peekLast().ebene; - return marker.ebene == aktuelleTiefe + 1 && istEroeffnung(marker); + var eltern = stapel.isEmpty() ? null : stapel.peekLast().label; + return marker.ebene == aktuelleTiefe + 1 && istEroeffnung(marker, eltern); } - private static boolean istEroeffnung(Marker marker) { + /** + * Ob das Label eine Ebene eröffnet. Die Dezimalgliederung trägt ihre Herkunft im Label: „6.1“ + * eröffnet die Unterebene von „6.“, aber nur dort — unter „7.“ hätte sie nichts zu suchen. + */ + private static boolean istEroeffnung(Marker marker, @Nullable String eltern) { + if (marker.label.contains(".")) { + return eltern != null + && marker.label.startsWith(eltern.endsWith(".") ? eltern : eltern + ".") + && marker.label.endsWith(".1"); + } return switch (marker.ebene) { case 1 -> marker.label.equals("1"); case 2 -> marker.label.equals("a"); @@ -112,6 +137,18 @@ final class GliederungsScanner { } private static boolean istNachfolger(String vorher, String nachher) { + // Dezimalgliederung: Nachfolger ist, wer denselben Vorspann trägt und im letzten Glied + // fortzählt („7.1.1“ → „7.1.2“). + if (vorher.contains(".") || nachher.contains(".")) { + int vorherTrenner = vorher.lastIndexOf('.'); + int nachherTrenner = nachher.lastIndexOf('.'); + if (vorherTrenner < 0 || nachherTrenner < 0) { + return false; + } + return vorher.substring(0, vorherTrenner).equals(nachher.substring(0, nachherTrenner)) + && istNachfolger( + vorher.substring(vorherTrenner + 1), nachher.substring(nachherTrenner + 1)); + } if (vorher.matches("\\d+[a-z]?") && nachher.matches("\\d+[a-z]?")) { var vorherZahl = Integer.parseInt(vorher.replaceAll("[a-z]$", "")); var vorherSuffix = vorher.replaceAll("^\\d+", ""); diff --git a/src/main/java/eu/mulk/aendggner/aenderung/parse/Lesereihenfolge.java b/src/main/java/eu/mulk/aendggner/aenderung/parse/Lesereihenfolge.java index d29ea8d..e7f60d5 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/Lesereihenfolge.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/Lesereihenfolge.java @@ -140,17 +140,26 @@ final class Lesereihenfolge { } /** - * Ein Band zwischen zwei breiten Zeilen: erst die linke Spalte, dann die rechte, jede in der - * Reihenfolge des Inhaltsstroms. + * Ein Band zwischen zwei breiten Zeilen: erst die linke Spalte, dann die rechte, jede von oben + * nach unten. + * + * <p>Die Spalte folgt ihrer Grundlinie und nicht dem Inhaltsstrom. Wo der Strom ohnehin von oben + * nach unten läuft — der Regelfall —, ändert das nichts; die Sortierung ist stabil, gleich hohe + * Zeilen behalten ihre Folge. Wo er es nicht tut, bewahrt sie vor dem Schlimmsten: Im + * hamburgischen Gesetzblatt fällt das Schlusswort der Eingangsformel („verordnet:“) mitten in + * einen Absatz der rechten Spalte und zerschneidet dort ein Wort. */ private static void bandAusgeben( List<Zeile> ergebnis, List<Zeile> links, List<Zeile> rechts, float oben, float unten) { for (var spalte : List.of(links, rechts)) { + var band = new ArrayList<Zeile>(); for (var zeile : spalte) { if (zeile.grundlinie() > oben && zeile.grundlinie() < unten) { - ergebnis.add(zeile); + band.add(zeile); } } + band.sort((a, b) -> Float.compare(a.grundlinie(), b.grundlinie())); + ergebnis.addAll(band); } } 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 af41dd1..073abfa 100644 --- a/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java +++ b/src/main/java/eu/mulk/aendggner/aenderung/parse/TextBereiniger.java @@ -284,6 +284,14 @@ public final class TextBereiniger { "[ \\t]*\\d{1,4}[ \\t]+Gesetz- und Verordnungsblatt für den Freistaat" + " Thüringen[ \\t]*"); + // HmbGVBl.: Der Kolumnentitel steht gleichfalls im Inhaltsstrom und trägt die Seitenzahl in der + // Mitte („Dienstag, den 26. Mai 2026 149HmbGVBl. Nr. 17“ bzw. „HmbGVBl. Nr. 17 Dienstag, den + // 26. Mai 2026 147“). Er fällt hinter das Zitat eines Befehls und nähme ihm den Schlusspunkt. + private static final Pattern GVBL_HH_KOPF = + Pattern.compile( + "[ \\t]*(?:HmbGVBl\\. Nr\\. \\d+[ \\t]*)?\\p{L}+tag, den \\d{1,2}\\. \\p{L}+ \\d{4}" + + "[ \\t]*\\d{0,4}[ \\t]*(?:HmbGVBl\\. Nr\\. \\d+)?[ \\t]*"); + // GBl. für Baden-Württemberg: Der Seitenfuß („Gesetzblatt für Baden-Württemberg, Jahrgang 2026, // Nr. 26 vom 27. Februar 2026 Seite 2 von 7“) steht gleichfalls im Inhaltsstrom, und zwar mitten // im Befehlstext — er trennt dort sogar den Zieltext eines Befehls von seinem Verb („… die @@ -317,6 +325,13 @@ public final class TextBereiniger { private static final Pattern SACHNUMMER_MIT_LEERZEICHEN = Pattern.compile("(§|Art\\.) (\\d+) ([a-z])(?![a-zäöüß).])"); + /** + * Leerraumzeichen, die Javas {@code \s} nicht kennt: das geschützte Leerzeichen und die + * typographischen Ausschlüsse (schmal, halbgeviert, geviert und dergleichen). + */ + private static final Pattern UNSICHTBARER_LEERRAUM = + Pattern.compile("[\\u00A0\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000]"); + /** C0-Steuerzeichen außer Tabulator und Zeilenumbruch; im Fließtext stets Extraktionsmüll. */ private static final Pattern STEUERZEICHEN = Pattern.compile("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]"); @@ -331,9 +346,11 @@ public final class TextBereiniger { // gewöhnlichen Ziffern und nähme SatzTeiler und Superskript ihre Grundlage. Die Umbruch-Marker // des FontgroessenFilters liegen im Private-Use-Bereich und bleiben unberührt. rohText = Normalizer.normalize(rohText, Normalizer.Form.NFC); - // Geschützte Leerzeichen (GVBl-Satz: „§ 1“, „Abs. 2“) sind für Javas \s und - // String.strip unsichtbar — früh auf gewöhnliche Leerzeichen normalisieren. - var text = rohText.replace(' ', ' ').replace(' ', ' '); + // Leerraum, der für Javas \s und String.strip unsichtbar ist: das geschützte Leerzeichen + // (GVBl-Satz: „§ 1“, „Abs. 2“) und die feinen Ausschlüsse des Bleisatzes. Das hamburgische + // Gesetzblatt setzt zwischen Paragraphenzeichen und Nummer ein schmales Leerzeichen (U+2009); + // ohne diese Normalisierung ist „§ 1“ dort kein Normkopf, und ein ganzes Heft bliebe ungelesen. + var text = UNSICHTBARER_LEERRAUM.matcher(rohText).replaceAll(" "); // Steuerzeichen aus fehlgeleiteten Glyphenzuordnungen (im GVBl. für Berlin trägt der Einzug // der Aufzählungsglieder ein U+0007). Sie sind unsichtbar, stehen aber vor dem Befehlstext und // ließen dessen Zeilenanfangs-Anker ins Leere greifen. @@ -348,6 +365,7 @@ public final class TextBereiniger { text = GVBL_BERLIN_KOPF.matcher(text).replaceAll("\n"); text = GVBL_TH_KOPF.matcher(text).replaceAll("\n"); text = GVBL_TH_FUSS.matcher(text).replaceAll("\n"); + text = GVBL_HH_KOPF.matcher(text).replaceAll("\n"); text = GBL_BW_FUSS.matcher(text).replaceAll("\n"); text = GVBL_RP_FUSS.matcher(text).replaceAll("\n"); var zeilen = entferneKolumnentitel(zerlegeInZeilen(text)); @@ -656,8 +674,16 @@ public final class TextBereiniger { var woerter = new java.util.HashSet<String>(); for (var zeile : zeilen) { for (var wort : WORTGRENZE.split(zeile.text())) { - if (!wort.isEmpty()) { - woerter.add(wort); + if (wort.isEmpty()) { + continue; + } + woerter.add(wort); + // Der Punkt gehört zur Wortgrenze nicht (wegen „Abs.“, „Nr.“); am Satzende klebt er + // gleichwohl am Wort. Ohne die abgestreifte Form fände „(Immo-“ + „WertV)“ seinen + // Beweis nicht, obgleich das Dokument „ImmoWertV.“ ein paar Zeilen weiter führt. + var ohnePunkte = wort.replaceAll("^\\.+|\\.+$", ""); + if (!ohnePunkte.isEmpty() && !ohnePunkte.equals(wort)) { + woerter.add(ohnePunkte); } } } diff --git a/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java b/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java index 2dfb7db..92cd569 100644 --- a/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java +++ b/src/main/java/eu/mulk/aendggner/anwendung/BefehlAnwender.java @@ -362,8 +362,32 @@ public final class BefehlAnwender { var belegt = new ArrayList<Set<String>>(anzahl); for (var schritt : schritte) { raeumt.add(geraeumteBezeichnungen(schritt.teil())); - belegt.add(belegteBezeichnungen(schritt.teil())); + belegt.add(new LinkedHashSet<>(belegteBezeichnungen(schritt.teil()))); + } + // Auch eine Neufassung kann eine Bezeichnung neu vergeben — aber nur dort, wo eine + // aufsteigende Umnummerierung sie zuvor räumt. „Nummer 3 erhält folgende Fassung: …“ neben + // „Die bisherige Nummer 3 wird Nummer 4“ meint die ursprüngliche Zählung (§ 8 Absatz 1 des + // Handbuchs); liefe die Neufassung zuerst, so verlöre die bisherige Nummer 3 ihren Wortlaut, + // und die Umnummerierung benennte alsdann die neue — ein stiller Verlust. Ohne einen solchen + // Räumer bleibt die Neufassung, was sie ist: das Umschreiben einer vorhandenen Einheit, das + // keinen Vorrang beansprucht. + var aufsteigendGeraeumt = new LinkedHashSet<String>(); + for (var schritt : schritte) { + for (var u : umnummerierungen(schritt.teil())) { + if (nummeriertAufwaerts(u)) { + aufsteigendGeraeumt.add(u.stelle().anzeigeText()); + } + } + } + for (int i = 0; i < anzahl; i++) { + for (var n : neufassungen(schritte.get(i).teil())) { + var bezeichnung = n.stelle().anzeigeText(); + if (belegbareBezeichnung(n.stelle()) && aufsteigendGeraeumt.contains(bezeichnung)) { + belegt.get(i).add(bezeichnung); + } + } } + var reihenfolge = new ArrayList<Integer>(anzahl); // 0 = offen, 1 = in Arbeit (Zyklus-Bremse), 2 = eingereiht. var stand = new byte[anzahl]; @@ -527,6 +551,50 @@ public final class BefehlAnwender { return belegt; } + /** + * Ob die Umnummerierung aufwärts zählt („Nummer 3 wird Nummer 4“) und damit ihre bisherige + * Bezeichnung für eine neue Einheit freigibt. Abwärts zählt, wer eine Lücke schließt; dort + * entsteht kein Platz. + */ + private static boolean nummeriertAufwaerts(Umnummerierung u) { + var alt = letzteMarke(u.stelle()); + var neu = letzteMarke(u.neu()); + if (alt == null || neu == null || !alt.matches("\\d+[a-z]?") || !neu.matches("\\d+[a-z]?")) { + return false; + } + int altZahl = Integer.parseInt(alt.replaceAll("[a-z]$", "")); + int neuZahl = Integer.parseInt(neu.replaceAll("[a-z]$", "")); + return neuZahl > altZahl || (neuZahl == altZahl && neu.compareTo(alt) > 0); + } + + /** Die Zählung der letzten Komponente einer Stelle; {@code null}, wenn sie keine trägt. */ + private static @Nullable String letzteMarke(Stelle stelle) { + if (stelle.komponenten().isEmpty()) { + return null; + } + return switch (stelle.komponenten().get(stelle.komponenten().size() - 1)) { + case Stelle.AbsatzNr a -> a.nummer(); + case Stelle.NummerNr n -> n.nummer(); + case Stelle.BuchstabeNr b -> b.kennung(); + default -> null; + }; + } + + /** + * Ob die Stelle eine gezählte Einheit benennt, deren Bezeichnung ein anderer Schritt räumen kann. + * Die Überschrift und der ganze Paragraph gehören nicht dazu: Sie tragen keine Zählung, die eine + * Umnummerierung verschöbe. + */ + private static boolean belegbareBezeichnung(Stelle stelle) { + if (stelle.komponenten().isEmpty()) { + return false; + } + var letzte = stelle.komponenten().get(stelle.komponenten().size() - 1); + return letzte instanceof Stelle.AbsatzNr + || letzte instanceof Stelle.NummerNr + || letzte instanceof Stelle.BuchstabeNr; + } + // Aufzählungsmarken in Zitatblöcken, je Ebene. private static final Pattern NUMMER_MARKER = Pattern.compile("(?m)^[ \\t]*(\\d+[a-z]?)\\.[ \\t]"); private static final Pattern BUCHSTABE_MARKER = @@ -657,6 +725,16 @@ public final class BefehlAnwender { }; } + /** Die Neufassungen eines Befehls — auch die in einem Verbund. */ + private static List<Neufassung> neufassungen(Aenderungsbefehl befehl) { + return switch (befehl) { + case Neufassung n -> List.of(n); + case Sammelbefehl s -> + s.teilbefehle().stream().flatMap(t -> neufassungen(t).stream()).toList(); + default -> List.of(); + }; + } + /** Die Struktur-Ersetzungen eines Befehls — auch die in einem Verbund. */ private static List<StrukturErsetzung> ersetzungen(Aenderungsbefehl befehl) { return switch (befehl) { @@ -1077,6 +1155,9 @@ public final class BefehlAnwender { if (brauchtFuge(befehl.alt(), befehl.neu())) { return TextErgebnis.ok(ersetzeMitFuge(text, befehl.alt(), befehl.neu())); } + if (verliertFuge(befehl.alt(), befehl.neu())) { + return TextErgebnis.ok(ersetzeOhneFuge(text, befehl.alt(), befehl.neu())); + } return TextErgebnis.ok(ersetzeWortweise(text, befehl.alt(), befehl.neu())); })); } @@ -1107,6 +1188,33 @@ public final class BefehlAnwender { return sb.append(text, von, text.length()).toString(); } + /** + * Der umgekehrte Fall: Tritt an die Stelle eines Wortes ein Satzzeichen, so weicht der + * Zwischenraum davor. „In Nummer 2 wird das Wort „und“ am Ende durch ein Komma ersetzt“ führt + * sonst auf „…Aufgaben ,“ statt auf „…Aufgaben,“. Ein Satzzeichen schließt an, es steht nicht für + * sich. + */ + private static boolean verliertFuge(String alt, String neu) { + return NUR_SATZZEICHEN.matcher(neu).matches() + && !alt.isEmpty() + && Character.isLetterOrDigit(alt.codePointAt(0)); + } + + /** Ersetzt und nimmt dabei den Zwischenraum vor dem Ersetzten mit. */ + private static String ersetzeOhneFuge(String text, String alt, String neu) { + var sb = new StringBuilder(); + int von = 0; + for (int idx = text.indexOf(alt); idx >= 0; idx = text.indexOf(alt, von)) { + int anfang = idx; + while (anfang > 0 && text.charAt(anfang - 1) == ' ') { + anfang--; + } + sb.append(text, von, anfang).append(neu); + von = idx + alt.length(); + } + return sb.append(text, von, text.length()).toString(); + } + private static String ersetzeMitFuge(String text, String alt, String neu) { var sb = new StringBuilder(); int von = 0; @@ -1759,6 +1867,31 @@ public final class BefehlAnwender { })); } + /** + * Hängt einen Satz an einen Text an. + * + * <p>Endet der Text auf ein Aufzählungsglied, so beginnt der angefügte Satz eine eigene Zeile: Er + * gehört dem Absatz und nicht dem letzten Glied. Das Handbuch der Rechtsförmlichkeit setzt einen + * solchen Schlusssatz linksbündig hinter die Aufzählung, und die amtlichen Fassungen tun es auch + * („… zur Erfüllung ihrer Aufgaben und“ / „4. …“ / „Die DIN EN ISO/IEC 17024 ist … + * niedergelegt.“). Sonst schließt er wie gewohnt mit einem Zwischenraum an. + */ + private static String haengeSatzAn(String text, String satz) { + var gestutzt = text.stripTrailing(); + int zeilenAnfang = gestutzt.lastIndexOf('\n') + 1; + var letzteZeile = gestutzt.substring(zeilenAnfang); + var gliedmarke = java.util.regex.Pattern.compile("^(?:\\d+[a-z]?\\.|[a-z]{1,3}\\))[ \\t]"); + if (!gliedmarke.matcher(letzteZeile.stripLeading()).find()) { + return gestutzt + " " + satz; + } + // Die neue Zeile erbt die Einrückung des Blockes, nicht die des letzten Gliedes: Die + // Einrückung trägt die Gliederung, und eine bündig gesetzte Zeile beendete den Block, dem der + // Satz gerade zugehören soll. + var erste = gestutzt.split("\n", 2)[0]; + var einrueckung = erste.substring(0, erste.length() - erste.stripLeading().length()); + return gestutzt + "\n" + einrueckung + satz; + } + private static AngewandteAenderung wendeAnfuegungAn(List<Norm> normen, Anfuegung befehl) { return switch (befehl.ebene()) { case ABSATZ -> { @@ -1774,9 +1907,7 @@ public final class BefehlAnwender { } case SATZ -> bearbeiteText( - normen, - befehl, - text -> TextErgebnis.ok(text.stripTrailing() + " " + befehl.text().strip())); + normen, befehl, text -> TextErgebnis.ok(haengeSatzAn(text, befehl.text().strip()))); // Ein Halbsatz beginnt keinen neuen Satz, sondern setzt den bestehenden hinter dem // Strichpunkt fort. Angehängt wird deshalb wie beim Satz — aber nur, wenn der Zieltext // tatsächlich auf einen Strichpunkt endet: Sonst stünde der Halbsatz hinter einem Punkt und diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java index 8c9743d..bb67978 100644 --- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java +++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java @@ -1700,6 +1700,84 @@ class EndToEndTest { .containsOnlyOnce("Fortgeschrieben durch: Änderungsgesetz vom 12. Februar 2026"); } + /** + * Hamburg: Die Verordnung zur Änderung der Verordnung über den Gutachterausschuss für + * Grundstückswerte (HmbGVBl. Nr. 17/2026, S. 147). + * + * <p>Der Fall bringt eine Gliederung, die im übrigen Landesrecht nicht vorkommt: Das + * Änderungsdokument selbst teilt sich in <em>Paragraphen</em> (§ 1 Änderung, § 2 Inkrafttreten), + * obgleich dasselbe Heft davor eine Verkündung führt, die sich in Artikel teilt; und seine + * Befehle sind <em>dezimal</em> gegliedert (6.1, 7.1.1, 7.2.3) statt a)/aa)/aaa). Dazu vier + * Idiome — „die Textstelle“, „Hinter § 7 wird … eingefügt“, „Es wird folgender Absatz angefügt“, + * „§§ 6 und 7 erhalten folgende Fassung“ — und ein schmales Leerzeichen zwischen + * Paragraphenzeichen und Nummer. + * + * <p>Vier Normen weichen von der amtlichen Nachfassung ab; drei davon liegen an der Vorlage und + * nicht am Werkzeug (siehe {@code Hamburg/SOURCES}). + */ + @Test + void gutachterausschussVOHamburg() throws Exception { + var alt = SAMPLEDATA.resolve("Hamburg/GAusschV-HH-alt.txt"); + var pdf = SAMPLEDATA.resolve("Hamburg/HmbGVBl-2026-17_GutachterausschussVO-AendVO.pdf"); + var neu = SAMPLEDATA.resolve("Hamburg/GAusschV-HH-neu.txt"); + assumeTrue(Files.exists(alt) && Files.exists(pdf), "Hamburger Beispieldaten fehlen"); + + var gesetz = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(alt); + assertThat(gesetz.jurabk()).isEqualTo("GAusschV HA 2009"); + assertThat(gesetz.normen()).hasSize(13); + + var text = TextBereiniger.bereinige(new PatchTextExtraktor().extrahiere(pdf)); + // Das schmale Leerzeichen (U+2009) zwischen § und Nummer ist normalisiert; ohne das wäre + // „§ 1“ kein Kopf, und das ganze Heft bliebe ungelesen. + assertThat(text).contains("\n§ 1\n").doesNotContain("§\u2009"); + // Der Kolumnentitel steht im Inhaltsstrom und fiele sonst hinter das Zitat eines Befehls. + assertThat(text).doesNotContain("Dienstag, den 26. Mai 2026 149"); + + var parseErgebnis = new AenderungsgesetzParser().parse(text, gesetz, null); + // Der Artikel-Modus findet nichts, das die Verordnung beträfe; erst die §-Teilung tut es. + assertThat(parseErgebnis.artikel()).containsExactly("1"); + assertThat(parseErgebnis.befehle()).hasSize(21); + + // Die Dezimalgliederung trägt ihre Herkunft im Label; der Pfad wiederholt sie nicht. + assertThat(befehlAn(parseErgebnis, "7.1.4")) + .isInstanceOf(Aenderungsbefehl.Umnummerierung.class); + assertThat(befehlAn(parseErgebnis, "8.1")).isInstanceOf(Aenderungsbefehl.Neufassung.class); + + // Zwei Befehle bleiben unerkannt, und zwar zu Recht: Die Nummern 2 und 3 des Heftes schreiben + // „wir die Textstelle … ersetzt“ statt „wird“. Das ist ein Verkündungsfehler, und geraten wird + // nicht. + var unerkannt = + parseErgebnis.befehle().stream() + .filter(b -> b instanceof UnbekannterBefehl) + .map(b -> b.provenienz().gliederungsPfad()) + .toList(); + assertThat(unerkannt).containsExactly("2.", "3."); + + var anwendung = BefehlAnwender.anwenden(gesetz, parseErgebnis.befehle()); + assertThat(anwendung.anzahlAngewandt()).isEqualTo(17); + + // Der angefügte Schlusssatz steht hinter der Aufzählung auf eigener Zeile und nicht am Ende + // des letzten Aufzählungsgliedes. + var neunter = anwendung.neu().norm("§ 9").orElseThrow().absaetze().get(0).text(); + assertThat(neunter) + .contains("\nDie DIN EN ISO/IEC 17024 ist zur kostenfreien Einsicht") + // Das Satzzeichen, das an die Stelle eines Wortes tritt, nimmt dessen Zwischenraum mit. + .contains("zur Erfüllung ihrer Aufgaben,\n") + // Die bisherige Nummer 3 ist erhalten: Die Umnummerierung geht der Neufassung vor, die + // ihre Bezeichnung neu vergäbe — sonst ginge jener Wortlaut verloren. + .contains("4. weitere Personen und Stellen"); + + assumeTrue(Files.exists(neu), "Hamburger Nachfassung fehlt"); + var soll = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(neu); + var abgleich = Nachfassungsabgleich.vergleiche(soll, anwendung.neu()); + assertThat(abgleich.fehlende()).isEmpty(); + assertThat(abgleich.ueberzaehlige()).isEmpty(); + // § 3 und § 4: der Verkündungsfehler „wir“. § 6: das Gesetzblatt setzt „1. in Präsenz“ ohne + // Komma, das Portal mit. § 9: die Neufassung der soeben geräumten Nummer 3. + assertThat(abgleich.abweichungen().stream().map(a -> a.enbez()).toList()) + .containsExactly("§ 3", "§ 4", "§ 6", "§ 9"); + } + private static Aenderungsbefehl befehlZu( AenderungsgesetzParser.ParseErgebnis ergebnis, String gliederungsPfad) { return ergebnis.befehle().stream() diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java index 327c8db..4c55154 100644 --- a/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java @@ -1544,4 +1544,92 @@ class BefehlErkennerTest { Stelle.LEER); assertThat(befehl).containsInstanceOf(Ersetzung.class); } + + // --- Hamburger Idiome (HmbGVBl. Nr. 17/2026) -------------------------------------------------- + + /** „Hinter“ steht dem „nach“ gleich; das hamburgische Blatt setzt es durchweg. */ + @Test + void erkenntEinfuegungHinterEinerNorm() { + var befehl = erkenne("Hinter § 7 wird folgender § 7a eingefügt: „§ 7a Aufgaben“", Stelle.LEER); + + assertThat(befehl).containsInstanceOf(StrukturEinfuegung.class); + var einfuegung = (StrukturEinfuegung) befehl.orElseThrow(); + assertThat(einfuegung.vorher()).isFalse(); + assertThat(einfuegung.stelle().anzeigeText()).isEqualTo("§ 7"); + assertThat(einfuegung.bezeichnung()).isEqualTo("7a"); + } + + @Test + void erkenntEinfuegungHinterEinemAbsatz() { + var befehl = + erkenne( + "Hinter Absatz 3 wird folgender neuer Absatz 4 eingefügt: „(4) Nicht" + + " grundstücksbezogene Auskünfte sind zu erteilen.“", + PARAGRAPH_5); + + assertThat(befehl).containsInstanceOf(StrukturEinfuegung.class); + var einfuegung = (StrukturEinfuegung) befehl.orElseThrow(); + assertThat(einfuegung.vorher()).isFalse(); + assertThat(einfuegung.bezeichnung()).isEqualTo("4"); + } + + /** Die Anfügung ohne Subjekt: Das Ziel steht im Rahmen, der Satz nennt nur das Angefügte. */ + @Test + void erkenntAnfuegungOhneSubjekt() { + var befehl = + erkenne( + "Es wird folgender Absatz 3 angefügt: „(3) Personenbezogene Daten sind zu löschen.“", + PARAGRAPH_5); + + assertThat(befehl).isPresent(); + assertThat(befehl.orElseThrow().stelle().anzeigeText()).isEqualTo("§ 5"); + } + + @Test + void erkenntSatzAnfuegungOhneSubjekt() { + var befehl = + erkenne("Es wird folgender Satz angefügt: „Die Norm ist niedergelegt.“", PARAGRAPH_5); + + assertThat(befehl).isPresent(); + } + + /** „Textstelle“ ist die hamburgische Nebenform zu „Wörter“ und „Angabe“. */ + @Test + void erkenntTextstellenErsetzung() { + var befehl = + erkenne( + "In § 3 Absatz 2 Nummer 1 wird die Textstelle „Absatz 5“ durch die Textstelle" + + " „Absatz 8“ ersetzt.", + Stelle.LEER); + + assertThat(befehl).containsInstanceOf(Ersetzung.class); + var ersetzung = (Ersetzung) befehl.orElseThrow(); + assertThat(ersetzung.alt()).isEqualTo("Absatz 5"); + assertThat(ersetzung.neu()).isEqualTo("Absatz 8"); + } + + @Test + void erkenntEinfuegungHinterEinerTextstelle() { + var befehl = + erkenne( + "In Satz 3 wird hinter der Textstelle „Sachverständige nach Absatz 1 Nummer 2“ die" + + " Textstelle „und 3“ eingefügt.", + PARAGRAPH_5); + + assertThat(befehl).containsInstanceOf(WoerterEinfuegung.class); + var einfuegung = (WoerterEinfuegung) befehl.orElseThrow(); + assertThat(einfuegung.woerter()).isEqualTo("und 3"); + } + + /** „§§ 6 und 7 erhalten folgende Fassung“ — die Neufassung einer Paragraphenfolge. */ + @Test + void erkenntNeufassungMehrererParagraphen() { + var befehl = + erkenne( + "§§ 6 und 7 erhalten folgende Fassung: „§ 6 Verfahren (1) Beraten wird gemeinsam." + + " § 7 Aufgaben Das vorsitzende Mitglied ist verantwortlich.“", + Stelle.LEER); + + assertThat(befehl).isPresent(); + } } diff --git a/src/test/resources/sampledata/Hamburg/GAusschV-HH-alt.txt b/src/test/resources/sampledata/Hamburg/GAusschV-HH-alt.txt new file mode 100644 index 0000000..c4f0f40 --- /dev/null +++ b/src/test/resources/sampledata/Hamburg/GAusschV-HH-alt.txt @@ -0,0 +1,56 @@ +Verordnung über den Gutachterausschuss für Grundstückswerte +(GAusschV HA 2009) +Vom 12. Mai 2009 +(HmbGVBl. 2009 S. 124) +§ 1 Gutachterausschuss, Geschäftsstelle +(1) Für die Freie und Hansestadt Hamburg wird ein selbständiger und unabhängiger Gutachterausschuss für Grundstückswerte gebildet. Er führt die Bezeichnung „Gutachterausschuss für Grundstückswerte in Hamburg“. +(2) Dem Gutachterausschuss obliegen die in § 193 BauGB in der jeweils geltenden Fassung genannten Aufgaben sowie die ihm in anderen Rechtsvorschriften übertragenen Aufgaben. +(3) Der Gutachterausschuss bedient sich einer Geschäftsstelle, die seinen Weisungen unterliegt. +§ 2 Bestellung der Mitglieder +(1) Das vorsitzende Mitglied und die stellvertretenden vorsitzenden Mitglieder sowie die ehrenamtlichen Mitglieder des Gutachterausschusses werden vom Senat bestellt. +(2) Das vorsitzende Mitglied sowie die weiteren Mitglieder des Gutachterausschusses sollen in der Ermittlung von Grundstückswerten oder sonstigen Wertermittlungen sachkundig und erfahren sein und dürfen nicht hauptamtlich mit der Verwaltung der Grundstücke der Freien und Hansestadt Hamburg befasst sein. Das vorsitzende Mitglied und die stellvertretenden vorsitzenden Mitglieder müssen Bedienstete der Freien und Hansestadt Hamburg sein. Sie müssen die Befähigung zum Richteramt, zum höheren allgemeinen Verwaltungsdienst oder zum höheren technischen Verwaltungsdienst der Fachrichtung Vermessungs- und Liegenschaftswesen besitzen. Für die Mitwirkung an der Ermittlung der Bodenrichtwerte sind mindestens zwei Bedienstete des für Verkehrssteuern und Grundbesitz zuständigen Finanzamts mit Erfahrung in der steuerlichen Bewertung von Grundstücken als ehrenamtliche Mitglieder zu bestellen. +(3) Die anderen ehrenamtlichen Mitglieder dürfen nicht im Dienst der Freien und Hansestadt Hamburg stehen. Vor ihrer Bestellung sollen Verbände und Organisationen, für die die Entwicklung der Grundstückspreise oder die Ermittlung von Grundstückswerten besondere Bedeutung hat, Gelegenheit erhalten, geeignete Personen vorzuschlagen. +(4) Als Gutachter darf nicht bestellt werden, wer nach § 21 Absatz 1 der Verwaltungsgerichtsordnung vom Amt des ehrenamtlichen Richters ausgeschlossen ist. Von einer Bestellung soll unter den Voraussetzungen des § 21 Absatz 2 der Verwaltungsgerichtsordnung abgesehen werden. +(5) Für die ehrenamtlichen Mitglieder gelten die §§ 83 und 84 des Hamburgischen Verwaltungsverfahrensgesetzes entsprechend. Vor Beginn ihrer Tätigkeit unterzeichnen die ehrenamtlichen Mitglieder eine schriftliche Erklärung über die Einhaltung der dort genannten Pflichten. +§ 3 Abberufung von Mitgliedern +(1) Der Senat hat Mitglieder abzuberufen, wenn die Bestellungsvoraussetzungen entfallen sind. +(2) Der Senat kann ein Mitglied abberufen, wenn +1. das Mitglied gegen die Pflichten nach § 2 Absatz 5 Satz 1 verstoßen hat, +2. das Mitglied an einem Gutachten mitgewirkt hat, obwohl es von der Mitwirkung nach § 4 Absatz 3 ausgeschlossen war, oder +3. ein anderer wichtiger Grund vorliegt. +(3) Die Amtszeit eines ehrenamtlichen Mitglieds endet ohne Abberufung, wenn es sein Amt niederlegt und das vorsitzende Mitglied zugestimmt hat. Das vorsitzende Mitglied kann die Zustimmung nur verweigern, wenn das Amt zur Unzeit niedergelegt werden soll. +§ 4 Besetzung des Gutachterausschusses im Einzelfall +(1) Der Gutachterausschuss erstattet seine Gutachten in der Besetzung mit dem vorsitzenden und zwei weiteren Mitgliedern. In besonders schwierigen Fällen der Wertermittlung kann das vorsitzende Mitglied zusätzliche Mitglieder hinzuziehen. Bei der Ermittlung von Bodenrichtwerten wird der Gutachterausschuss in der Besetzung mit dem vorsitzenden Mitglied und vier weiteren Mitgliedern tätig. Das vorsitzende Mitglied kann weitere Mitglieder hinzuziehen. Bei der Ermittlung der Bodenrichtwerte muss eines der weiteren Mitglieder zu den Mitgliedern im Sinne von § 2 Absatz 2 Satz 4 gehören. +(2) Das vorsitzende Mitglied bestimmt die Mitglieder, die im Einzelfall tätig werden. Hierbei ist die besondere Sachkunde der Mitglieder zu berücksichtigen. Alle Mitglieder sollen möglichst in gleichmäßigem Umfang und in regelmäßiger Folge herangezogen werden. +(3) Mitglieder des Gutachterausschusses sind von der Mitwirkung unter den Voraussetzungen des § 20 Absätze 1 und 5 des Hamburgischen Verwaltungsverfahrensgesetzes ausgeschlossen. Mitglieder haben das vorsitzende Mitglied rechtzeitig auf das Vorliegen eines Ausschließungsgrundes hinzuweisen. +§ 5 Gemeinsame Sitzung +Auf Antrag eines Drittels der Mitglieder sind alle Mitglieder zu gemeinsamen Sitzungen zusammenzurufen. +§ 6 Verfahren +(1) Gutachten und Bodenrichtwerte werden von den mitwirkenden Mitgliedern in gemeinsamer Sitzung beraten. Die Sitzungen sind nicht öffentlich. Das vorsitzende Mitglied kann mit Zustimmung der mitwirkenden Mitglieder Personen, die nicht an der Wertermittlung mitwirken, die Anwesenheit gestatten. +(2) Gutachten, Bodenrichtwerte und sonstige Beratungsergebnisse werden mit der Mehrheit der Stimmen der mitwirkenden Mitglieder beschlossen. Bei Stimmengleichheit gibt die Stimme des vorsitzenden Mitglieds den Ausschlag. +(3) Die Gutachten sind schriftlich zu erstatten und zu begründen. Sie werden von den beteiligten Mitgliedern auf dem Sitzungsexemplar unterschrieben. Die Reinschrift wird vom vorsitzenden Mitglied unterzeichnet und mit einem Zeichnungsvermerk der weiteren Mitglieder versehen. Die Antragstellerin bzw. der Antragsteller erhält Ausfertigungen in der beantragten Anzahl. +§ 7 Aufgaben des vorsitzenden Mitglieds +Das vorsitzende Mitglied vertritt den Gutachterausschuss nach außen. Es nimmt die Befugnisse nach § 197 BauGB wahr und erteilt die Weisungen nach § 1 Absatz 3. Es lädt zu allen Sitzungen ein und leitet sie. Es ermittelt die sonstigen für die Wertermittlung erforderlichen Daten. +§ 8 Einrichtung und Führung der Kaufpreissammlung +(1) Für die Einrichtung und Führung der Kaufpreissammlung werden alle dem Gutachterausschuss auf der Grundlage des § 195 Absatz 1 BauGB übersandten Urkunden ausgewertet. Im erforderlichen Umfang werden darüber hinaus weitere wertbeeinflussende Merkmale im Rahmen der Befugnisse nach § 197 BauGB erhoben. Die Auswertung der Urkunden nach Satz 1 ist unverzüglich vorzunehmen. +(2) Soweit möglich und für die Wertermittlung erforderlich werden auf der Grundlage der nach Absatz 1 übersandten Urkunden insbesondere die nachfolgend aufgeführten Daten in der Kaufpreissammlung gespeichert: +1. Angaben zur Identifikation des Grundstücks (insbesondere Lagebezeichnung, Flurstücksnummer, Angaben zur Georeferenzierung), +2. Angaben zu den Urkunden gemäß Absatz 1 Satz 1 (insbesondere Vertragsparteien, Art und Zeitpunkt des Übereignungsvorgangs, gegebenenfalls vereinbartes Entgelt, Zahlungsbedingungen), +3. Angaben zu ungewöhnlichen oder persönlichen Verhältnissen und Zustandsmerkmale nach §§ 3 bis 5 der Wertermittlungsverordnung vom 6. Dezember 1988 (BGBl. I S. 2209), geändert am 18. August 1997 (BGBl. I S. 2081, 2110), in der jeweils geltenden Fassung. +§ 9 Auskünfte aus der Kaufpreissammlung +(1) Auskünfte aus der Kaufpreissammlung erhalten: +1. Behörden und Gerichte zur Erfüllung ihrer Aufgaben, +2. öffentlich bestellte und vereidigte Sachverständige für die Bewertung von Grundstücken zur Erfüllung ihrer Aufgaben und +3. weitere Personen und Stellen, wenn sie ein berechtigtes Interesse an den Auskünften darlegen. +(2) Namen und Anschriften von Personen dürfen nicht mitgeteilt werden. +(3) Auskünfte dürfen nur zu dem Zweck verwendet werden, zu dem sie erteilt wurden. Behörden und Gerichte nach Absatz 1 Nummer 1 dürfen Auskünfte im Rahmen ihrer Aufgabenwahrnehmung auch an Dritte weitergeben. Sachverständige nach Absatz 1 Nummer 2 dürfen Auskünfte im Rahmen ihrer Aufgabenwahrnehmung nur insoweit an Dritte weitergeben, als sie durch eine der in Absatz 1 Nummer 1 genannten Stellen dazu verpflichtet werden. Im Übrigen dürfen Auskünfte Dritten nur in einer Form zugänglich gemacht werden, die keine unmittelbare Zuordnung zum betroffenen Grundstück erlaubt. +(4) § 195 Absatz 2 BauGB bleibt unberührt. +§ 10 Bodenrichtwerte +(1) Bodenrichtwerte sind zum Ende eines jeden geraden Kalenderjahres und nach Maßgabe des § 196 Absatz 1 Satz 7 BauGB auf einen abweichenden Zeitpunkt zu ermitteln. +(2) Bodenrichtwerte sind in geeigneter Form zu veröffentlichen. Auf die Veröffentlichung ist im Anschluss an jede Neuermittlung im Amtlichen Anzeiger hinzuweisen. +§ 11 Grundstücksmarktbericht +Für jedes Kalenderjahr sind die sonstigen zur Wertermittlung erforderlichen Daten sowie wesentliche Preis- und Umsatzdaten des Grundstücksmarktes in Form eines Grundstücksmarktberichts zu veröffentlichen. Auf die Veröffentlichung ist jeweils im Amtlichen Anzeiger hinzuweisen. +§ 12 Entschädigung der ehrenamtlichen Mitglieder +Die ehrenamtlichen Mitglieder erhalten eine Aufwandsentschädigung gemäß § 2 Absätze 1 und 4 des Entschädigungsleistungsgesetzes vom 1. Juli 1963 (HmbGVBl. S. 111), zuletzt geändert am 22. Juli 2008 (HmbGVBl. S. 278), in der jeweils zum Zeitpunkt der Sitzung geltenden Fassung. +§ 13 Inkrafttreten, Außerkrafttreten +Diese Verordnung tritt am 1. Juli 2009 in Kraft. Zum selben Zeitpunkt tritt die Verordnung über den Gutachterausschuss für Grundstückswerte vom 20. Februar 1990 (HmbGVBl. S. 37) in der geltenden Fassung außer Kraft. diff --git a/src/test/resources/sampledata/Hamburg/GAusschV-HH-neu.txt b/src/test/resources/sampledata/Hamburg/GAusschV-HH-neu.txt new file mode 100644 index 0000000..25819f9 --- /dev/null +++ b/src/test/resources/sampledata/Hamburg/GAusschV-HH-neu.txt @@ -0,0 +1,86 @@ +Verordnung über den Gutachterausschuss für Grundstückswerte +(GAusschV HA 2009) +Vom 12. Mai 2009 +(HmbGVBl. 2009 S. 124) +§ 1 Gutachterausschuss, Geschäftsstelle +(1) Für die Freie und Hansestadt Hamburg wird ein selbständiger und unabhängiger Gutachterausschuss für Grundstückswerte gebildet. Er führt die Bezeichnung „Gutachterausschuss für Grundstückswerte in Hamburg“. +(2) Dem Gutachterausschuss obliegen die in § 193 BauGB in der jeweils geltenden Fassung genannten Aufgaben sowie die ihm in anderen Rechtsvorschriften übertragenen Aufgaben. +(3) Der Gutachterausschuss bedient sich einer Geschäftsstelle, die seinen Weisungen unterliegt. +§ 2 Bestellung der Mitglieder +(1) Das vorsitzende Mitglied und die stellvertretenden vorsitzenden Mitglieder sowie die ehrenamtlichen Mitglieder des Gutachterausschusses werden vom Senat bestellt. +(2) Das vorsitzende Mitglied sowie die weiteren Mitglieder des Gutachterausschusses sollen in der Ermittlung von Grundstückswerten oder sonstigen Wertermittlungen sachkundig und erfahren sein und dürfen nicht hauptamtlich mit der Verwaltung der Grundstücke der Freien und Hansestadt Hamburg befasst sein. +(3) Das vorsitzende Mitglied muss Beamtin bzw. Beamter der Freien und Hansestadt Hamburg mit der Befähigung für die Laufbahngruppe 2, zweites Einstiegsamt der Laufbahn der Fachrichtung Allgemeine Dienste oder der Laufbahn der Fachrichtung Technische Dienste, Laufbahnzweig Geodäsie und Geoinformation oder Angestellte bzw. Angestellter der Freien und Hansestadt Hamburg mit gleichwertiger Qualifikation sein. Zu Hauptvertretenden des vorsitzenden Mitglieds werden Beamtinnen oder Beamte bzw. Angestellte der Freien und Hansestadt Hamburg bestellt, die die Befähigungen bzw. Qualifikationen nach Satz 1 besitzen. Ihnen obliegen die Aufgaben nach §§ 4 und 7, sofern das vorsitzende Mitglied verhindert ist. +(4) Beamtinnen bzw. Beamte der Freien und Hansestadt Hamburg mit der Befähigung für die Laufbahngruppe 2, erstes Einstiegsamt, der Laufbahn der Fachrichtung Allgemeine Dienste oder der Laufbahn der Fachrichtung Technische Dienste, Laufbahnzweig Geodäsie und Geoinformation, oder Angestellte der Freien und Hansestadt Hamburg mit gleichwertiger Qualifikation, können zu Nebenvertretenden bestellt werden. Zu Nebenvertretenden können auch Personen bestellt werden, die nicht Beamtinnen und Beamte oder Angestellte der Freien und Hansestadt Hamburg sind, sofern sie über einen Bachelorabschluss oder mindestens einen gleichwertigen Abschluss verfügen. Nebenvertretende sind stellvertretende vorsitzende Mitglieder, denen die Aufgaben nach § 7a obliegen, sofern das vorsitzende Mitglied und die Hauptvertretenden verhindert sind. +(5) Für die Mitwirkung an der Ermittlung der Bodenrichtwerte und der sonstigen für die Wertermittlung erforderlichen Daten im Sinne von § 193 Absatz 5 BauGB sind mindestens zwei Bedienstete, die Beamtinnen und Beamte oder Angestellte des für Verkehrssteuern und Grundbesitz zuständigen Finanzamts der Freien und Hansestadt Hamburg mit Erfahrung in der steuerlichen Bewertung von Grundstücken sind, als ehrenamtliche Mitglieder zu bestellen. +(6) Die anderen ehrenamtlichen Mitglieder dürfen nicht im Dienst der Freien und Hansestadt Hamburg stehen. Vor ihrer Bestellung sollen Verbände und Organisationen, für die die Entwicklung der Grundstückspreise oder die Ermittlung von Grundstückswerten besondere Bedeutung hat, Gelegenheit erhalten, geeignete Personen vorzuschlagen. +(7) Als Gutachterin bzw. Gutachter darf nicht bestellt werden, wer nach § 21 Absatz 1 der Verwaltungsgerichtsordnung vom Amt des ehrenamtlichen Richters ausgeschlossen ist. Von einer Bestellung soll unter den Voraussetzungen des § 21 Absatz 2 der Verwaltungsgerichtsordnung abgesehen werden. +(8) Für die ehrenamtlichen Mitglieder gelten die §§ 83 und 84 des Hamburgischen Verwaltungsverfahrensgesetzes vom 9. November 1977 (HmbGVBl. S. 333, 402), zuletzt geändert am 5. Mai 2025 (HmbGVBl. S. 338), in der jeweils geltenden Fassung entsprechend. Vor Beginn ihrer Tätigkeit unterzeichnen die ehrenamtlichen Mitglieder eine schriftliche Erklärung über die Einhaltung der dort genannten Pflichten. +§ 3 Abberufung von Mitgliedern +(1) Der Senat hat Mitglieder abzuberufen, wenn die Bestellungsvoraussetzungen entfallen sind. +(2) Der Senat kann ein Mitglied abberufen, wenn +1. das Mitglied gegen die Pflichten nach § 2 Absatz 8 Satz 1 verstoßen hat, +2. das Mitglied an einem Gutachten mitgewirkt hat, obwohl es von der Mitwirkung nach § 4 Absatz 3 ausgeschlossen war, oder +3. ein anderer wichtiger Grund vorliegt. +(3) Die Amtszeit eines ehrenamtlichen Mitglieds endet ohne Abberufung, wenn es sein Amt niederlegt und das vorsitzende Mitglied zugestimmt hat. Das vorsitzende Mitglied kann die Zustimmung nur verweigern, wenn das Amt zur Unzeit niedergelegt werden soll. +§ 4 Besetzung des Gutachterausschusses im Einzelfall +(1) Der Gutachterausschuss erstattet seine Gutachten in der Besetzung mit dem vorsitzenden und zwei weiteren Mitgliedern. In besonders schwierigen Fällen der Wertermittlung kann das vorsitzende Mitglied zusätzliche Mitglieder hinzuziehen. Bei der Ermittlung von Bodenrichtwerten wird der Gutachterausschuss in der Besetzung mit dem vorsitzenden Mitglied und vier weiteren Mitgliedern tätig. Das vorsitzende Mitglied kann weitere Mitglieder hinzuziehen. Bei der Ermittlung der Bodenrichtwerte muss eines der weiteren Mitglieder zu den Mitgliedern im Sinne von § 2 Absatz 5 gehören. +(2) Das vorsitzende Mitglied bestimmt die Mitglieder, die im Einzelfall tätig werden. Hierbei ist die besondere Sachkunde der Mitglieder zu berücksichtigen. Alle Mitglieder sollen möglichst in gleichmäßigem Umfang und in regelmäßiger Folge herangezogen werden. +(3) Mitglieder des Gutachterausschusses sind von der Mitwirkung unter den Voraussetzungen des § 20 Absätze 1 und 5 des Hamburgischen Verwaltungsverfahrensgesetzes ausgeschlossen. Mitglieder haben das vorsitzende Mitglied rechtzeitig auf das Vorliegen eines Ausschließungsgrundes hinzuweisen. +§ 5 Gemeinsame Sitzung +Auf Antrag eines Drittels der Mitglieder sind alle Mitglieder zu gemeinsamen Sitzungen zusammenzurufen. +§ 6 Verfahren +(1) Gutachten und Bodenrichtwerte und die sonstigen für die Wertermittlung erforderlichen Daten im Sinne von § 193 Absatz 5 BauGB werden von den mitwirkenden Mitgliedern in gemeinsamer Sitzung beraten. Die Sitzungen sind nicht öffentlich. Das vorsitzende Mitglied kann mit Zustimmung der mitwirkenden Mitglieder Personen, die nicht an der Wertermittlung mitwirken, die Anwesenheit gestatten. +(2) Gutachten, Bodenrichtwerte sowie die sonstigen für die Wertermittlung erforderlichen Daten im Sinne von § 193 Absatz 5 BauGB und die sonstigen Beratungsergebnisse werden mit Stimmenmehrheit durch die mitwirkenden Mitglieder beschlossen. Enthaltungen sind nicht zulässig. Bei Stimmengleichheit gibt die Stimme des vorsitzenden Mitglieds den Ausschlag. +(3) Das vorsitzende Mitglied legt fest, in welcher Form Sitzungen stattfinden: +1. in Präsenz, +2. in Präsenz und gleichzeitig online (hybride Sitzung) oder +3. ausschließlich online (virtuelle Sitzung). +In den Fällen von Satz 1 Nummern 2 und 3 ist sicherzustellen, dass die jeweils Mitwirkenden in die elektronische Kommunikation eingebunden sind. +(4) In den Gutachten sind die Namen der mitwirkenden Mitglieder des Gutachterausschusses anzugeben. Die Gutachten sind schriftlich zu erstatten und zu begründen sowie vom vorsitzenden Mitglied zu unterzeichnen. Die mitwirkenden Mitglieder stimmen dem Gutachten im Nachgang in Textform zu, soweit sie das Gutachten nicht bereits unterzeichnet haben. Die Form nach Satz 2 kann durch jede in § 3a des Hamburgischen Verwaltungsverfahrensgesetzes zugelassene elektronische Form ersetzt werden. Die Antragstellerin bzw. der Antragsteller erhält Ausfertigungen des Gutachtens in der beantragten Anzahl. +§ 7 Aufgaben des vorsitzenden Mitglieds +Das vorsitzende Mitglied des Gutachterausschusses bzw. dessen Hauptvertreterin bzw. Hauptvertreter ist für den laufenden Geschäftsbetrieb verantwortlich. Hierzu gehören insbesondere die +1. Vertretung des Gutachterausschusses nach außen, +2. Entscheidung über die Besetzung des Gutachterausschusses nach § 4, unbeschadet des § 7a, +3. Ladung der Mitglieder zu allen Sitzungen und Leitung der Sitzungen, +4. Wahrnehmung der Befugnisse nach § 197 BauGB, +5. Erteilung fachlicher Weisungen an die Geschäftsstelle nach § 1 Absatz 3, +6. Erläuterung der Gutachten vor Behörden und Gerichten. +§ 7a Aufgaben der Nebenvertretenden, Sitzungsentgelt +(1) Die Nebenvertretenden vertreten das vorsitzende Mitglied ausschließlich bei der Gutachtenerstattung. Hierzu gehört die +1. Erstattung von Gutachten nach § 4 Absatz 1 Satz 1, +2. Hinzuziehung zusätzlicher Mitglieder nach § 4 Absatz 1 Sätze 2 und 4, +3. Bestimmung der Mitglieder, die im Einzelfall nach § 4 Absatz 2 tätig werden, +4. Ladung und das Leiten von Ortsterminen und Sitzungen nach § 4, +5. Wahrnehmung der Befugnisse nach § 197 BauGB mit Ausnahme der Befugnisse zur Führung der Kaufpreissammlung und +5. Erläuterung der Gutachten vor Behörden und Gerichten. +(2) Die Nebenvertretenden des Gutachterausschusses nach § 2 Absatz 4 Satz 2 erhalten für Tätigkeiten nach Absatz 1 statt der Aufwandsentschädigung nach § 12 +1. für ihre Tätigkeiten nach Absatz 1 Satz 2 ein Honorar in Höhe des in § 9 Absatz 1 in Verbindung mit Anlage 1 Teil 1 Nummer 7 des Justizvergütungs- und -entschädigungsgesetzes (JVEG) vom 5. Mai 2004 (BGBl. I S. 718, 776), zuletzt geändert am 8. Dezember 2025 (BGBl. I Nr. 318 S. 1, 6), der jeweils geltenden Fassung vorgesehenen Stundensatzes; § 8 Absatz 2 JVEG gilt entsprechend, +2. für notwendige Reisen ein Fahrtkostenersatz nach § 5 JVEG. +§ 8 Einrichtung und Führung der Kaufpreissammlung +(1) Für die Einrichtung und Führung der Kaufpreissammlung werden alle dem Gutachterausschuss auf der Grundlage des § 195 Absatz 1 BauGB übersandten Urkunden ausgewertet. Im erforderlichen Umfang werden darüber hinaus weitere wertbeeinflussende Merkmale im Rahmen der Befugnisse nach § 197 BauGB erhoben. Die Auswertung der Urkunden nach Satz 1 ist unverzüglich vorzunehmen. +(2) Soweit möglich und für die Wertermittlung erforderlich werden auf der Grundlage der nach Absatz 1 übersandten Urkunden insbesondere die nachfolgend aufgeführten Daten in der Kaufpreissammlung gespeichert: +1. Angaben zur Identifikation des Grundstücks (insbesondere Lagebezeichnung, Flurstücksnummer, Angaben zur Georeferenzierung), +2. Angaben zu den Urkunden gemäß Absatz 1 Satz 1 (insbesondere Vertragsparteien, Art und Zeitpunkt des Übereignungsvorgangs, gegebenenfalls vereinbartes Entgelt, Zahlungsbedingungen), +3. Angaben zu Grundstücksmerkmalen nach § 2 der Immobilienwertermittlungsverordnung (ImmoWertV) vom 14. Juli 2021 (BGBl. I S. 2805) in der jeweils geltenden Fassung und ungewöhnlichen oder persönlichen Verhältnissen nach § 9 Absatz 2 ImmoWertV. +(3) Personenbezogene Daten sind unverzüglich zu löschen, sobald sie für die Auswertung der übersandten Daten und die Übernahme in die Kaufpreissammlung nicht mehr benötigt werden. Es ist sicherzustellen, dass Unbefugte keinen Zugriff auf die Kaufpreissammlung erlangen. +§ 9 Auskünfte aus der Kaufpreissammlung +(1) Grundstücksbezogene Auskünfte aus der Kaufpreissammlung erhalten: +1. Behörden und Gerichte zur Erfüllung ihrer Aufgaben, +2. öffentlich bestellte und vereidigte Sachverständige für die Bewertung von Grundstücken zur Erfüllung ihrer Aufgaben, +3. Personen, die von einer staatlichen, staatlich anerkannten oder nach DIN EN ISO/IEC 17024 (Konformitätsbewertung – Allgemeine Anforderungen an Stellen, die Personen zertifizieren) akkreditierten Stelle als Sachverständige oder Gutachter für die Wertermittlung von Grundstücken bestellt oder zertifiziert worden sind, zur Erfüllung ihrer Aufgaben und +4. weitere Personen und Stellen, wenn sie ein berechtigtes Interesse an den Auskünften darlegen. +Die DIN EN ISO/IEC 17024 ist zur kostenfreien Einsicht für jedermann im Staatsarchiv niedergelegt. +(2) Namen und Anschriften von Personen dürfen nicht mitgeteilt werden. +(3) Grundstücksbezogene Auskünfte dürfen nur zu dem Zweck verwendet werden, zu dem sie erteilt wurden. Behörden und Gerichte nach Absatz 1 Satz 1 Nummer 1 dürfen Auskünfte im Rahmen ihrer Aufgabenwahrnehmung auch an Dritte weitergeben. Sachverständige nach Absatz 1 Satz 1 Nummern 2 und 3 dürfen Auskünfte im Rahmen ihrer Aufgabenwahrnehmung nur insoweit an Dritte weitergeben, als sie durch eine der in Absatz 1 Satz 1 Nummer 1 genannten Stellen dazu verpflichtet werden. Im Übrigen dürfen Auskünfte Dritten nur in einer Form zugänglich gemacht werden, die keine unmittelbare Zuordnung zum betroffenen Grundstück erlaubt. +(4) Nicht grundstücksbezogene Auskünfte aus der Kaufpreissammlung sind auf schriftlichen Antrag bei berechtigtem Interesse zu erteilen. Der Verwendungszweck ist darzulegen. Sie sind ausschließlich in anonymisierter Form zu erteilen. Insbesondere dürfen die Auskünfte keine Rückschlüsse auf personenbezogene Daten ermöglichen oder nur mit einem unverhältnismäßigen großen Aufwand an Zeit, Kosten und Arbeitskraft bestimmten oder bestimmbaren Personen zugeordnet werden. +(5) § 195 Absatz 2 BauGB bleibt unberührt. +§ 10 Bodenrichtwerte und sonstige für die Wertermittlung erforderliche Daten +(1) Bodenrichtwerte sollen zum Beginn eines jeden Kalenderjahres und nach Maßgabe des § 196 BauGB ermittelt werden. +(2) Bodenrichtwerte sind in geeigneter Form zu veröffentlichen. Auf die Veröffentlichung ist im Anschluss an jede Neuermittlung im Amtlichen Anzeiger hinzuweisen. +(3) Die sonstigen für die Wertermittlung erforderlichen Daten nach § 193 Absatz 5 Satz 2 BauGB sind im Immobilienmarktbericht oder in anderer geeigneter Form zu veröffentlichen. Sie sind zum gleichen Stichtag wie die Bodenrichtwerte nach Absatz 1 zu ermitteln. Auf die Veröffentlichung ist jeweils im Amtlichen Anzeiger hinzuweisen. +§ 11 Immobilienmarktbericht +Für jedes Kalenderjahr sind die wesentlichen Preis- und Umsatzdaten einschließlich der Preis- und Umsatzentwicklung der wesentlichen Teilmärkte des Immobilienmarktes in Form eines Immobilienmarktberichts oder in anderer geeigneter Form im Internet zu veröffentlichen. Über Art und Umfang der Berichterstattung entscheidet der Gutachterausschuss. +§ 12 Entschädigung der ehrenamtlichen Mitglieder +Die ehrenamtlichen Mitglieder erhalten eine Aufwandsentschädigung gemäß § 2 Absätze 1 und 4 des Entschädigungsleistungsgesetzes vom 1. Juli 1963 (HmbGVBl. S. 111), zuletzt geändert am 22. Juli 2008 (HmbGVBl. S. 278), in der jeweils zum Zeitpunkt der Sitzung geltenden Fassung. +§ 13 Inkrafttreten, Außerkrafttreten +Diese Verordnung tritt am 1. Juli 2009 in Kraft. Zum selben Zeitpunkt tritt die Verordnung über den Gutachterausschuss für Grundstückswerte vom 20. Februar 1990 (HmbGVBl. S. 37) in der geltenden Fassung außer Kraft. diff --git a/src/test/resources/sampledata/Hamburg/HmbGVBl-2026-17_GutachterausschussVO-AendVO.pdf b/src/test/resources/sampledata/Hamburg/HmbGVBl-2026-17_GutachterausschussVO-AendVO.pdf Binary files differnew file mode 100644 index 0000000..7d7f362 --- /dev/null +++ b/src/test/resources/sampledata/Hamburg/HmbGVBl-2026-17_GutachterausschussVO-AendVO.pdf diff --git a/src/test/resources/sampledata/Hamburg/SOURCES b/src/test/resources/sampledata/Hamburg/SOURCES new file mode 100644 index 0000000..52095d2 --- /dev/null +++ b/src/test/resources/sampledata/Hamburg/SOURCES @@ -0,0 +1,74 @@ +Beispieldaten Hamburg — Herkunft und Aufbereitung +================================================= + +1. Änderungsdokument + HmbGVBl-2026-17_GutachterausschussVO-AendVO.pdf + Hamburgisches Gesetz- und Verordnungsblatt Teil I, Nr. 17 vom 26. Mai 2026, + S. 147: Verordnung zur Änderung der Verordnung über den Gutachterausschuss + für Grundstückswerte vom 12. Mai 2026. + Bezogen über https://www.luewu.de/wp-content/uploads/2026/05/GVBL_HH_2026-17.pdf + (abgerufen am 26.08.2026). Das Blatt gibt der Verlag Lütcke & Wulff heraus; + die Jahrgänge liegen unter https://www.luewu.de/gesetz-und-verordnungsblatt/ + frei und ohne Anmeldung. Das Heft trägt drei Verkündungen; maßgeblich ist die + zweite. + +2. Stammfassung (vor der Änderung) + GAusschV-HH-alt.txt + Verordnung über den Gutachterausschuss für Grundstückswerte vom 12. Mai 2009 + (HmbGVBl. S. 124) in ihrer Ursprungsfassung. + Die Änderungshistorie des Landesrechtsportals + (https://www.landesrecht-hamburg.de/bsha/document/jlr-NNLHH0000638E, Reiter + „Änderungshistorie“) weist für diese Verordnung genau eine Änderung aus — eben + die des Heftes Nr. 17/2026. Die Fassung vor der Änderung ist deshalb die + Ursprungsfassung, und die steht im Gesetzblatt selbst: + https://www.luewu.de/wp-content/uploads/2025/08/GVBL_HH_2009-20.pdf + (HmbGVBl. Nr. 20 vom 15. Mai 2009, S. 124). + Aufbereitet mit `--extract-only` des Erzeugnisses (der zweispaltige Satz des + Blattes verlangt dessen Lesereihenfolge; `pdftotext -layout` verschränkt die + Spalten) und von Hand in das kanonische Klartextformat gebracht: Titelzeile, + Klammerzusatz mit der juris-Abkürzung, Normköpfe „§ N␣␣Titel“. Entfernt wurden + die Eingangsformel, der Kolumnentitel und die Schlussformel. + +3. Nachfassung (nach der Änderung), nur als Prüfmaßstab + GAusschV-HH-neu.txt + Dieselbe Verordnung in der ab dem 1. Juli 2026 geltenden Fassung, entnommen der + Gesamtausgabe des Landesrechtsportals (Stand: 26.08.2026) und in dasselbe + Klartextformat gebracht. + + Zum Portal: landesrecht-hamburg.de ist — wie die Portale Hessens, Berlins, + Schleswig-Holsteins und Baden-Württembergs — eine juris-Einseitenanwendung. + Eine Skriptabfrage erhält nur die rund 5,6 kB große Anwendungshülle; der Text + kommt allein über eine Browsersteuerung. Anders als jene Portale führt Hamburg + keine „Gesamtausgaben-Liste“ mit datierbaren Adressen, sondern nur die aktuelle + Gesamtausgabe nebst einer Änderungshistorie. Für dieses Beispiel genügt das, + weil es nur eine einzige Änderung gibt; wo eine Vorschrift mehrfach geändert + worden ist, führt der Weg über die Hefte des Gesetzblattes. + +4. Was der Fall trägt + - Die Änderungsverordnung gliedert sich in *Paragraphen* (§ 1 Änderung, § 2 + Inkrafttreten), während die erste Verkündung desselben Heftes sich in Artikel + gliedert. Ein Sammelheft führt beide Gliederungen nebeneinander. + - Ihre Änderungsbefehle sind *dezimal* gegliedert (6.1, 7.1.1, 7.2.3), wo die + übrigen Blätter a)/aa) setzen. + - Idiome: „erhält folgende Fassung“, „die Textstelle „…“", „Hinter § 7 wird + folgender § 7a eingefügt“, „Es wird folgender Absatz 3 angefügt“, „§§ 6 und 7 + erhalten folgende Fassung“. + - Der Satz des Blattes setzt zwischen Paragraphenzeichen und Nummer ein + *schmales Leerzeichen* (U+2009). + - Der Kolumnentitel steht im Inhaltsstrom und fällt hinter das Zitat eines + Befehls; das Schlusswort der Eingangsformel („verordnet:“) fällt in der + rechten Spalte mitten in einen Absatz. + +5. Benannte Abweichungen von der amtlichen Nachfassung + § 3 und § 4: Die Nummern 2 und 3 des Heftes schreiben „wir die Textstelle … + ersetzt“ statt „wird“. Das ist ein Verkündungsfehler; beide Befehle bleiben + unerkannt. Geraten wird nicht — ein Werkzeug, das „wir“ als „wird“ liest, + läse morgen auch anderes, was dort nicht steht. + § 6: Das Gesetzblatt setzt in der neuen Fassung „1. in Präsenz“ ohne Komma, + das Portal „1. in Präsenz,“. Das Erzeugnis wendet den Wortlaut an. + § 9: Nummer 7.1.3 fasst die Nummer 3 neu, Nummer 7.1.4 macht die bisherige + Nummer 3 zur Nummer 4. Beide meinen die ursprüngliche Zählung. Vorgezogen + wird die Umnummerierung — sonst verlöre die bisherige Nummer 3 ihren + Wortlaut —, und die Neufassung findet ihre Bezeichnung alsdann geräumt und + bleibt liegen. Die Neufassung einer soeben geräumten Bezeichnung als + Einfügung zu vollziehen, leistet das Erzeugnis nicht. diff --git a/src/test/resources/sampledata/Landesrecht-Beispiele.adoc b/src/test/resources/sampledata/Landesrecht-Beispiele.adoc index 0bd4b9d..c7cae90 100644 --- a/src/test/resources/sampledata/Landesrecht-Beispiele.adoc +++ b/src/test/resources/sampledata/Landesrecht-Beispiele.adoc @@ -104,6 +104,22 @@ Kopfelement *vor* der Überschrift, die Aufzählungen sind echte `<ol>`-Listen, Stilblatt erzeugt, und an ehemaligen Trennstellen führt das Portal vereinzelt *Steuerzeichen* (U+0002) mit. Voller Akzeptanzfall (4/4 Befehle, alle 24 Normen). +|Hamburg +|`Hamburg/HmbGVBl-2026-17_GutachterausschussVO-AendVO.pdf` — HmbGVBl. Teil I Nr. 17 vom +26.05.2026, S. 147 (Verordnung zur Änderung der Verordnung über den Gutachterausschuss für +Grundstückswerte vom 12.05.2026) +|Sammelheft mit drei Verkündungen, deren erste sich in *Artikel*, deren zweite sich in +*Paragraphen* gliedert — beide Gliederungen nebeneinander in einem Heft. Die Änderungsbefehle +sind *dezimal* gegliedert („6.1“, „7.1.1“, „7.2.3“). Der Satz setzt zwischen § und Nummer ein +*schmales Leerzeichen* (U+2009), führt den Kolumnentitel im Inhaltsstrom und lässt das +Schlusswort der Eingangsformel („verordnet:“) in die rechte Spalte fallen. Idiome: „die +Textstelle „…““, „Hinter § 7 wird folgender § 7a eingefügt“, „Es wird folgender Absatz 3 +angefügt“, „§§ 6 und 7 erhalten folgende Fassung“. Voller Belegfall, Stammfassung +`Hamburg/GAusschV-HH-alt.txt` (Ursprungsfassung aus HmbGVBl. Nr. 20/2009, S. 124), Prüfmaßstab +`GAusschV-HH-neu.txt`. Das Blatt selbst liegt bei `luewu.de` frei; das Portal ist eine +juris-Einseitenanwendung *ohne* Fassungsliste — es führt nur die aktuelle Gesamtausgabe und +eine Änderungshistorie. + |Rheinland-Pfalz |`RheinlandPfalz/GVBl-2026-21_UKAPOGS-E3-AendVO.pdf` — GVBl. RP 2026 Nr. 21 vom 10.08.2026 (Erste Landesverordnung zur Änderung der Ausbildungs- und Prüfungsordnung … Unfallkasse) @@ -174,6 +190,19 @@ bleiben nur Abweichungen, in denen die amtliche Nachfassung selbst vom Befehlswo überzähliges Anführungszeichen aus dem Änderungsgesetz; § 93 LMG NRW: zusätzlich die Wörter „Film (“ gestrichen) — ÄndGgner wendet dort den Wortlaut an. Damit ist das Heft GV. NRW. 7/2026 vollständig belegt. +* *Hamburg* ist umgesetzt (`EndToEndTest.gutachterausschussVOHamburg`): 21 Befehle gelesen, + 17 angewandt; zehn von vierzehn Normen gleichen der amtlichen Nachfassung. Der Fall hat sechs + allgemeine Funde gebracht: die Dezimalgliederung der Befehle; die §-Teilung eines Heftes, das + daneben Artikel führt; das schmale Leerzeichen; den Kolumnentitel im Inhaltsstrom; die + Grundlinien-Ordnung innerhalb einer Spalte; und drei Anwendungsfunde (ein Satzzeichen, das an + die Stelle eines Wortes tritt, nimmt dessen Zwischenraum mit; ein an eine Aufzählung angefügter + Satz steht auf eigener Zeile in der Einrückung des Blockes; die Neufassung einer Bezeichnung, + die eine aufsteigende Umnummerierung räumt, tritt hinter diese zurück, damit der bisherige + Wortlaut nicht verlorengeht). Von den vier Abweichungen liegen drei an der Vorlage: Zwei Befehle + schreiben „wir“ statt „wird“ (ein Verkündungsfehler — geraten wird nicht), und in § 6 setzt das + Gesetzblatt ein Komma, das das Portal führt, nicht. Die vierte ist eine benannte Grenze: die + Neufassung einer soeben geräumten Bezeichnung als Einfügung zu vollziehen. + * *Berlin* ist umgesetzt (`EndToEndTest.asogLafAendGBerlin`). Artikel 2 (LAF-Errichtungsgesetz) ist ein *voller* Akzeptanzfall: beide Befehle angewandt, alle fünf Normen gleich der amtlichen Nachfassung vom 12. Juni 2026. Artikel 1 (ASOG) wird vollständig @@ -280,7 +309,7 @@ Klartext-Zweig des Loaders normalisiert zur Sicherheit mit. Baden-Württemberg jede Fassung datierbar ausgeben, fehlt dort. Ohne Vorfassung ist kein Akzeptanzfall möglich, sondern nur die Prüfung bis zur Befehlserkennung. Ein Ausweg wäre, die Vorfassung aus den *Einzelnormen* mit ihren Stichtagen zusammenzusetzen. -* Bremen, Hamburg, Mecklenburg-Vorpommern, Saarland, Sachsen-Anhalt: noch nicht erfasst. +* Bremen, Mecklenburg-Vorpommern, Saarland, Sachsen-Anhalt: noch nicht erfasst. == Wo weitermachen @@ -291,9 +320,12 @@ gegen die amtliche Fassung, und `--neufassung` gibt die errechnete Fassung als k Klartext aus. Ein weiteres Land kostet damit Stammfassung, Heft und Nachfassung — keinen eigenen Prüfcode mehr. Nach absteigendem Nutzen: -. *Weitere Länder erfassen* — Bremen, Hamburg, Mecklenburg-Vorpommern, Saarland, Sachsen-Anhalt. - Für Hamburg (`landesrecht-hamburg.de`) und Mecklenburg-Vorpommern (`landesrecht-mv.de`) ist - zuerst zu prüfen, ob die Portale einer Skriptabfrage antworten; sonst gilt der Browserweg. +. *Weitere Länder erfassen* — Bremen, Mecklenburg-Vorpommern, Saarland, Sachsen-Anhalt. + Für Mecklenburg-Vorpommern ist die Skriptprobe gemacht: `landesrecht-mv.de` antwortet wie + Hamburg nur mit der 5,6 kB großen Anwendungshülle. Das *Gesetzblatt* dagegen liegt frei — + `regierung-mv.de/static/Regierungsportal/Justizministerium/…/GVOBl.%20Nr.%20N%20v.%20T.M.JJJJ.pdf`, + und ein Änderungsgesetz ist damit ohne Browsersteuerung zu beschaffen; nur die Stammfassung + verlangt sie. Für Bremen ist `transparenz.bremen.de` zu prüfen (offenes HTML zu erwarten). *Erledigt in Welle 23:* Die Frage nach den _Anlagen-Nummern im gii-XML_ war falsch gestellt. Eine Zerlegung braucht es nicht — der `StellenAufloeser` probiert längst beide Wege. Der Fehler saß im |
