module Text.XML.HXQ.Types(readNum,toNum,toInt,toString,toFloat,
buildInTypes,isBuildInType,instanceOf,castAs,castableAs) where
import Char(isDigit)
import Text.XML.HXQ.Parser
import Text.XML.HXQ.XTree
buildInTypes :: [(String,String)]
buildInTypes
= map (\(n,m) -> ("xs:"++n,"xs:"++m))
[("untypedAtomic","anyAtomicType"),
("dateTime","anyAtomicType"),
("date","anyAtomicType"),
("time","anyAtomicType"),
("duration","anyAtomicType"),
("float","anyAtomicType"),
("double","anyAtomicType"),
("decimal","anyAtomicType"),
("gYearMonth","anyAtomicType"),
("gYear","anyAtomicType"),
("gMonthDay","anyAtomicType"),
("gDay","anyAtomicType"),
("gMonth","anyAtomicType"),
("boolean","anyAtomicType"),
("base64Binary","anyAtomicType"),
("hexBinary","anyAtomicType"),
("anyURI","anyAtomicType"),
("QName","anyAtomicType"),
("NOTATION","anyAtomicType"),
("yearMonthDuration","duration"),
("dayTmeDuration","duration"),
("Integer","decimal"),
("nonPositiveInteger","Integer"),
("negativeInteger","nonPositiveInteger"),
("long","Integer"),
("int","long"),
("short","int"),
("byte","short"),
("nonNegativeInteger","Integer"),
("unsignedLong","nonNegativeInteger"),
("unsignedInt","unsignedLong"),
("unsignedShort","unsignedInt"),
("unsignedByte","unsignedShort"),
("positiveInteger","nonNegativeInteger"),
("string","anyAtomicType"),
("normalizedString","string"),
("token","normalizedString"),
("language","token"),
("NMTOKEN","token"),
("Name","token"),
("NCName","Name"),
("ID","NCName"),
("IDREF","NCName"),
("ENTITY","NCName")]
isBuildInType :: String -> Bool
isBuildInType "xs:anyAtomicType" = True
isBuildInType name = memV name buildInTypes
findV var ((n,b):_) | n==var = b
findV var (_:xs) = findV var xs
findV var _ = error ("Undefined variable: "++var)
memV var ((n,_):_) | n==var = True
memV var (_:xs) = memV var xs
memV _ _ = False
toString :: XTree -> String
toString x
= case x of
XElem _ _ _ _ zs
-> concatMap toString zs
XAttr _ v -> v
XText x -> x
XInt n -> show n
XFloat n -> show n
XBool b -> if b then "true" else "false"
_ -> ""
readNum :: String -> Maybe XTree
readNum cs
= let readInt ('+':rest) = span isDigit rest
readInt ('-':rest) = let (s,rest1) = span isDigit rest
in ('-':s,rest1)
readInt rest = span isDigit rest
readExp ('e':cs) = readInt cs
readExp ('E':cs) = readInt cs
readExp cs = ("",cs)
(si,rest) = readInt cs
in case rest of
'.':rest1
-> let (sd,rest2) = span isDigit rest1
in case readExp rest2 of
("",[]) -> Just $ XFloat (read $ si ++ "." ++ sd)
(exp,[]) -> Just $ XFloat (read $ si ++ "." ++ sd ++ "e" ++ exp)
_ -> Nothing
_ -> case readExp rest of
("",[]) -> Just $ XInt (read si)
(exp,[]) -> Just $ XFloat (read $ si ++ "e" ++ exp)
_ -> Nothing
toNum :: XTree -> Maybe XTree
toNum (XElem _ _ _ _ [x]) = toNum x
toNum (XText s) = readNum s
toNum (x@(XInt n)) = Just x
toNum (x@(XFloat n)) = Just x
toNum (XBool b) = Just $ XInt (if b then 1 else 0)
toNum (XAttr _ v) = toNum (XText v)
toNum _ = Nothing
toInt :: XTree -> Maybe XTree
toInt (XElem _ _ _ _ [x]) = toInt x
toInt (XText s) = case readNum s of
Just (XFloat n) -> Just $ XInt (floor n)
x -> x
toInt (x@(XInt n)) = Just x
toInt (XFloat n) = Just $ XInt (floor n)
toInt (XBool b) = Just $ XInt (if b then 1 else 0)
toInt (XAttr _ v) = toInt (XText v)
toInt _ = Nothing
toFloat :: XTree -> Maybe XTree
toFloat (XElem _ _ _ _ [x]) = toFloat x
toFloat (XText s) = case readNum s of
Just (XInt n) -> Just $ XFloat $ fromIntegral n
x -> x
toFloat (XInt n) = Just $ XFloat $ fromIntegral n
toFloat (x@(XFloat n)) = Just x
toFloat (XBool b) = Just $ XFloat (if b then 1 else 0)
toFloat (XAttr _ v) = toFloat (XText v)
toFloat _ = Nothing
toBool :: XTree -> Maybe XTree
toBool (XText s) = Just $ XBool $ s /= ""
toBool (XInt n) = Just $ XBool $ n /= 0
toBool (XFloat n) = Just $ XBool $ n /= 0
toBool (XAttr _ v) = Just $ XBool $ v /= ""
toBool (XBool b) = Just $ XBool b
toBool x = Nothing
casts :: [(String,XTree->Maybe XTree)]
casts
= map (\(n,f) -> ("xs:"++n,f))
[("anyAtomicType",Just . id),
("string",Just . XText . toString),
("float",toFloat),
("Integer",toInt),
("nonNegativeInteger",\x -> do XInt n <- toInt x
return $! XInt $ abs n),
("boolean",toBool)]
instanceOf :: XSeq -> Ast -> Bool
instanceOf expr typ
= instOf expr typ
where instOf [] (Ast "empty-sequence" []) = True
instOf [] (Ast "?" _) = True
instOf [x] (Ast "?" [tp]) = instOfOne x tp
instOf xs (Ast "*" [tp])
= all (\x -> instOfOne x tp) xs
instOf xs (Ast "+" [tp])
= (not $ null xs) && all (\x -> instOfOne x tp) xs
instOf [x] tp = instOfOne x tp
instOf _ _ = False
instOfOne (XElem t _ _ _ xs) seqType
= case seqType of
Ast "item" [] -> True
Ast "node" [] -> True
Ast "element" [] -> True
Ast "element" [Avar tag]
-> tag == "*" || t == tag
Ast "element" [Avar tag,Ast "?" [tp]]
-> (tag == "*" || t == tag)
&& null xs || instOf xs tp
Ast "element" [Avar tag,tp]
-> (tag == "*" || t == tag) && instOf xs tp
_ -> False
instOfOne (XAttr nm v) seqType
= case seqType of
Ast "item" [] -> True
Ast "attribute" [Avar name]
-> name == "*" || nm == name
_ -> False
instOfOne x (Avar tname)
= if memV tname casts
then (findV tname casts x) /= Nothing
else if memV tname buildInTypes
then instOfOne x (Avar $ findV tname buildInTypes)
else error $ "Unrecognized build-in type: "++tname
instOfOne _ _ = False
castAs :: XSeq -> Ast -> XSeq
castAs [] (Ast "?" _) = []
castAs [XElem _ _ _ _ xs] tp = castAs xs tp
castAs [x] (Avar tname)
= if memV tname casts
then case findV tname casts x of
Just v -> [v]
Nothing -> error $ "Value "++show x++" cannot be cast to the atomic type: "++tname
else if memV tname buildInTypes
then castAs [x] (Avar $ findV tname buildInTypes)
else error $ "Unrecognized build-in type: "++tname
castAs xs tp = error $ "Value "++show xs++" cannot be cast to the type "++show tp
castableAs :: XSeq -> Ast -> Bool
castableAs [] (Ast "?" _) = True
castableAs [XElem _ _ _ _ xs] tp = castableAs xs tp
castableAs [x] (Avar tname)
= if memV tname casts
then case findV tname casts x of
Just _ -> True
Nothing -> False
else if memV tname buildInTypes
then castableAs [x] (Avar $ findV tname buildInTypes)
else error $ "Unrecognized build-in type: "++tname
castableAs _ _ = False