@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rml: <http://w3id.org/rml/> .
@prefix rmlsh: <http://w3id.org/rml/shapes/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<http://w3id.org/rml/lv/shapes> a owl:Ontology ;
    owl:imports <http://w3id.org/rml/core/shapes> ;
.

# rml:LogicalView
rmlsh:LogicalViewShape a sh:NodeShape ;
    rdfs:label "LogicalView" ;
    rdfs:comment """
    A logical view (rml:LogicalView) is a type of abstract logical source that is derived from another abstract logical source by defining fields with data from said abstract logical source.
    """ ;
    sh:message """
    Node must have a valid rml:LogicalView description.
    rml:LogicalView requires
    - exactly one rml:viewOn property, with a rml:AbstractLogicalSource as its value.
    - at least one rml:field property, with a rml:Field as its value.
    """ ;
    sh:targetClass rml:LogicalView ;
    sh:targetSubjectsOf rml:viewOn ;
    sh:targetObjectsOf rml:parentLogicalView ;
    sh:property [
        sh:description """
        The source of the logical view. This is the logical source from which the logical view is derived.
        """ ;
        sh:message """
        Exactly one rml:viewOn property must be specified for a rml:LogicalView, with a rml:AbstractLogicalSource as its value.
        """ ;
        sh:path rml:viewOn ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:node rmlsh:RMLAbstractLogicalSourceShape ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] , [
        sh:description """
        A field of the logical view.
        """ ;
        sh:message """
        At least one rml:field property must be specified for a rml:LogicalView, with either an rml:ExpressionField or an rml:IterableField its value.
        """ ;
        sh:path rml:field ;
        sh:minCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI ;
        sh:or (
            [ sh:node rmlsh:ExpressionFieldShape ]
            [ sh:node rmlsh:IterableFieldShape ]
        )
    ] , [
        sh:description """
        A left join of the logical view.
        """ ;
        sh:message """
        The value of a rml:leftJoin property for a rml:LogicalView must be a resource.
        """ ;
        sh:path rml:leftJoin ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] , [
        sh:description """
        An inner join of the logical view.
        """ ;
        sh:message """
        The value of a rml:innerJoin property for a rml:LogicalView must be a resource.
        """ ;
        sh:path rml:innerJoin ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] ,
    [
        sh:description """
        A structural annotation of the logical view.
        """ ;
        sh:message """
       The value of a rml:structuralAnnotation property must be a resource.
        """ ;
        sh:path rml:structuralAnnotation ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ]
.

rmlsh:FieldShape a sh:NodeShape ;
    rdfs:label "Field" ;
    rdfs:comment """
    A field is a construct that gives a name to data derived from an abstract logical source.
    """ ;
    sh:message """
    Node must have a valid rml:Field description.
    rml:Field requires a rml:fieldName and may have zero or more rml:field properties.
    Furthermore, subclass rml:ExpressionField requires what an rml:ExpressionMap requires, subclass rml:IterableField requires what an rml:Iterable requires,.
    """ ;
    sh:targetClass rml:Field ;
    sh:targetObjectsOf rml:field ;
    sh:property [
        sh:description """
        The name of the field.
        """ ;
        sh:message """
        Exactly one rml:fieldName property must be specified for a rml:Field, with a string as its value. Only alphanumeric characters are allowed.
        """ ;
        sh:path rml:fieldName ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:datatype xsd:string ;
        sh:nodeKind sh:Literal ;
        sh:pattern "^[A-Za-z0-9]+$" ;
    ] , [
        sh:description """
        A child field of the field.
        """ ;
        sh:message """
        The value of a rml:field property for a rml:Field must be a resource.
        """ ;
        sh:path rml:field ;
        sh:nodeKind sh:BlankNodeOrIRI ;
        sh:or (
            [ sh:node rmlsh:ExpressionFieldShape ]
            [ sh:node rmlsh:IterableFieldShape ]
        )
    ] .

