blob: 1c63a5fac9adbafdeb0e3e5a0902b03c68077e95 [file] [log] [blame]
package eu.mulk.aendggner.aenderung;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Eine Fundstelle im Stammgesetz, als Stapel immer feinerer Komponenten (z.B. „§ 5 Absatz 2 Satz
* 1“). Verschachtelte Änderungsbefehle konkatenieren ihre Stellen mit {@link #plus}.
*/
public record Stelle(List<Komponente> komponenten) {
public static final Stelle LEER = new Stelle(List.of());
public Stelle {
komponenten = List.copyOf(komponenten);
}
public sealed interface Komponente
permits Paragraph,
AbsatzNr,
SatzNr,
HalbsatzNr,
NummerNr,
BuchstabeNr,
Ueberschrift,
Inhaltsuebersicht,
Gliederungseinheit,
Absatzbezeichnung {}
/**
* „§ 5“, „§ 28a“, im bayerischen Landesrecht „Art. 5“ — die Normnummer ohne Sigel, dazu das
* Sigel selbst („§“ oder „Art.“).
*/
public record Paragraph(String nummer, String sigel) implements Komponente {
public Paragraph(String nummer) {
this(nummer, "§");
}
/** Die Einzelnormbezeichnung, wie sie im {@code Gesetz}-Modell steht: „§ 5“, „Art. 5“. */
public String enbez() {
return sigel + " " + nummer;
}
}
/** Eine Gliederungseinheit oberhalb des Paragraphen: „Teil 2“, „Abschnitt 3“, „Anlage 8“. */
public record Gliederungseinheit(String art, String nummer) implements Komponente {
public String bezeichnung() {
return nummer.isEmpty() ? art : art + " " + nummer;
}
}
/** „Die Absatzbezeichnung „(2)““ — die reine Absatznummer als Ziel einer Streichung. */
public record Absatzbezeichnung(String nummer) implements Komponente {}
/** „Absatz 2“ */
public record AbsatzNr(String nummer) implements Komponente {}
/** „Satz 1“ */
public record SatzNr(String nummer) implements Komponente {}
/** „Halbsatz 1“ — die Hälfte eines am Semikolon geteilten Satzes (bayerische Zitierweise). */
public record HalbsatzNr(String nummer) implements Komponente {}
/** „Nummer 4“ */
public record NummerNr(String nummer) implements Komponente {}
/** „Buchstabe c“ (auch Doppelbuchstabe „aa“) */
public record BuchstabeNr(String kennung) implements Komponente {}
/** „Die Überschrift“ */
public record Ueberschrift() implements Komponente {}
/** „In der Inhaltsübersicht“ */
public record Inhaltsuebersicht() implements Komponente {}
public boolean istLeer() {
return komponenten.isEmpty();
}
public Stelle plus(Stelle feiner) {
if (feiner.istLeer()) {
return this;
}
var neu = new ArrayList<>(komponenten);
neu.addAll(feiner.komponenten);
return new Stelle(neu);
}
public Optional<Paragraph> paragraph() {
return komponenten.stream()
.filter(Paragraph.class::isInstance)
.map(Paragraph.class::cast)
.findFirst();
}
public Optional<AbsatzNr> absatz() {
return komponenten.stream()
.filter(AbsatzNr.class::isInstance)
.map(AbsatzNr.class::cast)
.findFirst();
}
public boolean betrifftInhaltsuebersicht() {
return komponenten.stream().anyMatch(Inhaltsuebersicht.class::isInstance);
}
public boolean betrifftUeberschrift() {
return komponenten.stream().anyMatch(Ueberschrift.class::isInstance);
}
public List<Gliederungseinheit> gliederungsPfad() {
return komponenten.stream()
.filter(Gliederungseinheit.class::isInstance)
.map(Gliederungseinheit.class::cast)
.toList();
}
public boolean betrifftGliederung() {
return komponenten.stream().anyMatch(Gliederungseinheit.class::isInstance);
}
/**
* Wahr, wenn die Stelle eine Gliederungseinheit des Überschriften-Gerüsts (Teil, Abschnitt, …)
* nennt. Anhänge und Anlagen zählen nicht dazu: die sind im gii-XML eigene Normen mit Text und
* werden wie Paragraphen behandelt (siehe {@link #anlagenEnbez()}).
*/
public boolean betrifftEchteGliederung() {
return komponenten.stream()
.filter(Gliederungseinheit.class::isInstance)
.map(Gliederungseinheit.class::cast)
.anyMatch(g -> !istAnlagenArt(g.art()));
}
/** Die enbez der adressierten Anhang-/Anlagen-Norm („Anhang“, „Anlage 2“), falls vorhanden. */
public Optional<String> anlagenEnbez() {
return komponenten.stream()
.filter(Gliederungseinheit.class::isInstance)
.map(Gliederungseinheit.class::cast)
.filter(g -> istAnlagenArt(g.art()))
.findFirst()
.map(Gliederungseinheit::bezeichnung);
}
private static boolean istAnlagenArt(String art) {
return art.equals("Anhang") || art.equals("Anlage");
}
public Optional<Absatzbezeichnung> absatzbezeichnung() {
return komponenten.stream()
.filter(Absatzbezeichnung.class::isInstance)
.map(Absatzbezeichnung.class::cast)
.findFirst();
}
public String anzeigeText() {
if (komponenten.isEmpty()) {
return "(gesamtes Gesetz)";
}
var sb = new StringBuilder();
for (var komponente : komponenten) {
if (sb.length() > 0) {
sb.append(' ');
}
sb.append(
switch (komponente) {
case Paragraph p -> p.enbez();
case AbsatzNr a -> "Absatz " + a.nummer();
case SatzNr s -> "Satz " + s.nummer();
case HalbsatzNr h -> "Halbsatz " + h.nummer();
case NummerNr n -> "Nummer " + n.nummer();
case BuchstabeNr b -> "Buchstabe " + b.kennung();
case Ueberschrift u -> "Überschrift";
case Inhaltsuebersicht i -> "Inhaltsübersicht";
case Gliederungseinheit g -> g.bezeichnung();
case Absatzbezeichnung a -> "Absatzbezeichnung (" + a.nummer() + ")";
});
}
return sb.toString();
}
}