module PolicyTree ( NodePolicy (..), DomainTree (..), PolicyTree, restrictionsTree, mergeTrees, mergePolicyTrees, trimTree, mergeAndTrim, erasePolicy ,domainTree ) where import Control.Applicative import InputParser hiding (Policy(..)) import Data.String.Utils (split) import Utils data NodePolicy = None | Block | Unblock deriving (Eq, Show) data DomainTree a = Node { _name :: String, _value :: a, _children :: [DomainTree a] } type PolicyTree = DomainTree NodePolicy showTree :: Show a => Int -> DomainTree a -> String showTree lvl (Node name value children) = concat $ [replicate (lvl * 2) ' ', "\"", name, "\" - ", show value] ++ (('\n':) <$> showTree (lvl + 1) <$> children) instance Show a => Show (DomainTree a) where show = showTree 0 restrictionsTree :: NodePolicy -> Restrictions Domain -> Maybe PolicyTree restrictionsTree positivePolicy (Restrictions p n) = trimTree positivePolicy <$> mergedTree where negativePolicy = case positivePolicy of Block -> Unblock _ -> Block positiveTree = case p of Nothing -> Just $ Node "" positivePolicy [] Just p' -> concatTrees positivePolicy $ domainTree positivePolicy <$> p' negativeTree = concatTrees negativePolicy $ domainTree negativePolicy <$> n mergedTree = case negativeTree of Nothing -> positiveTree Just negativeTree' -> mergePolicyTrees negativePolicy negativeTree' <$> positiveTree erasePolicy :: NodePolicy -> PolicyTree -> PolicyTree erasePolicy policy (Node n p c) = Node n policy' (erasePolicy policy <$> c) where policy' | p == policy = None | otherwise = p domainTree :: NodePolicy -> Domain -> PolicyTree domainTree policy domain = makeTree policy $ ("":) $ reverse $ split "." domain makeTree :: NodePolicy -> [String] -> PolicyTree makeTree _ [] = error "No nodes proviced" makeTree policy [node] = Node node policy [] makeTree policy (node:nodes) = Node node None [makeTree policy nodes] mergeAndTrim :: NodePolicy -> PolicyTree -> PolicyTree -> PolicyTree mergeAndTrim trump = trimTree trump .*. mergePolicyTrees trump concatTrees :: NodePolicy -> [PolicyTree] -> Maybe PolicyTree concatTrees _ [] = Nothing concatTrees _ [tree] = Just tree concatTrees trump (tree:trees) = mergePolicyTrees trump tree <$> concatTrees trump trees mergePolicyTrees :: NodePolicy -> PolicyTree -> PolicyTree -> PolicyTree mergePolicyTrees trump = mergeTrees mergePolicy where mergePolicy policy1 policy2 | policy1 == None = policy2 | policy2 == None = policy1 | policy1 == trump = policy1 | otherwise = policy2 mergeTrees :: (a -> b -> b) -> DomainTree a -> DomainTree b -> DomainTree b mergeTrees mergeValue t1@(Node name1 value1 children1) t2@(Node name2 value2 children2) = Node mergeName (mergeValue value1 value2) (mergeChildren children1 children2) where -- names expected to be equal and/or empty mergeName | name1 == "" = name2 | otherwise = name1 t1Default = t1{_name = "", _children = []} t2Default = t2{_name = "", _children = []} mergeChildren [] [] = [] mergeChildren (t1Child:t1Children') [] = mergeTrees mergeValue t1Child t2Default : mergeChildren t1Children' [] mergeChildren [] (t2Child:t2Children') = mergeTrees mergeValue t1Default t2Child : mergeChildren [] t2Children' mergeChildren t1Children@(t1Child:t1Children') t2Children@(t2Child:t2Children') | _name t1Child == _name t2Child = mergeTrees mergeValue t1Child t2Child : mergeChildren t1Children' t2Children' | _name t1Child > _name t2Child = mergeTrees mergeValue t1Child t2Default : mergeChildren t1Children' t2Children | otherwise = mergeTrees mergeValue t1Default t2Child : mergeChildren t1Children t2Children' trimTree :: NodePolicy -> PolicyTree -> PolicyTree trimTree trump (Node name policy children) = Node name policy childrenFiltered where childrenFiltered = filter (not.redundantChild) childrenTrimmed childrenTrimmed = trimTree trump <$> children redundantChild (Node _ childPolicy childChildren) = samePolicy childPolicy && null childChildren samePolicy childPolicy = childPolicy == policy || (policy == None && childPolicy /= trump)