-- ------------------------------------------------------------ {- | Module : Control.Arrow.ArrowNavigatableTree Copyright : Copyright (C) 2010 Uwe Schmidt License : MIT Maintainer : Uwe Schmidt (uwe\@fh-wedel.de) Stability : experimental Portability: portable List arrows for navigatable trees Trees that implement the "Data.Tree.NavigatableTree.Class" interface, can be processed with these arrows. -} -- ------------------------------------------------------------ module Control.Arrow.ArrowNavigatableTree where import Control.Arrow import Control.Arrow.ArrowList import Control.Arrow.ArrowIf import Data.Maybe import Data.Tree.NavigatableTree.Class ( NavigatableTree , NavigatableTreeToTree , NavigatableTreeModify ) import qualified Data.Tree.NavigatableTree.Class as T import qualified Data.Tree.NavigatableTree.XPathAxis as T -- ------------------------------------------------------------ -- | The interface for navigatable tree arrows -- -- all functions have default implementations class (ArrowList a) => ArrowNavigatableTree a where -- move one step towards the root moveUp :: NavigatableTree t => a (t b) (t b) moveUp = arrL $ maybeToList . T.mvUp -- descend one step to the leftmost child moveDown :: NavigatableTree t => a (t b) (t b) moveDown = arrL $ maybeToList . T.mvDown -- move to the left neighbour moveLeft :: NavigatableTree t => a (t b) (t b) moveLeft = arrL $ maybeToList . T.mvLeft -- move to the right neighbour moveRight :: NavigatableTree t => a (t b) (t b) moveRight = arrL $ maybeToList . T.mvRight -- derived functions parentAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) parentAxis = arrL T.parentAxis -- | XPath axis: ancestor ancestorAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) ancestorAxis = arrL T.ancestorAxis -- | XPath axis: ancestor or self ancestorOrSelfAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) ancestorOrSelfAxis = arrL T.ancestorOrSelfAxis -- | XPath axis: child childAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) childAxis = arrL T.childAxis -- | XPath axis: descendant descendantAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) descendantAxis = arrL T.descendantAxis -- | XPath axis: descendant or self descendantOrSelfAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) descendantOrSelfAxis = arrL T.descendantOrSelfAxis -- | not an XPath axis but useful: descendant or following descendantOrFollowingAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) descendantOrFollowingAxis = descendantAxis <+> followingAxis -- | not an official XPath axis but useful: reverse descendant or self, used in preceding axis revDescendantOrSelfAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) revDescendantOrSelfAxis = arrL T.revDescendantOrSelfAxis -- | XPath axis: following sibling followingSiblingAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) followingSiblingAxis = arrL T.followingSiblingAxis -- | XPath axis: preceeding sibling precedingSiblingAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) precedingSiblingAxis = arrL T.precedingSiblingAxis -- | XPath axis: self selfAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) selfAxis = arrL T.selfAxis -- | XPath axis: following followingAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) followingAxis = arrL T.followingAxis -- | XPath axis: preceding precedingAxis :: (ArrowList a, NavigatableTree t) => a (t b) (t b) precedingAxis = arrL T.precedingAxis -- ------------------------------------------------------------ -- | move to the root moveToRoot :: (Arrow a, NavigatableTree t) => a (t b) (t b) moveToRoot = arr T.mvToRoot isAtRoot :: (ArrowList a, NavigatableTree t) => a (t b) (t b) isAtRoot = isA (null . T.ancestorAxis) -- ------------------------------------------------------------ -- | Conversion from a tree into a navigatable tree addNav :: ( ArrowList a , NavigatableTreeToTree nt t ) => a (t b) (nt b) addNav = arr T.fromTree -- | Conversion from a navigatable tree into an ordinary tree remNav :: ( ArrowList a , NavigatableTreeToTree nt t ) => a (nt b) (t b) remNav = arr T.toTree -- | apply an operation using navigation to an ordinary tree -- -- This root and all children may be visited in arbitrary order withNav :: ( ArrowList a , NavigatableTreeToTree nt t ) => a (nt b) (nt c) -> a (t b) (t c) withNav f = addNav >>> f >>> remNav -- | apply a simple operation without use of navigation to a navigatable tree -- -- This enables to apply arbitrary tree operations to navigatable trees withoutNav :: ( ArrowList a , NavigatableTreeToTree nt t , NavigatableTreeModify nt t ) => a (t b) (t b) -> a (nt b) (nt b) withoutNav f = ( (remNav >>> f) -- apply the simple arrow to the tree &&& this -- remember the navigation context ) >>> arr (uncurry T.substThisTree) -- resore the context -- ------------------------------------------------------------ -- | Filter an axis with an ordinary tree predicate -- -- Example: In a tree of Ints find all nodes in the subtrees (in preorder) that have label 42 -- -- > descendantAxis >>> filterAxis (hasNode (== 42)) -- -- Example: In an XML Tree find the following nodes of a node with attribute id and value 42 -- -- > descendantAxis >>> filterAxis (hasAttrValue "id" (=="42")) >>> followingAxis filterAxis :: ( ArrowIf a , NavigatableTreeToTree nt t ) => a (t b) c -> a (nt b) (nt b) filterAxis p = (remNav >>> p) `guards` this {-# INLINE filterAxis #-} -- | Move to the next tree on a given axis. Deterministic arrow -- -- Example: Move to the next node in a preorder visit: next child or else next following -- -- > moveOn descendantOrFollowingAxis moveOn :: ( ArrowList a , NavigatableTree t ) => a (t b) (t b) -> a (t b) (t b) moveOn axis = single $ axis {-# INLINE moveOn #-} -- ------------------------------------------------------------ -- | Change the current subtree of a navigatable tree. -- -- The arrow for computing the changes should be deterministic. If it fails -- nothing is changed. changeThisTree :: ( ArrowList a , ArrowIf a , NavigatableTreeToTree nt t , NavigatableTreeModify nt t ) => a (t b) (t b) -> a (nt b) (nt b) changeThisTree cf = withoutNav $ single cf `orElse` this -- | Substitute the current subtree of a navigatable tree by a given tree substThisTree :: ( ArrowList a , ArrowIf a , NavigatableTreeToTree nt t , NavigatableTreeModify nt t ) => t b -> a (nt b) (nt b) substThisTree t = changeThisTree (constA t) -- ------------------------------------------------------------ -- | apply an ordinary arrow to the current subtree of a navigatabe tree and add the result trees in front of the current tree. -- -- If this arrow is applied to the root, it will fail, because we want a tree as result, not a forest. addToTheLeft :: ( ArrowList a , NavigatableTreeToTree nt t , NavigatableTreeModify nt t ) => a (t b) (t b) -> a (nt b) (nt b) addToTheLeft = addToOneSide $ foldl (\ acc t -> acc >>= T.addTreeLeft t) {-# INLINE addToTheLeft #-} -- | apply an ordinary arrow to the current subtree of a navigatabe tree and add the result trees behind the current tree. -- -- If this arrow is applied to the root, it will fail, because we want a tree as result, not a forest. addToTheRight :: ( ArrowList a , NavigatableTreeToTree nt t , NavigatableTreeModify nt t ) => a (t b) (t b) -> a (nt b) (nt b) addToTheRight = addToOneSide $ foldr (\ t acc -> acc >>= T.addTreeRight t) {-# INLINE addToTheRight #-} -- | addToOneSide does the real work for 'addToTheLeft' and 'addToTheRight' addToOneSide :: ( ArrowList a , NavigatableTreeToTree nt t , NavigatableTreeModify nt t ) => ( Maybe (nt b) -> [t b] -> Maybe (nt b) ) -> a (t b) (t b) -> a (nt b) (nt b) addToOneSide side f = ( ( remNav >>> listA f ) &&& this ) >>> arrL ( uncurry (\ ts nt -> side (Just nt) ts) >>> maybeToList ) -- ------------------------------------------------------------ -- | drop the direct left sibling tree of the given navigatable tree -- -- If this arrow is applied to the root or a leftmost tree, it will fail, because there is nothing to remove dropFromTheLeft :: ( ArrowList a -- , NavigatableTreeToTree nt t , NavigatableTreeModify nt t ) => a (nt b) (nt b) dropFromTheLeft = arrL $ T.dropTreeLeft >>> maybeToList {-# INLINE dropFromTheLeft #-} -- | drop the direct left sibling tree of the given navigatable tree -- -- If this arrow is applied to the root or a rightmost tree, it will fail, because there is nothing to remove dropFromTheRight :: ( ArrowList a , NavigatableTreeModify nt t ) => a (nt b) (nt b) dropFromTheRight = arrL $ T.dropTreeRight >>> maybeToList {-# INLINE dropFromTheRight #-} -- ------------------------------------------------------------