# yaml-language-server: $schema=https://xleitstelle.usercontent.opencode.de/xmas-core/rules/migration/rules.schema.json
# XPlanung migration rules: one entry of `migrations:` per version hop.
#
# `rules:` maps a class of the `from` version to what happens to it, sorted by name. An
# enumeration takes a code table (`map`, `unmapped`), which rewrites every property of that
# enumeration wherever it appears. A feature or data type takes a list of operations, applied
# to the dict a source feature or data type was dumped to, in order, for every class in its
# inheritance chain from the most abstract to the most concrete.
# `pre:`/`post:` hold the only operations that may look at more than one feature.
#
# `templates:` holds what is written more than once - an operation, a whole rule or a guard
# clause - anchored there and aliased where it is used. Notes are `comment:` fields on
# operations and migrations; YAML comments below this header are refused.
#
# scripts/normalize_migration_rules.py keeps this file in canonical form (run by pre-commit).

templates:
  artDerFestlegung_fold: &artDerFestlegung_fold
    op: fold
    to: artDerFestlegung
    attrs:
      zweckbestimmung: allgemein
      detaillierteZweckbestimmung: detail

  bereich_version_bau_drop: &bereich_version_bau_drop
    - op: drop
      comment: >-
        the Bereich's legal basis, which `pre` has already copied onto the plan
      warn: null
      attrs:
        - versionBauNVODatum
        - versionBauNVOText
        - versionBauGBDatum
        - versionBauGBText
        - versionSonstRechtsgrundlageDatum
        - versionSonstRechtsgrundlageText

  bm_drop: &bm_drop
    op: drop
    attrs:
      - BMZmin
      - BMZmax
      - BMmin
      - BMmax

  dachgestaltung_form_fold: &dachgestaltung_form_fold
    op: fold
    to: dachgestaltung
    attrs:
      dachform: dachform
      detaillierteDachform: detaillierteDachform

  dachgestaltung_pitch_fold: &dachgestaltung_pitch_fold
    op: fold
    to: dachgestaltung
    attrs:
      DNmin: DNmin
      DNmax: DNmax
      DN: DN
      DNZwingend: DNzwingend

  denkmal_to_name: &denkmal_to_name
    op: move
    from: denkmal
    to: name

  detailArtDerFestlegung_drop: &detailArtDerFestlegung_drop
    op: drop
    attrs:
      - detailArtDerFestlegung

  detail_to_detailArtDerFestlegung: &detail_to_detailArtDerFestlegung
    op: move
    from: detaillierteZweckbestimmung
    to: detailArtDerFestlegung

  flaechig: &flaechig
    attr: position
    geom:
      - XP_Flaechengeometrie

  hatDarstellungMitBesondZweckbest_set: &hatDarstellungMitBesondZweckbest_set
    op: set
    attr: hatDarstellungMitBesondZweckbest
    value: false

  nutzungText_drop: &nutzungText_drop
    op: drop
    attrs:
      - nutzungText
    when:
      - attr: sondernutzung
        not: true

  rasterAenderung_drop: &rasterAenderung_drop
    op: drop
    warn: OBJECT_NOT_SUPPORTED
    message: Rasterplan_Aenderung wird nicht mehr unterstützt, das Objekt XP_RasterplanAenderung wird nicht übernommen.
    attrs:
      - rasterAenderung

  rechtscharakter_default: &rechtscharakter_default
    op: set
    attr: rechtscharakter
    value: "9998"
    if_unset: true

  refBegruendungInhalt_und_ausgleich_drop: &refBegruendungInhalt_und_ausgleich_drop
    op: drop
    attrs:
      - refBegruendungInhalt
      - wirdAusgeglichenDurchFlaeche
      - wirdAusgeglichenDurchABE
      - wirdAusgeglichenDurchSPEMassnahme
      - wirdAusgeglichenDurchSPEFlaeche
      - wirdAusgeglichenDurchMassnahme

  sondernutzung_fold: &sondernutzung_fold
    op: fold
    to: sondernutzung
    attrs:
      nutzungText: nutzungText
    when:
      - attr: sondernutzung

  sonstRechtscharakter_in: &sonstRechtscharakter_in
    attr: sonstRechtscharakter
    in:
      - FestsetzungBPlan
      - DarstellungFPlan

  spezifischePraegung_drop: &spezifischePraegung_drop
    op: drop
    attrs:
      - spezifischePraegung

  textlicheErgaenzung_drop: &textlicheErgaenzung_drop
    op: drop
    attrs:
      - textlicheErgaenzung
    when:
      - attr: zweckbestimmung
        not: true

  versionBauGB_to_versionBauGBDatum: &versionBauGB_to_versionBauGBDatum
    op: move
    from: versionBauGB
    to: versionBauGBDatum

  versionBauNVO_drop: &versionBauNVO_drop
    op: drop
    comment: a code the map did not carry has no text to become
    attrs:
      - versionBauNVO

  version_bau_gb_fold: &version_bau_gb_fold
    op: fold
    to: versionBauGB
    attrs:
      versionBauGBText: name
      versionBauGBDatum: datum

  version_bau_nvo_fold: &version_bau_nvo_fold
    op: fold
    to: versionBauNVO
    attrs:
      versionBauNVOText: name
      versionBauNVODatum: datum

  version_bau_nvo_map: &version_bau_nvo_map
    op: map
    map:
      "1000": Version_1962
      "2000": Version_1968
      "3000": Version_1977
      "4000": Version_1990
    from: versionBauNVO
    to: versionBauNVOText

  version_sonst_rechtsgrundlage_fold: &version_sonst_rechtsgrundlage_fold
    op: fold
    to: versionSonstRechtsgrundlage
    attrs:
      versionSonstRechtsgrundlageText: name
      versionSonstRechtsgrundlageDatum: datum

  weitere_detail_zweckbestimmung_1: &weitere_detail_zweckbestimmung_1
    op: move
    from:
      - weitereDetailZweckbestimmung1
    to: detaillierteZweckbestimmung

  weitere_detail_zweckbestimmung_1_2: &weitere_detail_zweckbestimmung_1_2
    op: move
    from:
      - weitereDetailZweckbestimmung1
      - weitereDetailZweckbestimmung2
    to: detaillierteZweckbestimmung

  weitere_detail_zweckbestimmung_1_3: &weitere_detail_zweckbestimmung_1_3
    op: move
    from:
      - weitereDetailZweckbestimmung1
      - weitereDetailZweckbestimmung2
      - weitereDetailZweckbestimmung3
    to: detaillierteZweckbestimmung

  weitere_detail_zweckbestimmung_1_4: &weitere_detail_zweckbestimmung_1_4
    op: move
    from:
      - weitereDetailZweckbestimmung1
      - weitereDetailZweckbestimmung2
      - weitereDetailZweckbestimmung3
      - weitereDetailZweckbestimmung4
    to: detaillierteZweckbestimmung

  weitere_detail_zweckbestimmung_1_5: &weitere_detail_zweckbestimmung_1_5
    op: move
    from:
      - weitereDetailZweckbestimmung1
      - weitereDetailZweckbestimmung2
      - weitereDetailZweckbestimmung3
      - weitereDetailZweckbestimmung4
      - weitereDetailZweckbestimmung5
    to: detaillierteZweckbestimmung

  weitere_massnahme: &weitere_massnahme
    - op: move
      from:
        - weitereMassnahme1
        - weitereMassnahme2
      to: massnahme

  weitere_und_besondere_zweckbestimmung_1_3: &weitere_und_besondere_zweckbestimmung_1_3
    op: move
    from:
      - weitereZweckbestimmung1
      - weitereZweckbestimmung2
      - weitereZweckbestimmung3
      - besondereZweckbestimmung
      - weitereBesondZweckbestimmung1
      - weitereBesondZweckbestimmung2
      - weitereBesondZweckbestimmung3
    to: zweckbestimmung

  weitere_und_besondere_zweckbestimmung_1_4: &weitere_und_besondere_zweckbestimmung_1_4
    op: move
    from:
      - weitereZweckbestimmung1
      - weitereZweckbestimmung2
      - weitereZweckbestimmung3
      - weitereZweckbestimmung4
      - besondereZweckbestimmung
      - weitereBesondZweckbestimmung1
      - weitereBesondZweckbestimmung2
      - weitereBesondZweckbestimmung3
      - weitereBesondZweckbestimmung4
    to: zweckbestimmung

  weitere_und_besondere_zweckbestimmung_1_5: &weitere_und_besondere_zweckbestimmung_1_5
    op: move
    from:
      - weitereZweckbestimmung1
      - weitereZweckbestimmung2
      - weitereZweckbestimmung3
      - weitereZweckbestimmung4
      - weitereZweckbestimmung5
      - besondereZweckbestimmung
      - weitereBesondZweckbestimmung1
      - weitereBesondZweckbestimmung2
      - weitereBesondZweckbestimmung3
      - weitereBesondZweckbestimmung4
      - weitereBesondZweckbestimmung5
    to: zweckbestimmung

  weitere_zweckbestimmung_1: &weitere_zweckbestimmung_1
    op: move
    from:
      - weitereZweckbestimmung1
    to: zweckbestimmung

  weitere_zweckbestimmung_1_2: &weitere_zweckbestimmung_1_2
    op: move
    from:
      - weitereZweckbestimmung1
      - weitereZweckbestimmung2
    to: zweckbestimmung

  weitere_zweckbestimmung_1_3: &weitere_zweckbestimmung_1_3
    op: move
    from:
      - weitereZweckbestimmung1
      - weitereZweckbestimmung2
      - weitereZweckbestimmung3
    to: zweckbestimmung

  weitere_zweckbestimmung_1_4: &weitere_zweckbestimmung_1_4
    op: move
    from:
      - weitereZweckbestimmung1
      - weitereZweckbestimmung2
      - weitereZweckbestimmung3
      - weitereZweckbestimmung4
    to: zweckbestimmung

  zweckbestimmung_fold: &zweckbestimmung_fold
    op: fold
    to: zweckbestimmung
    attrs:
      zweckbestimmung: allgemein
      detaillierteZweckbestimmung: detail

  zweckbestimmung_fold_2: &zweckbestimmung_fold_2
    op: fold
    to: zweckbestimmung
    attrs:
      textlicheErgaenzung: textlicheErgaenzung
    when:
      - attr: zweckbestimmung

  zweckbestimmung_to_artDerFestlegung: &zweckbestimmung_to_artDerFestlegung
    op: move
    from: zweckbestimmung
    to: artDerFestlegung