rmlsh:ExpressionFieldShape a sh:NodeShape ;
    rdfs:label "Expression Field" ;
    rdfs:comment """
    A expression field is an expression map that gives a name to an expression.
    """ ;
    sh:message """
    Node must have a valid rml:ExpressionField description.
    rml:ExpressionField requires what an rml:ExpressionMap and rml:Field requires.
    """ ;
    sh:targetClass rml:ExpressionField ;
    sh:node rmlsh:RMLExpressionMapShape ;
    ## Not using sh:node rmlsh:Field here due to shape recursion problem https://github.com/RDFLib/pySHACL/issues/154
    sh:property [
        sh:description """
        The name of the field.
        """ ;
        sh:message """
        Exactly one rml:fieldName property must be specified for a rml:Field, with a string as its value.
        """ ;
        sh:path rml:fieldName ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:datatype xsd:string ;
        sh:nodeKind sh:Literal ;
    ] , [
        sh:description """
        A child field of the field.
        """ ;
        sh:message """
        The value of a rml:field property for a rml:Field must be a resource.
        """ ;
        sh:path rml:field ;
        sh:nodeKind sh:BlankNodeOrIRI ;
        ## Disabled due to shape recursion. This is covered by rmlsh:FieldShape
        # sh:or (
        #     [ sh:node rmlsh:ExpressionFieldShape ]
        #     [ sh:node rmlsh:IterableFieldShape ]
        # )
    ]
.

rmlsh:IterableFieldShape a sh:NodeShape ;
    rdfs:label "Iterable Field" ;
    rdfs:comment """
    An iterable field is a construction that gives a name to logical iterations derived from the parent of the field.
    """ ;
    sh:message """
    Node must have a valid rml:IterableField description.
    rml:IterableField must have one or more rml:field properties.
    Furthermore, rml:IterableField requires what an rml:Iterable and rml:Field requires.
    """ ;
    sh:targetClass rml:IterableField ;
    sh:node rmlsh:RMLIterableShape ;
    ## Not using sh:node rmlsh:Field here due to shape recursion problem https://github.com/RDFLib/pySHACL/issues/154
    sh:property [
        sh:description """
        The name of the field.
        """ ;
        sh:message """
        Exactly one rml:fieldName property must be specified for a rml:Field, with a string as its value.
        """ ;
        sh:path rml:fieldName ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:datatype xsd:string ;
        sh:nodeKind sh:Literal ;
    ] ;
    sh:property [
        sh:description """
        A child field of the iterable field.
        """ ;
        sh:message """
        The value of a rml:field property for a rml:Field must be a resource that describes an rml:ExpressionField.
        """ ;
        sh:path rml:field ;
        sh:minCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] .

rmlsh:LogicalViewJoinShape a sh:NodeShape ;
    rdfs:label "LogicalViewJoin" ;
    rdfs:comment """
    A LogicalViewJoin is an operation that adapts the logical iterations of one logical view (the child logical view) with fields from another logical view (the parent logical view).
    """ ;
    sh:message """
    Node must have a valid rml:LogicalViewJoin description.
    rml:LogicalViewJoin requires:
    - exactly one rml:parentLogicalView property.
    - one or more rml:joinCondition properties.
    - one or more rml:field properties.
    Furthermore, rml:Field requires what an rml:ExpressionMap requires.
    """ ;
    sh:targetClass rml:LogicalViewJoin ;
    sh:targetObjectsOf rml:leftJoin , rml:innerJoin ;
    sh:property [
        sh:description """
        The parent logical view of the logical view join.
        """ ;
        sh:message """
        Exactly one rml:parentLogicalView property must be specified for a rml:LogicalViewJoin, with a resource as its value.
        """ ;
        sh:path rml:parentLogicalView ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] , [
        sh:description """
        A join condition of the logical view join.
        """ ;
        sh:message """
        At least one rml:parentLogicalView property must be specified for a rml:LogicalViewJoin, with a resource as its value.
        """ ;
        sh:path rml:joinCondition ;
        sh:minCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] , [
        sh:description """
        A field of the logical view join.
        """ ;
        sh:message """
        At least one rml:field property must be specified for a rml:LogicalViewJoin, with a leaf expression field as its value.
        """ ;
        sh:path rml:field ;
        sh:minCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI ;
        sh:node rmlsh:LeafExpressionFieldShape ;
    ] .

