1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2014 Alexandre Terrasa <alexandre@moz-code.org>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Collect metrics about inheritance usage
18 module inheritance_metrics
21 import mmodules_metrics
22 import mclasses_metrics
26 redef class ToolContext
27 var inheritance_metrics_phase
: Phase = new InheritanceMetricsPhase(self, null)
30 # Extract metrics about inheritance from model.
31 private class InheritanceMetricsPhase
33 redef fun process_mainmodule
(mainmodule
, given_mmodules
)
35 if not toolcontext
.opt_inheritance
.value
and not toolcontext
.opt_all
.value
then return
36 var csv
= toolcontext
.opt_csv
.value
37 var out
= "{toolcontext.opt_dir.value or else "metrics"}/inheritance"
40 print toolcontext
.format_h1
("\n# Inheritance metrics")
42 var hmetrics
= new MetricSet
43 hmetrics
.register
(new MDUI(mainmodule
))
44 hmetrics
.register
(new MDUIC(mainmodule
))
45 hmetrics
.register
(new MDUII(mainmodule
))
46 hmetrics
.register
(new MIF(mainmodule
))
47 hmetrics
.register
(new MIFC(mainmodule
))
48 hmetrics
.register
(new MIFI(mainmodule
))
50 var cmetrics
= new MetricSet
51 cmetrics
.register
(new CNOAC(mainmodule
))
52 cmetrics
.register
(new CNOPC(mainmodule
))
53 cmetrics
.register
(new CNOCC(mainmodule
))
54 cmetrics
.register
(new CNODC(mainmodule
))
55 cmetrics
.register
(new CNOPI(mainmodule
))
56 cmetrics
.register
(new CNOCI(mainmodule
))
57 cmetrics
.register
(new CNODI(mainmodule
))
58 cmetrics
.register
(new CDITC(mainmodule
))
59 cmetrics
.register
(new CDITI(mainmodule
))
61 var model
= toolcontext
.modelbuilder
.model
62 var mmodules
= new HashSet[MModule]
63 var mclasses
= new HashSet[MClass]
64 for mproject
in model
.mprojects
do
66 print toolcontext
.format_h2
("\n ## project {mproject}")
68 for mgroup
in mproject
.mgroups
do
69 if mgroup
.mmodules
.is_empty
then continue
72 print toolcontext
.format_h3
(" `- group {mgroup.full_name}")
74 var mod_mclasses
= new HashSet[MClass]
75 for mmodule
in mgroup
.mmodules
do mod_mclasses
.add_all
(mmodule
.intro_mclasses
)
76 if mod_mclasses
.is_empty
then continue
77 mmodules
.add_all
(mgroup
.mmodules
)
78 mclasses
.add_all
(mod_mclasses
)
80 cmetrics
.collect
(new HashSet[MClass].from
(mod_mclasses
))
81 cmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
82 if csv
then cmetrics
.to_csv
.save
("{out}/{mgroup}_classes.csv")
84 hmetrics
.collect
(new HashSet[MModule].from
(mgroup
.mmodules
))
85 hmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
86 if csv
then hmetrics
.to_csv
.save
("{out}/{mgroup}_inheritance.csv")
89 if not mclasses
.is_empty
then
91 print toolcontext
.format_h2
("\n ## global metrics")
93 cmetrics
.collect
(mclasses
)
94 cmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
95 if csv
then cmetrics
.to_csv
.save
("{out}/summary_classes.csv")
97 hmetrics
.collect
(mmodules
)
98 hmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
99 if csv
then hmetrics
.to_csv
.save
("{out}/summary_inheritance.csv")
104 # Module metric: proportion of MClasses Defined Using Inheritance
106 # Count MClasses that have another parents than Object
110 redef fun name
do return "mdui"
111 redef fun desc
do return "proportion of mclass defined using inheritance (has other parent than Object)"
113 var mainmodule
: MModule
114 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
116 redef fun collect
(mmodules
) do
117 for mmodule
in mmodules
do
119 for mclass
in mmodule
.intro_mclasses
do
120 if mclass
.in_hierarchy
(mainmodule
).greaters
.length
> 2 then count
+= 1
122 if mmodule
.intro_mclasses
.is_empty
then
123 values
[mmodule
] = 0.0
125 values
[mmodule
] = count
.to_f
/ mmodule
.intro_mclasses
.length
.to_f
131 # Module metric: proportion of abstract, concrete and extern Classes Defined Using Inheritance
133 # Count classes that have another parents than Object
137 redef fun name
do return "mduic"
138 redef fun desc
do return "proportion of class_kind defined using inheritance"
140 var mainmodule
: MModule
141 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
143 redef fun collect
(mmodules
) do
144 for mmodule
in mmodules
do
147 for mclass
in mmodule
.intro_mclasses
do
148 if mclass
.kind
== abstract_kind
or mclass
.kind
== concrete_kind
or mclass
.kind
== extern_kind
then
149 if mclass
.in_hierarchy
(mainmodule
).greaters
.length
> 2 then count
+= 1
153 if mmodule
.intro_mclasses
.is_empty
then
154 values
[mmodule
] = 0.0
156 values
[mmodule
] = count
.to_f
/ nb
.to_f
162 # Module metric: proportion of Interface Defined Using Inheritance
164 # Count interface that have another parents than Object
168 redef fun name
do return "mduii"
169 redef fun desc
do return "proportion of interface_kind defined using inheritance"
171 var mainmodule
: MModule
172 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
174 redef fun collect
(mmodules
) do
175 for mmodule
in mmodules
do
178 for mclass
in mmodule
.intro_mclasses
do
179 if mclass
.kind
== interface_kind
then
180 if mclass
.in_hierarchy
(mainmodule
).greaters
.length
> 2 then count
+= 1
184 if mmodule
.intro_mclasses
.is_empty
then
185 values
[mmodule
] = 0.0
187 values
[mmodule
] = count
.to_f
/ nb
.to_f
193 # Module metric: proportion of MClass Inherited From
195 # Count classes that have at least a child
199 redef fun name
do return "mif"
200 redef fun desc
do return "proportion of mclass inherited from"
202 var mainmodule
: MModule
203 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
205 redef fun collect
(mmodules
) do
206 for mmodule
in mmodules
do
208 for mclass
in mmodule
.intro_mclasses
do
209 if mclass
.in_hierarchy
(mainmodule
).direct_smallers
.length
> 0 then count
+= 1
211 if mmodule
.intro_mclasses
.is_empty
then
212 values
[mmodule
] = 0.0
214 values
[mmodule
] = count
.to_f
/ mmodule
.intro_mclasses
.length
.to_f
220 # Module metric: proportion of abstract, concrete and extern Class Inherited From
222 # Count classes that have at least a child
226 redef fun name
do return "mifc"
227 redef fun desc
do return "proportion of class_kind inherited from"
229 var mainmodule
: MModule
230 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
232 redef fun collect
(mmodules
) do
233 for mmodule
in mmodules
do
236 for mclass
in mmodule
.intro_mclasses
do
237 if mclass
.kind
== abstract_kind
or mclass
.kind
== concrete_kind
or mclass
.kind
== extern_kind
then
238 if mclass
.in_hierarchy
(mainmodule
).direct_smallers
.length
> 0 then count
+= 1
242 if mmodule
.intro_mclasses
.is_empty
then
243 values
[mmodule
] = 0.0
245 values
[mmodule
] = count
.to_f
/ nb
.to_f
251 # Module metric: proportion of Interface Inherited From
253 # Count interfaces that have at least a child
257 redef fun name
do return "mifi"
258 redef fun desc
do return "proportion of interface_kind inherited from"
260 var mainmodule
: MModule
261 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
263 redef fun collect
(mmodules
) do
264 for mmodule
in mmodules
do
267 for mclass
in mmodule
.intro_mclasses
do
268 if mclass
.kind
== interface_kind
then
269 if mclass
.in_hierarchy
(mainmodule
).direct_smallers
.length
> 0 then count
+= 1
273 if mmodule
.intro_mclasses
.is_empty
then
274 values
[mmodule
] = 0.0
276 values
[mmodule
] = count
.to_f
/ nb
.to_f
282 # MClass metric: Number of Class Ancestors
284 # Count only absrtract, concrete and extern classes
288 redef fun name
do return "cnoac"
289 redef fun desc
do return "number of class_kind ancestor"
291 var mainmodule
: MModule
292 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
294 redef fun collect
(mclasses
) do
295 for mclass
in mclasses
do
297 for parent
in mclass
.in_hierarchy
(mainmodule
).greaters
do
298 if parent
== mclass
then continue
299 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
303 values
[mclass
] = count
308 # MClass metric: Number of Class Parents
310 # Count only absrtract, concrete and extern classes
314 redef fun name
do return "cnopc"
315 redef fun desc
do return "number of class_kind parent"
317 var mainmodule
: MModule
318 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
320 redef fun collect
(mclasses
) do
321 for mclass
in mclasses
do
323 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_greaters
do
324 if parent
== mclass
then continue
325 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
329 values
[mclass
] = count
334 # MClass metric: Number of Class Children
336 # Count only absrtract, concrete and extern classes
340 redef fun name
do return "cnocc"
341 redef fun desc
do return "number of class_kind children"
343 var mainmodule
: MModule
344 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
346 redef fun collect
(mclasses
) do
347 for mclass
in mclasses
do
349 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_smallers
do
350 if parent
== mclass
then continue
351 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
355 values
[mclass
] = count
360 # MClass metric: Number of Class Descendants
362 # Count only absrtract, concrete and extern classes
366 redef fun name
do return "cnodc"
367 redef fun desc
do return "number of class_kind descendants"
369 var mainmodule
: MModule
370 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
372 redef fun collect
(mclasses
) do
373 for mclass
in mclasses
do
375 for parent
in mclass
.in_hierarchy
(mainmodule
).smallers
do
376 if parent
== mclass
then continue
377 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
381 values
[mclass
] = count
386 # MClass metric: Number of Interface Ancestors
388 # Count only interfaces
392 redef fun name
do return "cnoai"
393 redef fun desc
do return "number of interface_kind ancestor"
395 var mainmodule
: MModule
396 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
398 redef fun collect
(mclasses
) do
399 for mclass
in mclasses
do
401 for parent
in mclass
.in_hierarchy
(mainmodule
).greaters
do
402 if parent
== mclass
then continue
403 if parent
.kind
== interface_kind
then
407 values
[mclass
] = count
412 # MClass metric: Number of Interface Parents
414 # Count only interfaces
418 redef fun name
do return "cnopi"
419 redef fun desc
do return "number of interface_kind parent"
421 var mainmodule
: MModule
422 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
424 redef fun collect
(mclasses
) do
425 for mclass
in mclasses
do
427 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_greaters
do
428 if parent
== mclass
then continue
429 if parent
.kind
== interface_kind
then
433 values
[mclass
] = count
438 # MClass metric: Number of Interface Children
440 # Count only interfaces
444 redef fun name
do return "cnoci"
445 redef fun desc
do return "number of interface_kind children"
447 var mainmodule
: MModule
448 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
450 redef fun collect
(mclasses
) do
451 for mclass
in mclasses
do
453 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_smallers
do
454 if parent
== mclass
then continue
455 if parent
.kind
== interface_kind
then
459 values
[mclass
] = count
464 # MClass metric: Number of Interface Descendants
466 # Count only interfaces
470 redef fun name
do return "cnodi"
471 redef fun desc
do return "number of interface_kind descendants"
473 var mainmodule
: MModule
474 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
476 redef fun collect
(mclasses
) do
477 for mclass
in mclasses
do
479 for parent
in mclass
.in_hierarchy
(mainmodule
).smallers
do
480 if parent
== mclass
then continue
481 if parent
.kind
== interface_kind
then
485 values
[mclass
] = count
490 # MClass metric: Class Depth in Inheritance Tree
492 # Following the longest path composed only of extends edges from self to Object
496 redef fun name
do return "cditc"
497 redef fun desc
do return "depth in class tree following only class, abstract, extern kind"
499 var mainmodule
: MModule
500 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
502 redef fun collect
(mclasses
) do
503 for mclass
in mclasses
do
504 values
[mclass
] = mclass
.ditc
(mainmodule
)
509 # MClass metric: Interface Depth in Inheritance Tree
511 # Following the longest path composed only of implements edges from self to Object
515 redef fun name
do return "cditi"
516 redef fun desc
do return "depth in class tree following only interface_kind"
518 var mainmodule
: MModule
519 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
521 redef fun collect
(mclasses
) do
522 for mclass
in mclasses
do
523 values
[mclass
] = mclass
.diti
(mainmodule
)
532 # Class Depth in Inheritance Tree
534 # Following the longest path composed only of extends edges from self to Object
535 fun ditc
(mainmodule
: MModule): Int do
536 if in_hierarchy
(mainmodule
).direct_greaters
.is_empty
then
540 for p
in in_hierarchy
(mainmodule
).direct_greaters
do
541 if p
.kind
!= abstract_kind
and p
.kind
!= concrete_kind
and p
.kind
!= extern_kind
then continue
542 var d
= p
.ditc
(mainmodule
) + 1
543 if min
== -1 or d
< min
then
547 if min
== -1 then min
= 0
551 # Interface Depth in Inheritance Tree
553 # Following the longest path composed only of implements edges from self to Object
554 fun diti
(mainmodule
: MModule): Int do
555 if in_hierarchy
(mainmodule
).direct_greaters
.is_empty
then
559 for p
in in_hierarchy
(mainmodule
).direct_greaters
do
560 if p
.kind
!= interface_kind
then continue
561 var d
= p
.diti
(mainmodule
) + 1
562 if min
== -1 or d
< min
then
566 if min
== -1 then min
= 0