# Glossarist SHACL Shapes
#
# Validation shapes for the Glossarist ontology.
# Validates instance data against the ontology constraints.
#
# Requires: SHACL vocabulary (http://www.w3.org/ns/shacl#)

@prefix gloss:  <https://www.glossarist.org/ontologies/> .
@prefix sh:     <http://www.w3.org/ns/shacl#> .
@prefix skos:   <http://www.w3.org/2004/02/skos/core#> .
@prefix skosxl: <http://www.w3.org/2008/05/skos-xl#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix xsd:    <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl:    <http://www.w3.org/2002/07/owl#> .
@prefix prov:   <http://www.w3.org/ns/prov#> .
@prefix foaf:   <http://xmlns.com/foaf/0.1/> .
@prefix dcat:   <http://www.w3.org/ns/dcat#> .

# ══════════════════════════════════════════════════════════════════════════
# Concept shapes
# ══════════════════════════════════════════════════════════════════════════

gloss:ConceptShape a sh:NodeShape ;
  sh:targetClass gloss:Concept ;
  sh:class skos:Concept ;
  sh:property [
    sh:path gloss:identifier ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:uri ;
    sh:datatype xsd:anyURI ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasLocalization ;
    sh:class gloss:LocalizedConcept ;
  ] ;
  sh:property [
    sh:path gloss:hasStatus ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/status> ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasRelatedConcept ;
    sh:class gloss:RelatedConcept ;
  ] ;
  sh:property [
    sh:path gloss:hasPartitiveHyperedge ;
    sh:class gloss:PartitiveHyperedge ;
  ] ;
  sh:property [
    sh:path gloss:hasDate ;
    sh:class gloss:ConceptDate ;
  ] ;
  sh:property [
    sh:path gloss:hasSource ;
    sh:class gloss:ConceptSource ;
  ] ;
  sh:property [
    sh:path gloss:hasDomain ;
    sh:class gloss:Reference ;
  ] ;
  sh:property [
    sh:path gloss:tag ;
    sh:datatype xsd:string ;
  ] .

gloss:LocalizedConceptShape a sh:NodeShape ;
  sh:targetClass gloss:LocalizedConcept ;
  sh:class skos:Concept ;
  sh:property [
    sh:path gloss:isLocalizationOf ;
    sh:class gloss:Concept ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:language ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasDesignation ;
    sh:class gloss:Designation ;
  ] ;
  sh:property [
    sh:path gloss:hasDefinition ;
    sh:class gloss:DetailedDefinition ;
  ] ;
  sh:property [
    sh:path gloss:hasNote ;
    sh:class gloss:DetailedDefinition ;
  ] ;
  sh:property [
    sh:path gloss:hasExample ;
    sh:class gloss:DetailedDefinition ;
  ] ;
  sh:property [
    sh:path gloss:hasAnnotation ;
    sh:class gloss:DetailedDefinition ;
  ] ;
  sh:property [
    sh:path gloss:hasEntryStatus ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/entstatus> ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasRelatedConcept ;
    sh:class gloss:RelatedConcept ;
  ] ;
  sh:property [
    sh:path gloss:hasDate ;
    sh:class gloss:ConceptDate ;
  ] ;
  sh:property [
    sh:path gloss:hasSource ;
    sh:class gloss:ConceptSource ;
  ] ;
  sh:property [
    sh:path gloss:hasReference ;
    sh:class gloss:Reference ;
  ] ;
  sh:property [
    sh:path gloss:classification ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:domain ;
    sh:datatype xsd:anyURI ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:script ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:conversionSystem ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:release ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:lineageSimilarity ;
    sh:datatype xsd:integer ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:reviewType ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
    sh:in ( "editorial" "substantive" ) ;
  ] .