migrations:
  - from: "4.1"
    to: "5.4"
    comment: >-
      Running `pre` before the per-feature loop is what makes these pushes order-independent:
      an earlier release wrote them from inside XP_Bereich's rule, so whether a write landed
      depended on whether the referenced feature had already been dumped.

    rules:
      BP_AusgleichsFlaeche: *weitere_massnahme

      BP_AusgleichsMassnahme: *weitere_massnahme

      BP_Baugebiet:
        - op: remove

      BP_BaugebietsTeilFlaeche:
        - *bm_drop

      BP_Bereich:
        - *versionBauGB_to_versionBauGBDatum
        - *version_bau_nvo_map
        - *versionBauNVO_drop
        - op: move
          from:
            - inhaltBPlan
          to: planinhalt
        - *rasterAenderung_drop

      BP_BesondererNutzungszweckFlaeche:
        - *bm_drop

      BP_DenkmalschutzEinzelanlage:
        - op: retype
          to: SO_Denkmalschutzrecht
        - op: set
          attr: artDerFestlegung
          value: "1100"
        - *denkmal_to_name
        - *refBegruendungInhalt_und_ausgleich_drop

      BP_DenkmalschutzEnsembleFlaeche:
        - op: retype
          to: SO_Denkmalschutzrecht
        - op: set
          attr: artDerFestlegung
          value: "1000"
        - *denkmal_to_name
        - *refBegruendungInhalt_und_ausgleich_drop

      BP_EinfahrtPunkt:
        - op: drop
          attrs:
            - richtung

      BP_ErneuerbareEnergieFlaeche:
        - op: retype
          to: BP_TechnischeMassnahmenFlaeche
        - op: set
          attr: zweckbestimmung
          value: "2000"
        - op: move
          from: technischeMaßnahme
          to: technischeMassnahme

      BP_GemeinbedarfsFlaeche:
        - *weitere_und_besondere_zweckbestimmung_1_4
        - *weitere_detail_zweckbestimmung_1_4
        - *bm_drop

      BP_GemeinschaftsanlagenFlaeche:
        - *weitere_zweckbestimmung_1_4
        - *weitere_detail_zweckbestimmung_1_4

      BP_GenerischesObjekt:
        - *weitere_zweckbestimmung_1_4

      BP_GruenFlaeche:
        - *weitere_und_besondere_zweckbestimmung_1_4
        - *weitere_detail_zweckbestimmung_1_4
        - *bm_drop

      BP_KennzeichnungsFlaeche:
        - op: move
          from:
            - weitereZweckbestimmung
          to: zweckbestimmung

      BP_Landwirtschaft:
        - *weitere_zweckbestimmung_1_3
        - *weitere_detail_zweckbestimmung_1_3

      BP_LuftreinhalteFlaeche:
        - op: retype
          to: BP_TechnischeMassnahmenFlaeche
        - op: set
          attr: zweckbestimmung
          value: "1000"

      BP_NebenanlagenFlaeche:
        - *weitere_zweckbestimmung_1_4
        - *weitere_detail_zweckbestimmung_1_4

      BP_Objekt:
        - op: warn
          message: Nachrichtliche Bereiche konnten nicht übernommen werden.
          property: gehoertZuBP_Bereich
          when:
            - attrs:
                - gehoertZuBP_Bereich
                - gehoertZuBereich
        - op: move
          warn: OBJECT_DROPPED
          message: Mehrere Bereiche referenziert, nur der erste übernommen.
          from: gehoertZuBP_Bereich
          to: gehoertZuBereich
        - *rechtscharakter_default

      BP_Plan:
        - op: move
          from:
            refSatzung: "1060"
            refKoordinatenListe: "2000"
            refGrundstuecksverzeichnis: "2100"
            refPflanzliste: "2200"
            refGruenordnungsplan: "2300"
            refUmweltbericht: "1050"
          to: externeReferenz
          set: typ

      BP_RasterplanAenderung:
        - op: remove

      BP_SchutzPflegeEntwicklungsFlaeche: *weitere_massnahme

      BP_SchutzPflegeEntwicklungsMassnahme: *weitere_massnahme

      BP_Schutzgebiet:
        - op: retype
          to: SO_SchutzgebietNaturschutzrecht
        - *zweckbestimmung_to_artDerFestlegung
        - *detail_to_detailArtDerFestlegung
        - *refBegruendungInhalt_und_ausgleich_drop

      BP_SpielSportanlagenFlaeche:
        - *weitere_zweckbestimmung_1
        - *weitere_detail_zweckbestimmung_1
        - *bm_drop

      BP_StrassenVerkehrsFlaeche:
        - *bm_drop

      BP_UeberbaubareGrundstuecksFlaeche:
        - *bm_drop

      BP_VerEntsorgung:
        - *weitere_und_besondere_zweckbestimmung_1_3
        - *weitere_detail_zweckbestimmung_1_3
        - *bm_drop

      BP_VerkehrsflaecheBesondererZweckbestimmung:
        - *bm_drop

      BP_WaldFlaeche:
        - *weitere_zweckbestimmung_1_2
        - *weitere_detail_zweckbestimmung_1_2

      FP_AbgrabungsFlaeche:
        - op: retype
          to: FP_Abgrabung

      FP_AufschuettungsFlaeche:
        - op: retype
          to: FP_Aufschuettung

      FP_AusgleichsFlaeche: *weitere_massnahme

      FP_Bereich:
        - *versionBauGB_to_versionBauGBDatum
        - *version_bau_nvo_map
        - *versionBauNVO_drop
        - op: move
          from:
            - inhaltFPlan
          to: planinhalt
        - *rasterAenderung_drop

      FP_BesondereZweckbestimmungStrassenverkehr:
        map:
          "14010": "140010"
          "14011": "140011"

      FP_BodenschaetzeFlaeche:
        - op: retype
          to: FP_Bodenschaetze

      FP_Gemeinbedarf:
        - *weitere_und_besondere_zweckbestimmung_1_5
        - *weitere_detail_zweckbestimmung_1_5

      FP_GenerischesObjekt:
        - *weitere_zweckbestimmung_1_3

      FP_Gruen:
        - *weitere_und_besondere_zweckbestimmung_1_5
        - *weitere_detail_zweckbestimmung_1_5

      FP_Kennzeichnung:
        - *weitere_zweckbestimmung_1

      FP_LandwirtschaftsFlaeche:
        - *weitere_zweckbestimmung_1_3
        - *weitere_detail_zweckbestimmung_1_3

      FP_Objekt:
        - op: warn
          message: Nachrichtliche Bereiche konnten nicht übernommen werden.
          property: gehoertZuFP_Bereich
          when:
            - attrs:
                - gehoertZuFP_Bereich
                - gehoertZuBereich
        - op: move
          warn: OBJECT_DROPPED
          message: Mehrere Bereiche referenziert, nur der erste übernommen.
          from: gehoertZuFP_Bereich
          to: gehoertZuBereich
        - *rechtscharakter_default

      FP_Plan:
        - op: move
          from:
            refUmweltbericht: "1050"
            refErlaeuterung: "1080"
          to: externeReferenz
          set: typ

      FP_PrivilegiertesVorhaben:
        - op: move
          from:
            - weitereZweckbestimmung1
            - weitereZweckbestimmung2
            - besondereZweckbestimmung
            - weitereBesondZweckbestimmung1
            - weitereBesondZweckbestimmung2
          to: zweckbestimmung

      FP_RasterplanAenderung:
        - op: remove

      FP_SchutzPflegeEntwicklung: *weitere_massnahme

      FP_SpielSportanlage:
        - *weitere_zweckbestimmung_1
        - *weitere_detail_zweckbestimmung_1

      FP_Strassenverkehr:
        - op: move
          from: besondereZweckbestimmung
          to: zweckbestimmung
          if_unset: true
        - op: drop
          comment: >-
            what the move left behind, because the feature already had its own
          warn: VALUE_NOT_MAPPED
          message: Attributwert konnte nicht übernommen werden.
          attrs:
            - besondereZweckbestimmung

      FP_VerEntsorgung:
        - *weitere_und_besondere_zweckbestimmung_1_3
        - *weitere_detail_zweckbestimmung_1_3

      FP_WaldFlaeche:
        - *weitere_zweckbestimmung_1_2
        - *weitere_detail_zweckbestimmung_1_2

      LP_Bereich:
        - op: move
          from:
            - inhaltLPlan
          to: planinhalt
        - *rasterAenderung_drop

      LP_Denkmalschutzrecht:
        - op: retype
          to: SO_Denkmalschutzrecht
        - op: move
          from: detailTyp
          to: name
        - op: drop
          attrs:
            - refBegruendungInhalt
            - konkretisierung
        - op: set
          warn: MANUAL_CHECK_REQUIRED
          message: Bitte rechtscharakter überprüfen.
          attr: rechtscharakter
          value: "9998"

      LP_ErholungFreizeit:
        - op: move
          from:
            - weitereFunktion1
            - weitereFunktion2
          to: funktion
        - op: move
          from:
            - weitereDetailFunktion1
            - weitereDetailFunktion2
          to: detaillierteFunktion

      LP_Objekt:
        - op: move
          from: status
          to: rechtscharakter
        - *rechtscharakter_default
        - op: warn
          message: Nachrichtliche Bereiche konnten nicht übernommen werden.
          property: gehoertZuLP_Bereich
          when:
            - attrs:
                - gehoertZuLP_Bereich
                - gehoertZuBereich
        - op: move
          warn: OBJECT_DROPPED
          message: Mehrere Bereiche referenziert, nur der erste übernommen.
          from: gehoertZuLP_Bereich
          to: gehoertZuBereich

      LP_RasterplanAenderung:
        - op: remove

      LP_SchutzobjektBundesrecht:
        - op: remove
          comment: >-
            5.4 has no equivalent. An earlier release spelled this rule `_lpschutzobjektbundsrecht`
            ("bunds", not "bundes"), so the name-mangled dispatch never matched the class
            and the rule never ran in any release - these features were carried into 5.4 untouched.
          message: Klasse wurde in neuer Version eliminiert.

      LP_SonstigeAbgrenzuung:
        - op: remove

      LP_TextAbschnitt:
        - op: drop
          attrs:
            - status

      RP_Bereich:
        - op: remove
          warn: RP_PLAN_NOT_SUPPORTED
          message: Migration von RP-Plan wird nicht unterstützt.

      RP_Objekt:
        - op: remove
          warn: RP_PLAN_NOT_SUPPORTED

      RP_Plan:
        - op: remove
          warn: RP_PLAN_NOT_SUPPORTED
          message: Migration von RP-Plan wird nicht unterstützt.
          abort: true

      RP_RasterplanAenderung:
        - op: remove
          warn: RP_PLAN_NOT_SUPPORTED

      SO_Bereich:
        - op: move
          from:
            - inhaltSoPlan
          to: planinhalt
        - *rasterAenderung_drop

      SO_KlassifizNachWasserrecht:
        map:
          "1000": "10000"
          "1100": "10001"
          "1300": "10002"

      SO_Objekt:
        - op: warn
          message: Nachrichtliche Bereiche konnten nicht übernommen werden.
          property: gehoertZuSO_Bereich
          when:
            - attrs:
                - gehoertZuSO_Bereich
                - gehoertZuBereich
        - op: move
          warn: OBJECT_DROPPED
          message: Mehrere Bereiche referenziert, nur der erste übernommen.
          from: gehoertZuSO_Bereich
          to: gehoertZuBereich
        - *rechtscharakter_default
        - op: warn
          comment: >-
            before the map, whose guard it negates: afterwards a mapped rechtscharakter is
            no longer 9999, and this would report the very case the map handled
          property: sonstRechtscharakter
          when:
            - *sonstRechtscharakter_in
            - attr: rechtscharakter
              in:
                - "9999"
              not: true
        - op: set
          comment: a copy, since 5.4 still states sonstRechtscharakter
          attr: rechtscharakter
          from: sonstRechtscharakter
          when:
            - attr: rechtscharakter
              is: "9999"
            - *sonstRechtscharakter_in
          then:
            - op: map
              map:
                DarstellungFPlan: "2000"
                FestsetzungBPlan: "1000"
              from: rechtscharakter

      SO_Plan:
        - op: move
          from: planTyp
          to: planArt

      SO_RasterplanAenderung:
        - op: remove

      SO_Schienenverkehrsrecht:
        - op: move
          from: besondereArtDerFestlegung
          to: artDerFestlegung
          if_unset: true
        - op: drop
          comment: >-
            what the move left behind, because the feature already had its own
          warn: VALUE_NOT_MAPPED
          message: Attributwert konnte nicht übernommen werden.
          attrs:
            - besondereArtDerFestlegung

      SO_SchutzgebietSonstigesRecht:
        - op: retype
          to: SO_Luftverkehrsrecht
          when:
            - attr: zone
          then:
            - op: set
              attr: artDerFestlegung
              value: "6000"
        - op: move
          from: zone
          to: laermschutzzone

      XP_Bereich:
        - op: move
          from: erstellungsMasstab
          to: erstellungsMassstab
        - op: move
          comment: >-
            nachrichtliche Inhalte become ordinary Planinhalt; `pre` stamps the rechtscharakter
            they carry from now on onto the features themselves
          from:
            - nachrichtlich
          to: planinhalt
        - op: map
          comment: >-
            5.4 dropped these codes; the label they had is kept as free text
          map:
            "1000": Aenderungsbereich
            "1500": Ergaenzungsbereich
            "1650": Gesamtbereich
            "1700": Eingriffsbereich
            "2000": Nebenzeichnung
            "2500": Variante
            "3000": VertikaleGliederung
            "3500": Erstnutzung
            "4000": Folgenutzung
          from: bedeutung
          to: detaillierteBedeutung
          when:
            - attr: bedeutung
              in:
                - "1000"
                - "1500"
                - "1650"
                - "1700"
                - "2000"
                - "2500"
                - "3000"
                - "3500"
                - "4000"
          then:
            - op: set
              attr: bedeutung
              value: "9999"

      XP_BesondereZweckbestimmungVerEntsorgung:
        map:
          "10010": "100010"
          "28000": "10003"
          "28001": "10002"
          "28002": "10007"
          "28003": "10004"
          "28004": "10005"

      XP_ExterneReferenz:
        - op: drop
          warn: MANUAL_CHECK_REQUIRED
          message: Zulässige Werte für das Attribut XP_ExterneReferenz.art sind 'Dokument' und 'PlanMitGeoreferenz'.
          attrs:
            - art
          when:
            - attr: art
              in:
                - Dokument
                - PlanMitGeoreferenz
              not: true

      XP_Objekt:
        - op: move
          comment: >-
            into 5.4's single gehoertZuBereich: the first wins, and the per-plan `*_Objekt`
            rules, which run after this one, overwrite it with the Fachobjekt's own Bereich
          from: gehoertNachrichtlichZuBereich
          to: gehoertZuBereich
        - op: move
          from:
            informell: "9999"
            rechtsverbindlich: "9998"
          to: externeReferenz
          set: typ
        - op: drop
          attrs:
            - textSchluessel
            - textSchluesselBegruendung

      XP_Plan:
        - op: drop
          attrs:
            - xPlanGMLVersion
            - refExternalCodeList
        - op: move
          from:
            informell: "9999"
            rechtsverbindlich: "9998"
            refBeschreibung: "1000"
            refBegruendung: "1010"
            refLegende: "1020"
            refRechtsplan: "1030"
            refPlangrundlage: "1040"
          to: externeReferenz
          set: typ

      XP_RasterplanBasis:
        - op: retype
          to: XP_Rasterdarstellung

      XP_TextAbschnitt:
        - op: retype
          comment: 5.4 replaced the generic class with one per plan type
          by_plan:
            BP: BP_TextAbschnitt
            FP: FP_TextAbschnitt
            LP: LP_TextAbschnitt
            RP: RP_TextAbschnitt
            SO: SO_TextAbschnitt
        - op: drop
          comment: >-
            4.1 declares rechtscharakter on the per-plan subclasses, and 5.4 dropped its 9999
          attrs:
            - rechtscharakter
          when:
            - attr: rechtscharakter
              is: "9999"
        - op: set
          attr: rechtscharakter
          by_plan:
            BP: "1000"
            FP: "1000"
            LP: "9998"
            RP: "9998"
            SO: "9998"
          if_unset: true

    pre:
      - op: push
        comment: >-
          what a Bereich listed as nachrichtlich, each feature now states for itself
        each: XP_Bereich
        via:
          - nachrichtlich
        set:
          status: "3000"
        when:
          - is_a: LP_Objekt
      - op: push
        each: XP_Bereich
        via:
          - nachrichtlich
        set:
          rechtscharakter: "2000"
        when:
          - is_a:
              - BP_Objekt
              - FP_Objekt
              - RP_Objekt
              - SO_Objekt

  - from: "5.4"
    to: "6.0"

    rules:
      BP_AbstandsMass:
        - op: remove
          warn: INVALID_GEOMETRY
          message: Invalid geometry type for BP_Abstandsmass
          property: position
          when:
            - attr: position
              geom:
                - XP_Liniengeometrie
              not: true
        - op: drop
          attrs:
            - flaechenschluss
            - flussrichtung
            - nordwinkel

      BP_AbweichungVonUeberbaubererGrundstuecksFlaeche:
        - op: retype
          to: BP_AbweichungVonUeberbaubarerGrundstuecksFlaeche

      BP_AnpflanzungBindungErhaltung:
        - op: move
          from: baumArt
          to: pflanzenArt

      BP_BaugebietsTeilFlaeche:
        - *nutzungText_drop
        - op: fold
          to: sondernutzung
          attrs:
            sondernutzung: allgemein
            detaillierteSondernutzung: detail
        - *sondernutzung_fold
        - *dachgestaltung_pitch_fold
        - *dachgestaltung_form_fold

      BP_BebauungsArt:
        map:
          "8000": null

      BP_Bereich: *bereich_version_bau_drop

      BP_BesondererNutzungszweckFlaeche:
        - *dachgestaltung_pitch_fold
        - *dachgestaltung_form_fold

      BP_BodenschaetzeFlaeche:
        - op: retype
          to: BP_AbgrabungsFlaeche

      BP_EmissionskontingentLaerm:
        - op: set
          warn: MANUAL_CHECK_REQUIRED
          attr: pegelTyp
          value: "1000"
        - op: set
          warn: MANUAL_CHECK_REQUIRED
          attr: berechnungsgrundlage
          value: "1000"

      BP_ErhaltungsBereichFlaeche:
        - op: move
          from: grund
          to: gebietsArt
        - op: retype
          to: SO_Gebiet

      BP_ErhaltungsGrund:
        map:
          "1000": "17000"
          "2000": "17001"
          "3000": "17002"
        unmapped: drop

      BP_FirstRichtungsLinie:
        - op: set
          attr: typ
          value: "1000"
        - op: retype
          to: BP_GebaeudeStellung

      BP_GemeinbedarfsFlaeche:
        - *dachgestaltung_pitch_fold
        - *dachgestaltung_form_fold
        - *zweckbestimmung_fold

      BP_GemeinschaftsanlagenFlaeche:
        - *zweckbestimmung_fold

      BP_GewaesserFlaeche:
        - *artDerFestlegung_fold
        - op: set
          attr: flaechenschluss
          value: true
          when:
            - *flaechig
            - attr: ebene
              is: 0
        - op: retype
          to: SO_Gewaesser

      BP_GruenFlaeche:
        - *zweckbestimmung_fold

      BP_Landwirtschaft:
        - op: remove
          message: Object dropped due to invalid geometry
          property: position
          when:
            - attr: position
              geom:
                - XP_Flaechengeometrie
              not: true
        - op: retype
          to: BP_LandwirtschaftsFlaeche
        - *zweckbestimmung_fold

      BP_LandwirtschaftsFlaeche:
        - *zweckbestimmung_fold

      BP_NebenanlagenFlaeche:
        - *zweckbestimmung_fold

      BP_Plan:
        - op: drop
          comment: >-
            `pre` has already copied this onto the Bereich, so nothing is lost here
          warn: null
          attrs:
            - verfahren
        - op: drop
          attrs:
            - veraenderungssperre
        - op: fold
          comment: >-
            6.0 states the Veränderungssperre as one object, and only a complete one is worth
            having
          to: veraenderungssperre
          attrs:
            veraenderungssperreBeschlussDatum: beschlussDatum
            veraenderungssperreDatum: startDatum
            veraenderungssperreEndDatum: endDatum
            verlaengerungVeraenderungssperre: verlaengerung
          when:
            - attrs:
                - veraenderungssperreDatum
                - veraenderungssperreEndDatum
                - verlaengerungVeraenderungssperre
        - op: drop
          comment: >-
            whatever the fold above did not take is lost, and the drop reports that itself
          attrs:
            - veraenderungssperreDatum
            - veraenderungssperreEndDatum
            - verlaengerungVeraenderungssperre
            - veraenderungssperreBeschlussDatum
        - *version_bau_nvo_fold
        - *version_bau_gb_fold
        - *version_sonst_rechtsgrundlage_fold

      BP_Rechtscharakter:
        map:
          "3000": "6000"
          "4000": "8000"
          "5000": "7000"

      BP_RekultivierungsFlaeche:
        - op: set
          attr: artDerFestlegung
          value: "1500"
        - op: retype
          to: SO_SonstigesRecht

      BP_Sichtflaeche:
        - op: retype
          to: SO_Sichtflaeche

      BP_SpielSportanlagenFlaeche:
        - *zweckbestimmung_fold

      BP_StrassenVerkehrsFlaeche:
        - *hatDarstellungMitBesondZweckbest_set
        - op: retype
          to: SO_Strassenverkehr

      BP_TextlicheFestsetzungsFlaeche:
        - op: retype
          to: BP_TextAbschnittFlaeche

      BP_UeberbaubareGrundstuecksFlaeche:
        - *dachgestaltung_pitch_fold
        - *dachgestaltung_form_fold

      BP_VerEntsorgung:
        - *zweckbestimmung_fold
        - *zweckbestimmung_fold_2
        - *textlicheErgaenzung_drop

      BP_Veraenderungssperre:
        - op: remove
          warn: MISSING_DATES
          message: Missing date properties for BP_Veraenderungssperre
          when:
            - attrs:
                - veraenderungssperreStartDatum
                - gueltigkeitsDatum
                - verlaengerung
              not: true
              any: true
        - op: fold
          to: daten
          attrs:
            veraenderungssperreBeschlussDatum: beschlussDatum
            veraenderungssperreStartDatum: startDatum
            gueltigkeitsDatum: endDatum
            verlaengerung: verlaengerung
            refBeschluss: refBeschluss

      BP_VerkehrsflaecheBesondererZweckbestimmung:
        - op: set
          attr: hatDarstellungMitBesondZweckbest
          value: true
        - *artDerFestlegung_fold
        - op: retype
          to: SO_Strassenverkehr

      BP_WaldFlaeche:
        - *zweckbestimmung_fold

      BP_WasserwirtschaftsFlaeche:
        - *zweckbestimmung_to_artDerFestlegung
        - *detail_to_detailArtDerFestlegung
        - op: retype
          to: SO_Wasserwirtschaft

      BP_Wegerecht:
        - op: map
          comment: >-
            6.0 replaced the combined codes by the several they combined
          map:
            "1000":
              - "2000"
              - "2500"
            "3000":
              - "1000"
              - "2000"
            "4100":
              - "1000"
              - "4000"
            "4200":
              - "2000"
              - "4000"
            "5000":
              - "1000"
              - "2000"
              - "4000"
          from: typ

      BP_WohngebaeudeFlaeche:
        - *dachgestaltung_pitch_fold
        - *dachgestaltung_form_fold

      BP_ZweckbestimmungStrassenverkehr:
        map:
          "1000": "16000"
          "1100": "14002"
          "1200": "14000"
          "1300": "14003"
          "1400": "14004"
          "1500": "14005"
          "1550": "14006"
          "1560": "14007"
          "1580": "140012"
          "1600": "16001"
          "1700": "140010"
          "1800": "140011"
          "2000": "16002"
          "2100": "14001"
          "2200": "14014"
          "2300": "140013"
          "2400": "14015"
          "2500": "14008"
          "2600": "14009"
          "3000": "16005"
          "3100": "16006"
          "3200": "16003"
          "3300": "16004"

      FP_BebauungsFlaeche:
        - op: fold
          to: sondernutzung
          attrs:
            sonderNutzung: allgemein
            detaillierteSondernutzung: detail
        - *sondernutzung_fold
        - *nutzungText_drop

      FP_Bereich: *bereich_version_bau_drop

      FP_Bodenschaetze:
        - op: retype
          to: FP_Abgrabung

      FP_Gemeinbedarf:
        - *zweckbestimmung_fold

      FP_Gewaesser:
        - *artDerFestlegung_fold
        - *spezifischePraegung_drop
        - op: retype
          to: SO_Gewaesser

      FP_Gruen:
        - *zweckbestimmung_fold

      FP_Landwirtschaft:
        - *zweckbestimmung_fold

      FP_LandwirtschaftsFlaeche:
        - op: retype
          to: FP_Landwirtschaft
        - *zweckbestimmung_fold

      FP_NutzungsbeschraenkungsFlaeche:
        - op: retype
          to: FP_Nutzungsbeschraenkung

      FP_Plan:
        - *version_bau_nvo_fold
        - *version_bau_gb_fold
        - *version_sonst_rechtsgrundlage_fold

      FP_Rechtscharakter:
        map:
          "1000": "3000"
          "3000": "6000"
          "4000": "8000"
          "5000": "7000"

      FP_SpielSportanlage:
        - *zweckbestimmung_fold

      FP_Strassenverkehr:
        - *hatDarstellungMitBesondZweckbest_set
        - op: set
          comment: >-
            1300 became a flag of its own, and takes its detail entry with it
          attr: istOrtsdurchfahrt
          value: true
          when:
            - attr: zweckbestimmung
              contains: "1300"
        - op: map
          map:
            "1300": null
          from: zweckbestimmung
          paired: detaillierteZweckbestimmung
        - *artDerFestlegung_fold
        - *spezifischePraegung_drop
        - op: retype
          to: SO_Strassenverkehr

      FP_TextlicheDarstellungsFlaeche:
        - op: retype
          to: FP_TextAbschnittFlaeche

      FP_VerEntsorgung:
        - *zweckbestimmung_fold
        - *zweckbestimmung_fold_2
        - *textlicheErgaenzung_drop

      FP_WaldFlaeche:
        - *zweckbestimmung_fold

      FP_Wasserwirtschaft:
        - *zweckbestimmung_to_artDerFestlegung
        - *detail_to_detailArtDerFestlegung
        - *spezifischePraegung_drop
        - op: retype
          to: SO_Wasserwirtschaft

      FP_ZweckbestimmungStrassenverkehr:
        map:
          "3000": "16005"
          "3100": "16006"
          "3200": "16003"
          "3300": "16004"

      LP_Objekt:
        - op: remove
          warn: LP_OBJECT_NOT_TRANSFORMABLE
          message: LP Objekt kann nicht transformiert werden

      LP_Plan:
        - op: fold
          comment: >-
            6.0 states the planning body the way the other plan types do
          to: plangeber
          attrs:
            planungstraeger: name
            planungstraegerGKZ: kennziffer
        - op: move
          comment: >-
            6.0 splits each procedural date into a start and an end; the one date 5.4 states
            is taken as the start
          from: auslegungsDatum
          to: auslegungsStartDatum
        - op: move
          from: tOeBbeteiligungsDatum
          to: tOeBbeteiligungsStartDatum
        - op: move
          from: oeffentlichkeitsbeteiligungDatum
          to: oeffentlichkeitsBetStartDatum

      RP_Legendenobjekt:
        - op: remove

      RP_Rechtscharakter:
        map:
          "1000": "4000"
          "2000": "4100"
          "3000": "2000"
          "4000": "4200"
          "5000": "4300"
          "6000": "4400"
          "7000": "4500"
          "8000": "4600"
          "9000": "4700"

      RP_ZentralerOrt:
        - op: remove
          message: Object dropped due to invalid sonstigerTyp '9999'
          property: sonstigerTyp
          when:
            - attr: sonstigerTyp
              contains: "9999"

      SO_Bauverbotszone:
        - op: retype
          to: SO_Baubeschraenkung

      SO_GebietsArt:
        map:
          "1999": "1700"
          "2000": "17000"
          "2100": "17001"
          "2200": "17002"

      SO_Gewaesser:
        - op: fold
          to: artDerFestlegung
          attrs:
            artDerFestlegung: allgemein
            detailArtDerFestlegung: detail

      SO_KlassifizGewaesser:
        map:
          "2000": "4000"
          "10000": "20000"
          "10001": "20001"
          "10002": "20002"
          "10003": "3000"

      SO_KlassifizSchutzgebietSonstRecht:
        map:
          "1000": "1700"
          "2000": "1800"

      SO_Luftverkehrsrecht:
        - op: retype
          to: SO_Baubeschraenkung
          when:
            - attr: artDerFestlegung
              is: "7000"
          then:
            - op: set
              attr: rechtlicheGrundlage
              value: "1000"
            - op: set
              comment: >-
                the guard is read once, before the retype: this op destroys the value it tests
              attr: artDerFestlegung
              value: "2000"
        - op: drop
          attrs:
            - laermschutzzone

      SO_Rechtscharakter:
        map:
          "1500": "3000"
          "1800": "5300"
          "3000": "6000"
          "4000": "8000"
          "5000": "7000"

      SO_SchutzgebietNaturschutzrecht:
        - op: set
          attr: artDerFestlegung
          value: "9999"
          if_unset: true
        - op: set
          attr: artDerFestlegungText
          value: Unbekannt
          when:
            - attr: artDerFestlegung
              is: "9999"
        - op: set
          attr: rechtsstandSchG
          value: "9999"
          from: rechtsstand
          if_unset: true
        - op: set
          warn: MANUAL_CHECK_REQUIRED
          attr: rechtsstandSchGText
          value: Unbekannt
          when:
            - attr: rechtsstandSchG
              is: "9999"
        - op: set
          warn: MANUAL_CHECK_REQUIRED
          attr: gesetzlGeschBiotop
          value: "9999"
          when:
            - attr: artDerFestlegung
              is: "1700"
          then:
            - op: set
              warn: MANUAL_CHECK_REQUIRED
              attr: gesetzlGeschBiotopText
              value: Unbekannt
        - op: set
          warn: MANUAL_CHECK_REQUIRED
          attr: raumkonkretisierung
          value: "9998"
        - op: drop
          attrs:
            - sonstRechtscharakter
            - detailArtDerFestlegung
            - zone
        - op: retype
          to: LP_SchutzBestimmterTeileVonNaturUndLandschaft

      SO_SchutzgebietSonstigesRecht:
        - op: retype
          to: SO_SonstigesRecht

      SO_SonstigesRecht:
        - op: retype
          to: SO_Baubeschraenkung
          when:
            - attr: artDerFestlegung
              in:
                - "1000"
                - "1200"
          then:
            - op: map
              map:
                "1000": "9999"
                "1200": "2000"
              from: artDerFestlegung

      SO_Strassenverkehrsrecht:
        - op: retype
          to: SO_Strassenverkehr
        - *hatDarstellungMitBesondZweckbest_set
        - op: move
          from: artDerFestlegung
          to: einteilung
        - *detailArtDerFestlegung_drop

      SO_Wasserrecht:
        - op: retype
          comment: >-
            the water-body codes became a Gewässer of its own; everything below only applies
            then
          to: SO_Gewaesser
          when:
            - attr: artDerFestlegung
              in:
                - "1000"
                - "10000"
                - "10001"
                - "10002"
          then:
            - op: drop
              warn: PROPERTY_NOT_TRANSFORMABLE
              attrs:
                - istNatuerlichesUberschwemmungsgebiet
            - op: map
              map:
                "10000": "20000"
                "10001": "20001"
                "10002": "20002"
              from: artDerFestlegung
            - op: fold
              to: artDerFestlegung
              attrs:
                artDerFestlegung: allgemein
            - *detailArtDerFestlegung_drop
            - op: set
              attr: ebene
              value: 1
              when:
                - *flaechig
                - attr: ebene
                  is: 0
                - attr: flaechenschluss
                  is: false
            - op: set
              attr: flaechenschluss
              value: true
              when:
                - *flaechig
                - attr: ebene
                  is: 0

      XP_AbstraktesPraesentationsobjekt:
        - op: drop
          warn: null
          attrs:
            - index

      XP_Bereich:
        - op: drop
          comment: >-
            the association to a Rasterdarstellung whose content `pre` has already moved here
          warn: null
          attrs:
            - rasterBasis

      XP_ExterneReferenz:
        - op: drop
          attrs:
            - georefMimeType
            - informationssystemURL
        - op: set
          attr: referenzURL
          value: Unbekannt
          from: referenzName
          if_unset: true
        - op: set
          attr: referenzName
          value: Unbekannt
          if_unset: true

      XP_Objekt:
        - *rechtscharakter_default
        - op: fold
          comment: 6.0 names the legal basis where 5.4 had a code for it
          warn: MANUAL_CHECK_REQUIRED
          to: gesetzlicheGrundlage
          attrs:
            gesetzlicheGrundlage: name

      XP_Plan:
        - op: move
          from: aendert
          to: aendertPlan
        - op: move
          from: wurdeGeaendertVon
          to: wurdeGeaendertVonPlan

      XP_Rasterdarstellung:
        - op: remove
          comment: >-
            one that carries a text becomes a TextAbschnitt; one that only carried scans has
            been emptied by `pre` and goes without a word
          warn: null
          when:
            - attr: refText
              not: true
        - op: drop
          warn: null
          attrs:
            - refScan
            - refLegende
        - op: retype
          to: XP_TextAbschnitt
        - *rechtscharakter_default

      XP_RechtscharakterPlanaenderung:
        map:
          "1100": "10001"
          "20000": "2000"
          "20001": "3000"

      XP_TextAbschnitt:
        - *rechtscharakter_default
        - op: retype
          to: XP_TextAbschnitt

      XP_VerbundenerPlan:
        - op: move
          from: rechtscharakter
          to: aenderungsArt

      XP_ZweckbestimmungGemeinbedarf:
        map:
          "10003": "1000"
          "12004": "1200"
          "14003": "1400"
          "16004": "1600"
          "18001": "1800"
          "20002": "2000"
          "22002": "2200"
          "24003": "2400"
          "26001": "2600"

      XP_ZweckbestimmungGewaesser:
        map:
          "1000": "4000"
          "1100": "3000"
          "1200": "2000"
          "10000": "40000"

      XP_ZweckbestimmungGruen:
        map:
          "14007": "1400"
          "24002": "2400"

      XP_ZweckbestimmungVerEntsorgung:
        map:
          "18005": "1800"

    pre:
      - op: pull
        comment: >-
          6.0 folds the Rasterdarstellung into the objects that referenced it: its scans onto
          the Bereich, its text and legend onto the plan. Done here, before anything is migrated,
          the values it moves are migrated in place by the ordinary rules.
        each: XP_Bereich
        via:
          - rasterBasis
        from: refScan
        to: refScan
      - op: pull
        each: XP_Plan
        via:
          - bereich
          - rasterBasis
        from: id
        to: texte
        when:
          - attr: refText
      - op: pull
        each: XP_Plan
        via:
          - bereich
          - rasterBasis
        from: refLegende
        to: externeReferenz
        with:
          typ: "1020"
      - op: pull
        comment: >-
          5.x let the legal basis sit on either the plan or its Bereiche; 6.0 keeps it on
          the plan
        each: BP_Plan
        via:
          - bereich
        attrs:
          - versionBauNVOText
          - versionBauNVODatum
      - op: pull
        each: BP_Plan
        via:
          - bereich
        attrs:
          - versionBauGBText
          - versionBauGBDatum
      - op: pull
        each: BP_Plan
        via:
          - bereich
        attrs:
          - versionSonstRechtsgrundlageText
          - versionSonstRechtsgrundlageDatum
      - op: pull
        each: FP_Plan
        via:
          - bereich
        attrs:
          - versionBauNVOText
          - versionBauNVODatum
      - op: pull
        each: FP_Plan
        via:
          - bereich
        attrs:
          - versionBauGBText
          - versionBauGBDatum
      - op: pull
        each: FP_Plan
        via:
          - bereich
        attrs:
          - versionSonstRechtsgrundlageText
          - versionSonstRechtsgrundlageDatum
      - op: pull
        comment: >-
          `verfahren` is stated on the plan in 5.4 and on the Bereich in 6.0. Written here,
          into data rather than onto a 5.4 model that has no field for it, and after the enum
          tables, so a remapped code would already have been remapped when it is copied.
        each: BP_Bereich
        via:
          - gehoertZuPlan
        from: verfahren
        to: verfahren

  - from: "6.0"
    to: "6.1"
    comment: >-
      No rules: every change of this hop is the target schema's to answer. 6.1's `SO_Sichtflaeche.geschwindigkeit`
      is a Velocity where 6.0 had a plain Measure, and a Measure validates as a Velocity in
      km/h on its own.


      XP_TextAbschnitt and XP_BegruendungAbschnitt gained roles back to whatever references
      them. Nothing in a 6.0 document states them, but each is the inverse of a reference
      that is already there, so they are inferred rather than reported as missing.

    post:
      - op: push
        each: XP_Plan
        via:
          - texte
        to: gehoertZuPlan
      - op: push
        each: XP_Plan
        via:
          - begruendungsTexte
        to: gehoertZuPlan
      - op: push
        each: XP_Bereich
        via:
          - texte
        to: gehoertZuBereich
      - op: push
        each: XP_Objekt
        via:
          - refTextInhalt
        to: gehoertZuObjekt
      - op: push
        each: XP_Objekt
        via:
          - refBegruendungInhalt
        to: gehoertZuObjekt
