view Jungle.hs @ 10:29d0f605efa9

add updateRootNodeWith
author Daichi TOMA <toma@cr.ie.u-ryukyu.ac.jp>
date Tue, 21 Jan 2014 15:51:34 +0900
parents 947c5cfa4149
children 7360fbfc7e62
line wrap: on
line source

module Jungle
( Jungle
, Node
, Path
, Position
, createJungle
, createTree
, getRootNode
, updateRootNode
, updateRootNodeWith
, addNewChildAt
, deleteChildAt
, putAttribute
, deleteAttribute
, getAttributes
, drawNode
, printAttributes
, size
) where

import qualified Data.Map as M
import qualified Data.ByteString.Lazy.Char8 as B
import Control.Concurrent.STM
import Data.Maybe (fromJust)

data Jungle = Jungle { getJungleMap :: (M.Map String Tree) } 

data Tree = Tree
          { rootNode :: (TVar Node)
          , treeName :: String
          }

data Node = Node
          { children   :: Children
          , attributes :: Attributes
          } deriving (Show)

-- Mapのkeyやvalueの型は固定しているが、
-- jungle作成時、もしくは木作成時に与えるように変更することも容易
newtype Children   = Children   { getChildrenMap :: (M.Map Int Node) } deriving (Show)
newtype Attributes = Attributes { getAttributesMap :: (M.Map String B.ByteString) } deriving (Show)

type Path = [Int]
type Position = Int

createJungle :: Jungle
createJungle = Jungle M.empty

-- 同じ名前のTreeが存在する場合、上書きする
-- Eitherなどで失敗させるほうがいいかもしれない
createTree :: Jungle -> String -> IO Jungle
createTree (Jungle map) tree_name = atomically $ do
    tree <- emptyTree tree_name
    return (Jungle (M.insert tree_name tree map))

emptyTree :: String -> STM Tree
emptyTree tree_name = do
    node <- newTVar emptyNode
    return (Tree node tree_name)

emptyNode :: Node
emptyNode = Node (Children M.empty) (Attributes M.empty)

-- getRootNodeやupdateRootNodeをJungleとTree名を取るようにしたため不要
getTreeByName :: Jungle -> String -> Maybe Tree
getTreeByName (Jungle map) tree_name = M.lookup tree_name map

getRootNode :: Jungle -> String -> IO Node
getRootNode (Jungle map) tree_name = atomically $ readTVar root_node
  where
    root_node = case M.lookup tree_name map of
             Just x -> rootNode x

-- ルートノードを更新する
updateRootNode :: Jungle -> String -> Node -> IO ()
updateRootNode (Jungle map) tree_name node = atomically $ writeTVar root_node node
  where
    root_node = case M.lookup tree_name map of
             Just x -> rootNode x

updateRootNodeWith :: (Node -> Node) -> Jungle -> String -> IO ()
updateRootNodeWith f (Jungle map) tree_name = 
  atomically $ do
    n <- readTVar root_node
    writeTVar root_node (f n)
  where
    root_node = case M.lookup tree_name map of
             Just x -> rootNode x

-- 新しい木構造を作成し、最新のルートノードとなるNodeを返す
-- Pathの位置にNodeが存在しない場合どうするか?
addNewChildAt :: Node -> Path -> Position -> Node
addNewChildAt parent []     pos = addChild parent pos emptyNode
addNewChildAt parent (x:xs) pos = addChild parent x $ addNewChildAt x_node xs pos
  where
    map = getChildrenMap $ children parent
    x_node = case M.lookup x map of
               Just x -> x

-- 子を追加したNodeを新しく作成して返す
-- 同じ位置に既に子がある場合は?
-- 現在はinsertでそのまま上書き
addChild :: Node -> Position -> Node -> Node
addChild node pos child = Node new_child attr
  where
    map = getChildrenMap $ children node
    new_child = Children (M.insert pos child map)
    attr = attributes node