gloss:ConceptCollectionShape a sh:NodeShape ;
  sh:targetClass gloss:ConceptCollection ;
  sh:class skos:Collection ;
  sh:property [
    sh:path skos:member ;
    sh:class gloss:Concept ;
  ] ;
  sh:property [
    sh:path dcterms:title ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# ══════════════════════════════════════════════════════════════════════════
# Designation shapes
# ══════════════════════════════════════════════════════════════════════════

gloss:DesignationShape a sh:NodeShape ;
  sh:targetClass gloss:Designation ;
  sh:class skosxl:Label ;
  sh:property [
    sh:path skosxl:literalForm ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:normativeStatus ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/norm> ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasTermType ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/termtype> ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasDesignationRelationship ;
    sh:class gloss:DesignationRelationship ;
  ] ;
  sh:property [
    sh:path gloss:hasSource ;
    sh:class gloss:ConceptSource ;
  ] ;
  sh:property [
    sh:path gloss:hasPronunciation ;
    sh:class gloss:Pronunciation ;
  ] ;
  sh:property [
    sh:path gloss:language ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:script ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:conversionSystem ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:isInternational ;
    sh:datatype xsd:boolean ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:isAbsent ;
    sh:datatype xsd:boolean ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:register ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

gloss:ExpressionShape a sh:NodeShape ;
  sh:targetClass gloss:Expression ;
  sh:property [
    sh:path gloss:hasGrammarInfo ;
    sh:class gloss:GrammarInfo ;
  ] .

gloss:AbbreviationShape a sh:NodeShape ;
  sh:targetClass gloss:Abbreviation ;
  sh:property [
    sh:path gloss:isAcronym ;
    sh:datatype xsd:boolean ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:isInitialism ;
    sh:datatype xsd:boolean ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:isTruncation ;
    sh:datatype xsd:boolean ;
    sh:maxCount 1 ;
  ] .

gloss:SymbolShape a sh:NodeShape ;
  sh:targetClass gloss:Symbol .

gloss:LetterSymbolShape a sh:NodeShape ;
  sh:targetClass gloss:LetterSymbol ;
  sh:property [
    sh:path gloss:text ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

gloss:GraphicalSymbolShape a sh:NodeShape ;
  sh:targetClass gloss:GraphicalSymbol ;
  sh:property [
    sh:path gloss:text ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:image ;
    sh:datatype xsd:anyURI ;
    sh:maxCount 1 ;
  ] .

gloss:PrefixShape a sh:NodeShape ;
  sh:targetClass gloss:Prefix .

gloss:SuffixShape a sh:NodeShape ;
  sh:targetClass gloss:Suffix .

gloss:DesignationRelationshipShape a sh:NodeShape ;
  sh:targetClass gloss:DesignationRelationship ;
  sh:property [
    sh:path gloss:designationRelationshipType ;
    sh:class skos:Concept ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:designationRelationshipContent ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:relationshipTarget ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# ══════════════════════════════════════════════════════════════════════════
# Supporting class shapes
# ══════════════════════════════════════════════════════════════════════════

gloss:PronunciationShape a sh:NodeShape ;
  sh:targetClass gloss:Pronunciation ;
  sh:property [
    sh:path gloss:pronunciationContent ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:pronunciationLanguage ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:pronunciationScript ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:pronunciationCountry ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:pronunciationSystem ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

gloss:DetailedDefinitionShape a sh:NodeShape ;
  sh:targetClass gloss:DetailedDefinition ;
  sh:property [
    sh:path rdf:value ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasSource ;
    sh:class gloss:ConceptSource ;
  ] ;
  sh:property [
    sh:path gloss:hasScopedExample ;
    sh:class gloss:DetailedDefinition ;
  ] .

gloss:ConceptSourceShape a sh:NodeShape ;
  sh:targetClass gloss:ConceptSource ;
  sh:property [
    sh:path gloss:sourceId ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:sourceType ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/srctype> ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:sourceStatus ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/srcstatus> ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:sourceOrigin ;
    sh:class gloss:Citation ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:modification ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:sourcedFrom ;
    sh:class gloss:Citation ;
  ] ;
  sh:sparql [
    sh:message "sourced_from is only allowed on sources of type lineage"@en ;
    sh:select """
      PREFIX gloss: <https://www.glossarist.org/ontologies/>
      PREFIX srctype: <https://www.glossarist.org/ontologies/srctype/>
      SELECT $this
      WHERE {
        $this gloss:sourcedFrom ?target .
        FILTER NOT EXISTS {
          $this gloss:sourceType srctype:lineage .
        }
      }
    """ ;
  ] .

gloss:ConceptDateShape a sh:NodeShape ;
  sh:targetClass gloss:ConceptDate ;
  sh:property [
    sh:path gloss:dateValue ;
    sh:datatype xsd:dateTime ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:dateType ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/datetype> ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:eventDescription ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

gloss:NonVerbalRepresentationShape a sh:NodeShape ;
  sh:targetClass gloss:NonVerbalRepresentation ;
  sh:property [
    sh:path gloss:representationType ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:in ( "image" "table" "formula" ) ;
  ] ;
  sh:property [
    sh:path gloss:representationRef ;
    sh:datatype xsd:anyURI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasSource ;
    sh:class gloss:ConceptSource ;
  ] .

# === K1: Non-verbal entity shapes ===
# Class declarations (NonVerbalEntity, Figure, Table, Formula, SharedNonVerbalEntity)
# live in glossarist.ttl — this file contains validation shapes only.

gloss:FigureShape a sh:NodeShape ;
  sh:targetClass gloss:Figure ;
  sh:property [
    sh:path gloss:image ;
    sh:datatype xsd:anyURI ;
    sh:minCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:caption ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path dcterms:description ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

gloss:TableShape a sh:NodeShape ;
  sh:targetClass gloss:Table ;
  sh:property [
    sh:path gloss:content ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:caption ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path dcterms:title ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

gloss:FormulaShape a sh:NodeShape ;
  sh:targetClass gloss:Formula ;
  sh:property [
    sh:path gloss:expression ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:latexForm ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path dcterms:description ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# === K2: Image binary references ===

gloss:ImageShape a sh:NodeShape ;
  sh:targetClass foaf:Image ;
  sh:property [
    sh:path dcterms:format ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
  ] ;
  sh:property [
    sh:path dcterms:language ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path dcat:byteSize ;
    sh:datatype xsd:integer ;
    sh:maxCount 1 ;
  ] .

# === K4: NonVerbalRepresentation (concept-level NVR) ===
# Class declaration lives in glossarist.ttl as gloss:NonVerbalRepresentation.

gloss:NonVerbalRepresentationShape a sh:NodeShape ;
  sh:targetClass gloss:NonVerbalRepresentation ;
  sh:property [
    sh:path skosxl:prefLabel ;
    sh:minCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:image ;
    sh:datatype xsd:anyURI ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path dcterms:description ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path prov:wasDerivedFrom ;
    sh:class prov:Entity ;
    sh:maxCount 1 ;
  ] .

gloss:ReferenceShape a sh:NodeShape ;
  sh:targetClass gloss:Reference ;
  sh:or (
    [ sh:property [ sh:path gloss:source ; sh:minCount 1 ] ]
    [ sh:property [ sh:path gloss:refId ; sh:minCount 1 ] ]
  ) ;
  sh:property [
    sh:path gloss:source ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:refId ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:refVersion ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:refLink ;
    sh:datatype xsd:anyURI ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:refType ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:urn ;
    sh:datatype xsd:anyURI ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:term ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasLocality ;
    sh:class gloss:Locality ;
    sh:maxCount 1 ;
  ] .

gloss:LocalityShape a sh:NodeShape ;
  sh:targetClass gloss:Locality ;
  sh:property [
    sh:path gloss:localityType ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:referenceFrom ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:referenceTo ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

gloss:CitationShape a sh:NodeShape ;
  sh:targetClass gloss:Citation ;
  sh:property [
    sh:path gloss:hasCitationRef ;
    sh:class gloss:CitationRef ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasCitationLocality ;
    sh:class gloss:Locality ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:citationLink ;
    sh:datatype xsd:anyURI ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:citationOriginal ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasCustomLocality ;
    sh:class gloss:CustomLocality ;
  ] .

gloss:CitationRefShape a sh:NodeShape ;
  sh:targetClass gloss:CitationRef ;
  sh:property [
    sh:path gloss:citationRefSource ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:citationRefId ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:citationRefVersion ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

gloss:ConceptRefShape a sh:NodeShape ;
  sh:targetClass gloss:ConceptRef ;
  sh:property [
    sh:path gloss:conceptRefSource ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:conceptRefId ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:conceptRefText ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

gloss:CustomLocalityShape a sh:NodeShape ;
  sh:targetClass gloss:CustomLocality ;
  sh:property [
    sh:path gloss:customLocalityName ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:customLocalityValue ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] .

gloss:RelatedConceptShape a sh:NodeShape ;
  sh:targetClass gloss:RelatedConcept ;
  sh:property [
    sh:path gloss:relationshipType ;
    sh:class skos:Concept ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:relationshipRef ;
    sh:class gloss:ConceptRef ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:relationshipContent ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# ══════════════════════════════════════════════════════════════════════════
# Partitive Hyperedge shapes
# ══════════════════════════════════════════════════════════════════════════

gloss:PartitiveHyperedgeShape a sh:NodeShape ;
  sh:targetClass gloss:PartitiveHyperedge ;
  sh:property [
    sh:path gloss:comprehensive ;
    sh:class gloss:ConceptRef ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasPart ;
    sh:class gloss:ConceptRef ;
    sh:minCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:enumeration ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/partitiveEnumeration> ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasPluralityMarker ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/pluralityMarker> ;
  ] ;
  sh:property [
    sh:path gloss:hyperedgeContent ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# Ownership invariant — soft (warning severity). A PartitiveHyperedge
# is *conventionally* carried by its comprehensive's own concept entry:
#   <concept/X> gloss:hasPartitiveHyperedge [
#     gloss:comprehensive [ a gloss:ConceptRef ; gloss:ref-id "X" ]
#   ] .
#
# Datasets in the wild may legitimately cross-reference (a concept
# carries a hyperedge whose comprehensive points elsewhere — e.g. for
# indexing). We do not forbid this; we warn so reviewers can confirm
# the cross-reference is intentional.
#
# The SPARQL constraint matches when the carrying concept's URI-ending
# identifier differs from the comprehensive ConceptRef's `gloss:id`
# literal.
gloss:PartitiveHyperedgeOwnershipShape a sh:NodeShape ;
  sh:targetClass gloss:PartitiveHyperedge ;
  sh:severity sh:Warning ;
  sh:message "partitive_hyperedge is carried by a concept that is not its comprehensive — confirm the cross-reference is intentional."@en ;
  sh:sparql [
    a sh:SPARQLConstraint ;
    sh:prefixes [
      sh:declare [
        sh:prefix "gloss" ;
        sh:namespace "https://www.glossarist.org/ontologies/"^^xsd:anyURI ;
      ] ;
      sh:declare [
        sh:prefix "xsd" ;
        sh:namespace "http://www.w3.org/2001/XMLSchema#"^^xsd:anyURI ;
      ] ;
    ] ;
    sh:select """
      SELECT $this ?carryingConcept ?compRef
      WHERE {
        ?carryingConcept gloss:hasPartitiveHyperedge $this .
        $this gloss:comprehensive ?compRef .
        # Two encoding shapes exist:
        #   (a) ?compRef is a gloss:ConceptRef node with gloss:conceptRefId
        #   (b) ?compRef is a direct URI ending in /<concept_id>
        # We bind ?compKey to whichever form is present, then compare
        # against the URI suffix of the carrying concept.
        OPTIONAL { ?compRef gloss:conceptRefId ?literalId . }
        BIND(COALESCE(?literalId, STRAFTER(STR(?compRef), CONCAT(STR(?carryingConcept), "/"))) AS ?compKey)
        FILTER (BOUND(?compKey) && !STRENDS(STR(?carryingConcept), CONCAT("/", ?compKey)))
      }
    """ ;
  ] .

# ── Enum scheme shapes ───────────────────────────────────────────────────
# PartitiveEnumeration and PluralityMarker are modelled as owl:Class
# but functionally are SKOS ConceptSchemes. The shape asserts the
# scheme contract: every instance must be a skos:ConceptScheme.

gloss:PartitiveEnumerationShape a sh:NodeShape ;
  sh:targetClass gloss:PartitiveEnumeration ;
  sh:class skos:ConceptScheme .

gloss:PluralityMarkerShape a sh:NodeShape ;
  sh:targetClass gloss:PluralityMarker ;
  sh:class skos:ConceptScheme .

# ── Non-verbal entity hierarchy ──────────────────────────────────────────
# Abstract base + shared dataset-wide entity. Concrete shapes
# (Figure, Table, Formula) live elsewhere in this file.

gloss:NonVerbalEntityShape a sh:NodeShape ;
  sh:targetClass gloss:NonVerbalEntity ;
  sh:property [
    sh:path gloss:caption ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:altText ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:description ;
    sh:datatype xsd:string ;
  ] ;
  sh:property [
    sh:path gloss:hasSource ;
    sh:class gloss:ConceptSource ;
  ] .

gloss:SharedNonVerbalEntityShape a sh:NodeShape ;
  sh:targetClass gloss:SharedNonVerbalEntity ;
  sh:class gloss:NonVerbalEntity ;
  sh:property [
    sh:path gloss:role ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

gloss:FigureImageShape a sh:NodeShape ;
  sh:targetClass gloss:FigureImage ;
  sh:property [
    sh:path gloss:src ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:format ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:hasSubfigure ;
    sh:class gloss:FigureImage ;
  ] .

gloss:GrammarInfoShape a sh:NodeShape ;
  sh:targetClass gloss:GrammarInfo ;
  sh:property [
    sh:path gloss:gender ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/gender> ;
  ] ;
  sh:property [
    sh:path gloss:number ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/number> ;
  ] ;
  sh:property [
    sh:path gloss:partOfSpeech ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] .

# ══════════════════════════════════════════════════════════════════════════
# Dataset Register shapes
# ══════════════════════════════════════════════════════════════════════════

gloss:DatasetRegisterShape a sh:NodeShape ;
  sh:targetClass gloss:DatasetRegister ;
  sh:property [
    sh:path gloss:datasetId ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:datasetUrn ;
    sh:datatype xsd:anyURI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:datasetRef ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:datasetYear ;
    sh:datatype xsd:integer ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:datasetStatus ;
    sh:datatype xsd:string ;
    sh:in ( "current" "superseded" "retired" ) ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:supersedesDataset ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:owner ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:sourceRepo ;
    sh:datatype xsd:anyURI ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:datasetLanguage ;
    sh:datatype xsd:string ;
  ] ;
  sh:property [
    sh:path gloss:hasSection ;
    sh:class gloss:Section ;
  ] ;
  sh:property [
    sh:path gloss:hasDefaultOrdering ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/orderingmethod> ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:logo ;
    sh:datatype xsd:anyURI ;
    sh:maxCount 1 ;
  ] .

gloss:SectionShape a sh:NodeShape ;
  sh:targetClass gloss:Section ;
  sh:property [
    sh:path gloss:sectionId ;
    sh:datatype xsd:string ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:sectionName ;
    sh:datatype xsd:string ;
  ] ;
  sh:property [
    sh:path gloss:hasChildSection ;
    sh:class gloss:Section ;
  ] ;
  sh:property [
    sh:path gloss:hasParentSection ;
    sh:class gloss:Section ;
    sh:maxCount 1 ;
  ] ;
  sh:property [
    sh:path gloss:sectionOrdering ;
    sh:class skos:Concept ;
    sh:valuesFrom <https://www.glossarist.org/ontologies/orderingmethod> ;
    sh:maxCount 1 ;
  ] .
