PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Xmlable/Text/XML/Xmlable/Xmlable.hs

http://hixay.googlecode.com/
Haskell | 323 lines | 261 code | 45 blank | 17 comment | 18 complexity | 8c35e1d93b862c8fefc990e8793eb776 MD5 | raw file
Possible License(s): GPL-2.0
  1. {-# OPTIONS_GHC -XNoMonomorphismRestriction #-}
  2. module Text.XML.Xmlable.Xmlable where
  3. import Control.Monad.Identity
  4. import Control.Monad.Error
  5. import Control.Monad.ErrorMonoid
  6. import Control.Monad.State
  7. import Control.Monad.Reader
  8. import Data.Maybe
  9. import GHC.Real(Ratio)
  10. import Control.Arrow((***), (&&&), first, second)
  11. import Data.Char(toUpper, isDigit)
  12. import Data.Function(on)
  13. import Data.Monoid
  14. import Data.List
  15. -- import GHC.Exts(the)
  16. import qualified Data.Map as Map
  17. --import Debug.Trace
  18. type XName = String
  19. data Xml =
  20. ElemB XName Namespaces |
  21. ElemE |
  22. Attr XName String |
  23. Val String
  24. deriving (Eq, Show, Read)
  25. data XmlDesc = XtVal | XtAttr String | XtElem String deriving (Eq, Show)
  26. xdName (XtAttr s) = s; xdName (XtElem s) = s; xdName _ = ""
  27. isAttr (XtAttr _) = True; isAttr _ = False
  28. isElem (XtElem _) = True; isElem _ = False
  29. isVal XtVal = True; isVal _ = False
  30. newtype Errors = Errors { errors :: [(Int, String)] } deriving Eq
  31. instance Monoid Errors where
  32. mempty = Errors []
  33. mappend a b = case on compare (map fst . take 1 . errors) a b of
  34. EQ -> Errors $ nub $ on (++) errors a b
  35. GT -> a
  36. LT -> b
  37. instance Error Errors where
  38. noMsg = mempty
  39. strMsg = (\(a,b) -> Errors [(read a, b)]) . span isDigit
  40. instance Show Errors where
  41. show = unlines . map snd . errors
  42. data XmlDescCons = XdcVal | XdcAttr String | XdcElem String [XmlDesc] deriving (Eq, Show)
  43. -- (XmlDesc, [XmlDesc])
  44. xdcName (XdcAttr s) = s; xdcName (XdcElem s _) = s; xdcName _ = ""
  45. xdcFields (XdcElem _ xs) = xs
  46. xdcFields _ = []
  47. type XmlDescConsM = Maybe XmlDescCons
  48. type Namespaces = Map.Map String String
  49. type XmlEnv = (XmlDescConsM, Namespaces)
  50. type ParserXml = ReaderT XmlEnv (StateT [(Int, Xml)] (ErrorMonoidT Errors Identity))
  51. xd2xdc (XtElem n) = XdcElem n []; xd2xdc (XtAttr n) = XdcAttr n; xd2xdc XtVal = XdcVal
  52. xdc2xd (XdcElem n _) = XtElem n; xdc2xd (XdcAttr n) = XtAttr n; xdc2xd XdcVal = XtVal
  53. -- xdc2xd x = error $ "Invalid param in xdc2xd " ++ show x
  54. -- changeNames :: (String -> String) -> XmlDescCons
  55. -- changeNames f = second $ map (\x -> x { xdName = f $ xdName x} )
  56. rename m x = Map.findWithDefault x x m
  57. changeXmlDescNameBy :: (String -> String) -> XmlDesc -> XmlDesc
  58. changeXmlDescNameBy f (XtAttr n) = XtAttr $ f n
  59. changeXmlDescNameBy f (XtElem n) = XtElem $ f n
  60. changeXmlDescNameBy _ XtVal = XtVal
  61. changeXmlDescConsNameBy :: (String->String) -> XmlDescCons -> XmlDescCons
  62. changeXmlDescConsNameBy f (XdcAttr n) = XdcAttr $ f n
  63. changeXmlDescConsNameBy f (XdcElem n fs) = XdcElem (f n) fs
  64. changeXmlDescConsNameBy _ x = x
  65. changeXmlDescName :: Map.Map String String -> XmlDesc -> XmlDesc
  66. changeXmlDescName = changeXmlDescNameBy . rename
  67. changeFieldNamesBy :: (String -> String) -> XmlEnv -> XmlEnv
  68. changeFieldNamesBy = first . liftM . chf
  69. where
  70. chf f (XdcElem n fs) = XdcElem n $ map (changeXmlDescNameBy f) fs
  71. chf f x = x
  72. changeFieldNames :: Map.Map String String -> XmlEnv -> XmlEnv
  73. changeFieldNames = changeFieldNamesBy . rename
  74. changeNamesBy :: (String -> String) -> XmlEnv -> XmlEnv
  75. changeNamesBy = first . liftM . changeXmlDescConsNameBy
  76. changeNames :: Map.Map String String -> XmlEnv -> XmlEnv
  77. changeNames = changeNamesBy . rename
  78. setNewTypeField :: String -> XmlDesc -> XmlEnv -> XmlEnv
  79. setNewTypeField sc d = first $ liftM (\x -> case x of
  80. XdcElem n fs
  81. | n == sc -> XdcElem n [d]
  82. | otherwise -> x
  83. _ -> x
  84. )
  85. throwErrorX s = get >>= \xs -> throwError $ Errors $ case xs of
  86. [] -> [(-1, "There is not enough xml data")]
  87. ((n,x):_) -> [(n, s ++ " at " ++ show (map snd $ takeWhile ((==n) . fst) xs) ++ ". Position = " ++ show n)]
  88. bindX m s = m `catchError` (throwError . Errors . map (second $ (s++) . (" -> "++)) . errors)
  89. parseListChar = parseSimple id
  90. parseListSimple = modify (uncurry (++) . (concatMap getList . take 1 &&& drop 1)) >> liftM2 (:) parse parseSafe
  91. where
  92. getList (k, x) = map ((,) k) $ case x of
  93. Attr n v -> map (Attr n) $ words v
  94. Val v -> map Val $ words v
  95. parseListElem = liftM (take 1) get >>= \xml -> liftM2 (:) parse (local (first $ Just . fromMaybe (getEnv xml)) parseSafe)
  96. where
  97. getEnv [(_, Attr n _)] = XdcAttr n; getEnv [(_, Val _)] = XdcVal; getEnv [(_, ElemB n _)] = XdcElem n []
  98. getEnv x = error $ "Error in list parsing (getEnv). Param: " ++ show x
  99. class Xmlable a where
  100. parse :: ParserXml a
  101. parseZero :: ParserXml a
  102. parseZero = parseZeroS ""
  103. parseZeroS :: String -> ParserXml a
  104. parseZeroS = throwErrorX . ("There is no parseZero for the type " ++)
  105. parseSafe :: ParserXml a
  106. parseSafe = parse `mplus` parseZero
  107. parseList :: ParserXml [a]
  108. parseList = parseListElem
  109. toXml :: (Maybe XmlDesc) -> a -> [Xml]
  110. toXml = undefined
  111. -- isChar, fromChar - ????????? ???????, ????? ???????? ?????? ?? ??????? (??? ? HaXml)
  112. isChar :: a -> Bool
  113. isChar = const False
  114. xFromChar :: Char -> a
  115. xFromChar = undefined
  116. xToChar :: a -> Char
  117. xToChar = undefined
  118. prefName = (\(a, b) -> if null b then ([], a) else (a, drop 1 b)) . break (==':')
  119. compWithPrefix x = comp (prefName x) . prefName
  120. where
  121. comp (pa,a) (pb, b) = ask >>= \(_, nss) -> return (a == b && Map.lookup pa nss == Map.lookup pb nss)
  122. -- check :: ParserXml [(Int,Xml)]
  123. check = ask >>= \(d, _) -> get >>= \xmls -> -- traceShow (xmls, d) (
  124. case (xmls, d) of
  125. (_ : _, Nothing) -> return xmls
  126. ((_, ElemB n ns) : _, Just (XdcElem n' _)) -> setLocalNS n ns $ checkEA xmls n n' True
  127. ((_, Attr n _) : _, Just (XdcAttr n')) -> checkEA xmls n n' False
  128. ((_, Val _) : _, Just XdcVal) -> return xmls
  129. ([], _) -> throwErrorX "Check error: empty xml data "
  130. (x, Just y) -> throwErrorX $ "Check error for " ++ show y
  131. -- )
  132. where
  133. checkEA xmls n n' isE = bindX (compWithPrefix n n' >>=
  134. guard
  135. -- \b-> ask >>= \(_,ns) -> traceShow (n,n',b,ns) (guard b)
  136. >> return xmls) $
  137. "Check error (" ++ (if isE then "ElemB " else "Attr ") ++ n ++ " instead of " ++ n' ++ ")"
  138. instance Xmlable Char where
  139. parse = {- traceShow 1 -} check >>= \xmls -> case xmls of
  140. (k, Val (z:zs)) : xs -> parse' z zs xs $ (k, Val zs) : xs
  141. (k, Attr n (z:zs)) : xs -> parse' z zs xs $ (k, Attr n zs) : xs
  142. (k1, ElemB n ns) : (k2, Val (z:zs)) : (k3, ElemE) : xs -> parse' z zs xs $ (k1, ElemB n ns) : (k2, Val zs) : (k3, ElemE) : xs
  143. x:xs -> throwErrorX "Wrong char parsing"
  144. where
  145. parse' z zs xs next = put (if null zs then xs else next) >> return z
  146. parseZero = parseZeroS "Char"
  147. parseList = parseListChar
  148. toXml = toXmlSimple (:[])
  149. isChar = const True
  150. xFromChar = id
  151. xToChar = id
  152. parseSimple :: (Show a, Read a) => (String -> String) -> ParserXml a
  153. parseSimple f = {- traceShow 2 -} check >>= \xmls -> case xmls of
  154. (_, Val v) : xs -> make v xs
  155. (_, Attr _ v) : xs -> make v xs
  156. (_, ElemB _ _) : (_, Val v) : (_, ElemE) : xs -> make v xs
  157. _ -> throwErrorX "Unknown pattern in parseSimple"
  158. where
  159. make v xs = calc (readList ("[" ++ f v ++ "]")) >>= \r -> put xs >> return r
  160. calc [(ns, s)]
  161. | null ns || not (null $ tail ns) || not (null s) = throwErrorX $ "Error in parseSimple.calc " ++ show [(ns, s)]
  162. | otherwise = return $ head ns
  163. calc x = throwErrorX $ "Error in parseSimple.calc. Not a singleton " ++ show x
  164. toXmlSimple :: (a -> String) -> (Maybe XmlDesc) -> a -> [Xml]
  165. toXmlSimple f (Just d) = case d of
  166. XtVal -> (:[]) . Val . f
  167. XtAttr n -> (:[]) . Attr n . f
  168. XtElem n -> (\s -> [ElemB n mempty, Val s, ElemE]) . f
  169. toXmlSimple f Nothing = toXmlSimple f $ Just XtVal
  170. instance Xmlable Int where
  171. parse = parseSimple id
  172. parseZero = parseZeroS "Int"
  173. parseList = parseListSimple
  174. toXml = toXmlSimple show
  175. instance Xmlable Integer where
  176. parse = parseSimple id
  177. parseZero = parseZeroS "Integer"
  178. parseList = parseListSimple
  179. toXml = toXmlSimple show
  180. instance Xmlable Double where
  181. parse = parseSimple id
  182. parseZero = parseZeroS "Double"
  183. parseList = parseListSimple
  184. toXml = toXmlSimple show
  185. instance Xmlable Float where
  186. parse = parseSimple id
  187. parseZero = parseZeroS "Float"
  188. parseList = parseListSimple
  189. toXml = toXmlSimple show
  190. instance Xmlable Bool where
  191. parse = parseSimple initCap
  192. where
  193. initCap [] =[]
  194. initCap (x : xs) = toUpper x : xs
  195. parseZero = parseZeroS "Bool"
  196. parseList = parseListSimple
  197. toXml = toXmlSimple (\b -> if b then "true" else "false")
  198. instance (Read a, Integral a) => Xmlable (GHC.Real.Ratio a) where
  199. parseZero = parseZeroS "Ratio"
  200. parse = parseSimple id
  201. parseList = parseListSimple
  202. toXml = toXmlSimple show
  203. instance (Xmlable a) => Xmlable [a] where
  204. {-
  205. parse = parseList
  206. -}
  207. parse = get >>= \xmls -> return (getEnv $ take 1 $ xmls) >>= \desc -> mods xmls (liftM2 (:) parse $ local (first $ Just . fromMaybe desc) parseSafe)
  208. where
  209. lm _ _ [] = const (modify (drop 1) >> return [])
  210. lm k f v = liftM (\r -> if isChar (head r) then map xFromChar v else r) . (modify ((map (\c -> (k, f c)) (words v) ++) . tail) >>)
  211. getEnv [(_, Attr n _)] = XdcAttr n; getEnv [(_, Val _)] = XdcVal; getEnv [(_, ElemB n _)] = XdcElem n []
  212. getEnv x = error $ "Error in list parsing (getEnv). Param: " ++ show x
  213. mods ((k, Attr n v):_) = lm k (Attr n) v; mods ((k, Val v):_) = lm k Val v; mods ((_, ElemB _ _):_) = id
  214. mods x = const $ throwErrorX "Error in list parsing."
  215. parseZero = return []
  216. toXml Nothing xs = toXml (Just XtVal) xs
  217. toXml jd@(Just d) xs
  218. | any isChar xs = toXmlSimple (map xToChar) jd xs
  219. | otherwise = case d of
  220. XtVal -> (:[]) . Val . drop 1 . concatMap (\(~(Val s)) -> ' ' : s)
  221. XtAttr n -> (:[]) . Attr n . drop 1 . concatMap (\(~(Attr _ s)) -> ' ' : s)
  222. XtElem n -> id
  223. . concatMap (toXml jd) $ xs
  224. noEnv = local $ first $ const Nothing
  225. parseDef = noEnv parse
  226. instance (Xmlable a, Xmlable b) => Xmlable (a,b) where
  227. parse = liftM2 (,) parseDef parseDef -- (noEnv parse) (noEnv parse)
  228. toXml _ (a0,a1) = toXml Nothing a0 ++ toXml Nothing a1
  229. instance (Xmlable a, Xmlable b, Xmlable c) => Xmlable (a,b,c) where
  230. parse = liftM3 (,,) parseDef parseDef parseDef -- (noEnv parse) (noEnv parse) (noEnv parse)
  231. toXml _ (a0,a1,a2) = toXml Nothing a0 ++ toXml Nothing a1 ++ toXml Nothing a2
  232. instance (Xmlable a) => Xmlable (Maybe a) where
  233. parse = liftM Just parse
  234. parseZero = return Nothing
  235. toXml d = concat . maybeToList . liftM (toXml d)
  236. setEnv' :: XmlEnv -> ParserXml a -> ParserXml a
  237. setEnv' (Just env, ns') = local ((
  238. let n = xdcName env in
  239. Just . (\d -> case d of
  240. XdcAttr "" -> XdcAttr n
  241. XdcElem n0 _
  242. | null n0 -> XdcElem n $ xdcFields env
  243. | otherwise -> XdcElem n0 $ xdcFields env
  244. dd -> dd
  245. ) . fromMaybe env
  246. ) *** Map.union ns')
  247. setLocalNS n ns = local (second $ setDefaultNS n . Map.union ns)
  248. where
  249. setDefaultNS n m = (\(a,b) -> if null a then m else fromMaybe m $ liftM (\x-> Map.insert "" x m) $ Map.lookup a m) $ prefName n
  250. parseElem fp fc = check >>= \xmls -> case xmls of
  251. (_, ElemB n ns):xs ->
  252. -- trace (" parseElem! " ++ show (head xmls)) $
  253. setLocalNS n ns $ drop1 >> fp fc >>= \res -> get >>= \s -> case s of
  254. (_, ElemE):_ -> drop1 >> return res
  255. x -> throwErrorX $ se ++ "There is no ElemE. Instead we have " ++ show x
  256. -- ???????????? - ?????????? ???, ???? ?? ????? ElemE
  257. _ -> fp fc
  258. where
  259. se = "Error in element parsing (parseElem). "
  260. drop1 = modify (drop 1)
  261. -- utils
  262. run p e = runIdentity . runErrorT . runErrorMonoidT . runStateT (runReaderT p e) . zip [0..]
  263. eitherParse p e = (\res -> case res of
  264. Left w -> (Left $ show w, [])
  265. Right (a,b) -> (Right a, map snd b)
  266. ) . run p (second Map.fromList e)
  267. run' p e = (\res -> case res of
  268. Left w -> Left $ show w
  269. Right (a,b) -> Right a
  270. ) . run p (second Map.fromList e)
  271. run'' p e = (\res -> case res of
  272. Left w -> Left w
  273. Right (a,b) -> Right a
  274. ) . run p (second Map.fromList e)
  275. run''' p e = (\res -> case res of
  276. Left w -> Left mempty
  277. Right (a,b) -> Right a
  278. ) . run p (second Map.fromList e)