module Text.XML.HXT.Arrow.Pickle.Xml
where
import Control.Arrow.ArrowList
import Control.Arrow.ListArrows
import Control.Monad ( )
import Control.Monad.Error
import Control.Monad.State
import Data.Char (isDigit)
import Data.List (foldl')
import Data.Maybe
import Data.Map (Map)
import qualified Data.Map as M
import Text.XML.HXT.DOM.Interface
import qualified Text.XML.HXT.DOM.XmlNode as XN
import qualified Text.XML.HXT.DOM.ShowXml as XN
import Text.XML.HXT.Arrow.Edit (xshowEscapeXml)
import Text.XML.HXT.Arrow.Pickle.Schema
import Text.XML.HXT.Arrow.ReadDocument (xread)
import Text.XML.HXT.Arrow.WriteDocument (writeDocumentToString)
import Text.XML.HXT.Arrow.XmlState
data St = St { attributes :: [XmlTree]
, contents :: [XmlTree]
, nesting :: Int
, pname :: QName
, pelem :: Bool
} deriving (Show)
data PU a = PU { appPickle :: Pickler a
, appUnPickle :: Unpickler a
, theSchema :: Schema
}
type Pickler a = a -> St -> St
newtype Unpickler a = UP { runUP :: St -> (UnpickleVal a, St) }
type UnpickleVal a = Either UnpickleErr a
type UnpickleErr = (String, St)
instance Monad Unpickler where
return x = UP $ \ st -> (Right x, st)
u >>= f = UP $ \ st ->
let (r, st') = runUP u st in
case r of
Left err -> (Left err, st')
Right v -> runUP (f v) st'
fail = throwMsg
instance MonadState St Unpickler where
get = UP $ \ st -> (Right st, st)
put st = UP $ \ _ -> (Right (), st)
instance MonadError UnpickleErr Unpickler where
throwError err
= UP $ \ st -> (Left err, st)
catchError u handler
= UP $ \ st ->
let (r, st') = runUP u st in
case r of
Left err -> runUP (handler err) st
_ -> (r, st')
throwMsg :: String -> Unpickler a
throwMsg msg = UP $ \ st -> (Left (msg, st), st)
mchoice :: Unpickler a -> (a -> Unpickler b) -> Unpickler b -> Unpickler b
mchoice u f v = UP $ \ st ->
let (r, st') = runUP u st in
case r of
Right x
-> runUP (f x) st'
Left e@(_msg, st'')
-> if nesting st'' == nesting st
then runUP v st
else (Left e, st')
liftMaybe :: String -> Maybe a -> Unpickler a
liftMaybe e v = case v of
Nothing -> throwMsg e
Just x -> return x
liftUnpickleVal :: UnpickleVal a -> Unpickler a
liftUnpickleVal v = UP $ \ st -> (v, st)
getCont :: Unpickler XmlTree
getCont = do cs <- gets contents
case cs of
[] -> throwMsg "no more contents to be read"
(x : xs) -> do modify (\ s -> s {contents = xs})
return x
getAtt :: QName -> Unpickler XmlTree
getAtt qn = do as <- gets attributes
case findAtt as of
Nothing -> throwMsg $ "no attribute value found for " ++ show qn
Just (a, as') -> do modify (\ s -> s {attributes = as'})
return a
where
findAtt = findElem (maybe False (== qn) . XN.getAttrName)
getNSAtt :: String -> Unpickler ()
getNSAtt ns = do as <- gets attributes
case findNS as of
Nothing -> throwMsg $
"no namespace declaration found for namespace " ++ show ns
Just (_a, as') -> do modify (\ s -> s {attributes = as'})
return ()
where
isNS t = (fromMaybe False . fmap isNameSpaceName . XN.getAttrName $ t)
&&
XN.xshow (XN.getChildren t) == ns
findNS = findElem isNS
emptySt :: St
emptySt = St { attributes = []
, contents = []
, nesting = 0
, pname = mkName "/"
, pelem = True
}
putAtt :: QName -> [XmlTree] -> St -> St
putAtt qn v s = s {attributes = x : attributes s}
where
x = XN.mkAttr qn v
putCont :: XmlTree -> St -> St
putCont x s = s {contents = x : contents s}
findElem :: (a -> Bool) -> [a] -> Maybe (a, [a])
findElem p = find' id
where
find' _ [] = Nothing
find' prefix (x : xs)
| p x = Just (x, prefix xs)
| otherwise = find' (prefix . (x:)) xs
formatSt :: St -> String
formatSt st = fcx ++
fa (attributes st) ++
fc (contents st)
where
fcx = "\n" ++ "context: " ++
( if pelem st
then "element"
else "attribute"
) ++
" " ++ show (pname st)
fc [] = ""
fc cs = "\n" ++ "contents: " ++ formatXML cs
fa [] = ""
fa as = "\n" ++ "attributes: " ++ formatXML as
formatXML = format 80 . showXML
showXML = concat . runLA ( xshowEscapeXml unlistA )
format n s = let s' = take (n + 1) s in
if length s' <= n then s' else take n s ++ "..."
pickleDoc :: PU a -> a -> XmlTree
pickleDoc p v = XN.mkRoot (attributes st) (contents st)
where
st = appPickle p v emptySt
unpickleDoc :: PU a -> XmlTree -> Maybe a
unpickleDoc p = either (const Nothing) Just
. unpickleDoc' p
unpickleDoc' :: PU a -> XmlTree -> Either String a
unpickleDoc' p t
| XN.isRoot t = mapErr $
unpickleElem' p 0 t
| otherwise = unpickleDoc' p (XN.mkRoot [] [t])
where
mapErr = either ( Left .
\ (msg, st) -> msg ++ formatSt st
) Right
unpickleElem' :: PU a -> Int -> XmlTree -> UnpickleVal a
unpickleElem' p l t
= fst
. runUP (appUnPickle p)
$ St { attributes = fromMaybe [] .
XN.getAttrl $ t
, contents = XN.getChildren t
, nesting = l
, pname = fromJust .
XN.getName $ t
, pelem = XN.isElem t
}
showPickled :: (XmlPickler a) => SysConfigList -> a -> String
showPickled a = concat . (pickleDoc xpickle >>> runLA (writeDocumentToString a))
xpZero :: String -> PU a
xpZero err = PU { appPickle = const id
, appUnPickle = throwMsg err
, theSchema = scNull
}
xpUnit :: PU ()
xpUnit = xpLift ()
xpCheckEmptyContents :: PU a -> PU a
xpCheckEmptyContents pa = PU { appPickle = appPickle pa
, appUnPickle = do res <- appUnPickle pa
cs <- gets contents
if null cs
then return res
else contentsLeft
, theSchema = scNull
}
where
contentsLeft = throwMsg
"xpCheckEmptyContents: unprocessed XML content detected"
xpCheckEmptyAttributes :: PU a -> PU a
xpCheckEmptyAttributes pa
= PU { appPickle = appPickle pa
, appUnPickle = do res <- appUnPickle pa
as <- gets attributes
if null as
then return res
else attributesLeft
, theSchema = scNull
}
where
attributesLeft = throwMsg
"xpCheckEmptyAttributes: unprocessed XML attribute(s) detected"
xpCheckEmpty :: PU a -> PU a
xpCheckEmpty = xpCheckEmptyAttributes . xpCheckEmptyContents
xpLift :: a -> PU a
xpLift x = PU { appPickle = const id
, appUnPickle = return x
, theSchema = scEmpty
}
xpLiftMaybe :: Maybe a -> PU a
xpLiftMaybe v = (xpLiftMaybe'' v) { theSchema = scOption scEmpty }
where
xpLiftMaybe'' Nothing = xpZero "xpLiftMaybe: got Nothing"
xpLiftMaybe'' (Just x) = xpLift x
xpLiftEither :: Either String a -> PU a
xpLiftEither v = (xpLiftEither'' v) { theSchema = scOption scEmpty }
where
xpLiftEither'' (Left err) = xpZero err
xpLiftEither'' (Right x) = xpLift x
xpSeq :: (b -> a) -> PU a -> (a -> PU b) -> PU b
xpSeq f pa k
= PU { appPickle = ( \ b ->
let a = f b in
appPickle pa a . appPickle (k a) b
)
, appUnPickle = appUnPickle pa >>= (appUnPickle . k)
, theSchema = undefined
}
xpSeq' :: PU () -> PU a -> PU a
xpSeq' pa = xpWrap ( snd
, \ y -> ((), y)
) .
xpPair pa
xpChoice :: PU b -> PU a -> (a -> PU b) -> Unpickler b
xpChoice pb pa k = mchoice (appUnPickle pa) (appUnPickle . k) (appUnPickle pb)
xpWrap :: (a -> b, b -> a) -> PU a -> PU b
xpWrap (i, j) pa = (xpSeq j pa (xpLift . i)) { theSchema = theSchema pa }
xpWrapMaybe :: (a -> Maybe b, b -> a) -> PU a -> PU b
xpWrapMaybe (i, j) pa = (xpSeq j pa (xpLiftMaybe . i)) { theSchema = theSchema pa }
xpWrapEither :: (a -> Either String b, b -> a) -> PU a -> PU b
xpWrapEither (i, j) pa = (xpSeq j pa (xpLiftEither . i)) { theSchema = theSchema pa }
xpPair :: PU a -> PU b -> PU (a, b)
xpPair pa pb
= ( xpSeq fst pa (\ a ->
xpSeq snd pb (\ b ->
xpLift (a,b)))
) { theSchema = scSeq (theSchema pa) (theSchema pb) }
xpTriple :: PU a -> PU b -> PU c -> PU (a, b, c)
xpTriple pa pb pc
= xpWrap (toTriple, fromTriple) (xpPair pa (xpPair pb pc))
where
toTriple ~(a, ~(b, c)) = (a, b, c )
fromTriple ~(a, b, c ) = (a, (b, c))
xp4Tuple :: PU a -> PU b -> PU c -> PU d -> PU (a, b, c, d)
xp4Tuple pa pb pc pd
= xpWrap (toQuad, fromQuad) (xpPair pa (xpPair pb (xpPair pc pd)))
where
toQuad ~(a, ~(b, ~(c, d))) = (a, b, c, d )
fromQuad ~(a, b, c, d ) = (a, (b, (c, d)))
xp5Tuple :: PU a -> PU b -> PU c -> PU d -> PU e -> PU (a, b, c, d, e)
xp5Tuple pa pb pc pd pe
= xpWrap (toQuint, fromQuint) (xpPair pa (xpPair pb (xpPair pc (xpPair pd pe))))
where
toQuint ~(a, ~(b, ~(c, ~(d, e)))) = (a, b, c, d, e )
fromQuint ~(a, b, c, d, e ) = (a, (b, (c, (d, e))))
xp6Tuple :: PU a -> PU b -> PU c -> PU d -> PU e -> PU f -> PU (a, b, c, d, e, f)
xp6Tuple pa pb pc pd pe pf
= xpWrap (toSix, fromSix) (xpPair pa (xpPair pb (xpPair pc (xpPair pd (xpPair pe pf)))))
where
toSix ~(a, ~(b, ~(c, ~(d, ~(e, f))))) = (a, b, c, d, e, f )
fromSix ~(a, b, c, d, e, f) = (a, (b, (c, (d, (e, f)))))
xp7Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU (a, b, c, d, e, f, g)
xp7Tuple a b c d e f g
= xpWrap ( \ (a, (b, c, d, e, f, g)) -> (a, b, c, d, e, f, g)
, \ (a, b, c, d, e, f, g) -> (a, (b, c, d, e, f, g))
)
(xpPair a (xp6Tuple b c d e f g))
xp8Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU (a, b, c, d, e, f, g, h)
xp8Tuple a b c d e f g h
= xpWrap ( \ ((a, b), (c, d, e, f, g, h)) -> (a, b, c, d, e, f, g, h)
, \ (a, b, c, d, e, f, g, h) -> ((a, b), (c, d, e, f, g, h))
)
(xpPair (xpPair a b) (xp6Tuple c d e f g h))
xp9Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU (a, b, c, d, e, f, g, h, i)
xp9Tuple a b c d e f g h i
= xpWrap ( \ ((a, b, c), (d, e, f, g, h, i)) -> (a, b, c, d, e, f, g, h, i)
, \ (a, b, c, d, e, f, g, h, i) -> ((a, b, c), (d, e, f, g, h, i))
)
(xpPair (xpTriple a b c) (xp6Tuple d e f g h i))
xp10Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU (a, b, c, d, e, f, g, h, i, j)
xp10Tuple a b c d e f g h i j
= xpWrap ( \ ((a, b, c, d), (e, f, g, h, i, j)) -> (a, b, c, d, e, f, g, h, i, j)
, \ (a, b, c, d, e, f, g, h, i, j) -> ((a, b, c, d), (e, f, g, h, i, j))
)
(xpPair (xp4Tuple a b c d) (xp6Tuple e f g h i j))
xp11Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU (a, b, c, d, e, f, g, h, i, j, k)
xp11Tuple a b c d e f g h i j k
= xpWrap ( \ ((a, b, c, d, e), (f, g, h, i, j, k)) -> (a, b, c, d, e, f, g, h, i, j, k)
, \ (a, b, c, d, e, f, g, h, i, j, k) -> ((a, b, c, d, e), (f, g, h, i, j, k))
)
(xpPair (xp5Tuple a b c d e) (xp6Tuple f g h i j k))
xp12Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU (a, b, c, d, e, f, g, h, i, j, k, l)
xp12Tuple a b c d e f g h i j k l
= xpWrap ( \ ((a, b, c, d, e, f), (g, h, i, j, k, l)) -> (a, b, c, d, e, f, g, h, i, j, k, l)
, \ (a, b, c, d, e, f, g, h, i, j, k, l) -> ((a, b, c, d, e, f), (g, h, i, j, k, l))
)
(xpPair (xp6Tuple a b c d e f) (xp6Tuple g h i j k l))
xp13Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU (a, b, c, d, e, f, g, h, i, j, k, l, m)
xp13Tuple a b c d e f g h i j k l m
= xpWrap ( \ (a, (b, c, d, e, f, g), (h, i, j, k, l, m)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m) -> (a, (b, c, d, e, f, g), (h, i, j, k, l, m))
)
(xpTriple a (xp6Tuple b c d e f g) (xp6Tuple h i j k l m))
xp14Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n)
xp14Tuple a b c d e f g h i j k l m n
= xpWrap ( \ ((a, b), (c, d, e, f, g, h), (i, j, k, l, m, n)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n) -> ((a, b), (c, d, e, f, g, h), (i, j, k, l, m, n))
)
(xpTriple (xpPair a b) (xp6Tuple c d e f g h) (xp6Tuple i j k l m n))
xp15Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU o ->
PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)
xp15Tuple a b c d e f g h i j k l m n o
= xpWrap ( \ ((a, b, c), (d, e, f, g, h, i), (j, k, l, m, n, o)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) -> ((a, b, c), (d, e, f, g, h, i), (j, k, l, m, n, o))
)
(xpTriple (xpTriple a b c) (xp6Tuple d e f g h i) (xp6Tuple j k l m n o))
xp16Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU o ->
PU p -> PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
xp16Tuple a b c d e f g h i j k l m n o p
= xpWrap ( \ ((a, b, c, d), (e, f, g, h, i, j), (k, l, m, n, o, p)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) -> ((a, b, c, d), (e, f, g, h, i, j), (k, l, m, n, o, p))
)
(xpTriple (xp4Tuple a b c d) (xp6Tuple e f g h i j) (xp6Tuple k l m n o p))
xp17Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU o ->
PU p -> PU q -> PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q)
xp17Tuple a b c d e f g h i j k l m n o p q
= xpWrap ( \ ((a, b, c, d, e), (f, g, h, i, j, k), (l, m, n, o, p, q)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) -> ((a, b, c, d, e), (f, g, h, i, j, k), (l, m, n, o, p, q))
)
(xpTriple (xp5Tuple a b c d e) (xp6Tuple f g h i j k) (xp6Tuple l m n o p q))
xp18Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU o ->
PU p -> PU q -> PU r -> PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r)
xp18Tuple a b c d e f g h i j k l m n o p q r
= xpWrap ( \ ((a, b, c, d, e, f), (g, h, i, j, k, l), (m, n, o, p, q, r)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) -> ((a, b, c, d, e, f), (g, h, i, j, k, l), (m, n, o, p, q, r))
)
(xpTriple (xp6Tuple a b c d e f) (xp6Tuple g h i j k l) (xp6Tuple m n o p q r))
xp19Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU o ->
PU p -> PU q -> PU r -> PU s -> PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s)
xp19Tuple a b c d e f g h i j k l m n o p q r s
= xpWrap ( \ (a, (b, c, d, e, f, g), (h, i, j, k, l, m), (n, o, p, q, r, s)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) -> (a, (b, c, d, e, f, g), (h, i, j, k, l, m), (n, o, p, q, r, s))
)
(xp4Tuple a (xp6Tuple b c d e f g) (xp6Tuple h i j k l m) (xp6Tuple n o p q r s))
xp20Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU o ->
PU p -> PU q -> PU r -> PU s -> PU t ->
PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)
xp20Tuple a b c d e f g h i j k l m n o p q r s t
= xpWrap ( \ ((a, b), (c, d, e, f, g, h), (i, j, k, l, m, n), (o, p, q, r, s, t)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) -> ((a, b), (c, d, e, f, g, h), (i, j, k, l, m, n), (o, p, q, r, s, t))
)
(xp4Tuple (xpPair a b) (xp6Tuple c d e f g h) (xp6Tuple i j k l m n) (xp6Tuple o p q r s t))
xp21Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU o ->
PU p -> PU q -> PU r -> PU s -> PU t ->
PU u -> PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u)
xp21Tuple a b c d e f g h i j k l m n o p q r s t u
= xpWrap ( \ ((a, b, c), (d, e, f, g, h, i), (j, k, l, m, n, o), (p, q, r, s, t, u)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) -> ((a, b, c), (d, e, f, g, h, i), (j, k, l, m, n, o), (p, q, r, s, t, u))
)
(xp4Tuple (xpTriple a b c) (xp6Tuple d e f g h i) (xp6Tuple j k l m n o) (xp6Tuple p q r s t u))
xp22Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU o ->
PU p -> PU q -> PU r -> PU s -> PU t ->
PU u -> PU v -> PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v)
xp22Tuple a b c d e f g h i j k l m n o p q r s t u v
= xpWrap ( \ ((a, b, c, d), (e, f, g, h, i, j), (k, l, m, n, o, p), (q, r, s, t, u, v)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) -> ((a, b, c, d), (e, f, g, h, i, j), (k, l, m, n, o, p), (q, r, s, t, u, v))
)
(xp4Tuple (xp4Tuple a b c d) (xp6Tuple e f g h i j) (xp6Tuple k l m n o p) (xp6Tuple q r s t u v))
xp23Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU o ->
PU p -> PU q -> PU r -> PU s -> PU t ->
PU u -> PU v -> PU w -> PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w)
xp23Tuple a b c d e f g h i j k l m n o p q r s t u v w
= xpWrap ( \ ((a, b, c, d, e), (f, g, h, i, j, k), (l, m, n, o, p, q), (r, s, t, u, v, w)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w) -> ((a, b, c, d, e), (f, g, h, i, j, k), (l, m, n, o, p, q), (r, s, t, u, v, w))
)
(xp4Tuple (xp5Tuple a b c d e) (xp6Tuple f g h i j k) (xp6Tuple l m n o p q) (xp6Tuple r s t u v w))
xp24Tuple :: PU a -> PU b -> PU c -> PU d -> PU e ->
PU f -> PU g -> PU h -> PU i -> PU j ->
PU k -> PU l -> PU m -> PU n -> PU o ->
PU p -> PU q -> PU r -> PU s -> PU t ->
PU u -> PU v -> PU w -> PU x -> PU (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x)
xp24Tuple a b c d e f g h i j k l m n o p q r s t u v w x
= xpWrap ( \ ((a, b, c, d, e, f), (g, h, i, j, k, l), (m, n, o, p, q, r), (s, t, u, v, w, x)) -> (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x)
, \ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x) -> ((a, b, c, d, e, f), (g, h, i, j, k, l), (m, n, o, p, q, r), (s, t, u, v, w, x))
)
(xp4Tuple (xp6Tuple a b c d e f) (xp6Tuple g h i j k l) (xp6Tuple m n o p q r) (xp6Tuple s t u v w x))
xpText :: PU String
xpText = xpTextDT scString1
xpTextDT :: Schema -> PU String
xpTextDT sc = PU { appPickle = putCont . XN.mkText
, appUnPickle = do t <- getCont
liftMaybe "xpText: XML text expected" $ XN.getText t
, theSchema = sc
}
xpText0 :: PU String
xpText0 = xpText0DT scString1
xpText0DT :: Schema -> PU String
xpText0DT sc = xpWrap (fromMaybe "", emptyToNothing) $
xpOption $
xpTextDT sc
where
emptyToNothing "" = Nothing
emptyToNothing x = Just x
xpPrim :: (Read a, Show a) => PU a
xpPrim = xpWrapEither (readMaybe, show) xpText
where
readMaybe :: Read a => String -> Either String a
readMaybe str = val (reads str)
where
val [(x,"")] = Right x
val _ = Left $ "xpPrim: reading string " ++ show str ++ " failed"
xpInt :: PU Int
xpInt = xpWrapEither (readMaybe, show) xpText
where
readMaybe xs
| all isDigit xs = Right . foldl' (\ r c -> 10 * r + (fromEnum c fromEnum '0')) 0 $ xs
readMaybe ('-' : xs) = fmap (0 ) . readMaybe $ xs
readMaybe ('+' : xs) = readMaybe $ xs
readMaybe xs = Left $ "xpInt: reading an Int from string " ++ show xs ++ " failed"
xpTree :: PU XmlTree
xpTree = PU { appPickle = putCont
, appUnPickle = getCont
, theSchema = Any
}
xpTrees :: PU [XmlTree]
xpTrees = (xpList xpTree) { theSchema = Any }
xpXmlText :: PU String
xpXmlText = xpWrap ( showXML, readXML ) $ xpTrees
where
showXML = concat . runLA ( xshowEscapeXml unlistA )
readXML = runLA xread
xpOption :: PU a -> PU (Maybe a)
xpOption pa = PU { appPickle = ( \ a ->
case a of
Nothing -> id
Just x -> appPickle pa x
)
, appUnPickle = xpChoice (xpLift Nothing) pa (xpLift . Just)
, theSchema = scOption (theSchema pa)
}
xpDefault :: (Eq a) => a -> PU a -> PU a
xpDefault df = xpWrap ( fromMaybe df
, \ x -> if x == df then Nothing else Just x
) .
xpOption
xpList :: PU a -> PU [a]
xpList pa = PU { appPickle = ( \ a ->
case a of
[] -> id
_:_ -> appPickle pc a
)
, appUnPickle = xpChoice
(xpLift [])
pa
(\ x -> xpSeq id (xpList pa) (\xs -> xpLift (x:xs)))
, theSchema = scList (theSchema pa)
}
where
pc = xpSeq head pa (\ x ->
xpSeq tail (xpList pa) (\ xs ->
xpLift (x:xs) ))
xpList1 :: PU a -> PU [a]
xpList1 pa = ( xpWrap (\ (x, xs) -> x : xs
,\ x -> (head x, tail x)
) $
xpPair pa (xpList pa)
) { theSchema = scList1 (theSchema pa) }
xpMap :: Ord k => String -> String -> PU k -> PU v -> PU (Map k v)
xpMap en an xpk xpv
= xpWrap ( M.fromList
, M.toList
) $
xpList $
xpElem en $
xpPair ( xpAttr an $ xpk ) xpv
xpAlt :: (a -> Int) -> [PU a] -> PU a
xpAlt tag ps = PU { appPickle = \ a ->
appPickle (ps !! tag a) a
, appUnPickle = case ps of
[] -> throwMsg "xpAlt: no matching unpickler found for a sum datatype"
pa:ps1 -> xpChoice (xpAlt tag ps1) pa xpLift
, theSchema = scAlts (map theSchema ps)
}
xpElemQN :: QName -> PU a -> PU a
xpElemQN qn pa = PU { appPickle = ( \ a ->
let st' = appPickle pa a emptySt in
putCont (XN.mkElement qn (attributes st') (contents st'))
)
, appUnPickle = upElem
, theSchema = scElem (qualifiedName qn) (theSchema pa)
}
where
upElem = do t <- getCont
n <- liftMaybe "xpElem: XML element expected" $ XN.getElemName t
if n /= qn
then throwMsg ("xpElem: got element name " ++ show n ++ ", but expected " ++ show qn)
else do l <- gets nesting
liftUnpickleVal $ unpickleElem' (xpCheckEmpty pa) (l + 1) t
xpElem :: String -> PU a -> PU a
xpElem = xpElemQN . mkName
xpElemNS :: String -> String -> String -> PU a -> PU a
xpElemNS ns px lp
= xpElemQN $ mkQName px lp ns
xpElemWithAttrValue :: String -> String -> String -> PU a -> PU a
xpElemWithAttrValue name an av pa
= xpElem name $
xpAddFixedAttr an av $
pa
xpAttrQN :: QName -> PU a -> PU a
xpAttrQN qn pa = PU { appPickle = ( \ a ->
let st' = appPickle pa a emptySt in
putAtt qn (contents st')
)
, appUnPickle = upAttr
, theSchema = scAttr (qualifiedName qn) (theSchema pa)
}
where
upAttr = do a <- getAtt qn
l <- gets nesting
liftUnpickleVal $ unpickleElem' (xpCheckEmptyContents pa) l a
xpAttr :: String -> PU a -> PU a
xpAttr = xpAttrQN . mkName
xpAttrNS :: String -> String -> String -> PU a -> PU a
xpAttrNS ns px lp
= xpAttrQN (mkQName px lp ns)
xpTextAttr :: String -> PU String
xpTextAttr = flip xpAttr xpText
xpAttrImplied :: String -> PU a -> PU (Maybe a)
xpAttrImplied name pa
= xpOption $ xpAttr name pa
xpAttrFixed :: String -> String -> PU ()
xpAttrFixed name val
= ( xpWrapEither ( \ v ->
if v == val
then Right ()
else Left ( "xpAttrFixed: value "
++ show val
++ " expected, but got "
++ show v
)
, const val
) $
xpAttr name xpText
) { theSchema = scAttr name (scFixed val) }
xpAddFixedAttr :: String -> String -> PU a -> PU a
xpAddFixedAttr name val
= xpSeq' $ xpAttrFixed name val
xpAddNSDecl :: String -> String -> PU a -> PU a
xpAddNSDecl name val
= xpSeq' $ xpAttrNSDecl name' val
where
name'
| null name = "xmlns"
| otherwise = "xmlns:" ++ name
xpAttrNSDecl :: String -> String -> PU ()
xpAttrNSDecl name ns
= PU { appPickle = const $ putAtt (mkName name) [XN.mkText ns]
, appUnPickle = getNSAtt ns
, theSchema = scAttr name (scFixed ns)
}
xpIgnoreCont :: LA XmlTree XmlTree -> PU ()
xpIgnoreCont = xpIgnoreInput $ \ mf s -> s {contents = mf $ contents s}
xpIgnoreAttr :: LA XmlTree XmlTree -> PU ()
xpIgnoreAttr = xpIgnoreInput $ \ mf s -> s {attributes = mf $ attributes s}
xpFilterCont :: LA XmlTree XmlTree -> PU a -> PU a
xpFilterCont f = xpSeq' $ xpIgnoreCont f
xpFilterAttr :: LA XmlTree XmlTree -> PU a -> PU a
xpFilterAttr f = xpSeq' $ xpIgnoreAttr f
xpIgnoreInput :: (([XmlTree] -> [XmlTree]) -> St -> St) -> LA XmlTree XmlTree -> PU ()
xpIgnoreInput m f
= PU { appPickle = const id
, appUnPickle = do modify (m filterCont)
return ()
, theSchema = scNull
}
where
filterCont = runLA (unlistA >>> f)
class XmlPickler a where
xpickle :: PU a
instance XmlPickler Int where
xpickle = xpPrim
instance XmlPickler Integer where
xpickle = xpPrim
instance XmlPickler () where
xpickle = xpUnit
instance (XmlPickler a, XmlPickler b) => XmlPickler (a,b) where
xpickle = xpPair xpickle xpickle
instance (XmlPickler a, XmlPickler b, XmlPickler c) => XmlPickler (a,b,c) where
xpickle = xpTriple xpickle xpickle xpickle
instance (XmlPickler a, XmlPickler b, XmlPickler c, XmlPickler d) => XmlPickler (a,b,c,d) where
xpickle = xp4Tuple xpickle xpickle xpickle xpickle
instance (XmlPickler a, XmlPickler b, XmlPickler c, XmlPickler d, XmlPickler e) => XmlPickler (a,b,c,d,e) where
xpickle = xp5Tuple xpickle xpickle xpickle xpickle xpickle
instance XmlPickler a => XmlPickler [a] where
xpickle = xpList xpickle
instance XmlPickler a => XmlPickler (Maybe a) where
xpickle = xpOption xpickle