module DatabaseDesign.Ampersand.Fspec.Graphic.Graphics
(Dotable(..), makePictureObj, printDotGraph, DrawingType(..)
)where
import Data.GraphViz hiding (addExtension )
import DatabaseDesign.Ampersand.ADL1
import DatabaseDesign.Ampersand.Fspec.Fspec
import DatabaseDesign.Ampersand.Classes
import DatabaseDesign.Ampersand.Fspec.Switchboard
import DatabaseDesign.Ampersand.Misc
import DatabaseDesign.Ampersand.Basics (fatalMsg,eqCl,Collection(..),Identified(..))
import DatabaseDesign.Ampersand.Fspec.Graphic.Picture
import DatabaseDesign.Ampersand.Fspec.Graphic.ClassDiagram (ClassDiag,classdiagram2dot)
import Data.GraphViz.Attributes.Complete
import Data.List (nub)
import Data.String
fatal :: Int -> String -> a
fatal = fatalMsg "Fspec.Graphic.Graphics"
class Identified a => Navigatable a where
interfacename :: a -> String
itemstring :: a -> String
theURL :: Options -> a -> EscString
theURL flags x
= fromString ("Atlas.php?content=" ++ interfacename x
++ "&User=" ++ user
++ "&Script=" ++ script
++ "&"++interfacename x ++"="++qualify++itemstring x
)
where
script = fileName flags
user = namespace flags
qualify = "("++user ++ "." ++ script ++ ")"
instance Navigatable A_Concept where
interfacename _ = "Concept"
itemstring = name
instance Navigatable Declaration where
interfacename _ = "Relatiedetails"
itemstring x = name x ++ "["
++ (if source x==target x then name(source x) else name(source x)++"*"++name(target x))
++ "]"
data DrawingType
= Plain_CG
| Rel_CG
| Gen_CG
class Identified a => Dotable a where
conceptualGraph :: Fspc
-> Options
-> DrawingType
-> a -> DotGraph String
makePicture :: Options
-> Fspc
-> DrawingType
-> a
-> Picture
instance Dotable ClassDiag where
conceptualGraph _ _ _ _ = fatal 58 "TODO: ClassDiagram moet nog netjes naar nieuwe Graphviz worden verbouwd."
makePicture flags _ _ cd =
makePictureObj flags (name cd) PTClassDiagram (classdiagram2dot flags cd)
instance Dotable A_Concept where
conceptualGraph fSpec flags _ c = conceptual2Dot flags (name c) cpts rels idgs
where
rs = [r | r<-udefrules fSpec, c `elem` concs r]
idgs = [(s,g) |(s,g)<-gs, elem g cpts' || elem s cpts']
gs = fsisa fSpec
cpts = nub$cpts' ++ [g |(s,g)<-gs, elem g cpts' || elem s cpts'] ++ [s |(s,g)<-gs, elem g cpts' || elem s cpts']
cpts' = concs rs
rels = [r | r<-declsUsedIn rs
, not (isProp r)
]
makePicture flags fSpec variant x =
(makePictureObj flags (name x) PTConcept . conceptualGraph fSpec flags variant) x
instance Dotable Pattern where
conceptualGraph fSpec flags Plain_CG pat = conceptual2Dot flags (name pat) cpts (rels `uni` xrels) idgs
where
idgs = [(s,g) |(s,g)<-gs, g `elem` cpts, s `elem` cpts]
gs = fsisa fSpec
cpts = let cpts' = concs pat `uni` concs rels
in cpts' `uni` [g |cl<-eqCl id [g |(s,g)<-gs, s `elem` cpts'], length cl<3, g<-cl]
rels = [r | r@Sgn{}<-declsUsedIn pat
, not (isProp r)
]
xrels = let orphans = [c | c<-cpts, not(c `elem` map fst idgs || c `elem` map snd idgs || c `elem` map source rels || c `elem` map target rels)]
in nub [r | c<-orphans, r@Sgn{}<-declarations fSpec
, (c == source r && target r `elem` cpts) || (c == target r && source r `elem` cpts)
, source r /= target r, decusr r
]
conceptualGraph fSpec flags Rel_CG pat = conceptual2Dot flags (name pat) cpts rels idgs
where
idgs = [(s,g) |(s,g)<-gs, g `elem` cpts, s `elem` cpts]
gs = fsisa fSpec
cpts = concs (declarations pat) `uni` concs (gens pat)
rels = [r | r@Sgn{}<-declarations pat
, not (isProp r), decusr r
]
conceptualGraph fSpec flags Gen_CG pat = conceptual2Dot flags (name pat) cpts [] edges
where
idgs = [(s,g) |(s,g)<-gs, elem g cpts' || elem s cpts']
gs = fsisa fSpec
edges = clos gs idgs
cpts = concs edges
cpts' = concs pat >- concs gs
clos tuples ts = f (tuples>-ts) ts []
where f [] new result = result++new
f _ [] result = result
f tups new result = f (tups>-new) [ t |t<-tups, (not.null) (concs t `isc` concs result') ] result'
where result' = result++new
makePicture flags fSpec variant pat =
(makePictureObj flags (name pat) PTPattern . conceptualGraph fSpec flags variant) pat
instance Dotable FProcess where
conceptualGraph fSpec flags _ fproc = conceptual2Dot flags (name fproc) cpts rels idgs
where
idgs = [(s,g) |(s,g)<-gs, g `elem` cpts']
gs = fsisa fSpec
cpts = nub(cpts' ++ [g |(g,_)<-idgs] ++ [s |(_,s)<-idgs])
cpts' = concs (fpProc fproc)
rels = [r | r@Sgn{}<-declsUsedIn (fpProc fproc), decusr r
, not (isProp r)
]
makePicture flags _ _ x =
(makePictureObj flags (name x) PTProcess . processModel) x
instance Dotable Activity where
conceptualGraph fSpec flags _ ifc = conceptual2Dot flags (name ifc) cpts rels idgs
where
rs = [r | r<-udefrules fSpec, affected r]
affected r = not (null (declsUsedIn r `isc` declsUsedIn ifc))
idgs = [(s,g) |(s,g)<-gs, elem g cpts' || elem s cpts']
gs = fsisa fSpec
cpts = nub $ cpts' ++ [c |(s,g)<-idgs, c<-[g,s]]
cpts' = concs rs
rels = [r | r@Sgn{}<-declsUsedIn ifc, decusr r
, not (isProp r)
]
makePicture flags fSpec variant x =
(makePictureObj flags (name x) PTFinterface . conceptualGraph fSpec flags variant) x
instance Dotable SwitchBdDiagram where
conceptualGraph _ _ _ = sbdotGraph
makePicture flags fSpec variant x =
(makePictureObj flags (name x) PTSwitchBoard . conceptualGraph fSpec flags variant) x
instance Dotable Rule where
conceptualGraph fSpec flags _ r = conceptual2Dot flags (name r) cpts rels idgs
where
idgs = [(s,g) | (s,g)<-fsisa fSpec
, g `elem` concs r || s `elem` concs r]
cpts = nub $ concs r++[c |(s,g)<-idgs, c<-[g,s]]
rels = [d | d@Sgn{}<-declsUsedIn r, decusr d
, not (isProp d)
]
makePicture flags fSpec variant x =
(makePictureObj flags (name x) PTRule . conceptualGraph fSpec flags variant) x
conceptual2Dot :: Options
-> String
-> [A_Concept]
-> [Declaration]
-> [(A_Concept, A_Concept)]
-> DotGraph String
conceptual2Dot flags graphName cpts' rels idgs
= DotGraph { strictGraph = False
, directedGraph = True
, graphID = Just (Str (fromString graphName))
, graphStatements
= DotStmts { attrStmts = [GraphAttrs (handleFlags TotalPicture flags)]
, subGraphs = []
, nodeStmts = conceptNodes ++ relationNodes
, edgeStmts = relationEdges ++ isaEdges
}
}
where
cpts = cpts' `uni` concs rels `uni` concs idgs
conceptNodes = [constrNode (baseNodeId c) (CptOnlyOneNode c) flags | c<-cpts]
(relationNodes,relationEdges) = (concat a, concat b)
where (a,b) = unzip [relationNodesAndEdges r | r<-zip rels [1..]]
isaEdges = [constrEdge (baseNodeId s) (baseNodeId g) IsaOnlyOneEdge flags | (s,g)<-idgs]
baseNodeId :: A_Concept -> String
baseNodeId c
= case lookup c (zip cpts [(1::Int)..]) of
Just i -> "cpt_"++show i
_ -> fatal 169 $ "element "++name c++" not found by nodeLabel."
relationNodesAndEdges ::
(Declaration,Int)
-> ([DotNode String],[DotEdge String])
relationNodesAndEdges (r,n)
| doubleEdges flags
= ( [ relNameNode ]
, [ constrEdge (baseNodeId (source r)) (nodeID relNameNode) (RelSrcEdge r) flags
, constrEdge (nodeID relNameNode) (baseNodeId (target r)) (RelTgtEdge r) flags]
)
| otherwise
= ( []
, [constrEdge (baseNodeId (source r)) (baseNodeId (target r)) (RelOnlyOneEdge r) flags]
)
where
relNameNode = constrNode ("relName_"++show n) (RelIntermediateNode r) flags
constrNode :: a -> PictureObject -> Options -> DotNode a
constrNode nodeId pObj flags
= DotNode { nodeID = nodeId
, nodeAttributes = [ FontSize 10
, FontName (fromString(pangoFont flags))
]++handleFlags pObj flags
}
constrEdge :: a -> a -> PictureObject -> Options -> DotEdge a
constrEdge nodeFrom nodeTo pObj flags
= DotEdge { fromNode = nodeFrom
, toNode = nodeTo
, edgeAttributes = [ FontSize 12
, FontName (fromString(pangoFont flags))
, Dir Forward
, Color [WC(X11Color Gray35)Nothing]
, LabelFontColor (X11Color Black)
, LabelFloat False
, Decorate False
]++handleFlags pObj flags
}
data PictureObject = CptOnlyOneNode A_Concept
| CptConnectorNode A_Concept
| CptNameNode A_Concept
| CptEdge
| RelOnlyOneEdge Declaration
| RelSrcEdge Declaration
| RelTgtEdge Declaration
| RelIntermediateNode Declaration
| IsaOnlyOneEdge
| TotalPicture
handleFlags :: PictureObject -> Options -> [Attribute]
handleFlags po flags =
case po of
CptConnectorNode c
-> if crowfoot flags
then
[ (Label . StrLabel . fromString . name) c
, Shape PlainText
, Style [filled]
, URL (theURL flags c)
]
else [ Shape PointShape
, Style [filled]
]
CptNameNode c -> if crowfoot flags
then [ Shape PointShape
, Style [invis]]
else
[ (Label . StrLabel . fromString . name) c
, Shape PlainText
, Style [filled]
, URL (theURL flags c)
]
CptEdge -> [Style [invis]
]
CptOnlyOneNode c ->
[(Label . StrLabel . fromString . name) c
, Shape BoxShape
, Style [filled]
, URL (theURL flags c)
]
RelOnlyOneEdge r -> [ URL (theURL flags r)
, (XLabel . StrLabel .fromString.name) r
]
++[ ArrowTail noArrow, ArrowHead noArrow
, Dir Forward
, Style [SItem Tapered []] , PenWidth 5
]
RelSrcEdge r -> [ ArrowHead ( if crowfoot flags then normal else
if isFunction r then noArrow else
if isInvFunction r then noArrow else
noArrow
)
, ArrowTail ( if crowfoot flags then crowfootArrowType False r else
if isFunction r then noArrow else
if isInvFunction r then normal else
noArrow
)
,HeadClip False
]
RelTgtEdge r -> [ (Label . StrLabel . fromString . name) r
, ArrowHead ( if crowfoot flags then crowfootArrowType True r else
if isFunction r then normal else
if isInvFunction r then noArrow else
noArrow
)
, ArrowTail ( if crowfoot flags then noArrow else
if isFunction r then noArrow else
if isInvFunction r then AType [(noMod ,Inv)] else
AType [(noMod ,Inv)]
)
,TailClip False
]
RelIntermediateNode r ->
[ Label (StrLabel (fromString("")))
, Shape PlainText
, bgColor White
, URL (theURL flags r)
]
IsaOnlyOneEdge-> [ ArrowHead (AType [(open,Normal)])
, ArrowTail noArrow
, if blackWhite flags then Style [dotted] else Color [WC(X11Color Red)Nothing]
]
TotalPicture -> [ Sep (DVal (if doubleEdges flags then 1/2 else 2))
, OutputOrder EdgesFirst
, Overlap ScaleXYOverlaps
, Splines PolyLine
, Landscape False
]
isInvFunction :: Declaration -> Bool
isInvFunction d = isInj d && isSur d
crowfootArrowType :: Bool -> Declaration -> ArrowType
crowfootArrowType isHead r
= AType (case isHead of
True -> getCrowfootShape (isUni r) (isTot r)
False -> getCrowfootShape (isInj r) (isSur r)
)
where
getCrowfootShape :: Bool -> Bool -> [( ArrowModifier , ArrowShape )]
getCrowfootShape a b =
case (a,b) of
(True ,True ) -> [my_tee ]
(False,True ) -> [my_crow, my_tee ]
(True ,False) -> [my_odot, my_tee ]
(False,False) -> [my_crow, my_odot]
my_tee :: ( ArrowModifier , ArrowShape )
my_tee = ( noMod , Tee )
my_odot :: ( ArrowModifier , ArrowShape )
my_odot= ( open, DotArrow )
my_crow :: ( ArrowModifier , ArrowShape )
my_crow= ( open, Crow )
noMod :: ArrowModifier
noMod = ArrMod { arrowFill = FilledArrow
, arrowSide = BothSides
}
open :: ArrowModifier
open = noMod {arrowFill = OpenArrow}