{-# LANGUAGE PatternGuards #-}
module Text.XML.HaXml.Namespaces
( nullNamespace
, expandedName
, namespaceName
, localName
, printableName
, qualify
, deQualify
, qualifyExceptLocal
, initNamespaceEnv
, augmentNamespaceEnv
, resolveAllNames
) where
import Prelude hiding (lookup)
import Text.XML.HaXml.Types
import Data.Map as Map (Map, insert, lookup, empty)
import Data.List (isPrefixOf)
nullNamespace :: Namespace
nullNamespace = Namespace { nsPrefix="", nsURI="" }
expandedName :: QName -> (Maybe Namespace, String)
expandedName n = (namespaceName n, localName n)
namespaceName :: QName -> Maybe Namespace
namespaceName (N _) = Nothing
namespaceName (QN ns _) = Just ns
localName :: QName -> String
localName (N n) = n
localName (QN _ n) = n
printableName :: QName -> String
printableName (N n) = n
printableName (QN ns n) | null (nsPrefix ns) = n
| otherwise = nsPrefix ns++':':n
qualify :: Maybe Namespace -> Map String Namespace -> QName -> QName
qualify def env (N n)
| ':'`elem`n = let (pre,':':nm) = span (/=':') n in
QN (maybe nullNamespace{nsPrefix=pre} id
(Map.lookup pre env))
nm
| Just d <- def = QN d n
| otherwise = N n
qualify _ env qn@(QN ns n)
| null (nsURI ns) = QN (maybe ns id (Map.lookup (nsPrefix ns) env)) n
| otherwise = qn
deQualify :: Maybe Namespace -> Map String Namespace -> QName -> QName
deQualify _ _ (QN _ n) = N n
deQualify _ _ (N n) = N n
qualifyExceptLocal :: Maybe Namespace -> Map String Namespace -> QName -> QName
qualifyExceptLocal Nothing env qn = qualify Nothing env qn
qualifyExceptLocal (Just def) env (N n)
| ':'`elem`n = let (pre,':':nm) = span (/=':') n in
if nsPrefix def == pre then N nm
else QN (maybe nullNamespace{nsPrefix=pre} id
(Map.lookup pre env))
nm
| otherwise = N n
qualifyExceptLocal (Just def) env qn@(QN ns n)
| def==ns = N n
| null (nsURI ns) = QN (maybe ns id (Map.lookup (nsPrefix ns) env)) n
| otherwise = qn
initNamespaceEnv :: Map String Namespace
initNamespaceEnv =
Map.insert "xmlns" Namespace{nsPrefix="xmlns"
,nsURI="http://www.w3.org/2000/xmlns/"}
$ Map.insert "xml" Namespace{nsPrefix="xml"
,nsURI="http://www.w3.org/XML/1998/namespace"}
$ Map.empty
augmentNamespaceEnv :: Namespace -> Map String Namespace
-> Map String Namespace
augmentNamespaceEnv ns env = Map.insert (nsPrefix ns) ns env
resolveAllNames :: (Maybe Namespace -> Map String Namespace -> QName -> QName)
-> Document i -> Document i
resolveAllNames qualify (Document prolog entities elm misc) =
Document (walkProlog prolog) entities
(walkElem Nothing initNamespaceEnv elm) misc
where
qualifyInDTD = qualify Nothing initNamespaceEnv
walkProlog (Prolog xml misc0 mDTD misc1) =
Prolog xml misc0 (maybe Nothing (Just . walkDTD) mDTD) misc1
walkDTD (DTD qn ext mds) = DTD (qualifyInDTD qn) ext (map walkMD mds)
walkMD (Element ed) = Element (walkED ed)
walkMD (AttList ald) = AttList (walkALD ald)
walkMD md = md
walkED (ElementDecl qn cs) = ElementDecl (qualifyInDTD qn) (walkCS cs)
walkCS (ContentSpec cp) = ContentSpec (walkCP cp)
walkCS (Mixed m) = Mixed (walkM m)
walkCS cs = cs
walkCP (TagName qn m) = TagName (qualifyInDTD qn) m
walkCP cp = cp
walkM (PCDATAplus qns) = PCDATAplus (map qualifyInDTD qns)
walkM PCDATA = PCDATA
walkALD (AttListDecl qn ads) = AttListDecl (qualifyInDTD qn)
(map walkAD ads)
walkAD (AttDef qn at dd) = AttDef (qualifyInDTD qn) at dd
walkElem def env (Elem qn attrs conts) =
Elem (qualify def' env' qn)
(map (\ (a,v)-> (qualify Nothing env' a, v)) attrs)
(map (walkContent def' env') conts)
where def' = foldr const def
(map defNamespace (matching (=="xmlns") attrs))
env' = foldr augmentNamespaceEnv env
(map mkNamespace
(matching ("xmlns:"`isPrefixOf`) attrs))
defNamespace :: Attribute -> Maybe Namespace
defNamespace (_ , atv)
| null (show atv) = Nothing
| otherwise = Just nullNamespace{nsURI=show atv}
mkNamespace :: Attribute -> Namespace
mkNamespace (N n, atv) = let (_,':':nm) = span (/=':') n in
Namespace{nsPrefix=nm,nsURI=show atv}
matching :: (String->Bool) -> [Attribute] -> [Attribute]
matching p = filter (p . printableName . fst)
walkContent def env (CElem e i) = CElem (walkElem def env e) i
walkContent _ _ content = content