packages feed

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

-----------------------------------------------------------------------------
-- |
-- Module      :  Language.XML.Type2Xsd
-- 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 type representations to XML Schemas.
--
-----------------------------------------------------------------------------

module Language.XML.Type2Xsd where

import Data.Type
import Data.Equal
import Language.XML.Xml2Type
import Language.XML.Xsd2Type
import Language.XML.HaXmlAliases

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

-- (attributes of the current element,recursive complex elements)
type XsdGenM a = MonadPlus m => StateT ([Element ()],[Element ()]) m a

type2xsd :: Type a -> Maybe Schema
type2xsd t = type2doc t >>= doc2Xsd . one2posn

type2doc :: Type a -> Maybe (Document ())
type2doc t = evalStateT (type2doc' t) ([],[])

type2doc' :: Type a -> XsdGenM (Document ())
type2doc' t = do
	els <- type2topelements t
	(_,complexels) <- ST.get
	return $ Document
		(Prolog  (Just (XMLDecl "1.0" (Just (EncodingDecl "utf-8")) Nothing)) [] Nothing [])
		[] (Elem (N "xsd:schema")
			[ genAttribute "xmlns:xsd" "http://www.w3.org/2001/XMLSchema"]
			(celems els ++ celems complexels)
		) []
	
type2topelements :: Type a -> XsdGenM [Element ()]
type2topelements e@(Either a b) | not (isMaybe e) = do
	x <- type2element a 
	y <- type2element b
	return (maybeToList x ++ maybeToList y)
type2topelements t = do
	mb <- type2element t
	case mb of { Just el -> return [el]; Nothing -> error "top-level attribute?" }

type2element :: Type a -> XsdGenM (Maybe (Element ()))
type2element (Either One a) = type2element (Either a One)
type2element (Data "Maybe" (K One :+!: K a)) = type2element (Either a One)
type2element (Either a One) | isAtt a = data2attribute a >>= addAtt . maybe2attribute >> return Nothing
type2element a | isAtt a =  data2attribute a >>= addAtt . nonmaybe2attribute >> return Nothing
type2element (Either a One) = type2element a >>= return . fmap maybe2element
type2element (List a) = type2element a >>= return . fmap list2element
type2element d | isData d = data2element d >>= return . Just
type2element (Id (Var s)) = return $ Just $ Elem (N "xsd:element") [genAttribute "name" s,genAttribute "type" (ref s)] []
type2element Dynamic = return $ Just $ Elem (N "xsd:any") [] []
type2element t = error $ "type2element: " ++ show t

isMaybe :: Type a -> Bool
isMaybe (Either a One) = True
isMaybe (Either One a) = True
isMaybe (Data "Maybe" (K One :+!: K a)) = True
isMaybe _ = False

isBasicList :: Type a -> Bool
isBasicList (List a) = isBasic a
isBasicList _ = False

isDynamic :: Type a -> Bool
isDynamic Dynamic = True
isDynamic _ = False

nonmaybe2attribute :: Element () -> Element ()
nonmaybe2attribute (Elem n atts cts) = Elem n (atts ++ [genAttribute "use" "required"]) cts

maybe2attribute :: Element () -> Element ()
maybe2attribute (Elem n atts cts) = Elem n (atts ++ [genAttribute "use" "optional"]) cts

maybe2element :: Element () -> Element ()
maybe2element (Elem n atts cts) = Elem n (atts ++ [genAttribute "minOccurs" "0",genAttribute "maxOccurs" "1"]) cts

list2element :: Element () -> Element ()
list2element (Elem n atts cts) = Elem n (atts ++ [genAttribute "minOccurs" "0",genAttribute "maxOccurs" "unbounded"]) cts

basic2primitive :: Type a -> XsdGenM String
basic2primitive (List Char) = return "xsd:string"
basic2primitive (Data "Nat" _) = return "xsd:nonNegativeInteger"
basic2primitive Int = return "xsd:integer"
basic2primitive Bool = return "xsd:boolean"

basiclist2list :: Type a -> XsdGenM (Element ())
basiclist2list (List a) = do
	t <- basic2primitive a
	return $ Elem (N "xsd:list") [genAttribute "itemType" t] []

data2attribute :: Type a -> XsdGenM (Element ())
data2attribute (dataNameFctr -> Just (nodename -> ('@':s),f)) | isBasic repf = do
	t <- basic2primitive repf
	return $ Elem (N "xsd:attribute") [genAttribute "name" s,genAttribute "type" t] []
  where repf = rep f $ One
data2attribute a = error $ "data2attribute: " ++ show a

data2element :: Type a -> XsdGenM (Element ())
data2element (Data s f) = data2element (NewData s f)
data2element (NewData s f) | isOne repf = do
	let complex = Elem (N "xsd:complexType") [] (celems [Elem (N "xsd:sequence") [] []])
	return $ Elem (N "xsd:element") [genAttribute "name" (nodename s)] (celems [complex])
                           | isDynamic repf = do
	return $ Elem (N "xsd:element") [genAttribute "name" (nodename s)] []
                           | isBasic repf = do
	t <- basic2primitive repf
	return $ Elem (N "xsd:element") [genAttribute "name" (nodename s),genAttribute "type" t] []
                           | isBasicList repf = do
	l <- basiclist2list repf
	let simple = Elem (N "xsd:simpleType") [] (celems [l])
	return $ Elem (N "xsd:element") [genAttribute "name" (nodename s)] (celems [simple])
	                   | otherwise = do
	atts <- getAtts
	putAtts []
	mbels <- type2content False repf
	let els = maybe [] (:[]) mbels
	atts' <- getAtts
	let complex = Elem (N "xsd:complexType") [genAttribute "name" (ref $ refname s)] (celems els ++ celems atts')
	addTopComplex complex
	putAtts atts
	return $ Elem (N "xsd:element") [genAttribute "name" (nodename s),genAttribute "type" (ref $ refname s)] []
  where repf = rep f $ Id $ Var $ refname s

-- Bool for identifying if we need to create a sequence container or not
type2content :: Bool -> Type a -> XsdGenM (Maybe (Element ()))
type2content b (Either One a) = type2content b (Either a One)
type2content b (Data "Maybe" (K One :+!: K a)) = type2content b (Either a One)
type2content b (Either a One) | isAtt a = data2attribute a >>= addAtt . maybe2attribute >> return Nothing
type2content b a | isAtt a =  data2attribute a >>= addAtt . nonmaybe2attribute >> return Nothing
type2content b (Either a One) = type2content b a >>= return . fmap maybe2element
type2content b (List a) = type2content b a >>= return . fmap list2element
type2content b p@(Prod _ _) = prods2elements p >>= return . Just . Elem (N "xsd:sequence") [] . celems
type2content b e@(Either _ _) | not (isMaybe e) = sums2elements e >>= return . Just . Elem (N "xsd:choice") [] . celems
type2content False t = type2element t >>= return . fmap (\el -> Elem (N "xsd:sequence") [] (celems [el]))
type2content True t = type2element t

prods2elements :: Type a -> XsdGenM [Element ()]
prods2elements (Prod a b) = do
	x <- prods2elements a
	y <- prods2elements b
	return (x ++ y)
prods2elements t = type2content True t >>= return . maybe [] (:[])

sums2elements :: Type a -> XsdGenM [Element ()]
sums2elements e@(Either a b) | not (isMaybe e) = do
	x <- sums2elements a
	y <- sums2elements b
	return (x ++ y)
sums2elements t = type2content True t >>= return . maybe [] (:[])

ref :: String -> String
ref s = s++"REF"

celems :: [Element ()] -> [Content ()]
celems = map (\e -> CElem e ())

genAttribute :: String -> String -> Attribute
genAttribute n v = (N n, AttValue [Left $ v])

addTopComplex :: Element () -> XsdGenM ()
addTopComplex e = do
	(atts,tops) <- ST.get
	if (elem e tops) then return () else ST.put (atts,e:tops)

getAtts :: XsdGenM [Element ()]
getAtts = do
	(atts,_) <- ST.get
	return atts

putAtts :: [Element ()] -> XsdGenM ()
putAtts atts = do
	(_,tops) <- ST.get
	ST.put (atts,tops)

addAtt :: Element () -> XsdGenM ()
addAtt att = do
	(atts,tops) <- ST.get
	ST.put (atts++[att],tops)