@@ -1,454 +1,450 @@
1−{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE OverloadedStrings #-}
1{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE TupleSections #-}
10−import Control.Monad.RWS.CPS (RWS, execRWS)
11−import Control.Monad.Reader
12−import Control.Monad.State.Strict
13−import Control.Monad.Writer.CPS (Writer, tell, execWriter)
14−import Data.ByteString.Lazy as BL
17−import Data.Foldable as F
19−import Data.Map.Strict as M
22−import Data.Text.IO as T
23−import Data.Traversable
25−import System.Environment
32−makeNamePrefix :: Text -> Text
33−makeNamePrefix = T.map C.toLower . T.filter (\c -> C.isUpper c || C.isDigit c)
35−type GenMonad = ReaderT DatatypeMap (Writer Text)
37−class Fields a where
6+where
38− fields :: a -> [Text]
40−instance Fields Datatype where
41− fields (TypeComplex ty) = fields ty
42− fields (TypeSimple ty) = fields ty
44−instance Fields SimpleType where
45− fields ty@(STAtomic tyName _ _ restrictions) = if shouldBeNewtype ty
46− then [getNewtypeUnconsName tyName]
47− else getEnumeratedCode restrictions
49−instance Fields ComplexType where
50− fields (ComplexType _ _ _ (Just g)) = fields g
51− fields (ComplexType _ _ _ Nothing) = []
53−instance Fields ModelGroupSchema where
54− fields (CTSequence els) = snd . name <$> els
55− fields (CTChoice els) = snd . name <$> els
56− fields (CTAll els) = snd . name <$> els
58−class References a where
59− references :: a -> [Text]
61−instance References Datatype where
8+import Data.Text (Text)
62− references (TypeComplex (ComplexType _ _ _ mmgs)) =
9+import qualified Data.Text as Text
63− case mmgs of
10+import qualified Data.Text.IO as Text
64− Just mgs -> case mgs of
11+import Data.Maybe
65− CTSequence els -> F.concatMap references els
12+import qualified Data.List as List
66− CTChoice els -> F.concatMap references els
13+import qualified Data.Set as Set
67− CTAll els -> F.concatMap references els
14+import Data.Map (Map)
68− Nothing -> []
15+import qualified Data.Map as Map
69− references (TypeSimple _) = []
16+import Control.Monad
70−
17+import qualified System.IO as IO
71−instance References Element where
18+import qualified Xsd
72− references (Element _ t _ _ _) = references t
74−instance References DatatypeRef where
75− references (DatatypeRef ty) = [ty]
76− references (InlineComplex dt) = references dt
78−instance References a => References [a] where
79− references = F.concatMap references
81−-- | 'DatatypeMap' are the types left to process
82−-- 'S.Set Text' are the types already processed.
83−type PreMonad = StateT (DatatypeMap, S.Set Text) (Writer [(Text, Datatype)])
85−type PreElemMonad = RWS DatatypeMap [Element] [Element]
87−-- | Marks the given type as processed.
88−proceed :: Text -> Datatype -> PreMonad ()
91− modify $ \(f,s) -> (M.delete t f, S.insert t s)
93−-- | Marks element as processed.
94−proceed' :: Element -> PreElemMonad ()
99−untwistDeps :: [Element] -> DatatypeMap -> ([(Text, Datatype)], [Element])
100−untwistDeps e dm = (dList, e')
102− dList = disambiguateNames . nub . execWriter $ do
103− (a, _) <- runStateT sortDatatypesByDeps (dm, S.empty)
105− (_, e') = execRWS sortElemsByDeps (M.fromList dList) e
107−type Abbreviation = Text
108−type Subelement = Text
109−type DisambiguaterMonad = State (M.Map Abbreviation (S.Set Subelement))
111−-- | If any two datatypes with the same name abbreviation ('makeNamePrefix')
112−-- have any fields in common, then one of them gets a \"2\" appended to it
113−-- (\"_2\" if the last character of the name was a digit).
114−-- Repeat the same process until there are no conflicts.
115−-- If we have already appended a \"2\" to a datatype and it still has conflicts,
116−-- we change the 2 to a 3, a 3 to a 4 and so on.
117−disambiguateNames :: [(Text, Datatype)] -> [(Text, Datatype)]
118−disambiguateNames = flip evalState mempty . mapM disambiguateName
120− disambiguateName :: (Text, Datatype) -> DisambiguaterMonad (Text, Datatype)
121− disambiguateName (tyName', dt) = do
122− let rNames = S.fromList $ fields dt
123− (, dt) <$> attempt tyName' rNames 1
125− attempt :: Abbreviation -> S.Set Subelement -> Int -> DisambiguaterMonad Text
126− attempt tyName fieldNames n = do
127− processedTypes <- get
131− | shouldSeparateNumber = tyName <> "_" <> T.pack (show n)
132− | otherwise = tyName <> T.pack (show n)
133− abr = makeNamePrefix newTyName
134− case M.lookup abr processedTypes of
136− if S.null $ S.intersection fieldNames usedNames
137− then modify (M.adjust (S.union fieldNames) abr) >> return newTyName
138− else attempt tyName fieldNames (n + 1)
139− Nothing -> modify (M.insert abr fieldNames) >> return newTyName
140− shouldSeparateNumber = isDigit $ T.last tyName'
142−sortElemsByDeps :: PreElemMonad ()
145− if Prelude.null els then pure () else do
146− let first = Prelude.head els
147− -- traceM $ "sortElemsByDeps: " <> show first
148− case references first of
149− [] -> proceed' first >> sortElemsByDeps
151− results <- traverse memberDatatype xs
153− then proceed' first >> sortElemsByDeps
155− let failed = fmap snd $ L.filter (not . fst) $ L.zip results xs
156− error $ "impossible to lookup types: " <> show failed
158−memberDatatype :: Text -> PreElemMonad Bool
159−memberDatatype ty = do
160− -- traceM $ "memberDatatype: " <> show ty
162− case M.lookup ty dm of
163− Just _ -> return True
164− Nothing -> return False
166−sortDatatypesByDeps :: PreMonad ()
167−sortDatatypesByDeps = do
169− if M.null dm then pure () else do
170− let first = Prelude.head (M.toList dm)
171− traceM $ "sortDatatypesByDeps: " <> show first
172− case references $ snd first of
173− [] -> uncurry proceed first >> sortDatatypesByDeps
175− traverse_ lookupDatatype xs
176− uncurry proceed first
179−lookupDatatype :: Text -> PreMonad ()
180−lookupDatatype ty = do
181− traceM $ "lookupDatatype: " <> show ty
183− case M.lookup ty dm of
184− Nothing -> if S.member ty s then pure () else
185− error $ "can't look up datatype: " <> show ty
186− Just typ -> case references typ of
187− [] -> proceed ty typ
188− xs -> traverse_ lookupDatatype xs >> proceed ty typ
190−class Capitalizable t where
191− capitalize :: t -> t
193−instance Capitalizable String where
195− capitalize (a:as) = C.toUpper a : as
197−instance Capitalizable Text where
200− | otherwise = T.singleton (C.toUpper (T.head t)) <> T.tail t
202−data DataFlavour = QQ | Plain
204−-- TODO: improve after moving some XML types to xml-isogen
205−simpleTypeToIsoType :: DataFlavour -> SimpleAtomicType -> Text
206−simpleTypeToIsoType fl = \case
207− STAnyURI -> wrap fl "Text"
208− STBase64Binary -> wrap fl "ByteString"
209− STBoolean -> wrap fl "Bool"
210− STDate -> wrap fl "Date \"%YYYYj\""
211− STDateTime -> wrap fl "Text"
212− STDateTimeStamp -> wrap fl "Text"
213− STDecimal DTDecimal -> wrap fl "Double"
214− (STDecimal _) -> wrap fl "Integer"
215− STDouble -> wrap fl "Double"
216− (STDuration _) -> wrap fl "Integer"
217− STFloat -> wrap fl "Float"
218− STGDay -> wrap fl "Day"
219− STGMonth -> wrap fl "Text"
220− STGMonthDay -> wrap fl "Text"
221− STGYear -> wrap fl "Text"
222− STGYearMonth -> wrap fl "Text"
223− STHexBinary -> wrap fl "Text"
224− STNOTATION -> wrap fl "Text"
225− STQName -> wrap fl "Text"
226− (STString _) -> wrap fl "Text"
227− STTime -> wrap fl "Text"
229− wrap QQ t = "[t|" <> t <> "|]"
233− :: [Text] -- generated code
236−tellGen code comment =
237− tell $ "\n" <> comment <> T.unlines code
239−runGen :: DatatypeMap -> GenMonad a -> Text
20+import Options
240−runGen dm act = execWriter $ runReaderT act dm
21+import Gen
242−generateIsoXml :: Maybe Header -> ModuleName -> GenType -> XSD -> Text
25+main :: IO ()
243−generateIsoXml mHeader name genType xsd@(XSD (e,dm)) =
26+main = do
28+ schema <- Xsd.getSchema (oInput opts)
29+ res <- either fail return
30+ (generate opts schema)
31+ mapM_ (Text.hPutStrLn IO.stderr) (resultWarnings res)
32+ Text.putStrLn (resultCode res)
36+ , resultWarnings :: [Text]
39+generate :: Options -> Xsd.Schema -> Either String Result
40+generate opts schema = do
245− (dList, el) = untwistDeps e dm
42+ types = Map.union (Xsd.schemaTypes schema)
246− in (header mHeader name genType <>) . runGen dm $ do
43+ (elementsToTypes (Xsd.schemaElements schema))
247− for_ dList $ \(t,dt) -> do
44+ s <- runGen (initialGenState opts types) $ do
248− generateIsoDatatype genType (Just t) dt []
45+ genHeader opts
249− for_ el (generateIsoRecord genType)
46+ genTypes types
251−toQualifier :: Int -> Int -> Text
48+ { resultCode = Text.unlines (reverse (gsCode s))
252−toQualifier minOc maxOc
49+ , resultWarnings = reverse (gsWarnings s)
253− | minOc == 1 && maxOc == 1 = "!"
50+ }
254− | minOc == 0 && maxOc == 1 = "?"
51+
255− | minOc == 1 && maxOc > minOc = "+"
52+elementsToTypes :: Map Xsd.QName Xsd.Element -> Map Xsd.QName Xsd.Type
256− | minOc == 1 && maxOc < minOc = error "maxOccurs < minOccurs"
53+elementsToTypes = List.foldl' step Map.empty . Map.toList
259−header :: Maybe Header -> ModuleName -> GenType -> Text
260−header mHeader name genType = T.unlines $
261− maybeToList (T.unlines . fmap ("-- " <>) . T.lines <$> mHeader) <>
262− [ "module " <> name <> " where"
264− , "import Control.DeepSeq"
265− , "import Data.THGen.XML"
267− [ "import Prelude hiding ((*), (+))"
270− specImports = case genType of
55+ step types (_, e) = case Xsd.elementType e of
271− Generator -> [ xmlWriter ]
56+ Xsd.Ref _ -> types
272− Parser -> [ domParser ]
57+ Xsd.Inline t -> Map.insert (Xsd.elementName e) t types
273− Both -> [ domParser, xmlWriter ]
58+
274− xmlWriter, domParser :: Text
59+genTypes :: Map Xsd.QName Xsd.Type -> Gen ()
275− xmlWriter = "import Text.XML.Writer"
60+genTypes types = mapM_ (uncurry genTopType) . Map.toList $ types
276− domParser = "import Text.XML.DOM.Parser.FromDom"
61+
277−
62+genTopType :: Xsd.QName -> Xsd.Type -> Gen ()
278−quote :: Text -> Text
63+genTopType = genType []
279−quote t = "\"" <> t <> "\""
64+
280−
65+genType :: [Xsd.Annotation] -> Xsd.QName -> Xsd.Type -> Gen ()
281−generateIsoRecord :: GenType -> Element -> GenMonad ()
283− genType
66+genType annotations name tp = do
284− (Element (_, laxName) elXtype _ _ elAnnotations) = do
67+ known <- knownTypeName name
286− case elXtype of
68+ case known of
287− InlineComplex dt ->
69+ Just _ -> return () -- already generated
288− generateIsoDatatype genType laxName dt elAnnotations
70+ Nothing -> do
289− DatatypeRef nam -> if M.member nam written
71+ -- we should generate dependencies before the type itself to make
290− then pure ()
72+ -- sure they are in scope when xml-isogen quotes them
291− else error $ "Implementation failure: "
73+ -- (stage restriction in TH)
292− <> show nam <> " type never encountered before"
74+ genDependencies tp
293−
75+ typeName <- makeTypeName name tp
294−data CommentType = Haddock | Regular
76+ case tp of
295−
77+ Xsd.TypeSimple t -> genSimpleType typeName t annotations
296−toComment :: CommentType -> [Annotation] -> Text
78+ Xsd.TypeComplex t -> genComplexType typeName t annotations
297−toComment ct ann = case texts of
79+ genExtension name
299− (h:tl) -> T.unlines ((prefix <> h) : fmap ("-- "<>) tl)
81+genExtension :: Xsd.QName -> Gen ()
300− where
82+genExtension base = do
301− texts = stripTexts $ F.concatMap (wrap80 . fromDocumentation) ann
83+ exts <- extensions base
302− stripTexts = \case
84+ case exts of
303− [] -> []
85+ [] -> return ()
304− l@(h:tl) -> if T.all (\x -> x == ' ' || x == '\n') h then stripTexts tl else l
86+ types -> do
305− prefix = case ct of
87+ mapM_ (uncurry genTopType) types
306− Haddock -> "-- | "
88+ baseTypeName <- tnPrefixed <$> resolveTypeName base
307− Regular -> "-- "
89+ writeCode
308−
90+ [ "data Any" <> baseTypeName
309−fromDocumentation :: Annotation -> Text
91+ , " = The" <> baseTypeName <> " !" <> baseTypeName
310−fromDocumentation (Documentation t) = t
92+ ]
312−wrap80 :: Text -> [Text]
94+ mkConstructor (n, _) = do
313−wrap80 = L.foldl' go [] . T.split (==' ')
95+ tn <- tnPrefixed <$> resolveTypeName n
314− where
96+ exts' <- extensions n
317− let last' = L.last l
318− in if T.length last' + T.length e >= 73 -- 80 minus "-- | " and ' '
320− else L.init l <> [L.last l <> " " <> e]
328− -- ^ (type name for lookups, generated code)
332− (TypeComplex (ComplexType _ _ tyAnnotations mGroupSchema))
334− case mGroupSchema of
335− Just groupSchema -> case groupSchema of
336− CTSequence elements -> do
337− tyFields <- for elements (generateIsoField genType)
339− comment = toComment Haddock $ annotationsRec
98+ n' = case exts' of
340− <> [Documentation ""]
99+ [] -> tn
341− <> tyAnnotations
100+ _ -> "Any" <> tn
342− tyHeader =
101+ return (" | The" <> n' <> " !" <> n')
343− quote (capitalize eName)
102+ constructors <- mapM mkConstructor types
344− <> " =:= record "
103+ writeCode constructors
105+ [ " deriving (Eq, Show)"
109+ [ "instance NFData Any" <> baseTypeName <> " where"
114+ unless (mode == Generator) $ do
116+ [ "instance FromDom Any" <> baseTypeName <> " where"
118+ , " tp <- parseAttribute"
120+ "\"{http://www.w3.org/2001/XMLSchema-instance}type\")"
126+ tn <- resolveTypeName n
127+ exts' <- extensions n
129+ [] -> return (tnPrefixed tn)
130+ _ | n == base -> return (tnPrefixed tn)
131+ _ -> return ("Any" <> tnPrefixed tn)
139+ clauses <- mapM mkClause (base : map fst types)
142+ [ " _ -> throwParserError (PEOther \"Unexpected type\")"
145+ unless (mode == Parser) $ do
147+ [ "instance ToXML Any" <> baseTypeName <> " where"
148+ , " toXML (The" <> baseTypeName <> " a) = toXML a"
151+ mkClause (n, _) = do
152+ tn <- tnPrefixed <$> resolveTypeName n
153+ exts' <- extensions n
158+ return (" toXML (The" <> n' <> " a) = toXML a")
159+ clauses <- mapM mkClause types
163+ [ "instance ToXmlParentAttributes Any" <> baseTypeName <> " where"
167+ tn <- resolveTypeName n
168+ exts' <- extensions n
170+ [] -> return (tnPrefixed tn)
171+ _ | n == base -> return (tnPrefixed tn)
172+ _ -> return ("Any" <> tnPrefixed tn)
174+ [ " toXmlParentAttributes (The" <> n' <> " a) ="
175+ , " (\"{http://www.w3.org/2001/XMLSchema-instance}type\", \""
176+ <> tnName tn <> "\")"
177+ , " : toXmlParentAttributes a"
179+ attrLines <- mapM toAttrLine (base : map fst types)
180+ writeCode (concat attrLines)
182+genDependencies :: References a => a -> Gen ()
183+genDependencies a = forM_ (references a) $ \name -> do
184+ known <- knownTypeName name
186+ Just _ -> return () -- already generated
188+ tp <- resolveType name
191+genSimpleType :: TypeName -> Xsd.SimpleType -> [Xsd.Annotation]-> Gen ()
192+genSimpleType tn (Xsd.AtomicType restriction ann) parentAnn = do
194+ baseName r = case Xsd.simpleRestrictionBase r of
196+ Xsd.Inline (Xsd.AtomicType r' _) -> baseName r'
197+ Xsd.Inline (Xsd.ListType _ _) -> Nothing
198+ Xsd.Inline (Xsd.UnionType _ _) -> Nothing
199+ case baseName restriction of
201+ | isEnum restriction
203+ (Xsd.simpleRestrictionConstraints restriction)
206+ genNewtype tn base (parentAnn <> ann)
207+ _ -> genUnsupported tn
208+genSimpleType typeName (Xsd.ListType _ _) _ = genUnsupported typeName
209+genSimpleType typeName (Xsd.UnionType _ _) _ = genUnsupported typeName
211+genNewtype :: TypeName -> Xsd.QName -> [Xsd.Annotation] -> Gen ()
212+genNewtype tn base annots = do
213+ fieldTypeName <- tnPrefixed <$> resolveTypeName base
216+ typeName = tnPrefixed tn
217+ fieldName = "un" <> typeName
218+ toxml = case mode of
221+ writeCode $ makeComments tn annots
223+ [ "newtype " <> typeName
225+ , " { " <> fieldName <> " :: " <> fieldTypeName
226+ , " } deriving (Show, Eq, NFData" <> toxml <> ")"
229+ unless (mode == Generator) $ writeCode
230+ [ "instance FromDom " <> typeName <> " where"
231+ , " fromDom = " <> typeName <> " <$> fromDom"
235+genEnum :: TypeName -> [Xsd.Constraint] -> [Xsd.Annotation] -> Gen ()
236+genEnum tn constraints annots = do
239+ values = map toValue constraints
240+ toValue (Xsd.Enumeration v) = v
241+ enumLines <- forM values $ \v -> do
242+ enumName <- makeEnumName v
243+ return (" & \"" <> enumName <> "\"")
244+ writeCode $ makeComments tn annots
245+ writeCode $ "\"" <> tnName tn <> "\" Exhaustive =:= enum "
345− <> genTypeToText genType
246+ <> genTypeToText mode : enumLines
346− tellGen (tyHeader : tyFields) comment
247+ writeCode [""]
347− CTChoice _ -> error "'Choice' group not supported yet"
248+
348− CTAll _ -> error "'All' group not supported yet"
249+isEnum :: Xsd.SimpleRestriction -> Bool
349− Nothing -> error $ "No group schema provided for type: " <> T.unpack eName
250+isEnum = any isEnumConstraint . Xsd.simpleRestrictionConstraints
353− (TypeSimple ty@(STAtomic iName sat tyAnnotations restrictions))
358− toComment Haddock $ annotationsRec
359− <> [Documentation ""]
361− if not (shouldBeNewtype ty)
364− (quote $ capitalize eName)
365− (getEnumeratedCode restrictions))
368− tellGen (generateNewtype genType iName sat) comment
370−getEnumeratedCode :: [Restriction] -> [Text]
371−getEnumeratedCode = F.concatMap fromEnumeration . Prelude.filter isEnumeration
373− fromEnumeration (Enumeration x) = x
252+ isEnumConstraint (Xsd.Enumeration _) = True
375−isEnumeration :: Restriction -> Bool
254+genComplexType :: TypeName -> Xsd.ComplexType -> [Xsd.Annotation] -> Gen ()
376−isEnumeration (Enumeration _) = True
255+genComplexType typeName t parentAnn = do
378−shouldBeNewtype :: SimpleType -> Bool
379−shouldBeNewtype (STAtomic _ _ _ restrictions) =
380− not . Prelude.any isEnumeration $ restrictions
382−getNewtypeConsName :: Text -> Text
383−getNewtypeConsName = ("Xml" <>) . capitalize
385−getNewtypeUnconsName :: Text -> Text
386−getNewtypeUnconsName = ("un" <>) . getNewtypeConsName
388−generateNewtype :: GenType -> Text -> SimpleAtomicType -> [Text]
389−generateNewtype genType n sat =
391− constructor = getNewtypeConsName n
392− decons = getNewtypeUnconsName n
395− , "instance FromDom " <> constructor <> " where"
396− , " fromDom = " <> constructor <> " <$> fromDom"
399− [ "newtype " <> constructor
400− , " = " <> constructor
401− , " { " <> decons <> " :: " <> simpleTypeToIsoType Plain sat
402− , " } deriving (Show, Eq, NFData" <> case genType of
405− ] <> case genType of
410− :: Text -- type name
412− -> [Text] -- generated code
413−generateEnum enumName enums =
415− enumHeader = enumName <> " Exhaustive =:= enum Both"
258+ header = "\"" <> tnName typeName <> "\" =:= record "
416− eFields = ("& "<>) <$> enums
259+ <> genTypeToText mode
417− in pure enumHeader <> eFields
260+ fields <- genFields typeName (Xsd.complexContent t)
418−
261+ writeCode $ makeComments typeName
419−lookupSimpleType
262+ (parentAnn <> Xsd.complexAnnotations t)
420− :: DatatypeRef
263+ writeCode [header]
421− -> GenMonad (Maybe Text)
264+ writeCode fields
422−lookupSimpleType (InlineComplex _) = pure Nothing
265+ writeCode [""]
423−lookupSimpleType (DatatypeRef tyName) =
266+
424− asks (M.lookup tyName) >>= \case
267+makeComments :: TypeName -> [Xsd.Annotation] -> [Text]
425− Just (TypeComplex _) -> pure Nothing
268+makeComments typeName annotations = if null annotations
426− Just (TypeSimple (STAtomic nam _ _ _)) -> pure $ Just nam
269+ then
427− Nothing -> error $ "can't look up type: " <> show tyName
270+ [ "{-|"
429−generateIsoField :: GenType -> Element -> GenMonad Text
272+ , "-}"
430−generateIsoField
273+ ]
432− (Element (_, fieldName) fieldXtype minOc maxOc fieldAnnotations) = do
275+ [ "{-|"
434− qualifier = toQualifier minOc maxOc
277+ , ""
435− line <- lookupSimpleType fieldXtype >>= \case
278+ ] <> map makeDoc annotations <>
436− Just tyName ->
279+ [ "-}"
437− pure $ quote fieldName <> " [t|Xml" <> tyName <> "|]"
280+ ]
438− Nothing -> case fieldXtype of
281+ where
439− InlineComplex dt -> do
282+ generated = "Generated from: " <> prefix <> name
440− generateIsoDatatype genType (capitalize fieldName) dt fieldAnnotations
283+ qname = tnQName typeName
441− pure $ quote fieldName
284+ name = Xsd.qnName qname
442− DatatypeRef ref ->
285+ prefix = maybe "" ((<> ":") . Xsd.fromNamespace) (Xsd.qnNamespace qname)
443− pure $ quote fieldName <> " [t|Xml" <> capitalize ref <> "|]"
286+ makeDoc (Xsd.Documentation docs) = docs
444− pure $ " " <> qualifier <> " " <> line
287+
288+-- | Generate code for record fields
290+-- The first argument - record name
291+genFields :: TypeName -> Xsd.Content -> Gen [Text]
292+genFields typeName (Xsd.ContentPlain c) =
293+ maybe (return []) (genFieldsForModel typeName) (Xsd.plainContentModel c)
294+genFields _ (Xsd.ContentSimple _) = return []
295+genFields typeName (Xsd.ContentComplex (Xsd.ComplexContentExtension e)) = do
296+ base <- resolveType (Xsd.complexExtensionBase e)
297+ genDependencies base
298+ baseFields <- case base of
299+ Xsd.TypeComplex t -> genFields typeName (Xsd.complexContent t)
301+ fields <- maybe (return []) (genFieldsForModel typeName)
302+ (Xsd.complexExtensionModel e)
303+ return (baseFields ++ fields)
304+genFields _ (Xsd.ContentComplex (Xsd.ComplexContentRestriction _)) =
307+genFieldsForModel :: TypeName -> Xsd.ModelGroup -> Gen [Text]
308+genFieldsForModel typeName (Xsd.Sequence elements) =
309+ catMaybes <$> mapM (genField typeName) elements
310+genFieldsForModel _ (Xsd.Choice _) = do
311+ warn "choice: not implemented"
313+genFieldsForModel _ (Xsd.All _) = do
314+ warn "all: not implemented"
317+genField :: TypeName -> Xsd.RefOr Xsd.Element -> Gen (Maybe Text)
318+genField _ (Xsd.Ref _) = return Nothing
319+genField typeName (Xsd.Inline e) = do
320+ fieldName <- makeFieldName typeName (Xsd.elementName e)
322+ fieldTypeName <- case Xsd.elementType e of
324+ tn <- tnPrefixed <$> resolveTypeName name
325+ exts <- extensions name
328+ _ -> return ("Any" <> tn)
330+ genType (Xsd.elementAnnotations e) (Xsd.elementName e) t
331+ tnPrefixed <$> resolveTypeName (Xsd.elementName e)
333+ qualifier <- makeQualifier (Xsd.elementOccurs e)
448− Options{..} <- getOptions
449− xml <- BL.readFile oInput
451− xsd = case parseXSD def xml of
336+ fieldType = if Xsd.elementNillable e
452− Right xsd' -> xsd'
337+ then "Nillable " <> fieldTypeName
453− Left e -> error $ show e
338+ else fieldTypeName
454− T.hPutStrLn stdout $ generateIsoXml oHeader oModule oMode xsd
339+
340+ return $ Just $ mconcat
350+makeQualifier :: (Int, Xsd.MaxOccurs) -> Gen Text
351+makeQualifier (minOcc, maxOcc) = case maxOcc of
352+ Xsd.MaxOccursUnbound -> toQualifier minOcc maxBound
353+ Xsd.MaxOccurs v -> toQualifier minOcc v
355+toQualifier :: Int -> Int -> Gen Text
356+toQualifier 1 1 = return "!"
357+toQualifier 0 1 = return "?"
360+ = genError "maxOccurs < minOccurs"
364+toQualifier _ _ = return "*"
366+genUnsupported :: TypeName -> Gen ()
367+genUnsupported tn = do
368+ let typeName = tnPrefixed tn
369+ warn $ "generating stub for unsupported construct in " <> typeName
371+ writeCode (makeComments tn [])
373+ [ "data " <> typeName <> " = " <> typeName
374+ , " deriving (Eq, Show)"
376+ , "instance NFData " <> typeName <> " where"
381+ unless (mode == Parser) $ writeCode
382+ [ "instance ToXML " <> typeName <> " where"
383+ , " toXML _ = comment \"" <> typeName <> "\""
387+ unless (mode == Generator) $ writeCode
388+ [ "instance FromDom " <> typeName <> " where"
389+ , " fromDom = undefined"
393+genHeader :: Options -> Gen ()
395+ case oHeader opts of
396+ Nothing -> return ()
398+ writeCode . fmap ("-- " <>) . Text.lines $ h
402+ [ "{-# OPTIONS_GHC -Wno-unused-imports #-}"
403+ , "module " <> oModule opts <> " where"
405+ , "import Control.DeepSeq"
406+ , "import Data.Scientific"
407+ , "import Data.THGen.XML"
411+ [ "import Prelude hiding ((*), (+))"
416+ imports = case oMode opts of
417+ Generator -> [ xmlWriter, parentAttr ]
418+ Parser -> [ domParser ]
419+ ParserAndGenerator -> [ domParser, xmlWriter, parentAttr ]
420+ xmlWriter = "import Text.XML.Writer"
421+ parentAttr = "import Text.XML.ParentAttributes"
422+ domParser = "import Text.XML.DOM.Parser"
424+-- | We populate the initial state with all built-in types to make sure
425+-- they will be correctly resolved
426+initialGenState :: Options -> Map Xsd.QName Xsd.Type -> GenState
427+initialGenState opts types = GenState
429+ , gsExtensions = collectExtensions types
430+ , gsKnownTypes = builtInTypes
431+ , gsBoundTypeName = Set.empty
432+ , gsBoundFields = Set.empty
433+ , gsMode = oMode opts
439+ :: Map Xsd.QName Xsd.Type
440+ -> Map Xsd.QName [(Xsd.QName, Xsd.Type)]
441+collectExtensions = List.foldl' step Map.empty . Map.toList
443+ step exts (n, t) = case baseOf t of
445+ Just base -> Map.insertWith (++) base [(n, t)] exts
446+ baseOf (Xsd.TypeSimple _) = Nothing
447+ baseOf (Xsd.TypeComplex t) = case Xsd.complexContent t of
448+ Xsd.ContentComplex (Xsd.ComplexContentExtension e) ->
449+ Just (Xsd.complexExtensionBase e)