ideas-1.9.1: src/Ideas/Encoding/DecoderXML.hs
{-# LANGUAGE OverloadedStrings, GADTs #-}
-----------------------------------------------------------------------------
-- Copyright 2019, Ideas project team. This file is distributed under the
-- terms of the Apache License 2.0. For more information, see the files
-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer : bastiaan.heeren@ou.nl
-- Stability : provisional
-- Portability : portable (depends on ghc)
--
-- Services using XML notation
--
-----------------------------------------------------------------------------
module Ideas.Encoding.DecoderXML
( XMLDecoder, xmlTypeDecoder
) where
import Control.Applicative hiding (Const)
import Control.Monad ((>=>), foldM, guard, unless)
import Control.Monad.State hiding (State)
import Data.Char
import Ideas.Common.Library
import Ideas.Common.Traversal.Navigator
import Ideas.Encoding.Encoder
import Ideas.Encoding.OpenMathSupport
import Ideas.Encoding.Request hiding (XML)
import Ideas.Service.State
import Ideas.Service.Types
import Ideas.Text.MathML
import Ideas.Text.OpenMath.Object
import Ideas.Text.XML
import Ideas.Utils.Decoding
type XMLDecoder a t = DecoderX a String XML t
xmlTypeDecoder :: TypedDecoder a XML
xmlTypeDecoder tp =
case tp of
Tag s (Const String) ->
decodeChild s decodeData <|> decodeAttribute s
Tag s t
| s == "answer" ->
decodeChild "answer" (xmlTypeDecoder t)
| s == "Difficulty" -> do
g <- either errorStr return (equalM tDifficulty tp)
a <- decodeAttribute "difficulty"
maybe (errorStr "unknown difficulty level") (return . g) (readDifficulty a)
| otherwise ->
decodeChild s (xmlTypeDecoder t)
Iso p t -> from p <$> xmlTypeDecoder t
List t -> do
x <- xmlTypeDecoder t
xs <- xmlTypeDecoder (List t)
return (x:xs)
<|>
return []
Pair t1 t2 -> do
x <- xmlTypeDecoder t1
y <- xmlTypeDecoder t2
return (x, y)
t1 :|: t2 ->
Left <$> xmlTypeDecoder t1 <|> Right <$> xmlTypeDecoder t2
Unit -> return ()
Const ctp ->
case ctp of
State -> decodeState
Context -> decodeContext
Rule -> decodeRule
Environment -> decodeArgEnvironment
Term -> get >>= (fromXML' >=> maybe (errorStr "invalid OpenMath") return . fromOMOBJ)
Location -> decodeLocation
StratCfg -> decodeConfiguration
QCGen -> getQCGen
Script -> getScript
Exercise -> getExercise
Id -> -- improve!
decodeChild "location" $
gets (newId . getData)
MathML -> decodeMathML
String -> decodeData
XML -> get
_ -> errorStr $ "No support for argument type in XML: " ++ show tp
_ -> errorStr $ "No support for argument type in XML: " ++ show tp
-- <ruleid>
decodeRule :: XMLDecoder a (Rule (Context a))
decodeRule = decodeChild "ruleid" $ do
ex <- getExercise
xml <- get
maybe (errorStr "invalid rule") return . getRule ex . newId . getData $ xml
-- <location>
decodeLocation :: XMLDecoder a Location
decodeLocation = decodeChild "location" $
gets (toLocation . read . getData)
-- <state>
decodeState :: XMLDecoder a (State a)
decodeState = decodeChild "state" $ do
ex <- getExercise
ps <- decodePaths
ctx <- decodeContext
let prf = replayPaths ps (strategy ex) ctx
return (makeState ex prf ctx)
-- <prefix>
decodePaths :: XMLDecoder a [Path]
decodePaths = do
prefixText <- gets (either (const "") getData . findChild "prefix")
if all isSpace prefixText
then return [emptyPath]
else if prefixText ~= "no prefix"
then return []
else maybe (errorStr "invalid paths") return (readPaths prefixText)
where
a ~= b = g a == g b
g = map toLower . filter (not . isSpace)
decodeContext :: XMLDecoder a (Context a)
decodeContext = do
ex <- getExercise
expr <- decodeExpression
env <- decodeEnvironment
let ctx = setEnvironment env (inContext ex expr)
locRef = makeRef ("location" :: String)
case locRef ? env of
Just s -> maybe (errorStr "invalid location") return $ do
loc <- toLocation <$> readM s
navigateTo loc (deleteRef locRef ctx)
Nothing ->
return ctx
decodeExpression :: XMLDecoder a a
decodeExpression = withOpenMath f
where
f True = decodeOMOBJ
f False = decodeChild "expr" $ do
ex <- getExercise
get >>= either errorStr return . parser ex . getData
decodeOMOBJ :: XMLDecoder a a
decodeOMOBJ = decodeChild "OMOBJ" $ get >>= \xml -> do
ex <- getExercise
omobj <- fromXML' xml
case fromOpenMath ex omobj of
Just a -> return a
Nothing -> errorStr "Invalid OpenMath object for this exercise"
decodeMathML :: XMLDecoder a MathML
decodeMathML = decodeFirstChild "math" $ get >>= fromXML'
decodeEnvironment :: XMLDecoder a Environment
decodeEnvironment =
decodeChild "context" (get >>= foldM add mempty . children)
<|> return mempty
where
add env item = do
unless (getName item == "item") $
errorStr $ "expecting item tag, found " ++ show (getName item)
n <- findAttribute' "name" item
req <- getRequest
case findChild "OMOBJ" item of
-- OpenMath object found inside item tag
Right this | useOpenMath req ->
case xml2omobj this of
Left err -> errorStr err
Right omobj ->
case fromOMOBJ omobj of
Just term -> return $ insertRef (makeRef n) (term :: Term) env
Nothing -> errorStr "invalid openmath"
-- Simple value in attribute
_ -> do
value <- findAttribute' "value" item
return $ insertRef (makeRef n) value env
-- <configuration>
decodeConfiguration :: XMLDecoder a StrategyCfg
decodeConfiguration = decodeChild "configuration" $
get >>= \xml ->
mconcat <$> mapM decodeAction (children xml)
where
decodeAction item = do
guard (null (children item))
action <- maybe (errorStr "invalid action") return $ readM (show (getName item))
cfgloc <- findAttribute' "name" item
return (action `byName` newId cfgloc)
decodeArgEnvironment :: XMLDecoder a Environment
decodeArgEnvironment = get >>=
fmap makeEnvironment . mapM (decodeBinding //) . findChildren "argument"
decodeBinding :: XMLDecoder a Binding
decodeBinding = get >>= \xml -> do
a <- findAttribute' "description" xml
req <- getRequest
case findChild "OMOBJ" xml of
-- OpenMath object found inside tag
Right this | useOpenMath req ->
case xml2omobj this of
Left err -> errorStr err
Right omobj ->
case fromOMOBJ omobj of
Just term -> return (termBinding a term)
Nothing -> errorStr "invalid openmath"
-- Simple value
_ -> return (makeBinding (makeRef a) (getData xml))
where
termBinding :: String -> Term -> Binding
termBinding = makeBinding . makeRef
fromXML' :: InXML a => XML -> XMLDecoder s a
fromXML' = maybe (errorStr "fromXML'") return . fromXML
findAttribute' :: String -> XML -> XMLDecoder a String
findAttribute' s = either errorStr return . findAttribute s