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 projects, groups and modules
22 # FIXME do not use Object, but a better common interface of MModule and MGroup
24 super OrderedTree[Object]
26 # The model where to look for information
29 init(model
: Model) do self.model
= model
31 redef fun display
(a
) do
33 if a
.parent
== null then return "{a.mproject.name} ({a.filepath})"
34 return a
.name
+ " (group)"
35 else if a
isa MModule then
42 var alpha_comparator
= new AlphaComparator
44 var linex_comparator
: nullable LinexComparator = null
46 # Sort modules and groups with their names
49 sort_with
(alpha_comparator
)
52 # Sort modules and groups with a loosly adaptation of the linerarisation of modules
55 var c
= linex_comparator
57 c
= new LinexComparator(self)
64 # Just compare objects by using the `to_s` method
65 private class AlphaComparator
66 super AbstractSorter[Object]
67 redef fun compare
(a
,b
) do return a
.to_s
<=> b
.to_s
70 # Compare modules and groups using the
71 # FIXME do not use Object, but a better common interface of MModule and MGroup
72 private class LinexComparator
73 super AbstractSorter[Object]
74 var mins
= new HashMap [MGroup, nullable MModule]
75 var maxs
= new HashMap [MGroup, nullable MModule]
76 fun min
(o
: Object): nullable MModule do
77 if o
isa MModule then return o
79 if not mins
.has_key
(o
) then computeminmax
(o
)
82 fun max
(o
: Object): nullable MModule do
83 if o
isa MModule then return o
85 if not maxs
.has_key
(o
) then computeminmax
(o
)
88 fun computeminmax
(o
: MGroup) do
89 if not tree
.sub
.has_key
(o
) then
94 var subs
= tree
.sub
[o
]
95 var minres
= min
(subs
.first
)
96 var maxres
= max
(subs
.first
)
97 var order
= minres
.model
.mmodule_importation_hierarchy
100 if c
== null then continue
101 if minres
== null or order
.compare
(minres
, c
) > 0 then minres
= c
103 if c
== null then continue
104 if maxres
== null or order
.compare
(maxres
, c
) < 0 then maxres
= c
108 #if minres != maxres then print "* {o} {minres}..{maxres}"
110 redef fun compare
(a
,b
) do
114 if mb
== null then return 0 else return -1
115 else if mb
== null then
118 var order
= ma
.model
.mmodule_importation_hierarchy
119 return order
.compare
(ma
, mb
)
121 var tree
: OrderedTree[Object]
125 # Generate a MProjectTree based on the projects, groups and modules known in the model
126 fun to_mproject_tree
: MProjectTree
128 var res
= new MProjectTree(self)
129 for p
in mprojects
do
130 for g
in p
.mgroups
do
132 for m
in g
.mmodules
do
141 # Generate graphiz files based on projects, groups and modules
143 # Interessing elements must be selected. See `mmodules`, ``
144 # Display configuration can be set. See `cluster_group`, `project_group`
146 # The model where to look for information
149 # Set of projects to expand fully (ie all groups and modules are displayed)
150 # Initially empty, projects can be added
151 var mprojects
= new HashSet[MProject]
153 # Set of modules to display
154 # Initially empty, modules can be added
155 var mmodules
= new HashSet[MModule]
157 private fun node_for
(mmodule
: MModule): nullable String
159 return "m_{mmodule.object_id}"
162 # Should groups be shown as clusters?
163 var cluster_group
writable = true
165 # Should projects be shown as clusters?
166 var project_group
writable = true
168 # Recursively generate noed ans clusters for a mroup
169 private fun dot_cluster
(o
: OStream, mgroup
: MGroup)
171 # Open the cluster, if required
172 if mgroup
.parent
== null then
173 # is is a root group, so display the project
174 if project_group
then
175 o
.write
("subgraph cluster_{mgroup.object_id} \{\nlabel=\"{mgroup.mproject.name}\\n({mgroup.filepath})\"\ncolor
=black\nstyle
=dotted\n
")
178 if cluster_group then
179 o.write("subgraph cluster_
{mgroup.object_id} \
{\nlabel=\"{mgroup.name}\
"\ncolor=blue\nstyle=dotted\n")
183 # outputs the mmodules to show
184 for mmodule
in mgroup
.mmodules
do
185 if not mmodules
.has
(mmodule
) then continue
186 o
.write
("\t{node_for(mmodule)} [label=\"{mmodule.name}\
",color=green]\n")
189 # recusively progress on sub-clusters
190 for d
in mgroup
.in_nesting
.direct_smallers
do
194 # close the cluster if required
195 if mgroup
.parent
== null then
196 if project_group
then o
.write
("\}\n")
198 if cluster_group
then o
.write
("\}\n")
202 # Extends the set of `mmodules` by recurdively adding the most specific imported modules of foreign projects
203 fun collect_important_importation
205 var todo
= new List[MModule]
206 todo
.add_all
(mmodules
)
207 while not todo
.is_empty
do
210 for psm
in m
.in_importation
.greaters
do
211 if m
.mgroup
.mproject
!= psm
.mgroup
.mproject
then continue
212 for ssm
in psm
.in_importation
.direct_greaters
do
213 if psm
.mgroup
.mproject
== ssm
.mgroup
.mproject
then continue
221 # Generate the dot-file named `filepath` with the current configuration
222 fun render
(filepath
: String)
224 # Collect interessing nodes
225 for m
in model
.mmodules
do
226 # filter out modules outside wanted projects
227 if not mprojects
.has
(m
.mgroup
.mproject
) then continue
232 collect_important_importation
234 # Collect interessing edges
235 var sub_hierarchy
= new POSet[MModule]
237 sub_hierarchy
.add_node
(m
)
238 for sm
in m
.in_importation
.greaters
do
239 if sm
== m
then continue
240 if not mmodules
.has
(sm
) then continue
241 sub_hierarchy
.add_edge
(m
, sm
)
245 print
"generating {filepath}"
246 var dot
= new OFStream.open
(filepath
)
247 dot
.write
("digraph g \{\n")
248 dot
.write
("rankdir=BT;node[shape=box];\n")
250 for p
in model
.mprojects
do
251 dot_cluster
(dot
, p
.root
.as(not null))
255 for sm
in sub_hierarchy
[m
].direct_greaters
do
257 var nsm
= node_for
(sm
)
258 if m
.in_importation
.direct_greaters
.has
(sm
) then
259 dot
.write
("\t{nm} -> {nsm}[style=bold]\n")
261 dot
.write
("\t{nm} -> {nsm}[style=solid]\n")
267 # sys.system("xdot -f dot {filepath}")