% Copyright (C) 2009 John Millikin % % This program is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 3 of the License, or % any later version. % % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You should have received a copy of the GNU General Public License % along with this program. If not, see . \ignore{ \begin{code} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE DeriveDataTypeable #-} module DBus.Types.Containers ( Variant , Variable , toVariant , fromVariant , defaultSignature , variantSignature , Array , toArray , fromArray , arrayItems , arrayFromItems , arraySignature , Dictionary , toDictionary , fromDictionary , dictionaryItems , dictionaryFromItems , dictionarySignature , Structure (..) , structureSignature ) where import Control.Arrow ((***)) import Data.Typeable (Typeable, cast) import Data.Maybe (fromJust) import Data.Word (Word8, Word16, Word32, Word64) import Data.Int (Int16, Int32, Int64) import qualified DBus.Types.Signature as S import qualified DBus.Types.ObjectPath as O import qualified DBus.Types.Atom as A \end{code} } \subsection{Containers} \subsubsection{Variants} Any value which can be converted to a {\tt Variant} can be stored in D-Bus containers or marshaled. Additionally, external types may implement the {\tt Variable} interface to provide custom conversion to/from built-in D-Bus types. The {\tt defaultSignature} function will be passed {\tt unknown} to determine the signature an empty array or dictionary. \begin{code} class Variable a where defaultSignature :: a -> S.Signature toVariant :: a -> Variant fromVariant :: Variant -> Maybe a \end{code} \begin{code} data Variant = forall a. (Variable a, Typeable a, Show a) => Variant S.Signature a deriving (Typeable) instance Show Variant where showsPrec d (Variant sig x) = showParen (d > 10) $ s "Variant " . s sigStr . s " " . showsPrec 11 x where sigStr = show . S.strSignature $ sig s = showString \end{code} Ghetto Eq instance -- the types contained in variants are known to have fixed {\tt show} representations, so this works well enough. It might not work right for {\tt Double}, though. \begin{code} instance Eq Variant where x == y = show x == show y \end{code} Variants are themselves variables. \begin{code} instance Variable Variant where defaultSignature _ = sig' "v" toVariant = variant' "v" fromVariant = cast' \end{code} \begin{code} variantSignature :: Variant -> S.Signature variantSignature (Variant s _) = s \end{code} \begin{code} instance Variable Bool where defaultSignature _ = sig' "b" toVariant = variant' "b" fromVariant = cast' \end{code} \begin{code} instance Variable Word8 where defaultSignature _ = sig' "y" toVariant = variant' "y" fromVariant = cast' \end{code} \begin{code} instance Variable Word16 where defaultSignature _ = sig' "q" toVariant = variant' "q" fromVariant = cast' \end{code} \begin{code} instance Variable Word32 where defaultSignature _ = sig' "u" toVariant = variant' "u" fromVariant = cast' \end{code} \begin{code} instance Variable Word64 where defaultSignature _ = sig' "t" toVariant = variant' "t" fromVariant = cast' \end{code} \begin{code} instance Variable Int16 where defaultSignature _ = sig' "n" toVariant = variant' "n" fromVariant = cast' \end{code} \begin{code} instance Variable Int32 where defaultSignature _ = sig' "i" toVariant = variant' "i" fromVariant = cast' \end{code} \begin{code} instance Variable Int64 where defaultSignature _ = sig' "x" toVariant = variant' "x" fromVariant = cast' \end{code} \begin{code} instance Variable Double where defaultSignature _ = sig' "d" toVariant = variant' "d" fromVariant = cast' \end{code} \begin{code} instance Variable String where defaultSignature _ = sig' "s" toVariant = variant' "s" fromVariant = cast' \end{code} \begin{code} instance Variable O.ObjectPath where defaultSignature _ = sig' "o" toVariant = variant' "o" fromVariant = cast' \end{code} \begin{code} instance Variable S.Signature where defaultSignature _ = sig' "g" toVariant = variant' "g" fromVariant = cast' \end{code} \subsubsection{Arrays} Arrays are homogeneous sequences of any valid type. They may be converted to and from standard Haskell lists, where the list contains elements with a valid type. \begin{code} data Array = Array S.Signature [Variant] deriving (Show, Eq, Typeable) instance Variable Array where defaultSignature _ = sig' "ay" toVariant x = Variant (arraySignature x) x fromVariant = cast' toArray :: Variable a => [a] -> Maybe Array toArray vs@([]) = Just $ Array sig [] where itemSig = defaultSignature . head $ undefined:vs sig = sig' $ 'a' : S.strSignature itemSig toArray vs = arrayFromItems sig variants where variants = map toVariant vs sig = variantSignature . head $ variants fromArray :: Variable a => Array -> Maybe [a] fromArray (Array _ vs) = mapM fromVariant vs arrayItems :: Array -> [Variant] arrayItems (Array _ vs) = vs arrayFromItems :: S.Signature -> [Variant] -> Maybe Array arrayFromItems itemSig vs = maybeArray where maybeArray = if hasSignature itemSig vs then Just (Array sig vs) else Nothing sig = sig' $ 'a' : S.strSignature itemSig arraySignature :: Array -> S.Signature arraySignature (Array s _) = s \end{code} \subsubsection{Dictionaries} Dictionaries are a key $\rightarrow$ value mapping, where the keys must be of an {\tt Atomic} type, and the values may be of any valid DBus type. \begin{code} data Dictionary = Dictionary S.Signature [(A.Atom, Variant)] deriving (Show, Eq, Typeable) instance Variable Dictionary where defaultSignature _ = sig' "a{yy}" toVariant x = Variant (dictionarySignature x) x fromVariant = cast' toDictionary :: (A.Atomic a, Variable b) => [(a, b)] -> Maybe Dictionary toDictionary vs@([]) = Just $ Dictionary sig [] where fake = head $ (undefined, undefined) : vs kSig = S.strSignature . defaultSignature . fst $ fake vSig = S.strSignature . defaultSignature . snd $ fake sig = sig' $ "a{" ++ kSig ++ vSig ++ "}" toDictionary pairs = dictionaryFromItems kSig vSig pairs' where pairs' = map (A.toAtom *** toVariant) pairs kSig = A.atomSignature . fst . head $ pairs' vSig = variantSignature . snd . head $ pairs' fromDictionary :: (A.Atomic a, Variable b) => Dictionary -> Maybe [(a, b)] fromDictionary (Dictionary _ vs) = mapM fromVariant' vs where fromVariant' (k, v) = do k' <- A.fromAtom k v' <- fromVariant v return (k', v') dictionaryItems :: Dictionary -> [(A.Atom, Variant)] dictionaryItems (Dictionary _ vs) = vs dictionaryFromItems :: S.Signature -> S.Signature -> [(A.Atom, Variant)] -> Maybe Dictionary dictionaryFromItems kSig vSig pairs = maybeDict where maybeDict = if hasSignature kSig ks && hasSignature vSig vs then Just (Dictionary sig pairs) else Nothing ks = map (A.atomToVariant . fst) pairs vs = map snd pairs kSig' = S.strSignature kSig vSig' = S.strSignature vSig sig = sig' $ "a{" ++ kSig' ++ vSig' ++ "}" dictionarySignature :: Dictionary -> S.Signature dictionarySignature (Dictionary s _) = s \end{code} \subsubsection{Structures} Structures contain a heterogenous list of DBus values. Any value may be contained within a structure. \begin{code} data Structure = Structure [Variant] deriving (Show, Eq, Typeable) instance Variable Structure where defaultSignature _ = sig' "()" toVariant x = Variant (structureSignature x) x fromVariant = cast' structureSignature :: Structure -> S.Signature structureSignature (Structure vs) = sig where sigs = [s | (Variant s _) <- vs] sig = sig' $ "(" ++ concatMap S.strSignature sigs ++ ")" \end{code} \subsubsection*{Helper functions} \begin{code} sig' :: String -> S.Signature sig' = fromJust . S.mkSignature variant' :: (Variable a, Typeable a, Show a) => String -> a -> Variant variant' = Variant . sig' cast' :: Typeable a => Variant -> Maybe a cast' (Variant _ x) = cast x hasSignature :: S.Signature -> [Variant] -> Bool hasSignature _ [] = True hasSignature sig vs = all (== sig) . map variantSignature $ vs \end{code}