packages feed

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

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

module Language.XML.Xsd2Type where

import Data.Type
import Data.Equal
import Data.Transform.TwoLevel hiding (not)
import Generics.Pointless.Combinators

import Language.XML.HaXmlAliases

import Text.XML.HaXml.Types hiding (ElementDecl,Choice)
import qualified Text.XML.HaXml.Schema.XSDTypeModel as T(Any(..))
import Text.XML.HaXml.Schema.XSDTypeModel hiding (Any(..))

import Control.Monad
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe
import Control.Monad.State as ST
import Data.List

xsd2type :: MonadPlus m => Schema -> m DynType
xsd2type s = xsd2toptype s >>= return . fst

xsd2toptype :: MonadPlus m => Schema -> m (DynType,TopMap)
xsd2toptype (Schema _ _ _ _ _ _ _ items) = do
	-- process top items
	(_,deps) <- runStateT (topitems2type items) Map.empty
	-- process variable dependencies
	let toprocess = map (\(a,(x,y,z)) -> (a,x,z)) $ Map.toList deps
	(_,top) <- runStateT (dependencies deps toprocess) Map.empty
	-- process root elements
	let els = map (\(SchemaElement e) -> e) $ filter isElement items
	cl <- mapM (element2type (TopItems top)) els
	dynt <- applyDynT reshape (nestedEither cl)
	return (dynt,top)

isElement :: SchemaItem -> Bool
isElement (SchemaElement e) = True
isElement _ = False
isAttribute :: SchemaItem -> Bool
isAttribute (SchemaAttribute e) = True
isAttribute _ = False	

subset :: Eq a => [a] -> [a] -> Bool
subset [] l = True
subset (x:xs) l = elem x l && subset xs l

depends :: Deps -> [(Name,a,b)] -> [Name] -> ([(Name,a,b)],[(Name,a,b)])
depends deps [] vars = ([],[])
depends deps (x:xs) vars = let (l1,r1) = depend deps x vars
                               (l2,r2) = depends deps xs vars
                           in (l1++l2,r1++r2)
depend :: Deps -> (Name,a,b) -> [Name] -> ([(Name,a,b)],[(Name,a,b)])
depend deps (n,t,x) vars | depGraph deps n `subset` vars = ([(n,t,x)],[])
                       | otherwise = ([],[(n,t,x)])
depGraph :: Deps -> Name -> [Name]
depGraph deps name = case Map.lookup name deps of { Nothing -> []; Just (t,ns,x) -> ns }

dependencies :: MonadPlus m => Deps -> [(Name,DynType,SchemaItem)] -> StateT TopMap m ()
dependencies deps [] = return ()
dependencies deps l = do
	processed <- ST.get
	let vars = Map.keys processed
	    (now,after) = depends deps l vars
	if (null now) then error $ "unsupported mutual recursion or reference to undefined top-level type" ++ show (map (\(x,_,_)->x) after)
	else do
		mapM (dependency deps) now
		dependencies deps after	
