@prefix : <https://ontology.inferal.com/modules/rdf-delta/> .
@prefix prov: <http://www.w3.org/ns/prov#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sd: <http://www.w3.org/ns/sparql-service-description#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ss: <https://ontology.inferal.com/modules/scoped-statements/> .

:DatasetDeltaShape
    a sh:NodeShape ;
    rdfs:label "dataset delta shape"@en ;
    rdfs:comment "Requires one immutable baseline RDF dataset snapshot, one immutable result RDF dataset snapshot, and well-typed changes supplied through either membership direction. Empty and identity deltas remain valid, but an identity delta cannot report changes."@en ;
    sh:targetClass :DatasetDelta ;
    sh:property [
        sh:path :baselineDataset ;
        sh:class sd:Dataset ;
        sh:class prov:Entity ;
        sh:nodeKind sh:IRI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path :resultDataset ;
        sh:class sd:Dataset ;
        sh:class prov:Entity ;
        sh:nodeKind sh:IRI ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path [
            sh:alternativePath (
                :hasChange
                [ sh:inversePath :inDelta ]
            )
        ] ;
        sh:class :Change ;
    ] ;
    sh:sparql [
        a sh:SPARQLConstraint ;
        sh:message "A delta that reports a change must use distinct baseline and result snapshot IRIs."@en ;
        sh:select """
            PREFIX delta: <https://ontology.inferal.com/modules/rdf-delta/>
            SELECT $this
            WHERE {
                $this
                    delta:baselineDataset ?snapshot ;
                    delta:resultDataset ?snapshot .
                {
                    $this delta:hasChange ?change .
                }
                UNION
                {
                    ?change delta:inDelta $this .
                }
            }
        """ ;
    ] .

:ChangeShape
    a sh:NodeShape ;
    rdfs:label "change shape"@en ;
    rdfs:comment "Requires every change to be exactly one primitive addition or removal, identify one exact quad target, and occur in at least one dataset delta through either membership direction."@en ;
    sh:targetClass :Change ;
    sh:xone (
        [ sh:class :Addition ]
        [ sh:class :Removal ]
    ) ;
    sh:property [
        sh:path :changeTarget ;
        sh:class ss:QuadTarget ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path [
            sh:alternativePath (
                :inDelta
                [ sh:inversePath :hasChange ]
            )
        ] ;
        sh:class :DatasetDelta ;
        sh:minCount 1 ;
    ] .

:AdditionShape
    a sh:NodeShape ;
    rdfs:label "addition shape"@en ;
    rdfs:comment "Applies the common change contract to resources explicitly classified as additions."@en ;
    sh:targetClass :Addition ;
    sh:node :ChangeShape .

:RemovalShape
    a sh:NodeShape ;
    rdfs:label "removal shape"@en ;
    rdfs:comment "Applies the common change contract to resources explicitly classified as removals."@en ;
    sh:targetClass :Removal ;
    sh:node :ChangeShape .
