BASE                <https://w3id.org/itsdata/core/v1/>
PREFIX :            <https://w3id.org/itsdata/core/v1/>
PREFIX dcterms:     <http://purl.org/dc/terms/>
PREFIX owl:         <http://www.w3.org/2002/07/owl#>
PREFIX rdf:         <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX sh:          <http://www.w3.org/ns/shacl#>
PREFIX skos:        <http://www.w3.org/2004/02/skos/core#>
PREFIX xsd:         <http://www.w3.org/2001/XMLSchema#>
PREFIX i72:         <https://w3id.org/citydata/21972/v1/>
PREFIX cdm1:        <https://w3id.org/citydata/part1/v1/>
PREFIX cdm2:        <https://w3id.org/citydata/part2/v1/>
PREFIX vann:        <http://purl.org/vocab/vann/>

# =============================================
# Reusable SHACL Shapes Library (Core)
# =============================================

:CoreSHACL a owl:Ontology ;
    # Ontology metadata
    dcterms:title                  "ITS Core SHACL Library"@en;
    skos:definition                "Reusable SHACL shapes shared across ITS ontology topic areas."@en;
    dcterms:license                "http://creativecommons.org/licenses/by/4.0/";
    :draft                 "true";
    vann:preferredNamespaceUri     "https://w3id.org/itsdata/core/v1/";
    vann:preferredNamespacePrefix  "its-core";
    
    #Provenance metadata
    dcterms:creator                "Kenneth Vaughn";
    dcterms:created                "Draft";
    dcterms:modified               "2026-07-27"^^xsd:date ;
    owl:versionInfo                "0.0.3-alpha.4" ;
    owl:versionIRI                 <https://w3id.org/itsdata/core/v1/0.0.3-alpha.4> ;
    owl:priorVersion               <https://w3id.org/itsdata/core/v1/0.0.3-alpha.3> .

# ================================================
#                 Reusable Shapes
# ================================================

:ExactlyOneShape a sh:NodeShape ;
    sh:name "exactly 1" ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:message "Exactly one value is required." .

:MaxOneShape a sh:NodeShape ;
    sh:name "max 1" ;
    sh:maxCount 1 ;
    sh:message "At most one value is allowed." .

:MinOneShape a sh:NodeShape ;
    sh:name "min 1" ;
    sh:minCount 1 ;
    sh:message "At least one value is required." .

:UnlimitedShape a sh:NodeShape ;
    sh:name "0..*" ;
    # default 0..* – no minCount / maxCount
    sh:message "Property is optional and may have any number of values." .

:ExactlyOneMultilingualShape a sh:NodeShape ;
    sh:name "exactly 1 multilingual" ;
    sh:nodeKind sh:Literal ;
    sh:datatype rdf:langString ;
    sh:uniqueLang true ;
    sh:minCount 1 ;
    sh:message "At least one language string is required and only one per language is allowed." .

:MaxOneMultilingualShape a sh:NodeShape ;
    sh:name "max 1 multilingual" ;
    sh:nodeKind sh:Literal ;
    sh:datatype rdf:langString ;
    sh:uniqueLang true ;
    sh:message "Only one language string per language is allowed for this property." .

:MinOneMultilingualShape a sh:NodeShape ;
    sh:name "min 1 multilingual" ;
    sh:nodeKind sh:Literal ;
    sh:datatype rdf:langString ;
    sh:minCount 1 ;
    sh:message "At least one language string is required." .

:OidShape a sh:NodeShape ;
    sh:name "OID" ;
    sh:datatype xsd:string ;    # enforces literal type
    sh:pattern "^(0|1)(\\.(([1-3]{0,1})[0-9]))(\\.(0|[1-9][0-9]*))*|2(\\.(0|[1-9][0-9]*))+$" ;
     # Pattern explanation:
     # - At least two arcs total (e.g., "1.3")
     # - First arc: 0, 1, or 2
     # - If first arc is 0 or 1: second arc 0-39; if first arc is 2: second arc ≥0
     # - Subsequent arcs: non-negative integers with no leading zeros (except single '0')
     # - Allows for large integers in arcs (e.g., "2.999.1234567890")
    sh:message "Value must be a valid ASN.1 OID per ITU-T X.680 / ISO/IEC 8824-1." .

# ================================================
#                 Class-Specific Shapes
# ================================================

:AngleShape a sh:NodeShape ;
    sh:targetClass :Angle ;
    sh:property [
        sh:path i72:hasValue ;
        sh:node :ExactlyOneShape ;
        sh:datatype xsd:decimal ;
    ] ;
     sh:property [
        sh:path i72:hasUnit ;
        sh:node :ExactlyOneShape ;
        sh:class :AngleUnit ;
    ] .

:AngleUnitShape a sh:NodeShape ;
    sh:targetClass :AngleUnit ;
    sh:property [
        sh:path i72:hasUnit ;
        sh:node :ExactlyOneShape ;
        sh:class :AngleUnit ;
    ] .

:CodeShape
    a sh:NodeShape ;
    sh:targetClass :Code ;
    sh:property [
        sh:path :codeValue ;
        sh:node :ExactlyOneShape ;
        sh:datatype xsd:string ;
    ] ;
    sh:property [
        sh:path :fromCodeList ;
        sh:node :ExactlyOneShape ;
        sh:class :CodeList ;
    ] .

:CodeListShape
    a sh:NodeShape ;
    sh:targetClass :CodeList ;
    sh:property [
        sh:path cdm1:hasComponent ;
        sh:node :MinOneShape ;
        sh:class :Code ;
    ] .

:OfficialThingShape a sh:NodeShape ;
    sh:targetClass :OfficialThing ;
    sh:property [
        sh:path :issuingAuthority ;
        sh:node :ExactlyOneShape ;
        sh:class cdm1:Agent ;
    ] ;
    sh:property [
        sh:path :issuedOn ;
        sh:node :ExactlyOneShape ;
        sh:datatype xsd:dateTime ;
    ] ;
    sh:property [
        sh:path :enteredBy ;
        sh:node :ExactlyOneShape ;
        sh:datatype xsd:dateTime ;
    ] ;
    sh:property [
        sh:path :enteredOn ;
        sh:node :MaxOneShape ;
        sh:datatype xsd:dateTime ;
    ] ;
    sh:property [
        sh:path :hasName ;
        sh:node :UnlimitedShape ;
        sh:datatype rdf:langString ;
    ] .

:VersionedThingShape a sh:NodeShape ;
    sh:targetClass :VersionedThing ;
    sh:property [
        sh:path :hasVersion ;
        sh:node :ExactlyOneShape ;
        sh:datatype xsd:string ;
    ] ;
    sh:property [
        sh:path :managedBy ;
        sh:node :ExactlyOneShape ;
        sh:class cdm2:Organization ;
    ] ;
    sh:property [
        sh:path :lastModified ;
        sh:node :MaxOneShape ;
        sh:datatype xsd:date ;
    ] .