@@ -66,10 +66,6 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
66
66
var superClasses = List [Node ]()
67
67
var incomingImplicits = List [Node ]()
68
68
var outgoingImplicits = List [Node ]()
69
- var subClassesTooltip : Option [String ] = None
70
- var superClassesTooltip : Option [String ] = None
71
- var incomingImplicitsTooltip : Option [String ] = None
72
- var outgoingImplicitsTooltip : Option [String ] = None
73
69
isClassDiagram = false
74
70
75
71
d match {
@@ -89,23 +85,23 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
89
85
// if there are too many super / sub / implicit nodes, represent
90
86
// them by on node with a corresponding tooltip
91
87
superClasses = if (_superClasses.length > settings.docDiagramsMaxNormalClasses.value) {
92
- superClassesTooltip = Some (limitSize(_superClasses.map(_.tpe.name).mkString(" , " )))
93
- List (NormalNode (textTypeEntity(_superClasses.length + MultiSuffix ), None ))
88
+ val superClassesTooltip = Some (limitSize(_superClasses.map(_.tpe.name).mkString(" , " )))
89
+ List (NormalNode (textTypeEntity(_superClasses.length + MultiSuffix ), None , superClassesTooltip ))
94
90
} else _superClasses
95
91
96
92
subClasses = if (_subClasses.length > settings.docDiagramsMaxNormalClasses.value) {
97
- subClassesTooltip = Some (limitSize(_subClasses.map(_.tpe.name).mkString(" , " )))
98
- List (NormalNode (textTypeEntity(_subClasses.length + MultiSuffix ), None ))
93
+ val subClassesTooltip = Some (limitSize(_subClasses.map(_.tpe.name).mkString(" , " )))
94
+ List (NormalNode (textTypeEntity(_subClasses.length + MultiSuffix ), None , subClassesTooltip ))
99
95
} else _subClasses
100
96
101
97
incomingImplicits = if (_incomingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) {
102
- incomingImplicitsTooltip = Some (limitSize(_incomingImplicits.map(_.tpe.name).mkString(" , " )))
103
- List (ImplicitNode (textTypeEntity(_incomingImplicits.length + MultiSuffix ), None ))
98
+ val incomingImplicitsTooltip = Some (limitSize(_incomingImplicits.map(_.tpe.name).mkString(" , " )))
99
+ List (ImplicitNode (textTypeEntity(_incomingImplicits.length + MultiSuffix ), None , incomingImplicitsTooltip ))
104
100
} else _incomingImplicits
105
101
106
102
outgoingImplicits = if (_outgoingImplicits.length > settings.docDiagramsMaxImplicitClasses.value) {
107
- outgoingImplicitsTooltip = Some (limitSize(_outgoingImplicits.map(_.tpe.name).mkString(" , " )))
108
- List (ImplicitNode (textTypeEntity(_outgoingImplicits.length + MultiSuffix ), None ))
103
+ val outgoingImplicitsTooltip = Some (limitSize(_outgoingImplicits.map(_.tpe.name).mkString(" , " )))
104
+ List (ImplicitNode (textTypeEntity(_outgoingImplicits.length + MultiSuffix ), None , outgoingImplicitsTooltip ))
109
105
} else _outgoingImplicits
110
106
111
107
thisNode = _thisNode
@@ -128,14 +124,14 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
128
124
// dot cluster containing thisNode
129
125
val thisCluster = " subgraph clusterThis {\n " +
130
126
" style=\" invis\"\n " +
131
- node2Dot(thisNode, None ) +
127
+ node2Dot(thisNode) +
132
128
" }"
133
129
// dot cluster containing incoming implicit nodes, if any
134
130
val incomingCluster = {
135
131
if (incomingImplicits.isEmpty) " "
136
132
else " subgraph clusterIncoming {\n " +
137
133
" style=\" invis\"\n " +
138
- incomingImplicits.reverse.map(n => node2Dot(n, incomingImplicitsTooltip )).mkString +
134
+ incomingImplicits.reverse.map(n => node2Dot(n)).mkString +
139
135
(if (incomingImplicits.size > 1 )
140
136
incomingImplicits.map(n => " node" + node2Index(n)).mkString(" -> " ) +
141
137
" [constraint=\" false\" , style=\" invis\" , minlen=\" 0.0\" ];\n "
@@ -147,7 +143,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
147
143
if (outgoingImplicits.isEmpty) " "
148
144
else " subgraph clusterOutgoing {\n " +
149
145
" style=\" invis\"\n " +
150
- outgoingImplicits.reverse.map(n => node2Dot(n, outgoingImplicitsTooltip )).mkString +
146
+ outgoingImplicits.reverse.map(n => node2Dot(n)).mkString +
151
147
(if (outgoingImplicits.size > 1 )
152
148
outgoingImplicits.map(n => " node" + node2Index(n)).mkString(" -> " ) +
153
149
" [constraint=\" false\" , style=\" invis\" , minlen=\" 0.0\" ];\n "
@@ -189,9 +185,9 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
189
185
" edge [" + edgeAttributesStr + " ];\n " +
190
186
implicitsDot + " \n " +
191
187
// inheritance nodes
192
- nodes.map(n => node2Dot(n, None )).mkString +
193
- subClasses.map(n => node2Dot(n, subClassesTooltip )).mkString +
194
- superClasses.map(n => node2Dot(n, superClassesTooltip )).mkString +
188
+ nodes.map(n => node2Dot(n)).mkString +
189
+ subClasses.map(n => node2Dot(n)).mkString +
190
+ superClasses.map(n => node2Dot(n)).mkString +
195
191
// inheritance edges
196
192
edges.map{ case (from, tos) => tos.map(to => {
197
193
val id = " graph" + counter + " _" + node2Index(to) + " _" + node2Index(from)
@@ -213,7 +209,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
213
209
/**
214
210
* Generates the dot string of a given node.
215
211
*/
216
- private def node2Dot (node : Node , tooltip : Option [ String ] ) = {
212
+ private def node2Dot (node : Node ) = {
217
213
218
214
// escape HTML characters in node names
219
215
def escape (name : String ) = name.replace(" &" , " &" ).replace(" <" , " <" ).replace(" >" , " >" );
@@ -228,7 +224,7 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
228
224
}
229
225
230
226
// tooltip
231
- tooltip match {
227
+ node. tooltip match {
232
228
case Some (text) => attr += " tooltip" -> text
233
229
// show full name where available (instead of TraversableOps[A] show scala.collection.parallel.TraversableOps[A])
234
230
case None if node.tpl.isDefined => attr += " tooltip" -> node.tpl.get.qualifiedName
@@ -294,25 +290,23 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
294
290
*/
295
291
private def cssClass (node : Node ): String =
296
292
if (node.isImplicitNode && incomingImplicitNodes.contains(node))
297
- " implicit-incoming"
293
+ " implicit-incoming" + cssBaseClass(node, " " , " " )
298
294
else if (node.isImplicitNode)
299
- " implicit-outgoing"
300
- else if (node.isObjectNode)
301
- " object"
295
+ " implicit-outgoing" + cssBaseClass(node, " " , " " )
302
296
else if (node.isThisNode)
303
- " this" + cssBaseClass(node, " " )
297
+ " this" + cssBaseClass(node, " " , " " )
304
298
else if (node.isOutsideNode)
305
- " outside" + cssBaseClass(node, " " )
299
+ " outside" + cssBaseClass(node, " " , " " )
306
300
else
307
- cssBaseClass(node, " default" )
301
+ cssBaseClass(node, " default" , " " )
308
302
309
- private def cssBaseClass (node : Node , default : String ) =
303
+ private def cssBaseClass (node : Node , default : String , space : String ) =
310
304
if (node.isClassNode)
311
- " class"
305
+ space + " class"
312
306
else if (node.isTraitNode)
313
- " trait"
307
+ space + " trait"
314
308
else if (node.isObjectNode)
315
- " trait "
309
+ space + " object "
316
310
else
317
311
default
318
312
@@ -381,10 +375,31 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
381
375
// assign id and class attributes to edges and nodes:
382
376
// the id attribute generated by dot has the format: "{class}|{id}"
383
377
case g @ Elem (prefix, " g" , attribs, scope, children @ _* ) if (List (" edge" , " node" ).contains((g \ " @class" ).toString)) => {
384
- val res = new Elem (prefix, " g" , attribs, scope, (children map(x => transform(x))): _* )
378
+ var res = new Elem (prefix, " g" , attribs, scope, (children map(x => transform(x))): _* )
385
379
val dotId = (g \ " @id" ).toString
386
380
if (dotId.count(_ == '|' ) == 1 ) {
387
381
val Array (klass, id) = dotId.toString.split(" \\ |" )
382
+ /* Sometimes dot "forgets" to add the image -- that's very annoying, but it seems pretty random, and simple
383
+ * tests like excute 20K times and diff the output don't trigger the bug -- so it's up to us to place the image
384
+ * back in the node */
385
+ val kind = getKind(klass)
386
+ if (kind != " " )
387
+ if (((g \ " a" \ " image" ).isEmpty)) {
388
+ DiagramStats .addBrokenImage()
389
+ val xposition = getPosition(g, " x" , - 22 )
390
+ val yposition = getPosition(g, " y" , - 11.3334 )
391
+ if (xposition.isDefined && yposition.isDefined) {
392
+ val imageNode = <image xmlns : xlink =" http://www.w3.org/1999/xlink" xlink : href ={ (" ./lib/" + kind + " _diagram.png" ) } width =" 16px" height =" 16px" preserveAspectRatio =" xMinYMin meet" x ={ xposition.get.toString } y ={ yposition.get.toString }/>
393
+ val anchorNode = (g \ " a" ) match {
394
+ case Seq (Elem (prefix, " a" , attribs, scope, children @ _* )) =>
395
+ transform(new Elem (prefix, " a" , attribs, scope, (children ++ imageNode): _* ))
396
+ case _ =>
397
+ g \ " a"
398
+ }
399
+ res = new Elem (prefix, " g" , attribs, scope, anchorNode : _* )
400
+ DiagramStats .addFixedImage()
401
+ }
402
+ }
388
403
res % new UnprefixedAttribute (" id" , id, Null ) %
389
404
new UnprefixedAttribute (" class" , (g \ " @class" ).toString + " " + klass, Null )
390
405
}
@@ -399,6 +414,20 @@ class DotDiagramGenerator(settings: doc.Settings) extends DiagramGenerator {
399
414
case x => x
400
415
}
401
416
417
+ def getKind (klass : String ): String =
418
+ if (klass.contains(" class" )) " class"
419
+ else if (klass.contains(" trait" )) " trait"
420
+ else if (klass.contains(" object" )) " object"
421
+ else " "
422
+
423
+ def getPosition (g : xml.Node , axis : String , offset : Double ): Option [Double ] = {
424
+ val node = g \ " a" \ " text" \ (" @" + axis)
425
+ if (node.isEmpty)
426
+ None
427
+ else
428
+ Some (node.toString.toDouble + offset)
429
+ }
430
+
402
431
/* graph / node / edge attributes */
403
432
404
433
private val graphAttributes : Map [String , String ] = Map (
0 commit comments