hydra-0.15.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.Constants as Constants
import qualified Hydra.Context as Context
import qualified Hydra.Core as Core
import qualified Hydra.Decode.Core as DecodeCore
import qualified Hydra.Encode.Core as EncodeCore
import qualified Hydra.Errors as Errors
import qualified Hydra.Extract.Core as ExtractCore
import qualified Hydra.Graph as Graph
import qualified Hydra.Lib.Eithers as Eithers
import qualified Hydra.Lib.Lists as Lists
import qualified Hydra.Lib.Literals as Literals
import qualified Hydra.Lib.Logic as Logic
import qualified Hydra.Lib.Maps as Maps
import qualified Hydra.Lib.Math as Math
import qualified Hydra.Lib.Maybes as Maybes
import qualified Hydra.Lib.Pairs as Pairs
import qualified Hydra.Lib.Sets as Sets
import qualified Hydra.Lib.Strings as Strings
import qualified Hydra.Packaging as Packaging
import qualified Hydra.Rdf.Syntax as Syntax
import qualified Hydra.Rdf.Utils as Utils
import qualified Hydra.Shacl.Model as Model
import qualified Hydra.Strip as Strip
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 :: [Model.CommonConstraint] -> Model.CommonProperties
common constraints =
Model.CommonProperties {
Model.commonPropertiesConstraints = (Sets.fromList constraints),
Model.commonPropertiesDeactivated = Nothing,
Model.commonPropertiesMessage = (Syntax.LangStrings Maps.empty),
Model.commonPropertiesSeverity = Model.SeverityInfo,
Model.commonPropertiesTargetClass = Sets.empty,
Model.commonPropertiesTargetNode = Sets.empty,
Model.commonPropertiesTargetObjectsOf = Sets.empty,
Model.commonPropertiesTargetSubjectsOf = Sets.empty}
-- | Default CommonProperties with empty constraints and default severity
defaultCommonProperties :: Model.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 -> Context.Context -> Graph.Graph -> Either Errors.Error ([Syntax.Triple], Context.Context)
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 (Model.Definition Model.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.TypeMaybe 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 = Maybes.map (\_n -> Model.PropertyShapeConstraintMinCount _n) mn
maxC = Maybes.map (\_n -> Model.PropertyShapeConstraintMaxCount _n) mx
in Model.Definition {
Model.definitionIri = iri,
Model.definitionTarget = Model.PropertyShape {
Model.propertyShapeCommon = _cp,
Model.propertyShapeConstraints = (Sets.fromList (Maybes.cat [
minC,
maxC])),
Model.propertyShapeDefaultValue = Nothing,
Model.propertyShapeDescription = (Syntax.LangStrings Maps.empty),
Model.propertyShapeName = (Syntax.LangStrings Maps.empty),
Model.propertyShapeOrder = order,
Model.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] -> Context.Context -> Graph.Graph -> Either Errors.Error ([Syntax.Description], Context.Context)
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)) (Maybes.maybe (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))))) (Lists.uncons terms))
-- | Encode a LiteralType as SHACL CommonProperties with an XSD datatype constraint
encodeLiteralType :: Core.LiteralType -> Model.CommonProperties
encodeLiteralType lt =
let xsd = \local -> common [
Model.CommonConstraintDatatype (Utils.xmlSchemaDatatypeIri local)]
in case lt of
Core.LiteralTypeBinary -> xsd "base64Binary"
Core.LiteralTypeBoolean -> xsd "boolean"
Core.LiteralTypeFloat v0 -> case v0 of
Core.FloatTypeBigfloat -> xsd "decimal"
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 -> Context.Context -> Graph.Graph -> Either Errors.Error ([Syntax.Description], Context.Context)
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 (Maybes.fromMaybe descs (Maybes.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.TermMaybe v0 -> Maybes.maybe (Right ([], cx)) (\_inner -> encodeTerm subject _inner cx g) v0
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 cx "RDF-compatible term" "unsupported term variant"
-- | Encode a Hydra type as SHACL CommonProperties
encodeType :: Core.Name -> Core.Type -> t0 -> Either Errors.Error Model.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 [
Model.CommonConstraintProperty (Sets.fromList (Lists.map (\_p -> Model.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 [
Model.CommonConstraintXone (Sets.fromList (Lists.map (\_p -> Model.ReferenceAnonymous (node [
Model.CommonConstraintProperty (Sets.fromList [
Model.ReferenceDefinition _p])])) _props))]) (Eithers.mapList (\_ft -> encodeFieldType tname Nothing _ft cx) v0)
Core.TypeUnit -> any
Core.TypeVariable v0 -> Right (common [
Model.CommonConstraintNode (Sets.fromList [
Model.ReferenceNamed (Utils.nameToIri v0)])])
_ -> unexpectedE cx "type" "unsupported type variant"
-- | Construct an error result with a context and message
err :: t0 -> String -> Either Errors.Error t1
err cx 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 =
Maybes.maybe (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)))) (Lists.uncons xs)
-- | Construct a SHACL node shape from a list of common constraints
node :: [Model.CommonConstraint] -> Model.Shape
node constraints = Model.ShapeNode (Model.NodeShape {
Model.nodeShapeCommon = (common constraints)})
-- | Construct a default property shape with the given IRI as its path
property :: Syntax.Iri -> Model.PropertyShape
property iri =
Model.PropertyShape {
Model.propertyShapeCommon = defaultCommonProperties,
Model.propertyShapeConstraints = Sets.empty,
Model.propertyShapeDefaultValue = Nothing,
Model.propertyShapeDescription = (Syntax.LangStrings Maps.empty),
Model.propertyShapeName = (Syntax.LangStrings Maps.empty),
Model.propertyShapeOrder = Nothing,
Model.propertyShapePath = iri}
-- | Encode a module's type elements as a SHACL ShapesGraph
shaclCoder :: Packaging.Module -> t0 -> Graph.Graph -> Either Errors.Error (Model.ShapesGraph, t0)
shaclCoder mod cx g =
let typeEls =
Maybes.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 = (Maps.fromList [
(Constants.key_type, 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.typeDefinitionTypeScheme 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 -> Model.Definition {
Model.definitionIri = (elementIri el),
Model.definitionTarget = (Model.ShapeNode (Model.NodeShape {
Model.nodeShapeCommon = _cp}))}) (encodeType (Core.bindingName el) _typ cx))
in (Eithers.map (\_shapes -> (Model.ShapesGraph (Sets.fromList _shapes), cx)) (Eithers.mapList toShape typeEls))
-- | Construct an error for unexpected input, given expected and found descriptions
unexpectedE :: t0 -> String -> String -> Either Errors.Error t1
unexpectedE cx expected found =
err cx (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))}