dependency :: MonadPlus m => Deps -> (Name,DynType,SchemaItem) -> StateT TopMap m ()
dependency deps (n,DynT t,item) = do
	vars <- ST.get
	t' <- replacevar t vars
	ST.put (Map.insert n (DynT t') vars)

topitems2type :: MonadPlus m => [SchemaItem] -> StateT Deps m ()
topitems2type [] = return ()
topitems2type (x:xs) = do
	mb <- topitem2type x
	case mb of
		Just (n,t,deps) -> do
			top <- ST.get
			ST.put $ Map.insert n (t,deps,x) top
			topitems2type xs
		otherwise  -> topitems2type xs

topitem2type :: MonadPlus m => SchemaItem -> m (Maybe (Name,DynType,[Name]))
topitem2type item = do
	let name = itemName item
	case name of
		Nothing -> return Nothing
		Just n  -> do
			mb <- item2type (Self n) item
			case mb of
				Nothing -> return Nothing
				Just (DynT t) -> do
					return $ Just (n,DynT t,collectVars t)

type Deps = Map Name (DynType,[Name],SchemaItem)
type TopMap = Map Name DynType
data TopItems = Self Name | TopItems TopMap

findTopItem :: Map Name DynType -> Name -> Maybe DynType
findTopItem top name = Map.lookup name top

itemName :: SchemaItem -> Maybe Name
itemName (Include _ _) = Nothing
itemName (Import _ _ _) = Nothing
itemName (Redefine _ _) = Nothing
itemName (Annotation _) = Nothing
itemName (Simple s) = Nothing
itemName (Complex (ComplexType _ name _ _ _ _ _)) = name
itemName (SchemaElement (ElementDecl _ (Left (NT name _)) _ _ _ _ _ _ _ _ _)) = Just name
itemName (SchemaElement (ElementDecl _ (Right ref) _ _ _ _ _ _ _ _ _)) = Nothing
itemName (SchemaAttribute (AttributeDecl _ (Left (NT name _)) _ _ _ _)) = Just name
itemName (SchemaAttribute (AttributeDecl _ (Right ref) _ _ _ _)) = Nothing
itemName (AttributeGroup (AttrGroup _ (Left name) _)) = Just name
itemName (AttributeGroup (AttrGroup _ (Right ref) _)) = Nothing
itemName (SchemaGroup (Group _ (Left name) _ _)) = Just name
itemName (SchemaGroup (Group _ (Right ref) _ _)) = Nothing

item2type :: MonadPlus m => TopItems -> SchemaItem -> m (Maybe DynType)
item2type top (Include _ _) = error "xsd:include unsupported"
item2type top (Import _ _ _) = error "xsd:import unsupported"
item2type top (Redefine _ _) = error "xsd:import unsupported"
item2type top (Annotation _) = return Nothing
item2type top (Simple s) = simple2type top s >>= return . Just
item2type top (Complex c) = complex2type top c >>= return . Just
item2type top (SchemaElement e) = element2type top e >>= return . Just
item2type top (SchemaAttribute a) = attribute2type top a >>= return . Just
item2type top (AttributeGroup as) = attrgroup2type top as >>= return . Just
item2type top (SchemaGroup g) = group2type top g

simple2type :: MonadPlus m => TopItems -> SimpleType -> m DynType
simple2type top (Primitive p) = primitive2type p
simple2type top (Restricted _ _ _ _) = error "xsd:restriction unsupported"
simple2type top (ListOf _ _ _ stype) = do
	DynT s <- (simple2type top \/ qname2type) stype
	return $ DynT $ List s
simple2type top (UnionOf _ _ _ union members) = do
	u <- mapM (simple2type top) union
	m <- mapM qname2type members
	return $ nestedEither (u ++ m)

complex2type :: MonadPlus m => TopItems -> ComplexType -> m DynType
complex2type top (ComplexType _ name _ _ _ _ (SimpleContent _ _)) = error "simpleContent unsupported"
complex2type top (ComplexType _ name _ _ _ _ (ComplexContent _ _ _)) = error "complexContent unsupported"
complex2type top (ComplexType _ name _ _ _ _ (ThisType pas)) = particleattrs2type top pas

particleattrs2type :: MonadPlus m => TopItems -> ParticleAttrs -> m DynType
particleattrs2type top (PA pa atts _) = do
	el <- particle2type top pa
	ats <- mapM (attribute2type top \/ attrgroup2type top) atts
	return $ nestedProd $ filterJust (el:map Just ats)
	
particle2type :: MonadPlus m => TopItems -> Particle -> m (Maybe DynType)
particle2type top Nothing = return Nothing
particle2type top (Just (Left acs)) = acs2type top acs >>= return . Just
particle2type top (Just (Right group)) = group2type top group

group2type :: MonadPlus m => TopItems -> Group -> m (Maybe DynType)
group2type top (Group _ (Right ref) occurs _) = liftM (occurs2type occurs) (ref2type top ref) >>= return . Just
group2type top (Group _ (Left name) occurs Nothing) = return Nothing
group2type top (Group _ (Left name) occurs (Just acs)) = liftM (occurs2type occurs) (acs2type top acs) >>= return . Just

acs2type :: MonadPlus m => TopItems -> ChoiceOrSeq -> m DynType
acs2type top (All _ els) = error "xsd:all unsupported"
acs2type top (Choice _ occurs els) = liftM (occurs2type occurs) $ choice2type top els
acs2type top (Sequence _ occurs els) = liftM (occurs2type occurs) $ sequence2type top els

choice2type :: MonadPlus m => TopItems -> [ElementEtc] -> m DynType
choice2type top l = do
	cl <- mapM (elementetc2type top) l
	return $ nestedEither $ filterJust cl

sequence2type :: MonadPlus m => TopItems -> [ElementEtc] -> m DynType
sequence2type top l = do
	sl <- mapM (elementetc2type top) l
	return $ nestedProd $ filterJust sl

filterJust :: [Maybe a] -> [a]
filterJust = map fromJust . filter isJust

elementetc2type :: MonadPlus m => TopItems -> ElementEtc -> m (Maybe DynType)
elementetc2type top (HasElement e) = element2type top e >>= return . Just
elementetc2type top (HasGroup g) = group2type top g
elementetc2type top (HasCS acs) = acs2type top acs >>= return . Just
elementetc2type top (HasAny a) = any2type a >>= return . Just

any2type :: MonadPlus m => T.Any -> m DynType
any2type (T.Any _ _ _ occurs) = return $ occurs2type occurs $ DynT Dynamic

element2type :: MonadPlus m => TopItems -> ElementDecl -> m DynType
element2type top (ElementDecl _ (Right ref) occurs _ _ _ _ _ _ _ _) = liftM (occurs2type occurs) $ ref2type top ref
element2type top (ElementDecl _ (Left (NT name Nothing)) occurs _ _ _ _ _ _ Nothing _) = do
	liftM (occurs2type occurs) $ newElement top name (DynT Dynamic)
element2type top@(Self n) (ElementDecl _ (Left (NT name t)) occurs _ _ _ _ _ _ Nothing _) = do
	-- references to the same type are considered recursive invocations, even if the name does not match
	dynt <- typeref2type top t
	case dynt of
		DynT (Id Any) -> return $ occurs2type occurs dynt
		otherwise     -> liftM (occurs2type occurs) $ newElement top name dynt
element2type top (ElementDecl _ (Left (NT name t)) occurs _ _ _ _ _ _ Nothing _) = do
	dynt <- typeref2type top t
	liftM (occurs2type occurs) $ newElement top name dynt
element2type top (ElementDecl _ (Left (NT name Nothing)) occurs _ _ _ _ _ _ (Just content) _) = do
	dynt <- (simple2type top \/ complex2type top) content
	liftM (occurs2type occurs) $ newElement top name dynt
element2type top (ElementDecl _ (Left (NT name t)) occurs _ _ _ _ _ _ content _) = error "element has two types?"
	
attribute2type :: MonadPlus m => TopItems -> AttributeDecl -> m DynType
attribute2type top (AttributeDecl _ (Right ref) use _ _ _) = do
	att <- ref2type top ref
	return (use2type use att)
attribute2type top (AttributeDecl _ (Left (NT name t)) use _ _ _) = do
	dynt <- typeref2type top t
	att <- newAttribute top name dynt
	return (use2type use att)

occurs2type :: Occurs -> DynType -> DynType
occurs2type (Occurs (Just 0) (Just 9223372036854775807)) (DynT t) = DynT $ List t
occurs2type (Occurs (Just 0) (Just 1)) (DynT t) = DynT (Either t One)
occurs2type (Occurs (Just 1) (Just 1)) t = t
occurs2type (Occurs Nothing max) t = occurs2type (Occurs (Just 1) max) t
occurs2type (Occurs min Nothing) t = occurs2type (Occurs min (Just 1)) t
occurs2type (Occurs min max) t = error $ "occurs unsupported: " ++ show min ++ "-" ++ show max

use2type :: Use -> DynType -> DynType
use2type Prohibited t = DynT One
use2type Required t = t
use2type Optional (DynT t) = DynT (Either t One)

attrgroup2type :: MonadPlus m => TopItems -> AttrGroup -> m DynType
attrgroup2type top (AttrGroup _ (Right ref) _) = ref2type top ref
attrgroup2type top (AttrGroup _ (Left name) atts) = do
	ats <- mapM (attribute2type top \/ attrgroup2type top) atts
	return $ nestedProd ats

basicTypes :: Map Name DynType
basicTypes = Map.fromList
	[("integer",DynT Int),("positiveInteger",DynT Int),("int",DynT Int)
	,("decimal",DynT (List Char)),("float",DynT (List Char)),("double",DynT (List Char))
	,("string",DynT (List Char)),("byte",DynT Int),("boolean",DynT Bool),("date",DynT (List Char))
	,("NMTOKEN",DynT (List Char)),("NMTOKENS",DynT (List Char)),("ID",DynT (List Char))
	,("anyUri",DynT (List Char)),("language",DynT (List Char)),("NCName",DynT (List Char))
	,("NCName",DynT (List Char)),("dateTime",DynT (List Char)),("nonNegativeInteger",DynT nat)
	,("short",DynT Int),("long",DynT Int),("token",DynT (List Char)),("duration",DynT (List Char))
	,("normalizedString",DynT (List Char)),("Name",DynT (List Char))]

isBasicType :: QName -> Bool
isBasicType (QN (Namespace "xs" _) n) = Map.member n basicTypes
isBasicType (QN (Namespace "xsd" _) n) = Map.member n basicTypes
isBasicType _ = False

-- A simple type as a qualified name
qname2type :: MonadPlus m => QName -> m DynType
qname2type qn@(isBasicType -> True) = return $ fromJust $ Map.lookup (fromQName qn) basicTypes
qname2type qn = error $ show qn ++ " is not a basic type"

typeref2type :: MonadPlus m => TopItems -> Maybe QName -> m DynType
typeref2type top Nothing = return $ DynT Dynamic
typeref2type top (Just qn@(isBasicType -> True)) = return $ fromJust $ Map.lookup (fromQName qn) basicTypes
typeref2type top (Just qn) = ref2type top qn

ref2type :: MonadPlus m => TopItems -> QName -> m DynType
ref2type (Self name) (qname -> n) = if name == n
	then return $ DynT $ Id Any
	else return $ DynT $ Var n
ref2type (TopItems top) (qname -> n) = case findTopItem top n of
	Just t  -> return t
	Nothing -> error $ "reference to undefined top-level type " ++ show n

fromQName :: QName -> Name
fromQName (QN _ name) = name

qname :: QName -> Name
qname (N name) = name
qname (QN (Namespace ns _) name) = name

primitive2type :: MonadPlus m => PrimitiveType -> m DynType
primitive2type Boolean = return $ DynT Bool
primitive2type _ = return $ DynT (List Char)

nestedEither,nestedProd :: [DynType] -> DynType
nestedEither [] = DynT $ One
nestedEither [t] = t
nestedEither (DynT x:xs) = applyDynT (\y -> DynT $ Either x y) (nestedEither xs)
nestedProd [] = DynT $ One
nestedProd [t] = t
nestedProd (DynT x:xs) = applyDynT (\y -> DynT $ Prod x y) (nestedProd xs)

newElement :: MonadPlus m => TopItems -> Name -> DynType -> m DynType
newElement top name (DynT t) = do
	FRep f <- inferFctr (Id Any) t
	return $ DynT $ NewData name f

newAttribute :: MonadPlus m => TopItems -> Name -> DynType -> m DynType
newAttribute top name (DynT t) = do
	FRep f <- inferFctr (Id Any) t
	return $ DynT $ NewData ("@"++name) f