rmlsh:LeafExpressionFieldShape a sh:NodeShape ;
    rdfs:label "Leaf Expression Field" ;
    rdfs:comment """
    A leaf expression field is an expression field without nested fields.
    """ ;
    sh:message """
    Node must have a valid rml:ExpressionField description.
    """ ;
    sh:node rmlsh:ExpressionFieldShape ;
    sh:property [
        sh:description """
        A leaf expression field is an expression field without nested fields.
        """ ;
        sh:message """
        A leaf expression field has no nested fields.
        """ ;
        sh:path rml:field ;
        sh:maxCount 0 ;
    ] .

rmlsh:StructuralAnnotationShape a sh:NodeShape;
    rdfs:label "StructuralAnnotation" ;
    rdfs:comment """
    Structural annotations provide a mechanism to express relationships between logical views, as well as additional information about fields.
    """ ;
    sh:message """
    Node must have a valid rml:StructuralAnnotationShape description.
    All structural annotations of a logical view lv MUST have an on fields property (rml:onFields), linking the structural annotation to a list of field names occurring in lv.
    rml:StructuralAnnotationShape requires:
    - exactly one rml:onFields property.
    """ ;
    sh:targetClass rml:StructuralAnnotation ;
    sh:targetObjectsOf rml:structuralAnnotation ;
    sh:property [
        sh:description """
        property onFields specifies the fields in LogicalView that are involved by the structural annotation. The semantics of this involvement depends on the specific annotation.
        """ ;
        sh:message """
        Exactly one rml:onFields property must be specified for each rml:StructuralAnnotation
        """ ;
        sh:path rml:onFields ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] .

rmlsh:InclusionAnnotationShape a sh:NodeShape;
    rdfs:label "InclusionAnnotation" ;
    rdfs:comment """
    The InclusionAnnotaton annotation (rml:InclusionAnnotation) is analogous to the notion of inclusion dependency in databases.
    """ ;
    sh:message """
    Node must have a valid rml:InclusionAnnotation description.
    Each InclusionAnnotation annotation requires (additionally to the inherited `rml:onFields` property):
    - Exactly one `rml:targetView` property
    - Exactly one `rml:targetFields` property.
    """ ;
    sh:targetClass rml:InclusionAnnotation ;
    sh:property [
        sh:description """
        property targetView specifies the LogicalView target of the InclusionAnnotation annotation.
        """ ;
        sh:message """
        Exactly one rml:targetView property must be specified for each rml:InclusionAnnotation.
        """ ;
        sh:path rml:targetView ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] , [
        sh:description """
        property targetFields specifies the Fields target of the InclusionAnnotation.
        """ ;
        sh:message """
        Exactly one rml:targetFields property must be specified for each rml:InclusionAnnotation.
        """ ;
        sh:path rml:targetFields ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:nodeKind sh:BlankNodeOrIRI ;
    ] .

rmlsh:parentCyclesShape a sh:NodeShape ;
    sh:targetSubjectsOf rml:viewOn ;
    sh:targetSubjectsOf rml:field ;
    sh:property [
        sh:message "There must be no cycles via rml:viewOn or rml:field." ;
        sh:path [ sh:zeroOrMorePath [ sh:alternativePath ( [ sh:inversePath rml:viewOn ] [ sh:inversePath rml:field ] ) ] ] ;
        sh:disjoint rml:viewOn ;
        sh:disjoint rml:field ;
    ] .

rmlsh:joinCyclesShape a sh:NodeShape ;
    sh:targetSubjectsOf rml:viewOn ;
    sh:property [
        sh:message "There must be no cycles via rml:viewOn and rml:leftJoin or rml:innerJoin." ;
        sh:path ( [ sh:zeroOrMorePath ( [ sh:inversePath rml:parentLogicalView ] [ sh:alternativePath ( [ sh:inversePath rml:leftJoin ] [ sh:inversePath rml:innerJoin ] ) ] ) ] [ sh:inversePath rml:parentLogicalView ] ) ;
        sh:disjoint rml:leftJoin ;
        sh:disjoint rml:innerJoin ;
    ] .
