{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} module Nesting where import Control.Monad.State.Strict import Data.Conduit import Data.Conduit.Lift import qualified Data.List as List import qualified Data.Text as S import Data.XML.Types type Lang = S.Text data StrictList a = a :! !(StrictList a) | StrictNil data XMLState = XMLState { nestingLevel :: Int, langStack :: StrictList (Int,Lang) } type NestingXML o m a = ConduitM Event o (StateT XMLState m) a doNestingXML :: Monad m => NestingXML o m r -> ConduitM Event o m r doNestingXML m = evalStateC (XMLState 0 StrictNil) (trackNesting .| m) startNestingXML :: Monad m => NestingXML o m r -> ConduitM Event o m (r, XMLState) startNestingXML m = runStateC (XMLState 0 StrictNil) (trackNesting .| m) finishNestingXML :: Monad m => XMLState -> NestingXML o m r -> ConduitM Event o m r finishNestingXML = evalStateC nesting :: Monad m => NestingXML o m Int nesting = lift $ (return . nestingLevel) =<< get xmlLang :: Monad m => NestingXML o m (Maybe Lang) xmlLang = fmap (fmap snd . top . langStack) (lift get) where top ( a :! _as ) = Just a top _ = Nothing trackNesting :: Monad m => ConduitM Event Event (StateT XMLState m) () trackNesting = awaitForever doit where doit xml = do XMLState lvl langs <- lift get lift . put $ case xml of EventBeginElement _ attrs -> case lookupLang attrs of Nothing -> XMLState (lvl+1) langs Just lang -> XMLState (lvl+1) ( (lvl+1,lang) :! langs) EventEndElement _ -> case langs of (llvl,_) :! ls | llvl==lvl -> XMLState (lvl-1) ls _ | otherwise -> XMLState (lvl-1) langs _ -> XMLState lvl langs yield xml lookupLang :: [(Name, [Content])] -> Maybe S.Text lookupLang attrs = case List.find ( (=="xml:lang") . fst) attrs of Just (_,[ContentText x]) -> Just x Just (_,[ContentEntity x]) -> Just x _ -> Nothing awaitCloser :: Monad m => Int -> NestingXML o m () awaitCloser lvl = fix $ \loop -> do lvl' <- nesting when (lvl' >= lvl) $ do xml <- await maybe (return ()) (const loop) xml withXML :: Monad m => (i -> ConduitM i o m ()) -> ConduitM i o m () withXML f = await >>= maybe (return ()) f nextElement :: Monad m => NestingXML o m (Maybe Event) nextElement = do lvl <- nesting fix $ \loop -> do xml <- await case xml of Nothing -> return Nothing Just (EventBeginElement _ _) -> return xml Just _ -> do lvl' <- nesting if (lvl'>=lvl) then loop else return Nothing