module Text.XML.HaXml.Escape(
xmlEscape,
xmlUnEscape,
xmlEscapeContent,
xmlUnEscapeContent,
XmlEscaper,
stdXmlEscaper,
mkXmlEscaper,
) where
import Data.Char
import Text.XML.HaXml.Types
#if __GLASGOW_HASKELL__ >= 604 || __NHC__ >= 118 || defined(__HUGS__)
import qualified Data.Map as Map
type FiniteMap a b = Map.Map a b
listToFM :: Ord a => [(a,b)] -> FiniteMap a b
listToFM = Map.fromList
lookupFM :: Ord a => FiniteMap a b -> a -> Maybe b
lookupFM = flip Map.lookup
#elif __GLASGOW_HASKELL__ >= 504 || __NHC__ > 114
import Data.FiniteMap
#else
type FiniteMap a b = [(a,b)]
listToFM :: Eq a => [(a,b)] -> FiniteMap a b
listToFM = id
lookupFM :: Eq a => FiniteMap a b -> a -> Maybe b
lookupFM fm k = lookup k fm
#endif
data XmlEscaper = XmlEscaper {
toEscape :: FiniteMap Char String,
fromEscape :: FiniteMap String Char,
isEscape :: Char -> Bool
}
xmlEscape :: XmlEscaper -> Element i -> Element i
xmlEscape xmlEscaper element =
compressElement (escapeElement xmlEscaper element)
xmlEscapeContent :: XmlEscaper -> [Content i] -> [Content i]
xmlEscapeContent xmlEscaper cs =
compressContent (escapeContent xmlEscaper cs)
escapeElement :: XmlEscaper -> Element i -> Element i
escapeElement xmlEscaper (Elem name attributes content) =
Elem name (escapeAttributes xmlEscaper attributes)
(escapeContent xmlEscaper content)
escapeAttributes :: XmlEscaper -> [Attribute] -> [Attribute]
escapeAttributes xmlEscaper atts =
map
(\ (name,av) -> (name,escapeAttValue xmlEscaper av))
atts
escapeAttValue :: XmlEscaper -> AttValue -> AttValue
escapeAttValue xmlEscaper (AttValue attValList) =
AttValue (
concat (
map
(\ av -> case av of
Right _ -> [av]
Left s ->
map
(\ c -> if isEscape xmlEscaper c
then
Right (mkEscape xmlEscaper c)
else
Left [c]
)
s
)
attValList
)
)
escapeContent :: XmlEscaper -> [Content i] -> [Content i]
escapeContent xmlEscaper contents =
concat
(map
(\ content -> case content of
(CString b str i) ->
map
(\ c -> if isEscape xmlEscaper c
then
CRef (mkEscape xmlEscaper c) i
else
CString b [c] i
)
str
(CElem element i) -> [CElem (escapeElement xmlEscaper element) i]
_ -> [content]
)
contents
)
mkEscape :: XmlEscaper -> Char -> Reference
mkEscape (XmlEscaper {toEscape = toescape}) ch =
case lookupFM toescape ch of
Nothing -> RefChar (ord ch)
Just str -> RefEntity str
xmlUnEscape :: XmlEscaper -> Element i -> Element i
xmlUnEscape xmlEscaper element =
compressElement (unEscapeElement xmlEscaper element)
xmlUnEscapeContent :: XmlEscaper -> [Content i] -> [Content i]
xmlUnEscapeContent xmlEscaper cs =
compressContent (unEscapeContent xmlEscaper cs)
unEscapeElement :: XmlEscaper -> Element i -> Element i
unEscapeElement xmlEscaper (Elem name attributes content) =
Elem name (unEscapeAttributes xmlEscaper attributes)
(unEscapeContent xmlEscaper content)
unEscapeAttributes :: XmlEscaper -> [Attribute] -> [Attribute]
unEscapeAttributes xmlEscaper atts =
map
(\ (name,av) -> (name,unEscapeAttValue xmlEscaper av))
atts
unEscapeAttValue :: XmlEscaper -> AttValue -> AttValue
unEscapeAttValue xmlEscaper (AttValue attValList) =
AttValue (
map
(\ av -> case av of
Left _ -> av
Right ref -> case unEscapeChar xmlEscaper ref of
Just c -> Left [c]
Nothing -> av
)
attValList
)
unEscapeContent :: XmlEscaper -> [Content i] -> [Content i]
unEscapeContent xmlEscaper content =
map
(\ cntnt -> case cntnt of
CRef ref i -> case unEscapeChar xmlEscaper ref of
Just c -> CString False [c] i
Nothing -> cntnt
CElem element i -> CElem (unEscapeElement xmlEscaper element) i
_ -> cntnt
)
content
unEscapeChar :: XmlEscaper -> Reference -> Maybe Char
unEscapeChar xmlEscaper ref =
case ref of
RefChar i -> Just (chr i)
RefEntity name -> lookupFM (fromEscape xmlEscaper) name
compressElement :: Element i -> Element i
compressElement (Elem name attributes content) =
Elem name (compressAttributes attributes) (compressContent content)
compressAttributes :: [(QName,AttValue)] -> [(QName,AttValue)]
compressAttributes atts =
map
(\ (name,av) -> (name,compressAttValue av))
atts
compressAttValue :: AttValue -> AttValue
compressAttValue (AttValue l) = AttValue (compress l)
where
compress :: [Either String Reference] -> [Either String Reference]
compress [] = []
compress (Right ref : es) = Right ref : (compress es)
compress ( (ls @ (Left s1)) : es) =
case compress es of
(Left s2 : es2) -> Left (s1 ++ s2) : es2
es2 -> ls : es2
compressContent :: [Content i] -> [Content i]
compressContent [] = []
compressContent ((csb @ (CString b1 s1 i1)) : cs) =
case compressContent cs of
(CString b2 s2 _) : cs2
| b1 == b2
-> CString b1 (s1 ++ s2) i1: cs2
cs2 -> csb : cs2
compressContent (CElem element i : cs) =
CElem (compressElement element) i : compressContent cs
compressContent (c : cs) = c : compressContent cs
stdXmlEscaper :: XmlEscaper
stdXmlEscaper = mkXmlEscaper
[('\60',"lt"),('\62',"gt"),('\38',"amp"),('\39',"apos"),('\34',"quot")]
(\ ch ->
let
i = ord ch
in
i < 10 || (10<i && i<32) || i >= 127 ||
case ch of
'\'' -> True
'\"' -> True
'&' -> True
'<' -> True
'>' -> True
_ -> False
)
mkXmlEscaper :: [(Char,String)] -> (Char -> Bool) -> XmlEscaper
mkXmlEscaper escapes isescape =
XmlEscaper {
toEscape = listToFM escapes,
fromEscape = listToFM (map (\ (c,str) -> (str,c)) escapes),
isEscape = isescape
}