hydra-rdf-0.17.0: src/main/haskell/Hydra/Shacl/Coder.hs
-- Note: this is an automatically generated file. Do not edit.
-- | SHACL coder: converts Hydra types and terms to SHACL shapes and RDF descriptions
module Hydra.Shacl.Coder where
import qualified Hydra.Annotations as Annotations
import qualified Hydra.Ast as Ast
import qualified Hydra.Coders as Coders
import qualified Hydra.Constants as Constants
import qualified Hydra.Core as Core
import qualified Hydra.Decode.Core as DecodeCore
import qualified Hydra.Encode.Core as EncodeCore
import qualified Hydra.Error.Checking as Checking
import qualified Hydra.Error.Core as ErrorCore
import qualified Hydra.Error.Packaging as ErrorPackaging
import qualified Hydra.Errors as Errors
import qualified Hydra.Extract.Core as ExtractCore
import qualified Hydra.Formatting as Formatting
import qualified Hydra.Graph as Graph
import qualified Hydra.Json.Model as JsonModel
import qualified Hydra.Lexical as Lexical
import qualified Hydra.Overlay.Haskell.Lib.Eithers as Eithers
import qualified Hydra.Overlay.Haskell.Lib.Lists as Lists
import qualified Hydra.Overlay.Haskell.Lib.Literals as Literals
import qualified Hydra.Overlay.Haskell.Lib.Logic as Logic
import qualified Hydra.Overlay.Haskell.Lib.Maps as Maps
import qualified Hydra.Overlay.Haskell.Lib.Math as Math
import qualified Hydra.Overlay.Haskell.Lib.Optionals as Optionals
import qualified Hydra.Overlay.Haskell.Lib.Pairs as Pairs
import qualified Hydra.Overlay.Haskell.Lib.Sets as Sets
import qualified Hydra.Overlay.Haskell.Lib.Strings as Strings
import qualified Hydra.Names as Names
import qualified Hydra.Packaging as Packaging
import qualified Hydra.Parsing as Parsing
import qualified Hydra.Paths as Paths
import qualified Hydra.Query as Query
import qualified Hydra.Rdf.Syntax as Syntax
import qualified Hydra.Rdf.Utils as Utils
import qualified Hydra.Relational as Relational
import qualified Hydra.Shacl.Model as ShaclModel
import qualified Hydra.Strip as Strip
import qualified Hydra.Tabular as Tabular
import qualified Hydra.Testing as Testing
import qualified Hydra.Topology as Topology
import qualified Hydra.Typed as Typed
import qualified Hydra.Typing as Typing
import qualified Hydra.Util as Util
import qualified Hydra.Validation as Validation
import qualified Hydra.Variants as Variants
import Prelude hiding (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
import qualified Data.Scientific as Sci
-- | Construct CommonProperties from a list of constraints, using defaults for other fields
common :: [ShaclModel.CommonConstraint] -> ShaclModel.CommonProperties
common constraints =
ShaclModel.CommonProperties {
ShaclModel.commonPropertiesConstraints = (Sets.fromList constraints),
ShaclModel.commonPropertiesDeactivated = Nothing,
ShaclModel.commonPropertiesMessage = (Syntax.LangStrings Maps.empty),
ShaclModel.commonPropertiesSeverity = ShaclModel.SeverityInfo,
ShaclModel.commonPropertiesTargetClass = Sets.empty,
ShaclModel.commonPropertiesTargetNode = Sets.empty,
ShaclModel.commonPropertiesTargetObjectsOf = Sets.empty,
ShaclModel.commonPropertiesTargetSubjectsOf = Sets.empty}
-- | Default CommonProperties with empty constraints and default severity
defaultCommonProperties :: ShaclModel.CommonProperties
defaultCommonProperties = common []
-- | Convert a binding's name to an RDF IRI
elementIri :: Core.Binding -> Syntax.Iri
elementIri el = Utils.nameToIri (Core.bindingName el)
-- | Encode a record field as RDF triples with a given subject
encodeField :: Core.Name -> Syntax.Resource -> Core.Field -> Int -> Graph.Graph -> Either Errors.Error ([Syntax.Triple], Int)
encodeField rname subject field cx g =
let pair1 = Utils.nextBlankNode cx
node = Pairs.first pair1
cx1 = Pairs.second pair1
in (Eithers.bind (encodeTerm node (Core.fieldTerm field) cx1 g) (\_r1 ->
let descs = Pairs.first _r1
cx2 = Pairs.second _r1
in (Right (
Lists.concat2 (Utils.triplesOf descs) (Utils.forObjects subject (Utils.propertyIri rname (Core.fieldName field)) (Utils.subjectsOf descs)),
cx2))))
-- | Encode a FieldType as a SHACL property shape Definition
encodeFieldType :: Core.Name -> Maybe Integer -> Core.FieldType -> t0 -> Either Errors.Error (ShaclModel.Definition ShaclModel.PropertyShape)
encodeFieldType rname order ft cx =
let fname = Core.fieldTypeName ft
ftype = Core.fieldTypeType ft
iri = Utils.propertyIri rname fname
forType =
\mn -> \mx -> \t -> case (Strip.deannotateType t) of
Core.TypeOptional v0 -> forType (Just 0) mx v0
Core.TypeSet v0 -> forType mn Nothing v0
_ -> forTypeDefault mn mx t
forTypeDefault =
\mn -> \mx -> \t -> Eithers.map (\_cp ->
let baseProp = property iri
minC = Optionals.map (\_n -> ShaclModel.PropertyShapeConstraintMinCount _n) mn
maxC = Optionals.map (\_n -> ShaclModel.PropertyShapeConstraintMaxCount _n) mx
in ShaclModel.Definition {
ShaclModel.definitionIri = iri,
ShaclModel.definitionTarget = ShaclModel.PropertyShape {
ShaclModel.propertyShapeCommon = _cp,
ShaclModel.propertyShapeConstraints = (Sets.fromList (Optionals.cat [
minC,
maxC])),
ShaclModel.propertyShapeDefaultValue = Nothing,
ShaclModel.propertyShapeDescription = (Syntax.LangStrings Maps.empty),
ShaclModel.propertyShapeName = (Syntax.LangStrings Maps.empty),
ShaclModel.propertyShapeOrder = order,
ShaclModel.propertyShapePath = iri}}) (encodeType rname t cx)
in (forType (Just 1) (Just 1) ftype)
-- | Encode a list of terms as RDF list structure
encodeList :: Syntax.Resource -> [Core.Term] -> Int -> Graph.Graph -> Either Errors.Error ([Syntax.Description], Int)
encodeList subj terms cx0 g =
Logic.ifElse (Lists.null terms) (Right (
[
Syntax.Description {
Syntax.descriptionSubject = (Syntax.NodeIri (Syntax.Iri "http://www.w3.org/1999/02/22-rdf-syntax-ns#nil")),
Syntax.descriptionGraph = (Syntax.Graph Sets.empty)}],
cx0)) (Optionals.cases (Lists.uncons terms) (Right ([], cx0)) (\p ->
let pair1 = Utils.nextBlankNode cx0
node1 = Pairs.first pair1
cx1 = Pairs.second pair1
in (Eithers.bind (encodeTerm node1 (Pairs.first p) cx1 g) (\_r1 ->
let fdescs = Pairs.first _r1
cx2 = Pairs.second _r1
firstTriples = Lists.concat2 (Utils.triplesOf fdescs) (Utils.forObjects subj (Utils.rdfIri "first") (Utils.subjectsOf fdescs))
pair2 = Utils.nextBlankNode cx2
next = Pairs.first pair2
cx3 = Pairs.second pair2
in (Eithers.map (\_r2 ->
let rdescs = Pairs.first _r2
cx4 = Pairs.second _r2
restTriples = Lists.concat2 (Utils.triplesOf rdescs) (Utils.forObjects subj (Utils.rdfIri "rest") (Utils.subjectsOf rdescs))
in (
[
Syntax.Description {
Syntax.descriptionSubject = (Utils.resourceToNode subj),
Syntax.descriptionGraph = (Syntax.Graph (Sets.fromList (Lists.concat2 firstTriples restTriples)))}],
cx4)) (encodeList next (Pairs.second p) cx3 g))))))
-- | Encode a LiteralType as SHACL CommonProperties with an XSD datatype constraint
encodeLiteralType :: Core.LiteralType -> ShaclModel.CommonProperties
encodeLiteralType lt =
let xsd = \local -> common [
ShaclModel.CommonConstraintDatatype (Utils.xmlSchemaDatatypeIri local)]
in case lt of
Core.LiteralTypeBinary -> xsd "base64Binary"
Core.LiteralTypeBoolean -> xsd "boolean"
Core.LiteralTypeFloat v0 -> case v0 of
Core.FloatTypeFloat32 -> xsd "float"
Core.FloatTypeFloat64 -> xsd "double"
Core.LiteralTypeInteger v0 -> case v0 of
Core.IntegerTypeBigint -> xsd "integer"
Core.IntegerTypeInt8 -> xsd "byte"
Core.IntegerTypeInt16 -> xsd "short"
Core.IntegerTypeInt32 -> xsd "int"
Core.IntegerTypeInt64 -> xsd "long"
Core.IntegerTypeUint8 -> xsd "unsignedByte"
Core.IntegerTypeUint16 -> xsd "unsignedShort"
Core.IntegerTypeUint32 -> xsd "unsignedInt"
Core.IntegerTypeUint64 -> xsd "unsignedLong"
Core.LiteralTypeString -> xsd "string"
-- | Encode a Hydra term as a list of RDF Descriptions
encodeTerm :: Syntax.Resource -> Core.Term -> Int -> Graph.Graph -> Either Errors.Error ([Syntax.Description], Int)
encodeTerm subject term cx g =
case term of
Core.TermAnnotated v0 -> encodeTerm subject (Core.annotatedTermBody v0) cx g
Core.TermList v0 -> encodeList subject v0 cx g
Core.TermLiteral v0 -> Right (
[
Syntax.Description {
Syntax.descriptionSubject = (Syntax.NodeLiteral (Utils.encodeLiteral v0)),
Syntax.descriptionGraph = (Syntax.Graph Sets.empty)}],
cx)
Core.TermMap v0 -> Eithers.map (\_r -> (
[
Syntax.Description {
Syntax.descriptionSubject = (Utils.resourceToNode subject),
Syntax.descriptionGraph = (Syntax.Graph (Sets.fromList (Lists.concat (Pairs.first _r))))}],
(Pairs.second _r))) (foldAccumResult (\_cx0 -> \kv -> Eithers.bind (ExtractCore.string g (Strip.deannotateTerm (Pairs.first kv))) (\_ks ->
let pair2 = Utils.nextBlankNode _cx0
node2 = Pairs.first pair2
cx2 = Pairs.second pair2
in (Eithers.map (\_dr -> (
Lists.concat2 (Utils.forObjects subject (Utils.keyIri _ks) (Utils.subjectsOf (Pairs.first _dr))) (Utils.triplesOf (Pairs.first _dr)),
(Pairs.second _dr))) (encodeTerm node2 (Pairs.second kv) cx2 g)))) cx (Maps.toList v0))
Core.TermWrap v0 -> Eithers.map (\_dr ->
let descs = Pairs.first _dr
cx1 = Pairs.second _dr
in (
Optionals.fromOptional descs (Optionals.map (\p -> Lists.cons (withType (Core.wrappedTermTypeName v0) (Pairs.first p)) (Pairs.second p)) (Lists.uncons descs)),
cx1)) (encodeTerm subject (Core.wrappedTermBody v0) cx g)
Core.TermOptional v0 -> Optionals.cases v0 (Right ([], cx)) (\_inner -> encodeTerm subject _inner cx g)
Core.TermRecord v0 ->
let rname = Core.recordTypeName v0
fields = Core.recordFields v0
in (Eithers.map (\_r -> (
[
withType rname (Syntax.Description {
Syntax.descriptionSubject = (Utils.resourceToNode subject),
Syntax.descriptionGraph = (Syntax.Graph (Sets.fromList (Lists.concat (Pairs.first _r))))})],
(Pairs.second _r))) (foldAccumResult (\_cx0 -> \field -> encodeField rname subject field _cx0 g) cx fields))
Core.TermSet v0 -> Eithers.map (\_r -> (Lists.concat (Pairs.first _r), (Pairs.second _r))) (foldAccumResult (\_cx0 -> \t ->
let pair3 = Utils.nextBlankNode _cx0
node3 = Pairs.first pair3
cx3 = Pairs.second pair3
in (encodeTerm node3 t cx3 g)) cx (Sets.toList v0))
Core.TermInject v0 ->
let rname = Core.injectionTypeName v0
field = Core.injectionField v0
in (Eithers.map (\_r -> (
[
withType rname (Syntax.Description {
Syntax.descriptionSubject = (Utils.resourceToNode subject),
Syntax.descriptionGraph = (Syntax.Graph (Sets.fromList (Pairs.first _r)))})],
(Pairs.second _r))) (encodeField rname subject field cx g))
_ -> unexpectedE "RDF-compatible term" "unsupported term variant"
-- | Encode a Hydra type as SHACL CommonProperties
encodeType :: Core.Name -> Core.Type -> t0 -> Either Errors.Error ShaclModel.CommonProperties
encodeType tname typ cx =
let any = Right (common [])
in case (Strip.deannotateType typ) of
Core.TypeEither _ -> any
Core.TypeList _ -> any
Core.TypeLiteral v0 -> Right (encodeLiteralType v0)
Core.TypeMap _ -> any
Core.TypePair _ -> any
Core.TypeWrap _ -> any
Core.TypeRecord v0 -> Eithers.map (\_props -> common [
ShaclModel.CommonConstraintProperty (Sets.fromList (Lists.map (\_p -> ShaclModel.ReferenceDefinition _p) _props))]) (Eithers.mapList (\_pair -> encodeFieldType tname (Just (Pairs.first _pair)) (Pairs.second _pair) cx) (Lists.zip (Lists.map (\_i -> Literals.int32ToBigint _i) (Math.range 0 (Lists.length v0))) v0))
Core.TypeSet _ -> any
Core.TypeUnion v0 -> Eithers.map (\_props -> common [
ShaclModel.CommonConstraintXone (Sets.fromList (Lists.map (\_p -> ShaclModel.ReferenceAnonymous (node [
ShaclModel.CommonConstraintProperty (Sets.fromList [
ShaclModel.ReferenceDefinition _p])])) _props))]) (Eithers.mapList (\_ft -> encodeFieldType tname Nothing _ft cx) v0)
Core.TypeUnit -> any
Core.TypeVariable v0 -> Right (common [
ShaclModel.CommonConstraintNode (Sets.fromList [
ShaclModel.ReferenceNamed (Utils.nameToIri v0)])])
_ -> unexpectedE "type" "unsupported type variant"
-- | Construct an error result with the given message
err :: String -> Either Errors.Error t0
err msg = Left (Errors.ErrorOther (Errors.OtherError msg))
-- | Fold over a list, accumulating results and threading context through each step
foldAccumResult :: (t0 -> t1 -> Either t2 (t3, t0)) -> t0 -> [t1] -> Either t2 ([t3], t0)
foldAccumResult f cx xs =
Optionals.cases (Lists.uncons xs) (Right ([], cx)) (\p -> Eithers.bind (f cx (Pairs.first p)) (\_r -> Eithers.map (\_rest -> (Lists.cons (Pairs.first _r) (Pairs.first _rest), (Pairs.second _rest))) (foldAccumResult f (Pairs.second _r) (Pairs.second p))))
-- | Construct a SHACL node shape from a list of common constraints
node :: [ShaclModel.CommonConstraint] -> ShaclModel.Shape
node constraints = ShaclModel.ShapeNode (ShaclModel.NodeShape {
ShaclModel.nodeShapeCommon = (common constraints)})
-- | Construct a default property shape with the given IRI as its path
property :: Syntax.Iri -> ShaclModel.PropertyShape
property iri =
ShaclModel.PropertyShape {
ShaclModel.propertyShapeCommon = defaultCommonProperties,
ShaclModel.propertyShapeConstraints = Sets.empty,
ShaclModel.propertyShapeDefaultValue = Nothing,
ShaclModel.propertyShapeDescription = (Syntax.LangStrings Maps.empty),
ShaclModel.propertyShapeName = (Syntax.LangStrings Maps.empty),
ShaclModel.propertyShapeOrder = Nothing,
ShaclModel.propertyShapePath = iri}
-- | Encode a module's type elements as a SHACL ShapesGraph
shaclCoder :: Packaging.Module -> t0 -> Graph.Graph -> Either Errors.Error (ShaclModel.ShapesGraph, t0)
shaclCoder mod cx g =
let typeEls =
Optionals.cat (Lists.map (\d -> case d of
Packaging.DefinitionType v0 -> Just ((\name -> \typ ->
let schemaTerm = Core.TermVariable (Core.Name "hydra.core.Type")
dataTerm =
Annotations.normalizeTermAnnotations (Core.TermAnnotated (Core.AnnotatedTerm {
Core.annotatedTermBody = (EncodeCore.type_ typ),
Core.annotatedTermAnnotation = (Annotations.wrapAnnotationMap (Maps.fromList [
(Constants.keyType, schemaTerm)]))}))
in Core.Binding {
Core.bindingName = name,
Core.bindingTerm = dataTerm,
Core.bindingTypeScheme = (Just (Core.TypeScheme {
Core.typeSchemeVariables = [],
Core.typeSchemeBody = (Core.TypeVariable (Core.Name "hydra.core.Type")),
Core.typeSchemeConstraints = Nothing}))}) (Packaging.typeDefinitionName v0) (Core.typeSchemeBody (Packaging.typeDefinitionBody v0)))
_ -> Nothing) (Packaging.moduleDefinitions mod))
toShape =
\el -> Eithers.bind (Eithers.bimap (\_de -> Errors.ErrorOther (Errors.OtherError (Errors.unDecodingError _de))) (\_t -> _t) (DecodeCore.type_ g (Core.bindingTerm el))) (\_typ -> Eithers.map (\_cp -> ShaclModel.Definition {
ShaclModel.definitionIri = (elementIri el),
ShaclModel.definitionTarget = (ShaclModel.ShapeNode (ShaclModel.NodeShape {
ShaclModel.nodeShapeCommon = _cp}))}) (encodeType (Core.bindingName el) _typ cx))
in (Eithers.map (\_shapes -> (ShaclModel.ShapesGraph (Sets.fromList _shapes), cx)) (Eithers.mapList toShape typeEls))
-- | Construct an error for unexpected input, given expected and found descriptions
unexpectedE :: String -> String -> Either Errors.Error t0
unexpectedE expected found =
err (Strings.cat [
"Expected ",
expected,
", found: ",
found])
-- | Add an rdf:type triple to an RDF Description
withType :: Core.Name -> Syntax.Description -> Syntax.Description
withType name desc =
let subj = Syntax.descriptionSubject desc
triples = Syntax.unGraph (Syntax.descriptionGraph desc)
subjRes =
case subj of
Syntax.NodeIri v0 -> Syntax.ResourceIri v0
Syntax.NodeBnode v0 -> Syntax.ResourceBnode v0
triple =
Syntax.Triple {
Syntax.tripleSubject = subjRes,
Syntax.triplePredicate = (Utils.rdfIri "type"),
Syntax.tripleObject = (Syntax.NodeIri (Utils.nameToIri name))}
in Syntax.Description {
Syntax.descriptionSubject = subj,
Syntax.descriptionGraph = (Syntax.Graph (Sets.insert triple triples))}