{-# LANGUAGE ScopedTypeVariables #-} -- | GraphConnection contains various operations on graph connections module Graphs.GraphConnection( SubGraph(..), -- defines a subgraph as a subset of nodes and node types. -- The user is responsible for making sure that if a node is in -- the subgraph, so is its type! attachSuperGraph, -- :: SubGraph -- -> GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel -- -> GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel -- turn a graph connection into one, which when passing information back -- to the parent, ignores updates which don't lie in the subgraph. attachSubGraph, -- :: SubGraph -- -> GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel -- -> GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel -- Ditto, but only accepts updates (and parts of the original graph) -- which lie in the original graph. mapGraphConnection, ) where import Control.Monad(filterM) import qualified Data.Set as Set import Control.Concurrent import Util.Computation (done) import Graphs.Graph ----------------------------------------------------------------------------- -- Connection State ----------------------------------------------------------------------------- -- We keep track of the arcs currently not in the subgraph; this -- allows us to filter out attempts to delete from the subgraph. -- (Attempts to delete non-existent arcs is normally harmless but, -- apart from wasting time, could get passed onto other clients of the -- server, which might have themselves constructed identical arcs -- in their subgraphs.) newtype ConnectionState = ConnectionState (MVar (Set.Set Arc)) -- This contains the arcs NOT in the subgraph, because for our planned -- application newConnectionState :: IO ConnectionState newConnectionState = do mVar <- newMVar Set.empty return (ConnectionState mVar) arcIsInSubGraph :: ConnectionState -> Arc -> IO Bool arcIsInSubGraph (ConnectionState mVar) arc = do set <- takeMVar mVar let result = not (Set.member arc set) putMVar mVar set return result arcAdd :: ConnectionState -> Arc -> IO () arcAdd (ConnectionState mVar) arc = do set <- takeMVar mVar putMVar mVar (Set.union set (Set.singleton arc)) arcDelete :: ConnectionState -> Arc -> IO () arcDelete (ConnectionState mVar) arc = do set <- takeMVar mVar putMVar mVar (Set.difference set (Set.singleton arc)) ----------------------------------------------------------------------------- -- SubGraph ----------------------------------------------------------------------------- data SubGraph = SubGraph { nodeIn :: Node -> Bool, nodeTypeIn :: NodeType -> Bool } updateIsInSubGraph :: SubGraph -> ConnectionState -> Update nodeLabel nodeTypeLabel arcLabel arcTypeLabel -> IO Bool updateIsInSubGraph (SubGraph{nodeIn = nodeIn,nodeTypeIn = nodeTypeIn}) connectionState update = case update of NewNodeType nodeType _ -> return (nodeTypeIn nodeType) SetNodeTypeLabel nodeType _ -> return (nodeTypeIn nodeType) NewNode node _ _ -> return (nodeIn node) DeleteNode node -> return (nodeIn node) SetNodeLabel node _ -> return (nodeIn node) SetNodeType node _ -> return (nodeIn node) NewArc arc _ _ node1 node2 -> do let inSubGraph = nodeIn node1 && nodeIn node2 if inSubGraph then return True else do arcAdd connectionState arc return False DeleteArc arc -> do inSubGraph <- arcIsInSubGraph connectionState arc if inSubGraph then return True else do arcDelete connectionState arc return False SetArcLabel arc _ -> arcIsInSubGraph connectionState arc SetArcType arc _ -> arcIsInSubGraph connectionState arc _ -> return True ----------------------------------------------------------------------------- -- GraphConnection operations ----------------------------------------------------------------------------- attachSuperGraph :: SubGraph -> GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel -> GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel attachSuperGraph subGraph graphConnection parentChanges = do graphConnectionData <- graphConnection parentChanges -- all changes to the parent get passed on connectionState <- newConnectionState let oldGraphUpdate = graphUpdate graphConnectionData newGraphUpdate update = -- updates to the child only get passed on if in the subgraph. do isInSubGraph <- updateIsInSubGraph subGraph connectionState update if isInSubGraph then oldGraphUpdate update else done return (graphConnectionData {graphUpdate = newGraphUpdate}) attachSubGraph :: SubGraph -> GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel -> GraphConnection nodeLabel nodeTypeLabel arcLabel arcTypeLabel attachSubGraph subGraph graphConnection parentChanges = do connectionState <- newConnectionState let newParentChanges update = -- Changes from the parent only get passed on if in the subgraph. do isInSubGraph <- updateIsInSubGraph subGraph connectionState update if isInSubGraph then parentChanges update else done graphConnectionData <- graphConnection newParentChanges -- We have to filter the graph state. let oldGraphState = graphState graphConnectionData oldUpdates = updates oldGraphState newUpdates <- filterM (updateIsInSubGraph subGraph connectionState) oldUpdates let newGraphState = CannedGraph {updates = newUpdates} return (graphConnectionData {graphState = newGraphState}) {-# DEPRECATED attachSuperGraph,attachSubGraph "Functions need to be updated to cope with MultiUpdate" #-} -- | Throw away the old types in a graph, and recompute them from the -- node and arc labels. mapGraphConnection :: (nodeLabel1 -> (nodeLabel2,NodeType)) -- ^ function to compute node label in new graph and type -> (arcLabel1 -> (arcLabel2,ArcType)) -- ^ function to compute arc label in new graph and type -> [Update nodeLabel2 nodeTypeLabel2 arcLabel2 arcTypeLabel2] -- ^ updates prepended to initialse types. -- (The type declarations in the input graph are discarded) -> GraphConnection nodeLabel1 () arcLabel1 () -> GraphConnection nodeLabel2 nodeTypeLabel2 arcLabel2 arcTypeLabel2 -- NB. Changes to the child do not get passed back. mapGraphConnection (mapNode :: nodeLabel1 -> (nodeLabel2,NodeType)) (mapArc :: arcLabel1 -> (arcLabel2,ArcType)) (initialUpdates :: [Update nodeLabel2 nodeTypeLabel2 arcLabel2 arcTypeLabel2]) graphConnection1 updateFn2 = let mapUpdate :: Update nodeLabel1 () arcLabel1 () -> Update nodeLabel2 nodeTypeLabel2 arcLabel2 arcTypeLabel2 mapUpdate update = case update of NewNodeType _ _ -> nop SetNodeTypeLabel _ _ -> nop NewNode node _ nodeTypeLabel1 -> let (nodeTypeLabel2,nodeType2) = mapNode nodeTypeLabel1 in NewNode node nodeType2 nodeTypeLabel2 DeleteNode node -> DeleteNode node SetNodeLabel node nodeLabel1 -> let (nodeLabel2,nodeType2) = mapNode nodeLabel1 in MultiUpdate [ SetNodeLabel node nodeLabel2, SetNodeType node nodeType2 ] SetNodeType _ _ -> nop NewArcType _ _ -> nop SetArcTypeLabel _ _ -> nop NewArc arc _ arcLabel1 nodeFrom nodeTo -> let (arcLabel2,arcType2) = mapArc arcLabel1 in NewArc arc arcType2 arcLabel2 nodeFrom nodeTo DeleteArc arc -> DeleteArc arc SetArcLabel arc arcLabel1 -> let (arcLabel2,arcType2) = mapArc arcLabel1 in MultiUpdate [ SetArcLabel arc arcLabel2, SetArcType arc arcType2 ] SetArcType _ _ -> nop MultiUpdate updates -> MultiUpdate (fmap mapUpdate updates) updateFn1 update1 = updateFn2 (mapUpdate update1) nop = MultiUpdate [] in do graphConnectionData1 <- graphConnection1 updateFn1 let cannedGraph1 = graphState graphConnectionData1 updates1 = updates cannedGraph1 updates2 = initialUpdates ++ fmap mapUpdate updates1 cannedGraph2 = CannedGraph {updates = updates2} graphUpdate2 _ = done graphConnectionData2 = graphConnectionData1 { graphState = cannedGraph2, graphUpdate = graphUpdate2 } return graphConnectionData2