-- 子を削除した新しいNodeを追加するのに等しい
-- addNewChildAtのコピペ、一般化して関数に抽出したい
-- Nodeを操作してNodeを返す関数を渡せばいけそう
deleteChildAt :: Node -> Path -> Position -> Node
deleteChildAt parent []     pos = deleteChild parent pos
deleteChildAt parent (x:xs) pos = addChild parent x $ deleteChildAt x_node xs pos
  where
    map = getChildrenMap $ children parent
    x_node = case M.lookup x map of
               Just x -> x

deleteChild :: Node -> Position -> Node
deleteChild node pos = Node new_child attr
  where
    map = getChildrenMap $ children node
    new_child = Children (M.delete pos map)
    attr = attributes node

-- attribute関連はaddNewChildAtを利用する
-- 現在はコピペ、関数に抽出したい
putAttribute :: Node -> Path -> String -> B.ByteString -> Node
putAttribute parent []     key value = putAttr parent key value
putAttribute parent (x:xs) key value = addChild parent x $ putAttribute x_node xs key value
  where
    map = getChildrenMap $ children parent
    x_node = case M.lookup x map of
               Just x -> x

putAttr :: Node -> String -> B.ByteString -> Node
putAttr node key value = Node child attr
  where
    map = getAttributesMap $ attributes node
    attr = Attributes (M.insert key value map)
    child = children node

deleteAttribute :: Node -> Path -> String -> Node
deleteAttribute parent []     key = deleteAttr parent key
deleteAttribute parent (x:xs) key = addChild parent x $ deleteAttribute x_node xs key
  where
    map = getChildrenMap $ children parent
    x_node = case M.lookup x map of
               Just x -> x
    
deleteAttr :: Node -> String -> Node
deleteAttr node key = Node child attr
  where
    map = getAttributesMap $ attributes node
    attr = Attributes (M.delete key map)
    child = children node

-- データを取り出す関連の関数
-- getNodeとgetchild, getattributeなど?

getNode :: Node -> Path -> Node
getNode node []     = node
getNode node (x:xs) = getNode child xs
  where
    map = getChildrenMap $ children node
    child = case M.lookup x map of
              Just x -> x

getAttributes :: Node -> Path -> String -> Maybe B.ByteString
getAttributes node path key = M.lookup key map
  where
    target = getNode node path
    map = getAttributesMap $ attributes target

-- デバッグ用表示関数

-- 現在の木構造を整形して表示
drawNode :: Node -> String
drawNode node = unlines $ draw "root" node

draw :: String -> Node -> [String]
draw string node = string : drawSubTrees keys
  where
    map = getChildrenMap $ children node
    keys = M.keys map
    drawSubTrees [] = []
    drawSubTrees [t] = 
      "|" : shift "`- " "   " (draw (show t) (fromJust $ M.lookup t map))
    drawSubTrees (t:ts) = 
      "|" : shift "+- " "|  " (draw (show t) (fromJust $ M.lookup t map )) ++ drawSubTrees ts
    shift first other = zipWith (++) (first : repeat other)

-- Attributesを全て表示
printAttributes :: Node -> String
printAttributes node = unlines $ printAttr "root" node

printAttr :: String -> Node -> [String]
printAttr string node =
    if not $ M.null attr_map
      then ("Node: " ++ string) : ("  " ++ attr) : printSubTrees keys
      else printSubTrees keys
  where
    attr_map  = getAttributesMap $ attributes node
    show_attr [] = []
    show_attr [x] = fst x ++ ": " ++ (B.unpack $ snd x)
    show_attr (x:xs) = fst x ++ ": " ++ (B.unpack $ snd x) ++ "\n  " ++ show_attr xs
    attr = show_attr $ M.assocs attr_map
    map = getChildrenMap $ children node
    keys = M.keys map
    printSubTrees []     = []
    printSubTrees (x:xs) = printAttr (string ++ "-" ++ (show x)) (fromJust $ M.lookup x map) ++ printSubTrees xs

-- ルートノードの下にいくつの子があるか数える
size :: Node -> Int
size node = M.size map + subTreesSize keys
  where
    map = getChildrenMap $ children node
    keys = M.keys map
    subTreesSize [] = 0
    subTreesSize (x:xs) = size (getNode x) + subTreesSize xs
    getNode x = fromJust $ M.lookup x map