{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE NamedFieldPuns #-}

module Modelling.ActivityDiagram.PlantUMLConverter (
  PlantUmlConfig(..),
  defaultPlantUmlConfig,
  convertToPlantUML,
  convertToPlantUML',
  drawAdToFile
) where

import Data.ByteString (ByteString)
import Data.List ( delete, intercalate, intersect, union )
import Data.String.Interpolate ( i, __i )
import GHC.Generics (Generic)

import Capabilities.PlantUml            (MonadPlantUml (drawPlantUmlSvg))
import Capabilities.WriteFile           (MonadWriteFile (writeToFile))
import Modelling.ActivityDiagram.Datatype (
  AdNode (..),
  UMLActivityDiagram(..),
  getInitialNodes,
  adjNodes,
  )

data PlantUmlConfig = PlantUmlConfig {
  PlantUmlConfig -> Bool
suppressNodeNames :: Bool,
  PlantUmlConfig -> Bool
suppressBranchConditions :: Bool
} deriving ((forall x. PlantUmlConfig -> Rep PlantUmlConfig x)
-> (forall x. Rep PlantUmlConfig x -> PlantUmlConfig)
-> Generic PlantUmlConfig
forall x. Rep PlantUmlConfig x -> PlantUmlConfig
forall x. PlantUmlConfig -> Rep PlantUmlConfig x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. PlantUmlConfig -> Rep PlantUmlConfig x
from :: forall x. PlantUmlConfig -> Rep PlantUmlConfig x
$cto :: forall x. Rep PlantUmlConfig x -> PlantUmlConfig
to :: forall x. Rep PlantUmlConfig x -> PlantUmlConfig
Generic, ReadPrec [PlantUmlConfig]
ReadPrec PlantUmlConfig
Int -> ReadS PlantUmlConfig
ReadS [PlantUmlConfig]
(Int -> ReadS PlantUmlConfig)
-> ReadS [PlantUmlConfig]
-> ReadPrec PlantUmlConfig
-> ReadPrec [PlantUmlConfig]
-> Read PlantUmlConfig
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
$creadsPrec :: Int -> ReadS PlantUmlConfig
readsPrec :: Int -> ReadS PlantUmlConfig
$creadList :: ReadS [PlantUmlConfig]
readList :: ReadS [PlantUmlConfig]
$creadPrec :: ReadPrec PlantUmlConfig
readPrec :: ReadPrec PlantUmlConfig
$creadListPrec :: ReadPrec [PlantUmlConfig]
readListPrec :: ReadPrec [PlantUmlConfig]
Read, Int -> PlantUmlConfig -> ShowS
[PlantUmlConfig] -> ShowS
PlantUmlConfig -> String
(Int -> PlantUmlConfig -> ShowS)
-> (PlantUmlConfig -> String)
-> ([PlantUmlConfig] -> ShowS)
-> Show PlantUmlConfig
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PlantUmlConfig -> ShowS
showsPrec :: Int -> PlantUmlConfig -> ShowS
$cshow :: PlantUmlConfig -> String
show :: PlantUmlConfig -> String
$cshowList :: [PlantUmlConfig] -> ShowS
showList :: [PlantUmlConfig] -> ShowS
Show, PlantUmlConfig -> PlantUmlConfig -> Bool
(PlantUmlConfig -> PlantUmlConfig -> Bool)
-> (PlantUmlConfig -> PlantUmlConfig -> Bool) -> Eq PlantUmlConfig
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PlantUmlConfig -> PlantUmlConfig -> Bool
== :: PlantUmlConfig -> PlantUmlConfig -> Bool
$c/= :: PlantUmlConfig -> PlantUmlConfig -> Bool
/= :: PlantUmlConfig -> PlantUmlConfig -> Bool
Eq)

defaultPlantUmlConfig :: PlantUmlConfig
defaultPlantUmlConfig :: PlantUmlConfig
defaultPlantUmlConfig = PlantUmlConfig {
  suppressNodeNames :: Bool
suppressNodeNames = Bool
False,
  suppressBranchConditions :: Bool
suppressBranchConditions = Bool
True
}

drawAdToFile
  :: (MonadPlantUml m, MonadWriteFile m)
  => FilePath
  -> PlantUmlConfig
  -> UMLActivityDiagram
  -> m FilePath
drawAdToFile :: forall (m :: * -> *).
(MonadPlantUml m, MonadWriteFile m) =>
String -> PlantUmlConfig -> UMLActivityDiagram -> m String
drawAdToFile String
path PlantUmlConfig
conf UMLActivityDiagram
ad = do
  ByteString
renderedAd <- ByteString -> m ByteString
forall (m :: * -> *). MonadPlantUml m => ByteString -> m ByteString
drawPlantUmlSvg (ByteString -> m ByteString) -> ByteString -> m ByteString
forall a b. (a -> b) -> a -> b
$ PlantUmlConfig -> UMLActivityDiagram -> ByteString
convertToPlantUML' PlantUmlConfig
conf UMLActivityDiagram
ad
  String -> ByteString -> m ()
forall (m :: * -> *).
MonadWriteFile m =>
String -> ByteString -> m ()
writeToFile String
adFilename ByteString
renderedAd
  String -> m String
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return String
adFilename
  where
    adFilename :: FilePath
    adFilename :: String
adFilename = [i|#{path}Diagram.svg|]

convertToPlantUML :: UMLActivityDiagram -> ByteString
convertToPlantUML :: UMLActivityDiagram -> ByteString
convertToPlantUML = PlantUmlConfig -> UMLActivityDiagram -> ByteString
convertToPlantUML' PlantUmlConfig
defaultPlantUmlConfig

convertToPlantUML' :: PlantUmlConfig -> UMLActivityDiagram -> ByteString
convertToPlantUML' :: PlantUmlConfig -> UMLActivityDiagram -> ByteString
convertToPlantUML' PlantUmlConfig
conf UMLActivityDiagram
diag =
    let start :: [AdNode]
start = UMLActivityDiagram -> [AdNode]
getInitialNodes UMLActivityDiagram
diag
        body :: String
body = [AdNode] -> PlantUmlConfig -> UMLActivityDiagram -> String
convertNode [AdNode]
start PlantUmlConfig
conf UMLActivityDiagram
diag
        document :: String
document = String
"@startuml\n" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
body String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"@enduml"
    in [__i|#{document}|]

convertNode :: [AdNode] -> PlantUmlConfig -> UMLActivityDiagram -> String
convertNode :: [AdNode] -> PlantUmlConfig -> UMLActivityDiagram -> String
convertNode [AdNode]
queue PlantUmlConfig
conf UMLActivityDiagram
diag = [AdNode]
-> PlantUmlConfig -> UMLActivityDiagram -> [AdNode] -> String
convertNode' [AdNode]
queue PlantUmlConfig
conf UMLActivityDiagram
diag []

--Traverse the graph and serialize the nodes along the way to a PlantUML-String
convertNode'
  :: [AdNode]
  -> PlantUmlConfig
  -> UMLActivityDiagram
  -> [AdNode]
  -> String
convertNode' :: [AdNode]
-> PlantUmlConfig -> UMLActivityDiagram -> [AdNode] -> String
convertNode' [] PlantUmlConfig
_ UMLActivityDiagram
_ [AdNode]
_ = [__i||]
convertNode' (AdNode
current:[AdNode]
queue) conf :: PlantUmlConfig
conf@(PlantUmlConfig Bool
sn Bool
sb) UMLActivityDiagram
diag [AdNode]
seen =
  let newQueue :: [AdNode]
newQueue = (AdNode -> Bool) -> [AdNode] -> [AdNode]
forall a. (a -> Bool) -> [a] -> [a]
filter (AdNode -> [AdNode] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [AdNode]
seen) ([AdNode]
queue [AdNode] -> [AdNode] -> [AdNode]
forall a. [a] -> [a] -> [a]
++ AdNode -> UMLActivityDiagram -> [AdNode]
adjNodes AdNode
current UMLActivityDiagram
diag)
      newSeen :: [AdNode]
newSeen = [AdNode]
seen [AdNode] -> [AdNode] -> [AdNode]
forall a. [a] -> [a] -> [a]
++ [AdNode
current]
  in case AdNode
current of
        AdActionNode {String
name :: String
name :: AdNode -> String
name} ->
          [__i|:#{f sn name};
          #{convertNode' newQueue conf diag newSeen}|]
        AdObjectNode {String
name :: AdNode -> String
name :: String
name} ->
          [__i|:#{f sn name}]
          #{convertNode' newQueue conf diag newSeen}|]
        AdInitialNode {} ->
          [__i|start
          #{convertNode' newQueue conf diag newSeen}|]
        AdActivityFinalNode {} ->
          [__i|stop\n|]
        AdFlowFinalNode {} ->
          [__i|end\n|]
        AdMergeNode {} ->
          [__i|#{handleRepeat current conf diag newSeen repeatStart repeatEnd}|]
        AdDecisionNode {} ->
          [__i|#{handleDecisionOrFork current conf diag newSeen ifElseStart ifElseMid ifElseEnd}|]
        AdForkNode {} ->
          [__i|#{handleDecisionOrFork current conf diag newSeen forkStart forkMid forkEnd}|]
        AdNode
_ -> [__i||]
  where
    f :: Bool -> ShowS
f Bool
p String
x = if Bool
p then String
"        " else String
x
    repeatStart :: String
repeatStart = String
"repeat"
    repeatEnd :: String
repeatEnd = [__i|repeat while () #{f sb "is ([Y]) not ([X])"}|]
    ifElseStart :: String
ifElseStart = [__i|if () then #{f sb "([X])"}|]
    ifElseMid :: String
ifElseMid = [__i|else #{f sb "([Y])"}\n|]
    ifElseEnd :: String
ifElseEnd = String
"endif"
    forkStart :: String
forkStart = String
"fork"
    forkMid :: String
forkMid = String
"forkagain\n"
    forkEnd :: String
forkEnd = String
"forkend"


{-|
Strategy: Find the corresponding merge/join to the decision/fork:
That should be the first node reachable from all decision paths
This should be the head of the intersection of nodes traversed from the nodes
that the decision/fork points to
Then: Determine the subpaths between the decision and the merge
and handle them via 'convertNode''
-}
handleDecisionOrFork
  :: AdNode
  -> PlantUmlConfig
  -> UMLActivityDiagram
  -> [AdNode]
  -> String
  -> String
  -> String
  -> String
handleDecisionOrFork :: AdNode
-> PlantUmlConfig
-> UMLActivityDiagram
-> [AdNode]
-> String
-> String
-> ShowS
handleDecisionOrFork AdNode
startNode PlantUmlConfig
conf diag :: UMLActivityDiagram
diag@(UMLActivityDiagram [AdNode]
_ [AdConnection]
conns) [AdNode]
seen String
startToken String
midToken String
endToken =
  let endNode :: AdNode
endNode = [AdNode] -> AdNode
forall a. HasCallStack => [a] -> a
head ([AdNode] -> AdNode) -> [AdNode] -> AdNode
forall a b. (a -> b) -> a -> b
$ ([AdNode] -> [AdNode] -> [AdNode]) -> [[AdNode]] -> [AdNode]
forall a. (a -> a -> a) -> [a] -> a
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 [AdNode] -> [AdNode] -> [AdNode]
forall a. Eq a => [a] -> [a] -> [a]
intersect ([[AdNode]] -> [AdNode]) -> [[AdNode]] -> [AdNode]
forall a b. (a -> b) -> a -> b
$ [[AdNode]] -> [[AdNode]]
forall a. Eq a => [[a]] -> [[a]]
filterDisjunctSublists ([[AdNode]] -> [[AdNode]]) -> [[AdNode]] -> [[AdNode]]
forall a b. (a -> b) -> a -> b
$ (AdNode -> [AdNode]) -> [AdNode] -> [[AdNode]]
forall a b. (a -> b) -> [a] -> [b]
map (\AdNode
x -> AdNode -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode AdNode
x UMLActivityDiagram
diag [AdNode]
seen) ([AdNode] -> [[AdNode]]) -> [AdNode] -> [[AdNode]]
forall a b. (a -> b) -> a -> b
$ AdNode -> UMLActivityDiagram -> [AdNode]
adjNodes AdNode
startNode UMLActivityDiagram
diag
      pathsToEnd :: [[AdNode]]
pathsToEnd =
        (AdNode -> [AdNode]) -> [AdNode] -> [[AdNode]]
forall a b. (a -> b) -> [a] -> [b]
map
        ((AdNode -> Bool) -> [AdNode] -> [AdNode]
forall a. (a -> Bool) -> [a] -> [a]
filter (\ AdNode
x -> AdNode
x AdNode -> [AdNode] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` AdNode -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode AdNode
endNode UMLActivityDiagram
diag [AdNode]
seen)
        ([AdNode] -> [AdNode])
-> (AdNode -> [AdNode]) -> AdNode -> [AdNode]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\ AdNode
x -> AdNode -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode AdNode
x UMLActivityDiagram
diag [AdNode]
seen))
        (AdNode -> UMLActivityDiagram -> [AdNode]
adjNodes AdNode
startNode UMLActivityDiagram
diag)
      subDiags :: [UMLActivityDiagram]
subDiags = ([AdNode] -> UMLActivityDiagram)
-> [[AdNode]] -> [UMLActivityDiagram]
forall a b. (a -> b) -> [a] -> [b]
map ([AdNode] -> [AdConnection] -> UMLActivityDiagram
`UMLActivityDiagram` [AdConnection]
conns) [[AdNode]]
pathsToEnd
      subStrings :: [String]
subStrings = (UMLActivityDiagram -> String) -> [UMLActivityDiagram] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\UMLActivityDiagram
xs -> [AdNode]
-> PlantUmlConfig -> UMLActivityDiagram -> [AdNode] -> String
convertNode' [[AdNode] -> AdNode
forall a. HasCallStack => [a] -> a
head ([AdNode] -> AdNode) -> [AdNode] -> AdNode
forall a b. (a -> b) -> a -> b
$ UMLActivityDiagram -> [AdNode]
nodes UMLActivityDiagram
xs] PlantUmlConfig
conf UMLActivityDiagram
xs [AdNode]
seen) [UMLActivityDiagram]
subDiags
      newSeen :: [AdNode]
newSeen = [AdNode]
seen [AdNode] -> [AdNode] -> [AdNode]
forall a. [a] -> [a] -> [a]
++ ([AdNode] -> [AdNode] -> [AdNode]) -> [[AdNode]] -> [AdNode]
forall a. (a -> a -> a) -> [a] -> a
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 [AdNode] -> [AdNode] -> [AdNode]
forall a. Eq a => [a] -> [a] -> [a]
union [[AdNode]]
pathsToEnd [AdNode] -> [AdNode] -> [AdNode]
forall a. [a] -> [a] -> [a]
++ [AdNode
endNode]
      newQueue :: [AdNode]
newQueue = (AdNode -> Bool) -> [AdNode] -> [AdNode]
forall a. (a -> Bool) -> [a] -> [a]
filter (AdNode -> [AdNode] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [AdNode]
newSeen) (AdNode -> UMLActivityDiagram -> [AdNode]
adjNodes AdNode
endNode UMLActivityDiagram
diag)
  in
    [__i|
    #{startToken}
    #{intercalate midToken subStrings}#{endToken}
    #{convertNode' newQueue conf diag newSeen}
    |]


-- Filter out sublists that are disjunct with all other sublists
filterDisjunctSublists :: (Eq a) => [[a]] -> [[a]]
filterDisjunctSublists :: forall a. Eq a => [[a]] -> [[a]]
filterDisjunctSublists [[a]]
ws = [[a]] -> [[a]] -> [[a]]
forall {t :: * -> *} {a}.
(Foldable t, Eq a) =>
[[a]] -> t [a] -> [[a]]
filterDisjunctSublists' [[a]]
ws [[a]]
ws
  where filterDisjunctSublists' :: [[a]] -> t [a] -> [[a]]
filterDisjunctSublists' [[a]]
sublists = ([a] -> [[a]] -> [[a]]) -> [[a]] -> t [a] -> [[a]]
forall a b. (a -> b -> b) -> b -> t a -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\[a]
hs [[a]]
js -> if [a] -> [[a]] -> Bool
forall {a}. Eq a => [a] -> [[a]] -> Bool
allDisjunctWith [a]
hs [[a]]
sublists then [[a]]
js
                                                            else [a]
hs[a] -> [[a]] -> [[a]]
forall a. a -> [a] -> [a]
:[[a]]
js) []
        allDisjunctWith :: [a] -> [[a]] -> Bool
allDisjunctWith [a]
xs [[a]]
ys = ([a] -> Bool) -> [[a]] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all ([a] -> [a] -> Bool
forall {a}. Eq a => [a] -> [a] -> Bool
disjunct [a]
xs) ([a] -> [[a]] -> [[a]]
forall a. Eq a => a -> [a] -> [a]
delete [a]
xs [[a]]
ys)
        disjunct :: [a] -> [a] -> Bool
disjunct [a]
xs = [a] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([a] -> Bool) -> ([a] -> [a]) -> [a] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> [a] -> [a]
forall a. Eq a => [a] -> [a] -> [a]
intersect [a]
xs

{-|
Strategy:

* Find the corresponding decision to the merge:
  That should be the node reachable from the merge (but not traversed yet)
  that has an edge towards it
* Then: Determine the subpath between the merge and the decision
  and handle them via convertNode'
-}
handleRepeat
  :: AdNode
  -> PlantUmlConfig
  -> UMLActivityDiagram
  -> [AdNode]
  -> String
  -> String
  -> String
handleRepeat :: AdNode
-> PlantUmlConfig
-> UMLActivityDiagram
-> [AdNode]
-> String
-> ShowS
handleRepeat AdNode
merge PlantUmlConfig
conf diag :: UMLActivityDiagram
diag@(UMLActivityDiagram [AdNode]
_ [AdConnection]
conns) [AdNode]
seen String
startToken String
endToken =
  let repeatEnd :: AdNode
repeatEnd =  [AdNode] -> AdNode
forall a. HasCallStack => [a] -> a
head ([AdNode] -> AdNode) -> [AdNode] -> AdNode
forall a b. (a -> b) -> a -> b
$ (AdNode -> Bool) -> [AdNode] -> [AdNode]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (\AdNode
x -> AdNode
merge AdNode -> [AdNode] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` AdNode -> UMLActivityDiagram -> [AdNode]
adjNodes AdNode
x UMLActivityDiagram
diag) ([AdNode] -> [AdNode]) -> [AdNode] -> [AdNode]
forall a b. (a -> b) -> a -> b
$ AdNode -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode AdNode
merge UMLActivityDiagram
diag [AdNode]
seen
      pathToRepeatEnd :: [AdNode]
pathToRepeatEnd = [AdNode] -> [AdNode]
forall a. HasCallStack => [a] -> [a]
tail ([AdNode] -> [AdNode]) -> [AdNode] -> [AdNode]
forall a b. (a -> b) -> a -> b
$ (AdNode -> Bool) -> [AdNode] -> [AdNode]
forall a. (a -> Bool) -> [a] -> [a]
filter (\AdNode
x -> AdNode
x AdNode -> [AdNode] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` AdNode -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode AdNode
repeatEnd UMLActivityDiagram
diag [AdNode]
seen) ([AdNode] -> [AdNode]) -> [AdNode] -> [AdNode]
forall a b. (a -> b) -> a -> b
$ AdNode -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode AdNode
merge UMLActivityDiagram
diag [AdNode]
seen
      subString :: String
subString = [AdNode]
-> PlantUmlConfig -> UMLActivityDiagram -> [AdNode] -> String
convertNode' (AdNode -> UMLActivityDiagram -> [AdNode]
adjNodes AdNode
merge UMLActivityDiagram
diag) PlantUmlConfig
conf ([AdNode] -> [AdConnection] -> UMLActivityDiagram
UMLActivityDiagram [AdNode]
pathToRepeatEnd [AdConnection]
conns) [AdNode]
seen
      newSeen :: [AdNode]
newSeen = [AdNode]
seen [AdNode] -> [AdNode] -> [AdNode]
forall a. [a] -> [a] -> [a]
++ [AdNode]
pathToRepeatEnd [AdNode] -> [AdNode] -> [AdNode]
forall a. [a] -> [a] -> [a]
++ [AdNode
repeatEnd]
      newQueue :: [AdNode]
newQueue = (AdNode -> Bool) -> [AdNode] -> [AdNode]
forall a. (a -> Bool) -> [a] -> [a]
filter (AdNode -> [AdNode] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [AdNode]
newSeen) (AdNode -> UMLActivityDiagram -> [AdNode]
adjNodes AdNode
repeatEnd UMLActivityDiagram
diag)
  in
    [__i|
    #{startToken}
    #{subString}#{endToken}
    #{convertNode' newQueue conf diag newSeen}
    |]


-- | Get reachable (yet unhandled) nodes from passed node
traverseFromNode :: AdNode -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode :: AdNode -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode AdNode
node = [AdNode] -> [AdNode] -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode' [AdNode
node] [AdNode
node]

{-|
Implementation of BFS taking already previously handled (seen) nodes in account
-}
traverseFromNode'
  :: [AdNode]
  -> [AdNode]
  -> UMLActivityDiagram
  -> [AdNode]
  -> [AdNode]
traverseFromNode' :: [AdNode] -> [AdNode] -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode' [] [AdNode]
traversed UMLActivityDiagram
_ [AdNode]
_ = [AdNode]
traversed
traverseFromNode' (AdNode
current:[AdNode]
queue) [AdNode]
traversed UMLActivityDiagram
diag [AdNode]
seen =
  let nextNodes :: [AdNode]
nextNodes = (AdNode -> Bool) -> [AdNode] -> [AdNode]
forall a. (a -> Bool) -> [a] -> [a]
filter (AdNode -> [AdNode] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` ([AdNode]
traversed [AdNode] -> [AdNode] -> [AdNode]
forall a. [a] -> [a] -> [a]
++ [AdNode]
seen)) (AdNode -> UMLActivityDiagram -> [AdNode]
adjNodes AdNode
current UMLActivityDiagram
diag)
      newQueue :: [AdNode]
newQueue = [AdNode]
queue [AdNode] -> [AdNode] -> [AdNode]
forall a. [a] -> [a] -> [a]
++ [AdNode]
nextNodes
      newTraversed :: [AdNode]
newTraversed = [AdNode]
traversed [AdNode] -> [AdNode] -> [AdNode]
forall a. [a] -> [a] -> [a]
++ [AdNode]
nextNodes
  in [AdNode] -> [AdNode] -> UMLActivityDiagram -> [AdNode] -> [AdNode]
traverseFromNode' [AdNode]
newQueue [AdNode]
newTraversed UMLActivityDiagram
diag [AdNode]
seen