diff options
| author | Volpeon <github@volpeon.ink> | 2023-05-27 12:10:51 +0200 |
|---|---|---|
| committer | Volpeon <github@volpeon.ink> | 2023-05-27 12:10:51 +0200 |
| commit | 1bb6f74645e39bb45e33a7413771ea7f971628c9 (patch) | |
| tree | 7b8c2866ff9198264a99f4da60d9fe82a3bf21fb /src/Data/JLD/Expansion/Context.hs | |
| parent | Meta: Link to about page instead of raw readme (diff) | |
| download | hs-jsonld-1bb6f74645e39bb45e33a7413771ea7f971628c9.tar.gz hs-jsonld-1bb6f74645e39bb45e33a7413771ea7f971628c9.tar.bz2 hs-jsonld-1bb6f74645e39bb45e33a7413771ea7f971628c9.zip | |
Structural improvements
Diffstat (limited to 'src/Data/JLD/Expansion/Context.hs')
| -rw-r--r-- | src/Data/JLD/Expansion/Context.hs | 1020 |
1 files changed, 1020 insertions, 0 deletions
diff --git a/src/Data/JLD/Expansion/Context.hs b/src/Data/JLD/Expansion/Context.hs new file mode 100644 index 0000000..ce61644 --- /dev/null +++ b/src/Data/JLD/Expansion/Context.hs | |||
| @@ -0,0 +1,1020 @@ | |||
| 1 | module Data.JLD.Expansion.Context (BTDParams (..), EIParams (..), BACParams (..), buildTermDefinition, expandIri, buildActiveContext) where | ||
| 2 | |||
| 3 | import Data.JLD.Prelude | ||
| 4 | |||
| 5 | import Data.JLD.Control.Monad.RES (REST, withEnvRES, withErrorRES, withErrorRES', withStateRES) | ||
| 6 | import Data.JLD.Error (JLDError (..)) | ||
| 7 | import Data.JLD.Expansion.Global (JLDExpansionEnv (..), JLDExpansionState (..), JLDExpansionT, hoistEnv, modifyContextCache, modifyDocumentCache) | ||
| 8 | import Data.JLD.Model.ActiveContext (ActiveContext (..), containsProtectedTerm, lookupTerm, newActiveContext) | ||
| 9 | import Data.JLD.Model.Direction (Direction (..)) | ||
| 10 | import Data.JLD.Model.IRI (CompactIRI (..), endsWithGenericDelim, isBlankIri, parseCompactIri) | ||
| 11 | import Data.JLD.Model.Keyword (Keyword (..), allKeywords, isKeyword, isKeywordLike, isNotKeyword, parseKeyword) | ||
| 12 | import Data.JLD.Model.Language (Language (..)) | ||
| 13 | import Data.JLD.Model.TermDefinition (TermDefinition (..), newTermDefinition) | ||
| 14 | import Data.JLD.Model.URI (parseUri, uriToIri) | ||
| 15 | import Data.JLD.Options (ContextCache, Document (..), DocumentCache, DocumentLoader (..), JLDVersion (..)) | ||
| 16 | import Data.JLD.Util (flattenSingletonArray, valueContains, valueContainsAny, valueIsTrue, valueToArray) | ||
| 17 | |||
| 18 | import Control.Monad.Except (MonadError (..)) | ||
| 19 | import Data.Aeson (Object, Value (..)) | ||
| 20 | import Data.Aeson.Key qualified as K (fromText, toText) | ||
| 21 | import Data.Aeson.KeyMap qualified as KM (delete, keys, lookup, member, size) | ||
| 22 | import Data.Map.Strict qualified as M (delete, insert, lookup) | ||
| 23 | import Data.RDF (parseIRI, parseRelIRI, resolveIRI, serializeIRI, validateIRI) | ||
| 24 | import Data.Set qualified as S (insert, member, notMember, size) | ||
| 25 | import Data.Text qualified as T (drop, dropEnd, elem, findIndex, isPrefixOf, null, take, toLower) | ||
| 26 | import Data.Vector qualified as V (length) | ||
| 27 | import Text.URI (URI, isPathAbsolute, relativeTo) | ||
| 28 | import Text.URI qualified as U (render) | ||
| 29 | |||
| 30 | type BACT e m = REST (BACEnv e m) (Either (JLDError e) ()) BACState m | ||
| 31 | |||
| 32 | data BACEnv e m = BACEnv | ||
| 33 | { bacEnvGlobal :: JLDExpansionEnv e m | ||
| 34 | , bacEnvOverrideProtected :: Bool | ||
| 35 | , bacEnvValidateScopedContext :: Bool | ||
| 36 | , bacEnvPropagate :: Bool | ||
| 37 | } | ||
| 38 | deriving (Show) | ||
| 39 | |||
| 40 | data BACState = BACState | ||
| 41 | { bacStateGlobal :: JLDExpansionState | ||
| 42 | , bacStateActiveContext :: ActiveContext | ||
| 43 | , bacStateRemoteContexts :: Set Text | ||
| 44 | } | ||
| 45 | deriving (Show, Eq) | ||
| 46 | |||
| 47 | data BACParams = BACParams | ||
| 48 | { bacParamsOverrideProtected :: Bool | ||
| 49 | , bacParamsPropagate :: Bool | ||
| 50 | , bacParamsValidateScopedContext :: Bool | ||
| 51 | , bacParamsRemoteContexts :: Set Text | ||
| 52 | } | ||
| 53 | deriving (Show, Eq) | ||
| 54 | |||
| 55 | bacModifyContextCache :: Monad m => (ContextCache -> ContextCache) -> BACT e m () | ||
| 56 | bacModifyContextCache = modifyContextCache .> withStateRES bacStateGlobal (\s g -> s{bacStateGlobal = g}) | ||
| 57 | |||
| 58 | bacModifyDocumentCache :: Monad m => (DocumentCache -> DocumentCache) -> BACT e m () | ||
| 59 | bacModifyDocumentCache = modifyDocumentCache .> withStateRES bacStateGlobal (\s g -> s{bacStateGlobal = g}) | ||
| 60 | |||
| 61 | bacModifyActiveContext :: Monad m => (ActiveContext -> ActiveContext) -> BACT e m () | ||
| 62 | bacModifyActiveContext fn = modify \s -> s{bacStateActiveContext = fn (bacStateActiveContext s)} | ||
| 63 | |||
| 64 | bacModifyRemoteContexts :: Monad m => (Set Text -> Set Text) -> BACT e m () | ||
| 65 | bacModifyRemoteContexts fn = modify \s -> s{bacStateRemoteContexts = fn (bacStateRemoteContexts s)} | ||
| 66 | |||
| 67 | bacBuildTermDefinition :: Monad m => Object -> Maybe URI -> Text -> BACT e m () | ||
| 68 | bacBuildTermDefinition contextDefinition baseUrl term = do | ||
| 69 | BACEnv{..} <- ask | ||
| 70 | activeContext <- gets bacStateActiveContext | ||
| 71 | remoteContexts <- gets bacStateRemoteContexts | ||
| 72 | let params p = | ||
| 73 | p | ||
| 74 | { btdParamsBaseUrl = baseUrl | ||
| 75 | , btdParamsOverrideProtectedFlag = bacEnvOverrideProtected | ||
| 76 | , btdParamsProtectedFlag = contextDefinition |> KM.lookup (show KeywordProtected) .> maybe False valueIsTrue | ||
| 77 | , btdParamsRemoteContexts = remoteContexts | ||
| 78 | } | ||
| 79 | (activeContext', _) <- | ||
| 80 | buildTermDefinition activeContext contextDefinition term params | ||
| 81 | |> withEnvRES (const bacEnvGlobal) | ||
| 82 | |> withErrorRES Left | ||
| 83 | |> withStateRES bacStateGlobal (\bac global -> bac{bacStateGlobal = global}) | ||
| 84 | bacModifyActiveContext <| const activeContext' | ||
| 85 | |||
| 86 | bacBuildActiveContext :: Monad m => Value -> URI -> BACT e m () | ||
| 87 | bacBuildActiveContext context uri = do | ||
| 88 | BACEnv{..} <- ask | ||
| 89 | activeContext <- gets bacStateActiveContext | ||
| 90 | remoteContexts <- gets bacStateRemoteContexts | ||
| 91 | let params p = | ||
| 92 | p | ||
| 93 | { bacParamsValidateScopedContext = bacEnvValidateScopedContext | ||
| 94 | , bacParamsRemoteContexts = remoteContexts | ||
| 95 | } | ||
| 96 | activeContext' <- | ||
| 97 | buildActiveContext activeContext context (Just uri) params | ||
| 98 | |> withEnvRES (const bacEnvGlobal) | ||
| 99 | |> withErrorRES Left | ||
| 100 | |> withStateRES bacStateGlobal (\bac global -> bac{bacStateGlobal = global}) | ||
| 101 | bacModifyActiveContext <| const activeContext' | ||
| 102 | |||
| 103 | bacProcessItem :: Monad m => Maybe URI -> Value -> BACT e m () | ||
| 104 | bacProcessItem baseUrl item = do | ||
| 105 | BACEnv{..} <- ask | ||
| 106 | let JLDExpansionEnv{..} = hoistEnv (lift .> lift .> lift) bacEnvGlobal | ||
| 107 | |||
| 108 | result <- gets bacStateActiveContext | ||
| 109 | |||
| 110 | case item of | ||
| 111 | -- 5.1. | ||
| 112 | Null | ||
| 113 | -- 5.1.1. | ||
| 114 | | not bacEnvOverrideProtected && containsProtectedTerm result -> throwError <| Left InvalidContextNullification | ||
| 115 | -- 5.1.2. | ||
| 116 | | bacEnvPropagate -> | ||
| 117 | bacModifyActiveContext \ac -> newActiveContext \nac -> | ||
| 118 | nac | ||
| 119 | { activeContextBaseUrl = activeContextBaseUrl ac | ||
| 120 | , activeContextBaseIri = uriToIri =<< activeContextBaseUrl ac | ||
| 121 | } | ||
| 122 | | otherwise -> | ||
| 123 | bacModifyActiveContext \ac -> newActiveContext \nac -> | ||
| 124 | nac | ||
| 125 | { activeContextBaseUrl = activeContextBaseUrl ac | ||
| 126 | , activeContextBaseIri = uriToIri =<< activeContextBaseUrl ac | ||
| 127 | , activeContextPreviousContext = activeContextPreviousContext ac | ||
| 128 | } | ||
| 129 | -- 5.2. | ||
| 130 | String value -> bacFetchRemoteContext value baseUrl | ||
| 131 | -- 5.4. | ||
| 132 | Object contextDefinition -> do | ||
| 133 | -- 5.5. 5.5.1. 5.5.2. | ||
| 134 | case KM.lookup (show KeywordVersion) contextDefinition of | ||
| 135 | Just (String "1.1") | ||
| 136 | | JLD1_0 <- jldExpansionEnvProcessingMode -> throwError <| Left ProcessingModeConflict | ||
| 137 | | otherwise -> pure () | ||
| 138 | Just (Number 1.1) | ||
| 139 | | JLD1_0 <- jldExpansionEnvProcessingMode -> throwError <| Left ProcessingModeConflict | ||
| 140 | | otherwise -> pure () | ||
| 141 | Just value -> throwError <. Left <| InvalidKeywordValue KeywordVersion value | ||
| 142 | -- | ||
| 143 | Nothing -> pure () | ||
| 144 | |||
| 145 | -- 5.6. | ||
| 146 | contextDefinition' <- case KM.lookup (show KeywordImport) contextDefinition of | ||
| 147 | -- 5.6.1. | ||
| 148 | Just _ | JLD1_0 <- jldExpansionEnvProcessingMode -> throwError <| Left InvalidContextEntry | ||
| 149 | -- 5.6.3. | ||
| 150 | Just (String value) | ||
| 151 | | Just importUri <- parseUri value | ||
| 152 | , Just contextUri <- relativeTo importUri =<< baseUrl -> | ||
| 153 | runDocumentLoader jldExpansionEnvDocumentLoader contextUri >>= \case | ||
| 154 | Right (Object document) -> case KM.lookup (show KeywordContext) document of | ||
| 155 | Just (Object remoteContext) | ||
| 156 | -- 5.6.7. | ||
| 157 | | KM.member (show KeywordImport) remoteContext -> throwError <| Left InvalidContextEntry | ||
| 158 | -- 5.6.8. | ||
| 159 | | otherwise -> pure <| contextDefinition <> remoteContext | ||
| 160 | -- 5.6.6. | ||
| 161 | _ -> throwError <| Left InvalidRemoteContext | ||
| 162 | -- 5.6.6. | ||
| 163 | Right _ -> throwError <| Left InvalidRemoteContext | ||
| 164 | -- 5.6.5. | ||
| 165 | Left err -> throwError <. Left <| DocumentLoaderError err | ||
| 166 | -- 5.6.2. | ||
| 167 | Just value -> throwError <. Left <| InvalidKeywordValue KeywordImport value | ||
| 168 | -- | ||
| 169 | Nothing -> pure contextDefinition | ||
| 170 | |||
| 171 | -- 5.7. 5.7.1. | ||
| 172 | case KM.lookup (show KeywordBase) contextDefinition' of | ||
| 173 | -- 5.7.2. | ||
| 174 | Just Null -> bacModifyActiveContext \ac -> ac{activeContextBaseIri = Nothing} | ||
| 175 | Just (String "") -> pure () | ||
| 176 | Just (String value) | ||
| 177 | -- 5.7.3. | ||
| 178 | | Right iri <- parseIRI value -> bacModifyActiveContext \ac -> ac{activeContextBaseIri = Just iri} | ||
| 179 | -- 5.7.4. | ||
| 180 | | Just baseIri <- activeContextBaseIri result | ||
| 181 | , Right iri <- parseIRI =<< resolveIRI (serializeIRI baseIri) value -> | ||
| 182 | bacModifyActiveContext \ac -> ac{activeContextBaseIri = Just iri} | ||
| 183 | -- | ||
| 184 | Just _ -> throwError <| Left InvalidBaseIri | ||
| 185 | -- | ||
| 186 | Nothing -> pure () | ||
| 187 | |||
| 188 | -- 5.8. 5.8.1. | ||
| 189 | case KM.lookup (show KeywordVocab) contextDefinition' of | ||
| 190 | -- 5.8.2. | ||
| 191 | Just Null -> bacModifyActiveContext \ac -> ac{activeContextVocabularyMapping = Nothing} | ||
| 192 | -- 5.8.3. | ||
| 193 | Just (String value) | T.null value || isBlankIri value || isRight (parseIRI value) || isRight (parseRelIRI value) -> do | ||
| 194 | activeContext <- gets bacStateActiveContext | ||
| 195 | let params p = | ||
| 196 | p | ||
| 197 | { eiParamsVocab = True | ||
| 198 | , eiParamsDocumentRelative = True | ||
| 199 | } | ||
| 200 | (maybeVocabMapping, activeContext', _) <- | ||
| 201 | expandIri activeContext value params | ||
| 202 | |> withEnvRES (const bacEnvGlobal) | ||
| 203 | |> withErrorRES Left | ||
| 204 | |> withStateRES bacStateGlobal (\bac global -> bac{bacStateGlobal = global}) | ||
| 205 | bacModifyActiveContext <| const activeContext' | ||
| 206 | |||
| 207 | case maybeVocabMapping of | ||
| 208 | Just vocabMapping | isBlankIri vocabMapping || isRight (parseIRI vocabMapping) -> | ||
| 209 | bacModifyActiveContext \ac -> ac{activeContextVocabularyMapping = Just vocabMapping} | ||
| 210 | _ -> | ||
| 211 | throwError <| Left InvalidVocabMapping | ||
| 212 | Just _ -> throwError <| Left InvalidVocabMapping | ||
| 213 | -- | ||
| 214 | Nothing -> pure () | ||
| 215 | |||
| 216 | -- 5.9. 5.9.1. | ||
| 217 | case KM.lookup (show KeywordLanguage) contextDefinition' of | ||
| 218 | -- 5.9.2. | ||
| 219 | Just Null -> bacModifyActiveContext \ac -> ac{activeContextDefaultLanguage = Just NoLanguage} | ||
| 220 | -- 5.9.3. | ||
| 221 | Just (String language) -> bacModifyActiveContext \ac -> ac{activeContextDefaultLanguage = Just <| Language language} | ||
| 222 | Just _ -> throwError <| Left InvalidDefaultLanguage | ||
| 223 | -- | ||
| 224 | Nothing -> pure () | ||
| 225 | |||
| 226 | -- 5.10. 5.10.2. | ||
| 227 | case KM.lookup (show KeywordDirection) contextDefinition' of | ||
| 228 | -- 5.10.1. | ||
| 229 | Just _ | JLD1_0 <- jldExpansionEnvProcessingMode -> throwError <| Left InvalidContextEntry | ||
| 230 | -- 5.10.3. | ||
| 231 | Just Null -> bacModifyActiveContext \ac -> ac{activeContextDefaultBaseDirection = Nothing} | ||
| 232 | -- | ||
| 233 | Just (String (T.toLower -> "ltr")) -> bacModifyActiveContext \ac -> ac{activeContextDefaultBaseDirection = Just LTR} | ||
| 234 | Just (String (T.toLower -> "rtl")) -> bacModifyActiveContext \ac -> ac{activeContextDefaultBaseDirection = Just RTL} | ||
| 235 | Just _ -> throwError <| Left InvalidBaseDirection | ||
| 236 | -- | ||
| 237 | Nothing -> pure () | ||
| 238 | |||
| 239 | -- 5.11. | ||
| 240 | case KM.lookup (show KeywordPropagate) contextDefinition' of | ||
| 241 | -- 5.11.1. | ||
| 242 | Just _ | JLD1_0 <- jldExpansionEnvProcessingMode -> throwError <| Left InvalidContextEntry | ||
| 243 | Just (Bool _) -> pure () | ||
| 244 | Just invalid -> throwError <. Left <| InvalidKeywordValue KeywordPropagate invalid | ||
| 245 | -- | ||
| 246 | Nothing -> pure () | ||
| 247 | |||
| 248 | -- 5.13. | ||
| 249 | KM.keys contextDefinition' | ||
| 250 | |> fmap K.toText | ||
| 251 | .> filter | ||
| 252 | ( `isNotKeyword` | ||
| 253 | [ KeywordBase | ||
| 254 | , KeywordDirection | ||
| 255 | , KeywordImport | ||
| 256 | , KeywordLanguage | ||
| 257 | , KeywordPropagate | ||
| 258 | , KeywordProtected | ||
| 259 | , KeywordVersion | ||
| 260 | , KeywordVocab | ||
| 261 | ] | ||
| 262 | ) | ||
| 263 | .> mapM_ (bacBuildTermDefinition contextDefinition' baseUrl) | ||
| 264 | -- 5.3. | ||
| 265 | _ -> throwError <| Left InvalidLocalContext | ||
| 266 | |||
| 267 | bacFetchRemoteContext :: Monad m => Text -> Maybe URI -> BACT e m () | ||
| 268 | bacFetchRemoteContext url maybeBaseUrl | ||
| 269 | | Just uri <- parseUri url | ||
| 270 | , Just contextUri <- relativeTo uri =<< maybeBaseUrl -- 5.2.1. | ||
| 271 | , isPathAbsolute contextUri | ||
| 272 | , contextKey <- U.render contextUri = do | ||
| 273 | BACEnv{..} <- ask | ||
| 274 | let JLDExpansionEnv{..} = hoistEnv (lift .> lift .> lift) bacEnvGlobal | ||
| 275 | |||
| 276 | remoteContexts <- gets bacStateRemoteContexts | ||
| 277 | |||
| 278 | -- 5.2.2. | ||
| 279 | when (not bacEnvValidateScopedContext && S.member contextKey remoteContexts) <| throwError (Right ()) | ||
| 280 | |||
| 281 | -- 5.2.3. | ||
| 282 | when (S.size remoteContexts > jldExpansionEnvMaxRemoteContexts) <| throwError (Left ContextOverflow) | ||
| 283 | |||
| 284 | bacModifyRemoteContexts <| S.insert contextKey | ||
| 285 | |||
| 286 | -- 5.2.4. | ||
| 287 | gets (bacStateGlobal .> jldExpansionStateContextCache .> M.lookup contextKey) >>= \case | ||
| 288 | Just cachedContext -> do | ||
| 289 | bacBuildActiveContext cachedContext contextUri | ||
| 290 | throwError <| Right () | ||
| 291 | -- | ||
| 292 | Nothing -> pure () | ||
| 293 | |||
| 294 | -- 5.2.5. | ||
| 295 | document <- | ||
| 296 | gets (bacStateGlobal .> jldExpansionStateDocumentCache .> M.lookup contextKey) >>= \case | ||
| 297 | Just document -> pure document | ||
| 298 | Nothing -> | ||
| 299 | runDocumentLoader jldExpansionEnvDocumentLoader contextUri >>= \case | ||
| 300 | Right (Object document) -> pure <| Document contextUri document | ||
| 301 | -- 5.2.5.2. | ||
| 302 | Right _ -> throwError <| Left InvalidRemoteContext | ||
| 303 | -- 5.2.5.1. | ||
| 304 | Left err -> throwError <. Left <| DocumentLoaderError err | ||
| 305 | |||
| 306 | -- 5.2.5.3. | ||
| 307 | importedContext <- case KM.lookup (show KeywordContext) (documentContent document) of | ||
| 308 | Just (Object context) -> pure <. Object <. KM.delete (show KeywordBase) <| context | ||
| 309 | Just context -> pure context | ||
| 310 | Nothing -> throwError <| Left InvalidRemoteContext | ||
| 311 | |||
| 312 | bacModifyDocumentCache <| M.insert contextKey document | ||
| 313 | |||
| 314 | -- 5.2.6. | ||
| 315 | bacBuildActiveContext importedContext (documentUri document) | ||
| 316 | bacModifyContextCache <| M.insert contextKey importedContext | ||
| 317 | | otherwise = throwError <| Left LoadingRemoteContextError | ||
| 318 | |||
| 319 | buildActiveContext' :: Monad m => Value -> Maybe URI -> BACT e m () | ||
| 320 | buildActiveContext' localContext baseUrl = do | ||
| 321 | activeContext <- gets bacStateActiveContext | ||
| 322 | |||
| 323 | -- 1. | ||
| 324 | bacModifyActiveContext \ac -> ac{activeContextInverseContext = mempty} | ||
| 325 | |||
| 326 | -- 2. | ||
| 327 | propagate <- case localContext of | ||
| 328 | Object ctx | Just prop <- KM.lookup (show KeywordPropagate) ctx -> case prop of | ||
| 329 | Bool p -> pure p | ||
| 330 | _ -> throwError <. Left <| InvalidKeywordValue KeywordPropagate prop | ||
| 331 | _ -> asks bacEnvPropagate | ||
| 332 | |||
| 333 | -- 3. | ||
| 334 | previousContext <- gets <| activeContextPreviousContext <. bacStateActiveContext | ||
| 335 | when (not propagate && isNothing previousContext) do | ||
| 336 | bacModifyActiveContext \ac -> ac{activeContextPreviousContext = Just activeContext} | ||
| 337 | |||
| 338 | -- 4. 5. | ||
| 339 | forM_ (valueToArray localContext) | ||
| 340 | <| bacProcessItem baseUrl | ||
| 341 | .> withEnvRES (\env -> env{bacEnvPropagate = propagate}) | ||
| 342 | .> withErrorRES' (either (Left .> throwError) pure) | ||
| 343 | |||
| 344 | buildActiveContext :: Monad m => ActiveContext -> Value -> Maybe URI -> (BACParams -> BACParams) -> JLDExpansionT e m ActiveContext | ||
| 345 | buildActiveContext activeContext localContext baseUrl paramsFn = do | ||
| 346 | BACState{..} <- | ||
| 347 | (buildActiveContext' localContext baseUrl >> get) | ||
| 348 | |> withEnvRES env | ||
| 349 | |> withErrorRES' (either throwError (const get)) | ||
| 350 | |> withStateRES st (const bacStateGlobal) | ||
| 351 | pure bacStateActiveContext | ||
| 352 | where | ||
| 353 | BACParams{..} = | ||
| 354 | paramsFn | ||
| 355 | BACParams | ||
| 356 | { bacParamsOverrideProtected = False | ||
| 357 | , bacParamsPropagate = True | ||
| 358 | , bacParamsValidateScopedContext = True | ||
| 359 | , bacParamsRemoteContexts = mempty | ||
| 360 | } | ||
| 361 | |||
| 362 | env options = | ||
| 363 | BACEnv | ||
| 364 | { bacEnvGlobal = options | ||
| 365 | , bacEnvOverrideProtected = bacParamsOverrideProtected | ||
| 366 | , bacEnvValidateScopedContext = bacParamsValidateScopedContext | ||
| 367 | , bacEnvPropagate = bacParamsPropagate | ||
| 368 | } | ||
| 369 | |||
| 370 | st global = | ||
| 371 | BACState | ||
| 372 | { bacStateGlobal = global | ||
| 373 | , bacStateActiveContext = activeContext | ||
| 374 | , bacStateRemoteContexts = bacParamsRemoteContexts | ||
| 375 | } | ||
| 376 | |||
| 377 | -- | ||
| 378 | |||
| 379 | type EIT e m = REST (EIEnv e m) (JLDError e) EIState m | ||
| 380 | |||
| 381 | data EIEnv e m = EIEnv | ||
| 382 | { eiEnvGlobal :: JLDExpansionEnv e m | ||
| 383 | , eiEnvDocumentRelative :: Bool | ||
| 384 | , eiEnvVocab :: Bool | ||
| 385 | , eiEnvLocalContext :: Maybe Object | ||
| 386 | } | ||
| 387 | deriving (Show) | ||
| 388 | |||
| 389 | data EIState = EIState | ||
| 390 | { eiStateGlobal :: JLDExpansionState | ||
| 391 | , eiStateDefined :: Map Text Bool | ||
| 392 | , eiStateActiveContext :: ActiveContext | ||
| 393 | } | ||
| 394 | deriving (Show, Eq) | ||
| 395 | |||
| 396 | data EIParams = EIParams | ||
| 397 | { eiParamsDocumentRelative :: Bool | ||
| 398 | , eiParamsVocab :: Bool | ||
| 399 | , eiParamsLocalContext :: Maybe Object | ||
| 400 | , eiParamsDefined :: Map Text Bool | ||
| 401 | } | ||
| 402 | deriving (Show, Eq) | ||
| 403 | |||
| 404 | eiBuildTermDefinition :: Monad m => Text -> EIT e m () | ||
| 405 | eiBuildTermDefinition value = do | ||
| 406 | EIEnv{..} <- ask | ||
| 407 | defined <- gets eiStateDefined | ||
| 408 | activeContext <- gets eiStateActiveContext | ||
| 409 | let params p = p{btdParamsDefined = defined} | ||
| 410 | localContext = fromMaybe mempty eiEnvLocalContext | ||
| 411 | (activeContext', defined') <- | ||
| 412 | buildTermDefinition activeContext localContext value params | ||
| 413 | |> withEnvRES (const eiEnvGlobal) | ||
| 414 | |> withStateRES eiStateGlobal (\ei global -> ei{eiStateGlobal = global}) | ||
| 415 | modify \s -> | ||
| 416 | s | ||
| 417 | { eiStateActiveContext = activeContext' | ||
| 418 | , eiStateDefined = defined' | ||
| 419 | } | ||
| 420 | |||
| 421 | eiInitLocalContext :: Monad m => Text -> EIT e m () | ||
| 422 | eiInitLocalContext value = | ||
| 423 | -- 3. | ||
| 424 | asks eiEnvLocalContext >>= \case | ||
| 425 | Just localContext | Just (String entry) <- KM.lookup (K.fromText value) localContext -> do | ||
| 426 | defined <- gets eiStateDefined | ||
| 427 | when (maybe True not (M.lookup entry defined)) <| eiBuildTermDefinition value | ||
| 428 | _ -> pure () | ||
| 429 | |||
| 430 | eiInitPropertyContext :: Monad m => Text -> Text -> Text -> EIT e m Text | ||
| 431 | eiInitPropertyContext prefix suffix value = do | ||
| 432 | -- 6.3. | ||
| 433 | defined <- gets eiStateDefined | ||
| 434 | asks eiEnvLocalContext >>= \case | ||
| 435 | Just localContext | ||
| 436 | | KM.member (K.fromText prefix) localContext | ||
| 437 | , M.lookup prefix defined /= Just True -> | ||
| 438 | eiBuildTermDefinition prefix | ||
| 439 | _ -> pure () | ||
| 440 | |||
| 441 | -- 6.4. | ||
| 442 | gets (eiStateActiveContext .> lookupTerm prefix) >>= \case | ||
| 443 | Just prefixDefiniton | ||
| 444 | | Just iriMapping <- termDefinitionIriMapping prefixDefiniton | ||
| 445 | , termDefinitionPrefixFlag prefixDefiniton -> | ||
| 446 | pure <| iriMapping <> suffix | ||
| 447 | _ -> pure value | ||
| 448 | |||
| 449 | eiExpandResult :: Monad m => Text -> EIT e m (Maybe Text) | ||
| 450 | eiExpandResult value = do | ||
| 451 | EIEnv{..} <- ask | ||
| 452 | activeContext <- gets eiStateActiveContext | ||
| 453 | case activeContextVocabularyMapping activeContext of | ||
| 454 | -- 7. | ||
| 455 | Just vocabMapping | eiEnvVocab -> pure <. Just <| vocabMapping <> value | ||
| 456 | -- 8. | ||
| 457 | _ | ||
| 458 | | eiEnvDocumentRelative | ||
| 459 | , baseIri <- serializeIRI <$> activeContextBaseIri activeContext | ||
| 460 | , Right iri <- maybe (Right value) (`resolveIRI` value) baseIri -> | ||
| 461 | pure <| Just iri | ||
| 462 | -- 9. | ||
| 463 | _ -> pure <| Just value | ||
| 464 | |||
| 465 | expandIri' :: Monad m => Text -> EIT e m (Maybe Text) | ||
| 466 | expandIri' value | ||
| 467 | -- 1. | ||
| 468 | | Just _ <- parseKeyword value = pure <| Just value | ||
| 469 | -- 2. | ||
| 470 | | isKeywordLike value = pure Nothing | ||
| 471 | -- | ||
| 472 | | otherwise = do | ||
| 473 | EIEnv{..} <- ask | ||
| 474 | |||
| 475 | -- 3. | ||
| 476 | eiInitLocalContext value | ||
| 477 | |||
| 478 | gets (eiStateActiveContext .> lookupTerm value) >>= \case | ||
| 479 | -- 4. 5. | ||
| 480 | Just definition | ||
| 481 | | Just iriMapping <- termDefinitionIriMapping definition | ||
| 482 | , Just _ <- parseKeyword iriMapping -> | ||
| 483 | pure <| Just iriMapping | ||
| 484 | | eiEnvVocab -> | ||
| 485 | pure <| termDefinitionIriMapping definition | ||
| 486 | -- 6. 6.1. | ||
| 487 | _ | ||
| 488 | | Just idx <- (+ 1) <$> T.findIndex (== ':') (T.drop 1 value) | ||
| 489 | , prefix <- T.take idx value | ||
| 490 | , suffix <- T.drop (idx + 1) value -> | ||
| 491 | -- 6.2. | ||
| 492 | if "_" `T.isPrefixOf` prefix || "//" `T.isPrefixOf` suffix | ||
| 493 | then pure <| Just value | ||
| 494 | else do | ||
| 495 | value' <- eiInitPropertyContext prefix suffix value | ||
| 496 | |||
| 497 | if isBlankIri value' || isRight (validateIRI value') | ||
| 498 | then pure <| Just value' | ||
| 499 | else eiExpandResult value' | ||
| 500 | -- | ||
| 501 | _ -> eiExpandResult value | ||
| 502 | |||
| 503 | expandIri :: Monad m => ActiveContext -> Text -> (EIParams -> EIParams) -> JLDExpansionT e m (Maybe Text, ActiveContext, Map Text Bool) | ||
| 504 | expandIri activeContext value paramsFn = do | ||
| 505 | (value', EIState{..}) <- | ||
| 506 | (expandIri' value >>= \v -> gets (v,)) | ||
| 507 | |> withEnvRES env | ||
| 508 | |> withStateRES st (const eiStateGlobal) | ||
| 509 | pure (value', eiStateActiveContext, eiStateDefined) | ||
| 510 | where | ||
| 511 | EIParams{..} = | ||
| 512 | paramsFn | ||
| 513 | EIParams | ||
| 514 | { eiParamsDocumentRelative = False | ||
| 515 | , eiParamsVocab = False | ||
| 516 | , eiParamsLocalContext = Nothing | ||
| 517 | , eiParamsDefined = mempty | ||
| 518 | } | ||
| 519 | |||
| 520 | env options = | ||
| 521 | EIEnv | ||
| 522 | { eiEnvGlobal = options | ||
| 523 | , eiEnvDocumentRelative = eiParamsDocumentRelative | ||
| 524 | , eiEnvVocab = eiParamsVocab | ||
| 525 | , eiEnvLocalContext = eiParamsLocalContext | ||
| 526 | } | ||
| 527 | |||
| 528 | st global = | ||
| 529 | EIState | ||
| 530 | { eiStateGlobal = global | ||
| 531 | , eiStateDefined = eiParamsDefined | ||
| 532 | , eiStateActiveContext = activeContext | ||
| 533 | } | ||
| 534 | |||
| 535 | -- | ||
| 536 | |||
| 537 | type BTDT e m = REST (BTDEnv e m) (Either (JLDError e) ()) BTDState m | ||
| 538 | |||
| 539 | data BTDEnv e m = BTDEnv | ||
| 540 | { btdEnvGlobal :: JLDExpansionEnv e m | ||
| 541 | , btdEnvLocalContext :: Object | ||
| 542 | , btdEnvBaseUrl :: Maybe URI | ||
| 543 | , btdEnvProtectedFlag :: Bool | ||
| 544 | , btdEnvOverrideProtectedFlag :: Bool | ||
| 545 | , btdEnvRemoteContexts :: Set Text | ||
| 546 | } | ||
| 547 | deriving (Show) | ||
| 548 | |||
| 549 | data BTDState = BTDState | ||
| 550 | { btdStateGlobal :: JLDExpansionState | ||
| 551 | , btdStateDefined :: Map Text Bool | ||
| 552 | , btdStateTermDefinition :: TermDefinition | ||
| 553 | , btdStateActiveContext :: ActiveContext | ||
| 554 | } | ||
| 555 | deriving (Show, Eq) | ||
| 556 | |||
| 557 | data BTDParams = BTDParams | ||
| 558 | { btdParamsBaseUrl :: Maybe URI | ||
| 559 | , btdParamsProtectedFlag :: Bool | ||
| 560 | , btdParamsOverrideProtectedFlag :: Bool | ||
| 561 | , btdParamsRemoteContexts :: Set Text | ||
| 562 | , btdParamsDefined :: Map Text Bool | ||
| 563 | , btdParamsTermDefinition :: TermDefinition | ||
| 564 | } | ||
| 565 | deriving (Show, Eq) | ||
| 566 | |||
| 567 | btdModifyActiveContext :: Monad m => (ActiveContext -> ActiveContext) -> BTDT e m () | ||
| 568 | btdModifyActiveContext fn = modify \s -> s{btdStateActiveContext = fn (btdStateActiveContext s)} | ||
| 569 | |||
| 570 | btdModifyTermDefinition :: Monad m => (TermDefinition -> TermDefinition) -> BTDT e m () | ||
| 571 | btdModifyTermDefinition fn = modify \s -> s{btdStateTermDefinition = fn (btdStateTermDefinition s)} | ||
| 572 | |||
| 573 | btdModifyDefined :: Monad m => (Map Text Bool -> Map Text Bool) -> BTDT e m () | ||
| 574 | btdModifyDefined fn = modify \s -> s{btdStateDefined = fn (btdStateDefined s)} | ||
| 575 | |||
| 576 | btdValidateContainer :: JLDExpansionEnv e m -> Value -> Bool | ||
| 577 | btdValidateContainer _ Null = False | ||
| 578 | btdValidateContainer JLDExpansionEnv{..} value | ||
| 579 | | JLD1_0 <- jldExpansionEnvProcessingMode = case value of | ||
| 580 | String value' -> isNotKeyword value' [KeywordGraph, KeywordId, KeywordType] | ||
| 581 | _ -> False | ||
| 582 | | otherwise = case flattenSingletonArray value of | ||
| 583 | String container' -> | ||
| 584 | isKeyword | ||
| 585 | container' | ||
| 586 | [ KeywordGraph | ||
| 587 | , KeywordId | ||
| 588 | , KeywordIndex | ||
| 589 | , KeywordLanguage | ||
| 590 | , KeywordList | ||
| 591 | , KeywordSet | ||
| 592 | , KeywordType | ||
| 593 | ] | ||
| 594 | container@(Array (V.length -> len)) | ||
| 595 | | len > 3 -> | ||
| 596 | False | ||
| 597 | | valueContains (show KeywordGraph) container | ||
| 598 | , valueContainsAny (show <$> [KeywordId, KeywordIndex]) container -> | ||
| 599 | len == 2 || valueContains (show KeywordSet) container | ||
| 600 | | len == 2 | ||
| 601 | , valueContains (show KeywordSet) container | ||
| 602 | , valueContainsAny (show <$> [KeywordGraph, KeywordId, KeywordIndex, KeywordLanguage, KeywordType]) container -> | ||
| 603 | True | ||
| 604 | _ -> False | ||
| 605 | |||
| 606 | btdExpandIri :: Monad m => Text -> BTDT e m (Maybe Text) | ||
| 607 | btdExpandIri value = do | ||
| 608 | BTDEnv{..} <- ask | ||
| 609 | defined <- gets btdStateDefined | ||
| 610 | activeContext <- gets btdStateActiveContext | ||
| 611 | let params p = | ||
| 612 | p | ||
| 613 | { eiParamsLocalContext = Just btdEnvLocalContext | ||
| 614 | , eiParamsVocab = True | ||
| 615 | , eiParamsDefined = defined | ||
| 616 | } | ||
| 617 | (expanded, activeContext', defined') <- | ||
| 618 | expandIri activeContext value params | ||
| 619 | |> withEnvRES (const btdEnvGlobal) | ||
| 620 | |> withErrorRES Left | ||
| 621 | |> withStateRES btdStateGlobal (\btd global -> btd{btdStateGlobal = global}) | ||
| 622 | modify \s -> | ||
| 623 | s | ||
| 624 | { btdStateActiveContext = activeContext' | ||
| 625 | , btdStateDefined = defined' | ||
| 626 | } | ||
| 627 | pure expanded | ||
| 628 | |||
| 629 | btdBuildTermDefinition :: Monad m => Text -> BTDT e m () | ||
| 630 | btdBuildTermDefinition term = do | ||
| 631 | BTDEnv{..} <- ask | ||
| 632 | defined <- gets btdStateDefined | ||
| 633 | activeContext <- gets btdStateActiveContext | ||
| 634 | let params p = p{btdParamsDefined = defined} | ||
| 635 | (activeContext', defined') <- | ||
| 636 | buildTermDefinition activeContext btdEnvLocalContext term params | ||
| 637 | |> withEnvRES (const btdEnvGlobal) | ||
| 638 | |> withErrorRES Left | ||
| 639 | |> withStateRES btdStateGlobal (\btd global -> btd{btdStateGlobal = global}) | ||
| 640 | modify \env -> | ||
| 641 | env | ||
| 642 | { btdStateActiveContext = activeContext' | ||
| 643 | , btdStateDefined = defined' | ||
| 644 | } | ||
| 645 | |||
| 646 | buildTermDefinition' :: Monad m => Text -> BTDT e m () | ||
| 647 | buildTermDefinition' "" = throwError <| Left InvalidTermDefinition -- 2. | ||
| 648 | buildTermDefinition' term = do | ||
| 649 | BTDEnv{..} <- ask | ||
| 650 | let JLDExpansionEnv{..} = btdEnvGlobal | ||
| 651 | |||
| 652 | -- 1. | ||
| 653 | gets (btdStateDefined .> M.lookup term) >>= \case | ||
| 654 | Just True -> throwError <| Right () | ||
| 655 | Just False -> throwError <| Left CyclicIriMapping | ||
| 656 | Nothing -> pure () | ||
| 657 | |||
| 658 | -- 2. | ||
| 659 | btdModifyDefined <| M.insert term False | ||
| 660 | |||
| 661 | -- 3. | ||
| 662 | let value = btdEnvLocalContext |> KM.lookup (K.fromText term) .> fromMaybe Null | ||
| 663 | |||
| 664 | -- 4. | ||
| 665 | case term of | ||
| 666 | ((`isKeyword` [KeywordType]) -> True) | ||
| 667 | | JLD1_0 <- jldExpansionEnvProcessingMode -> throwError <| Left KeywordRedefinition | ||
| 668 | | Object map' <- value -> | ||
| 669 | if | ||
| 670 | | KM.size map' == 1 | ||
| 671 | , Just container <- KM.lookup (show KeywordContainer) map' -> | ||
| 672 | when (container /= String (show KeywordSet)) <| throwError (Left KeywordRedefinition) | ||
| 673 | | KM.size map' == 2 | ||
| 674 | , Just container <- KM.lookup (show KeywordContainer) map' | ||
| 675 | , KM.member (show KeywordProtected) map' -> | ||
| 676 | unless (valueContains (show KeywordSet) container) <| throwError (Left KeywordRedefinition) | ||
| 677 | | KM.size map' /= 1 || not (KM.member (show KeywordProtected) map') -> | ||
| 678 | throwError <| Left KeywordRedefinition | ||
| 679 | | otherwise -> pure () | ||
| 680 | | otherwise -> throwError <| Left KeywordRedefinition | ||
| 681 | -- 5. | ||
| 682 | (parseKeyword -> Just _) -> throwError <| Left KeywordRedefinition | ||
| 683 | (isKeywordLike -> True) -> throwError <| Right () | ||
| 684 | _ -> pure () | ||
| 685 | |||
| 686 | -- 6. | ||
| 687 | maybePreviousDefinition <- gets (btdStateActiveContext .> lookupTerm term) | ||
| 688 | btdModifyActiveContext \ac -> ac{activeContextTerms = M.delete term (activeContextTerms ac)} | ||
| 689 | |||
| 690 | -- 7. 8. 9. | ||
| 691 | (valueObject, idValue, simpleTerm) <- case value of | ||
| 692 | Null -> pure (mempty, Just Null, False) | ||
| 693 | (String s) -> pure (mempty, Just (String s), True) | ||
| 694 | (Object o) -> pure (o, KM.lookup (show KeywordId) o, False) | ||
| 695 | _ -> throwError <| Left InvalidTermDefinition | ||
| 696 | |||
| 697 | -- 10. | ||
| 698 | btdModifyTermDefinition <| const (newTermDefinition btdEnvProtectedFlag id) | ||
| 699 | |||
| 700 | -- 11. | ||
| 701 | case KM.lookup (show KeywordProtected) valueObject of | ||
| 702 | Just _ | JLD1_0 <- jldExpansionEnvProcessingMode -> throwError <| Left InvalidTermDefinition | ||
| 703 | Just (Bool protected) -> btdModifyTermDefinition \d -> d{termDefinitionProtectedFlag = protected} | ||
| 704 | Just invalid -> throwError <. Left <| InvalidKeywordValue KeywordProtected invalid | ||
| 705 | Nothing -> pure () | ||
| 706 | |||
| 707 | -- 12. | ||
| 708 | case KM.lookup (show KeywordType) valueObject of | ||
| 709 | -- 12.2. | ||
| 710 | Just (String type') -> | ||
| 711 | btdExpandIri type' >>= \case | ||
| 712 | Nothing -> throwError <| Left InvalidTypeMapping | ||
| 713 | Just expandedType | ||
| 714 | -- 12.3. | ||
| 715 | | isKeyword expandedType [KeywordJson, KeywordNone] | ||
| 716 | , JLD1_0 <- jldExpansionEnvProcessingMode -> | ||
| 717 | throwError <| Left InvalidTypeMapping | ||
| 718 | -- 12.4. | ||
| 719 | | isNotKeyword expandedType [KeywordId, KeywordJson, KeywordNone, KeywordVocab] | ||
| 720 | , Left _ <- validateIRI expandedType -> | ||
| 721 | throwError <| Left InvalidTypeMapping | ||
| 722 | -- 12.5. | ||
| 723 | | otherwise -> | ||
| 724 | btdModifyTermDefinition \d -> d{termDefinitionTypeMapping = Just expandedType} | ||
| 725 | -- 12.1. | ||
| 726 | Just _ -> throwError <| Left InvalidTypeMapping | ||
| 727 | -- | ||
| 728 | Nothing -> pure () | ||
| 729 | |||
| 730 | -- 13. | ||
| 731 | case KM.lookup (show KeywordReverse) valueObject of | ||
| 732 | -- 13.1. | ||
| 733 | Just _ | KM.member (show KeywordId) valueObject || KM.member (show KeywordNest) valueObject -> throwError <| Left InvalidReverseProperty | ||
| 734 | Just (String (isKeywordLike -> True)) -> throwError <| Right () | ||
| 735 | -- 13.3. | ||
| 736 | Just (String reverse') -> do | ||
| 737 | -- 13.4. | ||
| 738 | btdExpandIri reverse' >>= \case | ||
| 739 | Just (validateIRI -> Right expandedReverse) -> | ||
| 740 | btdModifyTermDefinition \d -> d{termDefinitionIriMapping = Just expandedReverse} | ||
| 741 | _ -> throwError <| Left InvalidIriMapping | ||
| 742 | |||
| 743 | -- 13.5. | ||
| 744 | case KM.lookup (show KeywordContainer) valueObject of | ||
| 745 | Just (String container) | isKeyword container [KeywordSet, KeywordIndex] -> do | ||
| 746 | btdModifyTermDefinition \d -> | ||
| 747 | d | ||
| 748 | { termDefinitionContainerMapping = S.insert container <| termDefinitionContainerMapping d | ||
| 749 | } | ||
| 750 | Just Null -> pure () | ||
| 751 | Just _ -> throwError <| Left InvalidReverseProperty | ||
| 752 | Nothing -> pure () | ||
| 753 | |||
| 754 | -- 13.6. | ||
| 755 | btdModifyTermDefinition \d -> d{termDefinitionReversePropertyFlag = True} | ||
| 756 | |||
| 757 | -- 13.7. | ||
| 758 | definition <- gets btdStateTermDefinition | ||
| 759 | btdModifyActiveContext \ac -> ac{activeContextTerms = activeContextTerms ac |> M.insert term definition} | ||
| 760 | btdModifyDefined <| M.insert term True | ||
| 761 | |||
| 762 | throwError <| Right () | ||
| 763 | -- 13.2. | ||
| 764 | Just _ -> throwError <| Left InvalidIriMapping | ||
| 765 | -- | ||
| 766 | Nothing -> pure () | ||
| 767 | |||
| 768 | -- 14. 15. 16. 17. 18. | ||
| 769 | maybeVocabMapping <- gets (btdStateActiveContext .> activeContextVocabularyMapping) | ||
| 770 | if | ||
| 771 | -- 14. 14.1. | ||
| 772 | | Just idValue' <- idValue | ||
| 773 | , idValue' /= String term -> case idValue' of | ||
| 774 | Null -> pure () | ||
| 775 | String id' | ||
| 776 | -- 14.2.2. | ||
| 777 | | isNothing (parseKeyword id') && isKeywordLike id' -> throwError <| Right () | ||
| 778 | | otherwise -> do | ||
| 779 | -- 14.2.3. | ||
| 780 | iriMapping <- | ||
| 781 | btdExpandIri id' >>= \case | ||
| 782 | Nothing -> throwError <| Left InvalidIriMapping | ||
| 783 | Just expandedId | ||
| 784 | | isKeyword expandedId [KeywordContext] -> | ||
| 785 | throwError <| Left InvalidKeywordAlias | ||
| 786 | | Nothing <- parseKeyword expandedId | ||
| 787 | , Left _ <- validateIRI expandedId | ||
| 788 | , isBlankIri expandedId -> | ||
| 789 | throwError <| Left InvalidIriMapping | ||
| 790 | | otherwise -> | ||
| 791 | expandedId <$ btdModifyTermDefinition \d -> d{termDefinitionIriMapping = Just expandedId} | ||
| 792 | |||
| 793 | -- 14.2.4. | ||
| 794 | when (T.elem ':' (T.dropEnd 1 <. T.drop 1 <| term) || T.elem '/' term) do | ||
| 795 | -- 14.2.4.1 | ||
| 796 | btdModifyDefined <| M.insert term True | ||
| 797 | |||
| 798 | -- 14.2.4.2. | ||
| 799 | expandedTerm <- btdExpandIri term | ||
| 800 | when (expandedTerm /= Just iriMapping) <| throwError (Left InvalidIriMapping) | ||
| 801 | |||
| 802 | -- 14.2.5. | ||
| 803 | definition <- gets btdStateTermDefinition | ||
| 804 | when (not <| termDefinitionPrefixFlag definition) do | ||
| 805 | let validIri = isRight <. validateIRI <. T.dropEnd 1 <| iriMapping | ||
| 806 | let prefix = | ||
| 807 | not (T.elem ':' term || T.elem '/' term) | ||
| 808 | && simpleTerm | ||
| 809 | && ((endsWithGenericDelim iriMapping && validIri) || isBlankIri iriMapping) | ||
| 810 | btdModifyTermDefinition \d -> d{termDefinitionPrefixFlag = prefix} | ||
| 811 | -- 14.2.1. | ||
| 812 | _ -> throwError <| Left InvalidIriMapping | ||
| 813 | -- 15. | ||
| 814 | | T.elem ':' (T.drop 1 term) -> do | ||
| 815 | let maybeCompactIri = parseCompactIri term | ||
| 816 | |||
| 817 | -- 15.1. | ||
| 818 | case maybeCompactIri of | ||
| 819 | Just (CompactIRI prefix _) | KM.member (K.fromText prefix) btdEnvLocalContext -> do | ||
| 820 | btdBuildTermDefinition prefix | ||
| 821 | _ -> pure () | ||
| 822 | |||
| 823 | -- 15.2. | ||
| 824 | activeContextTerms <- gets (btdStateActiveContext .> activeContextTerms) | ||
| 825 | case maybeCompactIri of | ||
| 826 | Just (CompactIRI prefix suffix) | ||
| 827 | | Just term' <- M.lookup prefix activeContextTerms | ||
| 828 | , iriMapping <- (<> suffix) <$> termDefinitionIriMapping term' -> | ||
| 829 | btdModifyTermDefinition \d -> d{termDefinitionIriMapping = iriMapping} | ||
| 830 | -- 15.3. | ||
| 831 | _ | ||
| 832 | | isRight (validateIRI term) || isBlankIri term -> | ||
| 833 | btdModifyTermDefinition \d -> d{termDefinitionIriMapping = Just term} | ||
| 834 | _ -> pure () | ||
| 835 | -- 16. | ||
| 836 | | T.elem '/' term -> | ||
| 837 | btdExpandIri term >>= \case | ||
| 838 | Just expandedTerm -> btdModifyTermDefinition \d -> d{termDefinitionIriMapping = Just expandedTerm} | ||
| 839 | Nothing -> throwError <| Left InvalidIriMapping | ||
| 840 | -- 17. | ||
| 841 | | isKeyword term [KeywordType] -> btdModifyTermDefinition \d -> d{termDefinitionIriMapping = Just term} | ||
| 842 | -- 18. | ||
| 843 | | Just vocabMapping <- maybeVocabMapping -> btdModifyTermDefinition \d -> d{termDefinitionIriMapping = Just (vocabMapping <> term)} | ||
| 844 | -- | ||
| 845 | | otherwise -> throwError <| Left InvalidIriMapping | ||
| 846 | |||
| 847 | -- 19. | ||
| 848 | case KM.lookup (show KeywordContainer) valueObject of | ||
| 849 | Just container -> do | ||
| 850 | when (not <| btdValidateContainer btdEnvGlobal container) <| throwError (Left InvalidContainerMapping) | ||
| 851 | |||
| 852 | forM_ (valueToArray container) \case | ||
| 853 | String item -> btdModifyTermDefinition \d -> d{termDefinitionContainerMapping = termDefinitionContainerMapping d |> S.insert item} | ||
| 854 | _ -> pure () | ||
| 855 | |||
| 856 | definition <- gets btdStateTermDefinition | ||
| 857 | when (S.member (show KeywordType) <| termDefinitionContainerMapping definition) do | ||
| 858 | let typeMapping = termDefinitionTypeMapping definition |> fromMaybe (show KeywordId) | ||
| 859 | btdModifyTermDefinition \d -> d{termDefinitionTypeMapping = Just typeMapping} | ||
| 860 | when (isNotKeyword typeMapping [KeywordId, KeywordVocab]) do | ||
| 861 | throwError <| Left InvalidTypeMapping | ||
| 862 | -- | ||
| 863 | Nothing -> pure () | ||
| 864 | |||
| 865 | -- 20. | ||
| 866 | containerMapping <- gets (btdStateTermDefinition .> termDefinitionContainerMapping) | ||
| 867 | case KM.lookup (show KeywordIndex) valueObject of | ||
| 868 | -- 20.1. | ||
| 869 | Just _ | jldExpansionEnvProcessingMode == JLD1_0 || S.notMember (show KeywordIndex) containerMapping -> throwError <| Left InvalidTermDefinition | ||
| 870 | -- 20.2. | ||
| 871 | Just (String index) -> | ||
| 872 | btdExpandIri index >>= \case | ||
| 873 | Just (validateIRI -> Right _) -> btdModifyTermDefinition \d -> d{termDefinitionIndexMapping = Just index} | ||
| 874 | _ -> throwError <| Left InvalidTermDefinition | ||
| 875 | Just _ -> throwError <| Left InvalidTermDefinition | ||
| 876 | -- | ||
| 877 | Nothing -> pure () | ||
| 878 | |||
| 879 | -- 21. | ||
| 880 | case KM.lookup (show KeywordContext) valueObject of | ||
| 881 | -- 21.1. | ||
| 882 | Just _ | JLD1_0 <- jldExpansionEnvProcessingMode -> throwError <| Left InvalidTermDefinition | ||
| 883 | -- 21.2. | ||
| 884 | Just context -> do | ||
| 885 | -- 21.3. | ||
| 886 | activeContext <- gets btdStateActiveContext | ||
| 887 | let params p = | ||
| 888 | p | ||
| 889 | { bacParamsOverrideProtected = True | ||
| 890 | , bacParamsRemoteContexts = btdEnvRemoteContexts | ||
| 891 | , bacParamsValidateScopedContext = False | ||
| 892 | } | ||
| 893 | buildActiveContext activeContext context btdEnvBaseUrl params | ||
| 894 | |> withEnvRES (const btdEnvGlobal) | ||
| 895 | |> withStateRES btdStateGlobal (\btd global -> btd{btdStateGlobal = global}) | ||
| 896 | |> withErrorRES (const <| Left InvalidScopedContext) | ||
| 897 | |> void | ||
| 898 | |||
| 899 | -- 21.4. | ||
| 900 | btdModifyTermDefinition \d -> | ||
| 901 | d | ||
| 902 | { termDefinitionLocalContext = Just context | ||
| 903 | , termDefinitionBaseUrl = btdEnvBaseUrl | ||
| 904 | } | ||
| 905 | -- | ||
| 906 | Nothing -> pure () | ||
| 907 | |||
| 908 | -- 22. 23. | ||
| 909 | unless (KM.member (show KeywordType) valueObject) do | ||
| 910 | -- 22. | ||
| 911 | case KM.lookup (show KeywordLanguage) valueObject of | ||
| 912 | Just Null -> btdModifyTermDefinition \d -> d{termDefinitionLanguageMapping = Just NoLanguage} | ||
| 913 | Just (String language) -> btdModifyTermDefinition \d -> d{termDefinitionLanguageMapping = Just <| Language language} | ||
| 914 | Just _ -> throwError <| Left InvalidLanguageMapping | ||
| 915 | Nothing -> pure () | ||
| 916 | |||
| 917 | -- 23. | ||
| 918 | case KM.lookup (show KeywordDirection) valueObject of | ||
| 919 | Just Null -> btdModifyTermDefinition \d -> d{termDefinitionDirectionMapping = Just NoDirection} | ||
| 920 | Just (String "ltr") -> btdModifyTermDefinition \d -> d{termDefinitionDirectionMapping = Just LTR} | ||
| 921 | Just (String "rtl") -> btdModifyTermDefinition \d -> d{termDefinitionDirectionMapping = Just RTL} | ||
| 922 | Just _ -> throwError <| Left InvalidBaseDirection | ||
| 923 | Nothing -> pure () | ||
| 924 | |||
| 925 | -- 24. | ||
| 926 | case KM.lookup (show KeywordNest) valueObject of | ||
| 927 | -- 24.1. | ||
| 928 | Just _ | JLD1_0 <- jldExpansionEnvProcessingMode -> throwError <| Left InvalidTermDefinition | ||
| 929 | Just (String nest) | ||
| 930 | | parseKeyword nest /= Just KeywordNest -> throwError <. Left <| InvalidKeywordValue KeywordNest (String nest) | ||
| 931 | | otherwise -> btdModifyTermDefinition \d -> d{termDefinitionNestValue = Just nest} | ||
| 932 | Just invalid -> throwError <. Left <| InvalidKeywordValue KeywordNest invalid | ||
| 933 | Nothing -> pure () | ||
| 934 | |||
| 935 | -- 25. | ||
| 936 | maybeIriMapping <- gets (btdStateTermDefinition .> termDefinitionIriMapping) | ||
| 937 | case KM.lookup (show KeywordPrefix) valueObject of | ||
| 938 | -- 25.1. | ||
| 939 | Just _ | ||
| 940 | | jldExpansionEnvProcessingMode == JLD1_0 || T.elem ':' term || T.elem '/' term -> | ||
| 941 | throwError <| Left InvalidTermDefinition | ||
| 942 | Just (Bool prefix) | ||
| 943 | | prefix, Just _ <- parseKeyword =<< maybeIriMapping -> throwError <| Left InvalidTermDefinition | ||
| 944 | | otherwise -> btdModifyTermDefinition \d -> d{termDefinitionPrefixFlag = prefix} | ||
| 945 | Just invalid -> throwError <. Left <| InvalidKeywordValue KeywordPrefix invalid | ||
| 946 | Nothing -> pure () | ||
| 947 | |||
| 948 | -- 26. | ||
| 949 | unless | ||
| 950 | ( allKeywords | ||
| 951 | (KM.keys valueObject <&> K.toText) | ||
| 952 | [ KeywordId | ||
| 953 | , KeywordReverse | ||
| 954 | , KeywordContainer | ||
| 955 | , KeywordContext | ||
| 956 | , KeywordDirection | ||
| 957 | , KeywordIndex | ||
| 958 | , KeywordLanguage | ||
| 959 | , KeywordNest | ||
| 960 | , KeywordPrefix | ||
| 961 | , KeywordProtected | ||
| 962 | , KeywordType | ||
| 963 | ] | ||
| 964 | ) | ||
| 965 | do throwError <| Left InvalidTermDefinition | ||
| 966 | |||
| 967 | -- 27. | ||
| 968 | definition <- gets btdStateTermDefinition | ||
| 969 | |||
| 970 | case maybePreviousDefinition of | ||
| 971 | Just previousDefinition | not btdEnvOverrideProtectedFlag && termDefinitionProtectedFlag previousDefinition -> do | ||
| 972 | -- 27.1. | ||
| 973 | when (definition{termDefinitionProtectedFlag = True} /= previousDefinition) do | ||
| 974 | throwError <| Left ProtectedTermRedefinition | ||
| 975 | |||
| 976 | -- 27.2. | ||
| 977 | btdModifyActiveContext \ac -> ac{activeContextTerms = activeContextTerms ac |> M.insert term previousDefinition} | ||
| 978 | -- | ||
| 979 | _ -> | ||
| 980 | btdModifyActiveContext \ac -> ac{activeContextTerms = activeContextTerms ac |> M.insert term definition} | ||
| 981 | |||
| 982 | btdModifyDefined <| M.insert term True | ||
| 983 | |||
| 984 | buildTermDefinition :: Monad m => ActiveContext -> Object -> Text -> (BTDParams -> BTDParams) -> JLDExpansionT e m (ActiveContext, Map Text Bool) | ||
| 985 | buildTermDefinition activeContext localContext term paramsFn = do | ||
| 986 | BTDState{..} <- | ||
| 987 | (buildTermDefinition' term >> get) | ||
| 988 | |> withEnvRES env | ||
| 989 | |> withErrorRES' (either throwError (const get)) | ||
| 990 | |> withStateRES st (const btdStateGlobal) | ||
| 991 | pure (btdStateActiveContext, btdStateDefined) | ||
| 992 | where | ||
| 993 | BTDParams{..} = | ||
| 994 | paramsFn | ||
| 995 | BTDParams | ||
| 996 | { btdParamsBaseUrl = Nothing | ||
| 997 | , btdParamsProtectedFlag = False | ||
| 998 | , btdParamsOverrideProtectedFlag = False | ||
| 999 | , btdParamsRemoteContexts = mempty | ||
| 1000 | , btdParamsDefined = mempty | ||
| 1001 | , btdParamsTermDefinition = newTermDefinition False id | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | env options = | ||
| 1005 | BTDEnv | ||
| 1006 | { btdEnvGlobal = options | ||
| 1007 | , btdEnvLocalContext = localContext | ||
| 1008 | , btdEnvBaseUrl = btdParamsBaseUrl | ||
| 1009 | , btdEnvProtectedFlag = btdParamsProtectedFlag | ||
| 1010 | , btdEnvOverrideProtectedFlag = btdParamsOverrideProtectedFlag | ||
| 1011 | , btdEnvRemoteContexts = btdParamsRemoteContexts | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | st global = | ||
| 1015 | BTDState | ||
| 1016 | { btdStateGlobal = global | ||
| 1017 | , btdStateDefined = btdParamsDefined | ||
| 1018 | , btdStateTermDefinition = btdParamsTermDefinition | ||
| 1019 | , btdStateActiveContext = activeContext | ||
| 1020 | } | ||
