packages feed

multifocal-0.0.1: src/Language/XML/Type2Xml.hs

-----------------------------------------------------------------------------
-- |
-- Module      :  Language.XML.Type2Xml
-- Copyright   :  (c) 2011 University of Minho
-- License     :  BSD3
--
-- Maintainer  :  hpacheco@di.uminho.pt
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Multifocal:
-- Bidirectional Two-level Transformation of XML Schemas
-- 
-- Translation from haskell values to XML documents.
--
-----------------------------------------------------------------------------

module Language.XML.Type2Xml where
	
import Data.Type
import Data.Equal
import Data.Spine
import Language.XML.Xml2Type
import Language.XML.Xsd2Type
import Language.XML.Type2Xsd
import Language.XML.HaXmlAliases
import Generics.Pointless.Functors hiding (rep)
import Generics.Pointless.RecursionPatterns
import Generics.Pointless.Combinators

import Text.XML.HaXml.Schema.XSDTypeModel hiding (K)
import Text.XML.HaXml.Types
import Text.XML.HaXml.Posn
import Data.List
import Data.Maybe
import Control.Monad.State as ST

type XmlGenM b = MonadPlus m => StateT [Attribute] m b

value2xml :: FilePath -> Type a -> a -> Maybe (Document ())
value2xml schemaloc t v = evalStateT (value2xml' schemaloc t v) []

value2xml' :: FilePath -> Type a -> a -> XmlGenM (Document ())
value2xml' schemaloc t v = do
	el <- value2topelement schemaloc t v
	return $ Document (Prolog  (Just (XMLDecl "1.0" (Just (EncodingDecl "utf-8")) Nothing)) [] Nothing []) [] el []

value2topelement :: FilePath -> Type a -> a -> XmlGenM (Element ())
value2topelement schemaloc t v = do
	els <- value2elements t v
	let header = [ genAttribute "xmlns:xsi" "http://www.w3.org/2001/XMLSchema-instance"
	             , genAttribute "xsi:noNamespaceSchemaLocation" schemaloc]
	case els of
		[Elem name atts cts] -> return $ Elem name (header++atts) cts
		otherwise -> error "multiple or inexistent top elements"
		
value2elements :: Type a -> a -> XmlGenM [Element ()]
value2elements (Either One a) (Left _) | isAtt a = return []
value2elements (Either One a) (Right x) | isAtt a = datavalue2attribute a x >>= addAttVal >> return []
value2elements (Data "Maybe" (K One :+!: K a)) v | isAtt a = case (out v) of
	Left _  -> return []
	Right x -> datavalue2attribute a x >>= addAttVal >> return []
value2elements (Either a One) (Left x) | isAtt a = datavalue2attribute a x >>= addAttVal >> return []
value2elements (Either a One) (Right _) | isAtt a = return []
value2elements (Either One a) (Left _) = return []
value2elements (Either One a) (Right x) = value2elements a x
value2elements (Data "Maybe" (K One :+!: K a)) v = case (out v) of
	Left _  -> return []
	Right x -> value2elements a x
value2elements (Either a One) (Left x) = value2elements a x
value2elements (Either a One) (Right _) = return []
value2elements (List a) lv = mapM (value2elements a) lv  >>= return . concat
value2elements a v | isAtt a = datavalue2attribute a v >>= addAttVal >> return []
value2elements d v | isData d = datavalue2element d v >>= return . (:[])
value2elements Dynamic (Dyn t v) = datavalue2element t v >>= return . (:[])
value2elements (Prod a b) (x,y) = do
	elsx <- value2elements a x
	elsy <- value2elements b y
	return $ elsx ++ elsy
value2elements e@(Either a b) (Left x) | not (isMaybe e) = value2elements a x
value2elements e@(Either a b) (Right y) | not (isMaybe e) = value2elements b y

basicvalue2primitive :: Type a -> a -> XmlGenM String
basicvalue2primitive (List Char) str = return str
basicvalue2primitive (isNat -> Just Eq) (Nat n) = return $ show n
basicvalue2primitive Int i = return $ show i
basicvalue2primitive Bool True = return "true"
basicvalue2primitive Bool False = return "false"

datavalue2attribute :: Type a -> a -> XmlGenM Attribute
datavalue2attribute (Data s f) v = datavalue2attribute (NewData s f) (nu (vnn v) v)
datavalue2attribute d@(NewData ('@':s) f) v | isAtt d && isBasic repf = do
	bv <- basicvalue2primitive repf (out v)
	return $ genAttribute s bv
  where repf = rep f d

dynvalue2elements :: Type a -> a -> XmlGenM [Content ()]
dynvalue2elements Dynamic (Dyn t v) = value2elements t v >>= return . celems

listvalue2element :: Type a -> a -> XmlGenM (Content ())
listvalue2element (List a) lv = mapM (basicvalue2primitive a) lv >>= return . cstring . unwords

datavalue2element :: Type a -> a -> XmlGenM (Element ())
datavalue2element d@(Data s f) v = datavalue2element (NewData s f) (nu (vnn v) v)
datavalue2element d@(NewData (nodename -> s) f) v
	| isDynamic repf = dynvalue2elements repf (out v) >>= return . Elem (N s) []
	| isBasic repf = basicvalue2primitive repf (out v) >>= return . Elem (N s) [] . (:[]) . cstring
	| isBasicList repf = listvalue2element repf (out v) >>= return . Elem (N s) [] . (:[])
	| otherwise = do
        	atts <- getAttVals
        	putAttVals []
        	els <- value2elements repf (out v)
        	atts' <- getAttVals
        	putAttVals atts
        	return $ Elem (N s) atts' (celems els)
  where repf = rep f d

cstring :: String -> Content ()
cstring str = CString False str ()

getAttVals :: XmlGenM [Attribute]
getAttVals = ST.get

putAttVals :: [Attribute] -> XmlGenM ()
putAttVals atts = ST.put atts

addAttVal :: Attribute -> XmlGenM ()
addAttVal att = do
	atts <- ST.get
	ST.put (atts++[att])