----------------------------------------------------------------------------- -- | -- Module : HSP.XML -- Copyright : (c) Niklas Broberg 2008 -- License : BSD-style (see the file LICENSE.txt) -- -- Maintainer : Niklas Broberg, nibro@cs.chalmers.se -- Stability : experimental -- Portability : Haskell 98 -- -- Datatypes and type classes comprising the basic model behind -- the scenes of Haskell Server Pages tags. ----------------------------------------------------------------------------- module HSP.XML ( -- * The 'XML' datatype XML(..), XMLMetaData(..), Domain, Name, Attributes, Children, pcdata, cdata, -- * The Attribute type Attribute(..), AttrValue(..), attrVal, pAttrVal, -- * Functions renderXML, isElement, isCDATA ) where import Data.List (intersperse) import HSP.XML.PCDATA (escape) --------------------------------------------------------------- -- Data types type Domain = Maybe String type Name = (Domain, String) type Attributes = [Attribute] type Children = [XML] -- | The XML datatype representation. Is either an Element or CDATA. data XML = Element Name Attributes Children | CDATA Bool String deriving Show -- |The XMLMetaData datatype -- -- Specify the DOCTYPE, content-type, and preferred render for XML data. -- -- See also: 'HSP.Monad.setMetaData' and 'HSP.Monad.withMetaData' data XMLMetaData = XMLMetaData { doctype :: (Bool, String) -- ^ (show doctype when rendering, DOCTYPE string) , contentType :: String , preferredRenderer :: XML -> String } {- instance Show XML where show = renderXML -} -- | Test whether an XML value is an Element or CDATA isElement, isCDATA :: XML -> Bool isElement (Element {}) = True isElement _ = False isCDATA = not . isElement -- | Embeds a string as a CDATA XML value. cdata , pcdata :: String -> XML cdata = CDATA False pcdata = CDATA True --------------------------------------------------------------- -- Attributes newtype Attribute = MkAttr (Name, AttrValue) deriving Show -- | Represents an attribue value. data AttrValue = Value Bool String -- | Create an attribue value from a string. attrVal, pAttrVal :: String -> AttrValue attrVal = Value False pAttrVal = Value True instance Show AttrValue where show (Value _ str) = str ------------------------------------------------------------------ -- Rendering -- TODO: indents are incorrectly calculated -- | Pretty-prints XML values. renderXML :: XML -> String renderXML xml = renderXML' 0 xml "" data TagType = Open | Close | Single renderXML' :: Int -> XML -> ShowS renderXML' _ (CDATA needsEscape cd) = showString (if needsEscape then escape cd else cd) renderXML' n (Element name attrs []) = renderTag Single n name attrs renderXML' n (Element name attrs children) = let open = renderTag Open n name attrs cs = renderChildren n children close = renderTag Close n name [] in open . cs . close where renderChildren :: Int -> Children -> ShowS renderChildren n' cs = foldl (.) id $ map (renderXML' (n'+2)) cs renderTag :: TagType -> Int -> Name -> Attributes -> ShowS renderTag typ n name attrs = let (start,end) = case typ of Open -> (showChar '<', showChar '>') Close -> (showString "') Single -> (showChar '<', showString "/>") nam = showName name as = renderAttrs attrs in start . nam . as . end where renderAttrs :: Attributes -> ShowS renderAttrs [] = nl renderAttrs attrs' = showChar ' ' . ats . nl where ats = foldl (.) id $ intersperse (showChar ' ') $ fmap renderAttr attrs' renderAttr :: Attribute -> ShowS renderAttr (MkAttr (nam, (Value needsEscape val))) = showName nam . showChar '=' . renderAttrVal (if needsEscape then escape val else val) renderAttrVal :: String -> ShowS renderAttrVal s = showChar '\"' . showString s . showChar '\"' showName (Nothing, s) = showString s showName (Just d, s) = showString d . showChar ':' . showString s nl = showChar '\n' . showString (replicate n ' ')