1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Visualisation of Nit models
21 # A simple specialisation of OrderedTree to display packages, groups and modules
23 super OrderedTree[MConcern]
25 # The model where to look for information
28 redef fun display
(a
) do
30 if a
.parent
== null then return "{a.mpackage.name} ({a.filepath or else "?"})"
31 return a
.name
+ " (group)"
32 else if a
isa MModule then
39 private var linex_comparator
: nullable LinexComparator = null
41 # Sort modules and groups with their names
44 sort_with
(alpha_comparator
)
47 # Sort modules and groups with a loosely adaptation of the linearization of modules
50 var c
= linex_comparator
52 c
= new LinexComparator(self)
59 # Compare modules and groups using the
60 private class LinexComparator
63 redef type COMPARED: MConcern
65 var mins
= new HashMap [MGroup, nullable MModule]
66 var maxs
= new HashMap [MGroup, nullable MModule]
67 fun mini
(o
: Object): nullable MModule do
68 if o
isa MModule then return o
70 if not mins
.has_key
(o
) then computeminmax
(o
)
73 fun maxi
(o
: Object): nullable MModule do
74 if o
isa MModule then return o
76 if not maxs
.has_key
(o
) then computeminmax
(o
)
79 fun computeminmax
(o
: MGroup) do
80 if not tree
.sub
.has_key
(o
) then
85 var subs
= tree
.sub
[o
]
86 var minres
= mini
(subs
.first
)
87 var maxres
= maxi
(subs
.first
)
88 var order
= o
.model
.mmodule_importation_hierarchy
91 if c
== null then continue
92 if minres
== null or order
.compare
(minres
, c
) > 0 then minres
= c
94 if c
== null then continue
95 if maxres
== null or order
.compare
(maxres
, c
) < 0 then maxres
= c
99 #if minres != maxres then print "* {o} {minres}..{maxres}"
101 redef fun compare
(a
,b
) do
105 if mb
== null then return 0 else return -1
106 else if mb
== null then
109 var order
= ma
.model
.mmodule_importation_hierarchy
110 return order
.compare
(ma
, mb
)
112 var tree
: OrderedTree[MConcern]
116 # Generate a MPackageTree based on the packages, groups and modules known in the model
117 fun to_mpackage_tree
: MPackageTree
119 var res
= new MPackageTree(self)
120 for p
in mpackages
do
121 for g
in p
.mgroups
do
123 for m
in g
.mmodules
do
132 # Generate graphiz files based on packages, groups and modules
134 # Interesting elements must be selected. See `mmodules`, ``
135 # Display configuration can be set. See `cluster_group`, `package_group`
139 # The model where to look for information
142 # Set of packages to expand fully (ie all groups and modules are displayed)
143 # Initially empty, packages can be added
144 var mpackages
= new HashSet[MPackage]
146 # Set of modules to display
147 # Initially empty, modules can be added
148 var mmodules
= new HashSet[MModule]
150 private fun node_for
(mmodule
: MModule): String
152 return "m_{mmodule.object_id}"
155 # Should groups be shown as clusters?
156 var cluster_group
= true is writable
158 # Should packages be shown as clusters?
159 var package_group
= true is writable
161 # Recursively generate node and clusters for a mgroup
162 private fun dot_cluster
(o
: Writer, mgroup
: MGroup)
164 # Open the cluster, if required
165 if mgroup
.parent
== null then
166 # is is a root group, so display the package
167 if package_group
then
168 o
.write
("subgraph cluster_{mgroup.object_id} \{\nlabel=\"{mgroup.mpackage.name}\\n({mgroup.filepath or else "?"})\"\ncolor
=black\nstyle
=dotted\n
")
171 if cluster_group then
172 o.write("subgraph cluster_
{mgroup.object_id} \
{\nlabel=\"{mgroup.name}\
"\ncolor=blue\nstyle=dotted\n")
176 # outputs the mmodules to show
177 for mmodule
in mgroup
.mmodules
do
178 if not mmodules
.has
(mmodule
) then continue
179 o
.write
("\t{node_for(mmodule)} [label=\"{mmodule.name}\
",color=green]\n")
182 # recursively progress on sub-clusters
183 for d
in mgroup
.in_nesting
.direct_smallers
do
187 # close the cluster if required
188 if mgroup
.parent
== null then
189 if package_group
then o
.write
("\}\n")
191 if cluster_group
then o
.write
("\}\n")
195 # Extends the set of `mmodules` by recursively adding the most specific imported modules of foreign packages
196 fun collect_important_importation
198 var todo
= new List[MModule]
199 todo
.add_all
(mmodules
)
200 while not todo
.is_empty
do
203 for psm
in m
.in_importation
.greaters
do
204 if m
.mgroup
.mpackage
!= psm
.mgroup
.mpackage
then continue
205 for ssm
in psm
.in_importation
.direct_greaters
do
206 if psm
.mgroup
.mpackage
== ssm
.mgroup
.mpackage
then continue
214 # Generate the dot content with the current configuration
215 redef fun write_to
(stream
)
217 # Collect interesting nodes
218 for m
in model
.mmodules
do
219 # filter out modules outside wanted packages
220 if m
.mgroup
== null then continue
221 if not mpackages
.has
(m
.mgroup
.mpackage
) then continue
226 collect_important_importation
228 # Collect interesting edges
229 var sub_hierarchy
= new POSet[MModule]
231 sub_hierarchy
.add_node
(m
)
232 for sm
in m
.in_importation
.greaters
do
233 if sm
== m
then continue
234 if not mmodules
.has
(sm
) then continue
235 sub_hierarchy
.add_edge
(m
, sm
)
239 stream
.write
("digraph g \{\n")
240 stream
.write
("rankdir=BT;node[shape=box];\n")
242 for p
in model
.mpackages
do
243 dot_cluster
(stream
, p
.root
.as(not null))
247 for sm
in sub_hierarchy
[m
].direct_greaters
do
249 var nsm
= node_for
(sm
)
250 if m
.in_importation
.direct_greaters
.has
(sm
) then
251 stream
.write
("\t{nm} -> {nsm}[style=bold]\n")
253 stream
.write
("\t{nm} -> {nsm}[style=solid]\n")