module Api ( API, NameSpace(..), Enum(..), EnumVariety(..), Member(..), Object(..), Class(..), Boxed(..), Field(..), Constructor(..), Parameter(..), Method(..), Property(..), Signal(..), Misc(..), extractAPI ) where import Prelude hiding (Enum, elem) import Data.Maybe (catMaybes, isJust) import Data.Char (isSpace) import Data.List (find) import qualified Text.XML.HaXml as Xml ------------------------------------------------------------------------------- -- Types representing the content of the API XML file ------------------------------------------------------------------------------- type API = [NameSpace] data NameSpace = NameSpace { namespace_name :: String, namespace_library :: String, namespace_objects :: [Object], namespace_classes :: [Class], namespace_boxed :: [Boxed], namespace_enums :: [Enum], namespace_misc :: [Misc] } deriving Show data Enum = Enum { enum_name :: String, enum_cname :: String, enum_variety :: EnumVariety, enum_members :: [Member] } deriving Show data EnumVariety = EnumVariety | FlagsVariety deriving Show data Member = Member { member_name :: String, member_cname :: String, member_value :: String } deriving Show data Object = Object { object_name :: String, object_cname :: String, object_parent :: String, object_constructors :: [Constructor], object_methods :: [Method], object_properties :: [Property], object_childprops :: [Property], object_signals :: [Signal], object_implements :: [String], object_deprecated :: Bool, object_isinterface ::Bool } deriving Show data Class = Class { class_name :: String, class_cname :: String, class_methods :: [Method] } deriving Show data Boxed = Boxed { boxed_name :: String, boxed_cname :: String, boxed_constructors :: [Constructor], boxed_methods :: [Method], boxed_fields :: [Field], boxed_opaque :: Bool } deriving Show data Field = Field { field_name :: String, field_cname :: String, field_type :: String, field_public :: Bool, --public or private field_bits :: Int } deriving Show data Constructor = Constructor { constructor_cname :: String, constructor_parameters :: [Parameter] } deriving Show data Parameter = Parameter { parameter_type :: String, parameter_name :: String, parameter_out :: Bool, parameter_isArray :: Bool } | VarArgs deriving Show data Method = Method { method_name :: String, method_cname :: String, method_return_type :: String, method_return_owned :: Bool, method_parameters :: [Parameter], method_shared :: Bool, method_deprecated :: Bool } deriving Show data Property = Property { property_name :: String, property_cname :: String, property_type :: String, property_readable :: Bool, property_writeable :: Bool, property_construct :: Bool, property_constructonly :: Bool } deriving Show data Signal = Signal { signal_name :: String, signal_cname :: String, signal_when :: String, signal_action :: Bool, signal_return_type :: String, signal_parameters :: [Parameter] } deriving Show data Misc = Struct { misc_name :: String, misc_cname :: String } | Alias { misc_name :: String, misc_cname :: String } | Callback { misc_name :: String, misc_cname :: String } deriving Show ------------------------------------------------------------------------------- -- extract functions to convert the api xml file to the internal representation ------------------------------------------------------------------------------- extractAPI :: Xml.Document -> API extractAPI (Xml.Document _ _ (Xml.Elem "apidoc" [] namespaces) _) = catMaybes (map extractNameSpace (concatMap (Xml.foldXml white) namespaces)) where -- remove empty CString constructors from the whole document white :: Xml.CFilter white (Xml.CString False str) | all isSpace str = [] white elem = [elem] extractAPI (Xml.Document _ _ (Xml.Elem notApi [] namespaces) _) = error $ "extractAPI: expected `apidoc` top-element, but found "++notApi extractAPI _other = error $ "extractAPI: other" extractNameSpace :: Xml.Content -> Maybe NameSpace extractNameSpace (Xml.CElem (Xml.Elem "namespace" [("name", Xml.AttValue name), ("library", Xml.AttValue lib)] content)) = Just $ NameSpace { namespace_name = Xml.verbatim name, namespace_library = Xml.verbatim lib, namespace_objects = catMaybes (map extractObject content), namespace_classes = catMaybes (map extractClass content), namespace_boxed = catMaybes (map extractBoxed content), namespace_enums = catMaybes (map extractEnum content), namespace_misc = catMaybes (map extractMisc content) } extractNameSpace _ = Nothing extractEnum :: Xml.Content -> Maybe Enum extractEnum (Xml.CElem (Xml.Elem "enum" [("name", Xml.AttValue name), ("cname", Xml.AttValue cname), ("gtype", Xml.AttValue _gtype), ("type", Xml.AttValue variety)] members)) = Just $ Enum { enum_name = Xml.verbatim name, enum_cname = Xml.verbatim cname, enum_variety = case Xml.verbatim variety of "enum" -> EnumVariety "flags" -> FlagsVariety, enum_members = map extractEnumMember members } extractEnum (Xml.CElem (Xml.Elem "enum" [("name", Xml.AttValue name), ("cname", Xml.AttValue cname), ("type", Xml.AttValue variety)] members)) = Just $ Enum { enum_name = Xml.verbatim name, enum_cname = Xml.verbatim cname, enum_variety = case Xml.verbatim variety of "enum" -> EnumVariety "flags" -> FlagsVariety, enum_members = map extractEnumMember members } extractEnum (Xml.CElem (Xml.Elem "enum" [("name", Xml.AttValue name), ("cname", Xml.AttValue cname), ("deprecated", Xml.AttValue [Left "1"]), ("gtype", Xml.AttValue _gtype), ("type", Xml.AttValue variety)] members)) = Just $ Enum { enum_name = Xml.verbatim name, enum_cname = Xml.verbatim cname, enum_variety = case Xml.verbatim variety of "enum" -> EnumVariety "flags" -> FlagsVariety, enum_members = map extractEnumMember members } extractEnum (Xml.CElem (Xml.Elem "enum" [("name", Xml.AttValue name), ("cname", Xml.AttValue cname), ("deprecated", Xml.AttValue [Left "1"]), ("type", Xml.AttValue variety)] members)) = Just $ Enum { enum_name = Xml.verbatim name, enum_cname = Xml.verbatim cname, enum_variety = case Xml.verbatim variety of "enum" -> EnumVariety "flags" -> FlagsVariety, enum_members = map extractEnumMember members } extractEnum other@(Xml.CElem (Xml.Elem "enum" _ _)) = error $ "extractEnum: " ++ Xml.verbatim other extractEnum _ = Nothing extractEnumMember :: Xml.Content -> Member extractEnumMember (Xml.CElem (Xml.Elem "member" (("cname", Xml.AttValue cname): ("name", Xml.AttValue name):value) [])) = Member { member_name = Xml.verbatim name, member_cname = Xml.verbatim cname, member_value = case value of [] -> "" [("value", Xml.AttValue v)] -> Xml.verbatim v } extractEnumMember other = error $ "extractEnumMember: " ++ Xml.verbatim other extractObject :: Xml.Content -> Maybe Object extractObject (Xml.CElem (Xml.Elem "object" (("name", Xml.AttValue name): ("cname", Xml.AttValue cname): remainder) content)) = let (parent, deprecated) = case remainder of [] -> ([Left "Unknown"], False) [("parent", Xml.AttValue parent')] -> (parent', False) [("deprecated", Xml.AttValue _), ("parent", Xml.AttValue parent')] -> (parent', True) in Just $ Object { object_name = Xml.verbatim name, object_cname = Xml.verbatim cname, object_parent = Xml.verbatim parent, object_constructors = catMaybes (map extractConstructor content), object_methods = catMaybes (map extractMethod content), object_properties = catMaybes (map extractProperty content), object_childprops = catMaybes (map extractChildProperty content), object_signals = catMaybes (map extractSignal content), object_implements = concat (catMaybes (map extractImplements content)), object_deprecated = deprecated, object_isinterface = False } extractObject (Xml.CElem (Xml.Elem "interface" [("name", Xml.AttValue name), ("cname", Xml.AttValue cname)] content)) = Just $ Object { object_name = Xml.verbatim name, object_cname = Xml.verbatim cname, object_parent = "GObject", object_constructors = catMaybes (map extractConstructor content), object_methods = catMaybes (map extractMethod content), object_properties = catMaybes (map extractProperty content), object_childprops = [], object_signals = catMaybes (map extractSignal content), object_implements = concat (catMaybes (map extractImplements content)), object_deprecated = False, object_isinterface = True } extractObject (Xml.CElem (Xml.Elem "object" [("name", Xml.AttValue name)] [])) | null (Xml.verbatim name) = Nothing extractObject other@(Xml.CElem (Xml.Elem "object" _ _)) = error $ "extractObject: " ++ Xml.verbatim other extractObject other@(Xml.CElem (Xml.Elem "interface" _ _)) = error $ "extractObject: " ++ Xml.verbatim other extractObject _ = Nothing extractClass :: Xml.Content -> Maybe Class extractClass (Xml.CElem (Xml.Elem "class" [("name", Xml.AttValue name), ("cname", Xml.AttValue cname)] content)) = Just $ Class { class_name = Xml.verbatim name, class_cname = Xml.verbatim cname, class_methods = catMaybes (map extractMethod content) } extractClass _ = Nothing extractBoxed :: Xml.Content -> Maybe Boxed extractBoxed (Xml.CElem (Xml.Elem "boxed" (("name", Xml.AttValue name): ("cname", Xml.AttValue cname):remainder) content)) = Just $ Boxed { boxed_name = Xml.verbatim name, boxed_cname = Xml.verbatim cname, boxed_methods = catMaybes (map extractMethod content), boxed_constructors = catMaybes (map extractConstructor content), boxed_fields = catMaybes (map extractField content), boxed_opaque = case remainder of [] -> False [("opaque", _)] -> True } extractBoxed _ = Nothing extractField :: Xml.Content -> Maybe Field extractField (Xml.CElem (Xml.Elem "field" [("name", Xml.AttValue name), ("cname", Xml.AttValue cname), ("type", Xml.AttValue type_)] _content)) = Just $ Field { field_name = Xml.verbatim name, field_cname = Xml.verbatim cname, field_type = Xml.verbatim type_, field_public = False, field_bits = -1 } extractField (Xml.CElem (Xml.Elem "field" [("name", Xml.AttValue name), ("cname", Xml.AttValue cname), ("type", Xml.AttValue type_), ("access", Xml.AttValue [Left "public"])] _content)) = Just $ Field { field_name = Xml.verbatim name, field_cname = Xml.verbatim cname, field_type = Xml.verbatim type_, field_public = True, field_bits = -1 } extractField (Xml.CElem (Xml.Elem "field" [("name", Xml.AttValue name), ("cname", Xml.AttValue cname), ("bits", Xml.AttValue bits), ("type", Xml.AttValue type_)] _content)) = Just $ Field { field_name = Xml.verbatim name, field_cname = Xml.verbatim cname, field_type = Xml.verbatim type_, field_public = False, field_bits = read (Xml.verbatim bits) } extractField _ = Nothing extractMethod :: Xml.Content -> Maybe Method extractMethod (Xml.CElem (Xml.Elem "method" (("name", Xml.AttValue name): ("cname", Xml.AttValue cname): remainder) (Xml.CElem (Xml.Elem "return-type" (("type", Xml.AttValue return_type):remainder') []) :content))) = let (shared, deprecated) = case remainder of [] -> (False, False) [("shared", _)] -> (True, False) [("deprecated", _)] -> (False, True) [("deprecated", _), ("shared", _)] -> (True, True) owned = case remainder' of [] -> False [("owned", Xml.AttValue [Left "true"])] -> True in Just $ Method { method_name = Xml.verbatim name, method_cname = Xml.verbatim cname, method_return_type = Xml.verbatim return_type, method_return_owned = owned, method_parameters = case content of [] -> [] [Xml.CElem (Xml.Elem "parameters" [] parameters)] -> map extractParameter parameters, method_shared = shared, method_deprecated = deprecated } extractMethod other@(Xml.CElem (Xml.Elem "method" _ _)) = error $ "extractMethod: " ++ Xml.verbatim other extractMethod _ = Nothing extractParameter :: Xml.Content -> Parameter extractParameter (Xml.CElem (Xml.Elem "parameter" [("ellipsis", _)] [])) = VarArgs extractParameter (Xml.CElem (Xml.Elem "parameter" [("ellipsis", _) ,("printf_format_args", _)] [])) = VarArgs extractParameter (Xml.CElem (Xml.Elem "parameter" [("type", Xml.AttValue type_), ("name", Xml.AttValue name)] [])) = Parameter { parameter_type = Xml.verbatim type_, parameter_name = Xml.verbatim name, parameter_out = False, parameter_isArray = False } extractParameter (Xml.CElem (Xml.Elem "parameter" [("type", Xml.AttValue type_), ("pass_as", Xml.AttValue [Left "out"]), ("name", Xml.AttValue name)] [])) = Parameter { parameter_type = Xml.verbatim type_, parameter_name = Xml.verbatim name, parameter_out = True, parameter_isArray = False } extractParameter (Xml.CElem (Xml.Elem "parameter" [("name", Xml.AttValue name), ("type", Xml.AttValue type_)] [])) = Parameter { parameter_type = Xml.verbatim type_, parameter_name = Xml.verbatim name, parameter_out = False, parameter_isArray = False } extractParameter (Xml.CElem (Xml.Elem "parameter" [("type", Xml.AttValue type_), ("name", Xml.AttValue name), ("printf_format" ,_)] [])) = Parameter { parameter_type = Xml.verbatim type_, parameter_name = Xml.verbatim name, parameter_out = False, parameter_isArray = False } extractParameter (Xml.CElem (Xml.Elem "parameter" [("type", Xml.AttValue type_), ("array", _), ("name", Xml.AttValue name)] [])) = Parameter { parameter_type = Xml.verbatim type_, parameter_name = Xml.verbatim name, parameter_out = False, parameter_isArray = True } extractParameter (Xml.CElem (Xml.Elem "callback" [("cname", Xml.AttValue cname)] _)) = Parameter { parameter_type = "callback", parameter_name = Xml.verbatim cname, parameter_out = False, parameter_isArray = False } extractParameter other = error $ "extractParameter: " ++ Xml.verbatim other extractConstructor :: Xml.Content -> Maybe Constructor extractConstructor (Xml.CElem (Xml.Elem "constructor" [("cname", Xml.AttValue cname)] content)) = Just $ Constructor { constructor_cname = Xml.verbatim cname, constructor_parameters = case content of [] -> [] [Xml.CElem (Xml.Elem "parameters" [] parameters)] -> map extractParameter parameters } extractConstructor _ = Nothing extractProperty :: Xml.Content -> Maybe Property extractProperty (Xml.CElem (Xml.Elem "property" (("name", Xml.AttValue name): ("cname", Xml.AttValue cname): ("type", Xml.AttValue type_):others) [])) = Just $ Property { property_name = Xml.verbatim name, property_cname = Xml.verbatim cname, property_type = Xml.verbatim type_, property_readable = (not.null) [ () | ("readable", _) <- others], property_writeable = (not.null) [ () | ("writeable", _) <- others], property_construct = (not.null) [ () | ("construct", _) <- others], property_constructonly = (not.null) [ () | ("construct-only", _) <- others] } extractProperty _ = Nothing extractChildProperty :: Xml.Content -> Maybe Property extractChildProperty (Xml.CElem (Xml.Elem "childprop" (("name", Xml.AttValue name): ("cname", Xml.AttValue cname): ("type", Xml.AttValue type_):others) [])) = Just $ Property { property_name = Xml.verbatim name, property_cname = Xml.verbatim cname, property_type = Xml.verbatim type_, property_readable = (not.null) [ () | ("readable", _) <- others], property_writeable = (not.null) [ () | ("writeable", _) <- others], property_construct = (not.null) [ () | ("construct", _) <- others], property_constructonly = (not.null) [ () | ("construct-only", _) <- others] } extractChildProperty _ = Nothing extractSignal :: Xml.Content -> Maybe Signal extractSignal (Xml.CElem (Xml.Elem "signal" (("name", Xml.AttValue name): ("cname", Xml.AttValue cname):remainder) (Xml.CElem (Xml.Elem "return-type" [("type", Xml.AttValue return_type)] []) :content))) = Just $ Signal { signal_name = Xml.verbatim name, signal_cname = Xml.verbatim cname, signal_when = head [ Xml.verbatim when | ("when", Xml.AttValue when) <- remainder ] ++ "", signal_action = isJust $ find ((== "action") . fst) remainder, signal_return_type = Xml.verbatim return_type, signal_parameters = case content of [] -> [] [Xml.CElem (Xml.Elem "parameters" [] parameters)] -> map extractParameter parameters } extractSignal _ = Nothing extractImplements :: Xml.Content -> Maybe [String] extractImplements (Xml.CElem (Xml.Elem "implements" [] interfaces)) = Just $ map extractInterface interfaces extractImplements _ = Nothing extractInterface :: Xml.Content -> String extractInterface (Xml.CElem (Xml.Elem "interface" [("cname", Xml.AttValue cname)] [] )) = Xml.verbatim cname extractInterface other = error $ "extractInterface: " ++ Xml.verbatim other extractMisc :: Xml.Content -> Maybe Misc extractMisc (Xml.CElem (Xml.Elem elem (("name", Xml.AttValue name): ("cname", Xml.AttValue cname):_) _)) | elem == "struct" = Just Struct { misc_name = Xml.verbatim name, misc_cname = Xml.verbatim cname } | elem == "alias" = Just Alias { misc_name = Xml.verbatim name, misc_cname = Xml.verbatim cname } | elem == "callback" = Just Callback { misc_name = Xml.verbatim name, misc_cname = Xml.verbatim cname } extractMisc (Xml.CElem (Xml.Elem "object" _ _)) = Nothing extractMisc (Xml.CElem (Xml.Elem "class" _ _)) = Nothing extractMisc (Xml.CElem (Xml.Elem "boxed" _ _)) = Nothing extractMisc (Xml.CElem (Xml.Elem "interface" _ _)) = Nothing extractMisc (Xml.CElem (Xml.Elem "enum" _ _)) = Nothing extractMisc other = error $ "extractMisc: " ++ Xml.verbatim other