module Text.XML.Expat.Cursor
(
Cursor, CursorG(..), Path, PathG
, Tag(..), getTag, fromTag
, fromTree
, fromForest
, toForest
, toTree
, parent
, root
, getChild
, getChildM
, firstChild
, firstChildM
, lastChild
, lastChildM
, left
, leftM
, right
, rightM
, nextDF
, nextDFM
, findChild
, findLeft
, findRight
, findRec
, findRecM
, isRoot
, isFirst
, isFirstM
, isLast
, isLastM
, isLeaf
, isChild
, hasChildren
, getNodeIndex
, setContent
, modifyContent
, modifyContentList
, modifyContentListM
, modifyContentM
, insertLeft
, insertRight
, insertManyLeft
, insertManyRight
, insertFirstChild
, insertLastChild
, insertManyFirstChild
, insertManyLastChild
, insertGoLeft
, insertGoRight
, removeLeft
, removeLeftM
, removeRight
, removeRightM
, removeGoLeft
, removeGoLeftM
, removeGoRight
, removeGoRightM
, removeGoUp
) where
import Text.XML.Expat.Tree
import Control.Monad (mzero, mplus)
import Data.Maybe(isNothing)
import Data.Monoid
import Data.Functor.Identity
import Data.List.Class (List(..), ListItem(..), cons, foldlL, lengthL)
data Tag tag text = Tag { tagName :: tag
, tagAttribs :: Attributes tag text
} deriving (Show)
fromTag :: MkElementClass n c => Tag tag text -> c (n c tag text) -> n c tag text
fromTag t cs = mkElement (tagName t) (tagAttribs t) cs
type PathG n c tag text = [(c (n c tag text),Tag tag text,c (n c tag text))]
type Path tag text = PathG NodeG [] tag text
data CursorG n c tag text = Cur
{ current :: n c tag text
, lefts :: c (n c tag text)
, rights :: c (n c tag text)
, parents :: PathG n c tag text
}
instance (Show (n c tag text), Show (c (n c tag text)), Show tag, Show text)
=> Show (CursorG n c tag text) where
show (Cur c l r p) = "Cur { current="++show c++
", lefts="++show l++
", rights="++show r++
", parents="++show p++" }"
type Cursor tag text = CursorG NodeG [] tag text
parent :: MkElementClass n c => CursorG n c tag text -> Maybe (CursorG n c tag text)
parent loc =
case parents loc of
(pls,v,prs) : ps -> Just
Cur { current = (fromTag v
(combChildren (lefts loc) (current loc) (rights loc)))
, lefts = pls, rights = prs, parents = ps
}
[] -> Nothing
root :: MkElementClass n c => CursorG n c tag text -> CursorG n c tag text
root loc = maybe loc root (parent loc)
left :: CursorG n [] tag text -> Maybe (CursorG n [] tag text)
left loc = runIdentity $ leftM loc
leftM :: List c => CursorG n c tag text -> ItemM c (Maybe (CursorG n c tag text))
leftM loc = do
let l = lefts loc
li <- runList l
case li of
Nil -> return Nothing
Cons t ts -> return $ Just loc { current = t, lefts = ts
, rights = cons (current loc) (rights loc) }
right :: CursorG n [] tag text -> Maybe (CursorG n [] tag text)
right loc = runIdentity $ rightM loc
rightM :: List c => CursorG n c tag text -> ItemM c (Maybe (CursorG n c tag text))
rightM loc = do
let r = rights loc
li <- runList r
case li of
Nil -> return Nothing
Cons t ts -> return $ Just loc { current = t, lefts = cons (current loc) (lefts loc)
, rights = ts }
firstChild :: (NodeClass n [], Monoid tag) => CursorG n [] tag text -> Maybe (CursorG n [] tag text)
firstChild loc = runIdentity $ firstChildM loc
firstChildM :: (NodeClass n c, Monoid tag) => CursorG n c tag text -> ItemM c (Maybe (CursorG n c tag text))
firstChildM loc = do
case downParents loc of
Just (l, ps) -> do
li <- runList l
return $ case li of
Cons t ts -> Just $ Cur { current = t, lefts = mzero, rights = ts , parents = ps }
Nil -> Nothing
Nothing -> return $ Nothing
lastChild :: (NodeClass n [], Monoid tag) => CursorG n [] tag text -> Maybe (CursorG n [] tag text)
lastChild loc = runIdentity $ lastChildM loc
lastChildM :: (NodeClass n c, Monoid tag) => CursorG n c tag text -> ItemM c (Maybe (CursorG n c tag text))
lastChildM loc = do
case downParents loc of
Just (l, ps) -> do
li <- runList (reverseL l)
return $ case li of
Cons t ts -> Just $ Cur { current = t, lefts = ts, rights = mzero , parents = ps }
Nil -> Nothing
Nothing -> return $ Nothing
findLeft :: NodeClass n [] =>
(CursorG n [] tag text -> Bool)
-> CursorG n [] tag text
-> Maybe (CursorG n [] tag text)
findLeft p loc = runIdentity (findLeftM p loc)
findLeftM :: NodeClass n c =>
(CursorG n c tag text -> Bool)
-> CursorG n c tag text
-> ItemM c (Maybe (CursorG n c tag text))
findLeftM p loc = do
mLoc1 <- leftM loc
case mLoc1 of
Just loc1 -> if p loc1 then return (Just loc1) else findLeftM p loc1
Nothing -> return Nothing
findRight :: (CursorG n [] tag text -> Bool)
-> CursorG n [] tag text
-> Maybe (CursorG n [] tag text)
findRight p loc = runIdentity $ findRightM p loc
findRightM :: List c =>
(CursorG n c tag text -> Bool)
-> CursorG n c tag text
-> ItemM c (Maybe (CursorG n c tag text))
findRightM p loc = do
mLoc1 <- rightM loc
case mLoc1 of
Just loc1 -> if p loc1 then return $ Just loc1 else findRightM p loc1
Nothing -> return Nothing
findChild :: (NodeClass n [], Monoid tag) =>
(CursorG n [] tag text -> Bool)
-> CursorG n [] tag text
-> Maybe (CursorG n [] tag text)
findChild p loc = runIdentity $ findChildM p loc
findChildM :: (NodeClass n c, Monoid tag) =>
(CursorG n c tag text -> Bool)
-> CursorG n c tag text
-> ItemM c (Maybe (CursorG n c tag text))
findChildM p loc = do
mLoc1 <- firstChildM loc
case mLoc1 of
Just loc1 -> if p loc1 then return $ Just loc1 else findRightM p loc1
Nothing -> return Nothing
nextDF :: (MkElementClass n [], Monoid tag) => CursorG n [] tag text -> Maybe (CursorG n [] tag text)
nextDF c = runIdentity $ nextDFM c
nextDFM :: (MkElementClass n c, Monoid tag) => CursorG n c tag text -> ItemM c (Maybe (CursorG n c tag text))
nextDFM c = do
mFirst <- firstChildM c
case mFirst of
Just c' -> return $ Just c'
Nothing -> up c
where
up x = do
mRight <- rightM x
case mRight of
Just c' -> return $ Just c'
Nothing ->
case parent x of
Just p -> up p
Nothing -> return Nothing
findRec :: (MkElementClass n [], Monoid tag) =>
(CursorG n [] tag text -> Bool)
-> CursorG n [] tag text
-> Maybe (CursorG n [] tag text)
findRec p c = runIdentity $ findRecM (return . p) c
findRecM :: (MkElementClass n c, Monoid tag) =>
(CursorG n c tag text -> ItemM c Bool)
-> CursorG n c tag text
-> ItemM c (Maybe (CursorG n c tag text))
findRecM p c = do
found <- p c
if found
then return $ Just c
else do
mC' <- nextDFM c
case mC' of
Just c' -> findRecM p c'
Nothing -> return Nothing
getChild :: (NodeClass n [], Monoid tag) => Int -> CursorG n [] tag text -> Maybe (CursorG n [] tag text)
getChild n loc = runIdentity $ getChildM n loc
getChildM :: (NodeClass n c, Monoid tag) =>
Int
-> CursorG n c tag text
-> ItemM c (Maybe (CursorG n c tag text))
getChildM n loc = do
let mParents = downParents loc
case mParents of
Just (ts, ps) -> do
mSplit <- splitChildrenM ts n
case mSplit of
Just (ls,t,rs) -> return $ Just $
Cur { current = t, lefts = ls, rights = rs, parents = ps }
Nothing -> return Nothing
Nothing -> return Nothing
downParents :: (NodeClass n c, Monoid tag) => CursorG n c tag text -> Maybe (c (n c tag text), PathG n c tag text)
downParents loc =
case current loc of
e | isElement e ->
let n = getName e
a = getAttributes e
c = getChildren e
in Just ( c
, cons (lefts loc, Tag n a, rights loc) (parents loc)
)
_ -> Nothing
getTag :: Node tag text -> Tag tag text
getTag e = Tag { tagName = eName e
, tagAttribs = eAttributes e
}
fromTree :: List c => n c tag text -> CursorG n c tag text
fromTree t = Cur { current = t, lefts = mzero, rights = mzero, parents = [] }
fromForest :: NodeClass n [] => [n [] tag text] -> Maybe (CursorG n [] tag text)
fromForest l = runIdentity $ fromForestM l
fromForestM :: List c => c (n c tag text) -> ItemM c (Maybe (CursorG n c tag text))
fromForestM l = do
li <- runList l
return $ case li of
Cons t ts -> Just Cur { current = t, lefts = mzero, rights = ts
, parents = [] }
Nil -> Nothing
toTree :: MkElementClass n c => CursorG n c tag text -> n c tag text
toTree loc = current (root loc)
toForest :: MkElementClass n c => CursorG n c tag text -> c (n c tag text)
toForest loc = let r = root loc in combChildren (lefts r) (current r) (rights r)
isRoot :: CursorG n c tag text -> Bool
isRoot loc = null (parents loc)
isFirst :: CursorG n [] tag text -> Bool
isFirst loc = runIdentity $ isFirstM loc
isFirstM :: List c => CursorG n c tag text -> ItemM c Bool
isFirstM loc = do
li <- runList (lefts loc)
return $ case li of
Nil -> True
_ -> False
isLast :: CursorG n [] tag text -> Bool
isLast loc = runIdentity $ isLastM loc
isLastM :: List c => CursorG n c tag text -> ItemM c Bool
isLastM loc = do
li <- runList (rights loc)
return $ case li of
Nil -> True
_ -> False
isLeaf :: (NodeClass n c, Monoid tag) => CursorG n c tag text -> Bool
isLeaf loc = isNothing (downParents loc)
isChild :: CursorG n c tag text -> Bool
isChild loc = not (isRoot loc)
getNodeIndex :: CursorG n [] tag text -> Int
getNodeIndex loc = runIdentity $ getNodeIndexM loc
getNodeIndexM :: List c => CursorG n c tag text -> ItemM c Int
getNodeIndexM loc = lengthL (lefts loc)
hasChildren :: (NodeClass n c, Monoid tag) => CursorG n c tag text -> Bool
hasChildren loc = not (isLeaf loc)
setContent :: n c tag text -> CursorG n c tag text -> CursorG n c tag text
setContent t loc = loc { current = t }
modifyContent :: (n c tag text -> n c tag text) -> CursorG n c tag text -> CursorG n c tag text
modifyContent f loc = setContent (f (current loc)) loc
modifyContentList :: NodeClass n [] =>
(n [] tag text -> [n [] tag text]) -> CursorG n [] tag text -> Maybe (CursorG n [] tag text)
modifyContentList f loc = runIdentity $ modifyContentListM f loc
modifyContentListM :: NodeClass n c =>
(n c tag text -> c (n c tag text))
-> CursorG n c tag text
-> ItemM c (Maybe (CursorG n c tag text))
modifyContentListM f loc = removeGoRightM $ insertManyRight (f $ current loc) loc
modifyContentM :: Monad m => (n [] tag text -> m (n [] tag text)) -> CursorG n [] tag text -> m (CursorG n [] tag text)
modifyContentM f loc = do x <- f (current loc)
return (setContent x loc)
insertLeft :: List c => n c tag text -> CursorG n c tag text -> CursorG n c tag text
insertLeft t loc = loc { lefts = t `cons` lefts loc }
insertRight :: List c => n c tag text -> CursorG n c tag text -> CursorG n c tag text
insertRight t loc = loc { rights = t `cons` rights loc }
insertManyLeft :: List c => c (n c tag text) -> CursorG n c tag text -> CursorG n c tag text
insertManyLeft t loc = loc { lefts = reverseL t `mplus` lefts loc }
insertManyRight :: List c => c (n c tag text) -> CursorG n c tag text -> CursorG n c tag text
insertManyRight t loc = loc { rights = t `mplus` rights loc }
mapChildren :: NodeClass n c => (c (n c tag text) -> c (n c tag text))
-> CursorG n c tag text
-> Maybe (CursorG n c tag text)
mapChildren f loc = let e = current loc in
if isElement e then
Just $ loc { current = modifyChildren f e }
else
Nothing
insertFirstChild :: NodeClass n c => n c tag text -> CursorG n c tag text -> Maybe (CursorG n c tag text)
insertFirstChild t = mapChildren (t `cons`)
insertLastChild :: NodeClass n c => n c tag text -> CursorG n c tag text -> Maybe (CursorG n c tag text)
insertLastChild t = mapChildren (`mplus` return t)
insertManyFirstChild :: NodeClass n c => c (n c tag text) -> CursorG n c tag text -> Maybe (CursorG n c tag text)
insertManyFirstChild t = mapChildren (t `mplus`)
insertManyLastChild :: NodeClass n c => c (n c tag text) -> CursorG n c tag text -> Maybe (CursorG n c tag text)
insertManyLastChild t = mapChildren (`mplus` t)
removeLeft :: CursorG n [] tag text -> Maybe (n [] tag text, CursorG n [] tag text)
removeLeft loc = runIdentity $ removeLeftM loc
removeLeftM :: List c => CursorG n c tag text -> ItemM c (Maybe (n c tag text, CursorG n c tag text))
removeLeftM loc = do
li <- runList (lefts loc)
return $ case li of
Cons l ls -> Just $ (l,loc { lefts = ls })
Nil -> Nothing
removeRight :: CursorG n [] tag text -> Maybe (n [] tag text, CursorG n [] tag text)
removeRight loc = runIdentity $ removeRightM loc
removeRightM :: List c => CursorG n c tag text -> ItemM c (Maybe (n c tag text, CursorG n c tag text))
removeRightM loc = do
li <- runList (rights loc)
return $ case li of
Cons l ls -> Just $ (l,loc { rights = ls })
Nil -> Nothing
insertGoLeft :: List c => n c tag text -> CursorG n c tag text -> CursorG n c tag text
insertGoLeft t loc = loc { current = t, rights = current loc `cons` rights loc }
insertGoRight :: List c => n c tag text -> CursorG n c tag text -> CursorG n c tag text
insertGoRight t loc = loc { current = t, lefts = current loc `cons` lefts loc }
removeGoLeft :: CursorG n [] tag text -> Maybe (CursorG n [] tag text)
removeGoLeft loc = case lefts loc of
l : ls -> Just loc { current = l, lefts = ls }
[] -> Nothing
removeGoLeftM :: List c => CursorG n c tag text -> ItemM c (Maybe (CursorG n c tag text))
removeGoLeftM loc = do
li <- runList (lefts loc)
return $ case li of
Cons l ls -> Just loc { current = l, lefts = ls }
Nil -> Nothing
removeGoRight :: CursorG n [] tag text -> Maybe (CursorG n [] tag text)
removeGoRight loc = runIdentity $ removeGoRightM loc
removeGoRightM :: List c => CursorG n c tag text -> ItemM c (Maybe (CursorG n c tag text))
removeGoRightM loc = do
li <- runList (rights loc)
return $ case li of
Cons l ls -> Just loc { current = l, rights = ls }
Nil -> Nothing
removeGoUp :: MkElementClass n c => CursorG n c tag text -> Maybe (CursorG n c tag text)
removeGoUp loc =
case (parents loc) of
[] -> Nothing
(pls, v, prs):ps -> Just $
Cur { current = fromTag v (reverseL (lefts loc) `mplus` rights loc)
, lefts = pls, rights = prs, parents = ps
}
splitChildrenM :: List c => c a -> Int -> ItemM c (Maybe (c a,a,c a))
splitChildrenM _ n | n < 0 = return Nothing
splitChildrenM cs pos = loop mzero cs pos
where
loop acc l n = do
li <- runList l
case li of
Nil -> return Nothing
Cons x l' -> if n == 0
then return $ Just (acc, x, l')
else loop (cons x acc) l' $! n1
combChildren :: List c =>
c a
-> a
-> c a
-> c a
combChildren ls t rs = joinL $ foldlL (flip cons) (cons t rs) ls
reverseL :: List c => c a -> c a
reverseL = joinL . foldlL (flip cons) mzero