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
24 redef class ToolContext
26 # Inheritance related metrics phase
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 var model
= toolcontext
.modelbuilder
.model
41 var model_view
= model
.private_view
43 print toolcontext
.format_h1
("\n# Inheritance metrics")
45 var hmetrics
= new MetricSet
46 hmetrics
.register
(new MDUI(mainmodule
, model_view
))
47 hmetrics
.register
(new MDUIC(mainmodule
, model_view
))
48 hmetrics
.register
(new MDUII(mainmodule
, model_view
))
49 hmetrics
.register
(new MIF(mainmodule
, model_view
))
50 hmetrics
.register
(new MIFC(mainmodule
, model_view
))
51 hmetrics
.register
(new MIFI(mainmodule
, model_view
))
53 var cmetrics
= new MetricSet
54 cmetrics
.register
(new CNOAC(mainmodule
, model_view
))
55 cmetrics
.register
(new CNOPC(mainmodule
, model_view
))
56 cmetrics
.register
(new CNOCC(mainmodule
, model_view
))
57 cmetrics
.register
(new CNODC(mainmodule
, model_view
))
58 cmetrics
.register
(new CNOPI(mainmodule
, model_view
))
59 cmetrics
.register
(new CNOCI(mainmodule
, model_view
))
60 cmetrics
.register
(new CNODI(mainmodule
, model_view
))
61 cmetrics
.register
(new CDITC(mainmodule
, model_view
))
62 cmetrics
.register
(new CDITI(mainmodule
, model_view
))
64 var mmodules
= new HashSet[MModule]
65 var mclasses
= new HashSet[MClass]
66 for mpackage
in model
.mpackages
do
68 print toolcontext
.format_h2
("\n ## package {mpackage}")
70 for mgroup
in mpackage
.mgroups
do
71 if mgroup
.mmodules
.is_empty
then continue
74 print toolcontext
.format_h3
(" `- group {mgroup.full_name}")
76 var mod_mclasses
= new HashSet[MClass]
77 for mmodule
in mgroup
.mmodules
do mod_mclasses
.add_all
(mmodule
.intro_mclasses
)
78 if mod_mclasses
.is_empty
then continue
79 mmodules
.add_all
(mgroup
.mmodules
)
80 mclasses
.add_all
(mod_mclasses
)
82 cmetrics
.collect
(new HashSet[MClass].from
(mod_mclasses
))
83 cmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
84 if csv
then cmetrics
.to_csv
.write_to_file
("{out}/{mgroup}_classes.csv")
86 hmetrics
.collect
(new HashSet[MModule].from
(mgroup
.mmodules
))
87 hmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
88 if csv
then hmetrics
.to_csv
.write_to_file
("{out}/{mgroup}_inheritance.csv")
91 if not mclasses
.is_empty
then
93 print toolcontext
.format_h2
("\n ## global metrics")
95 cmetrics
.collect
(mclasses
)
96 cmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
97 if csv
then cmetrics
.to_csv
.write_to_file
("{out}/summary_classes.csv")
99 hmetrics
.collect
(mmodules
)
100 hmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
101 if csv
then hmetrics
.to_csv
.write_to_file
("{out}/summary_inheritance.csv")
106 # Module metric: proportion of MClasses Defined Using Inheritance
108 # Count MClasses that have another parents than Object
112 redef fun name
do return "mdui"
113 redef fun desc
do return "proportion of mclass defined using inheritance (has other parent than Object)"
115 redef fun collect
(mmodules
) do
116 for mmodule
in mmodules
do
118 for mclass
in mmodule
.intro_mclasses
do
119 if mclass
.in_hierarchy
(mainmodule
).greaters
.length
> 2 then count
+= 1
121 if mmodule
.intro_mclasses
.is_empty
then
122 values
[mmodule
] = 0.0
124 values
[mmodule
] = count
.to_f
/ mmodule
.intro_mclasses
.length
.to_f
130 # Module metric: proportion of abstract, concrete and extern Classes Defined Using Inheritance
132 # Count classes that have another parents than Object
136 redef fun name
do return "mduic"
137 redef fun desc
do return "proportion of class_kind defined using inheritance"
139 redef fun collect
(mmodules
) do
140 for mmodule
in mmodules
do
143 for mclass
in mmodule
.intro_mclasses
do
144 if mclass
.kind
== abstract_kind
or mclass
.kind
== concrete_kind
or mclass
.kind
== extern_kind
then
145 if mclass
.in_hierarchy
(mainmodule
).greaters
.length
> 2 then count
+= 1
149 if mmodule
.intro_mclasses
.is_empty
then
150 values
[mmodule
] = 0.0
152 values
[mmodule
] = count
.to_f
/ nb
.to_f
158 # Module metric: proportion of Interface Defined Using Inheritance
160 # Count interface that have another parents than Object
164 redef fun name
do return "mduii"
165 redef fun desc
do return "proportion of interface_kind defined using inheritance"
167 redef fun collect
(mmodules
) do
168 for mmodule
in mmodules
do
171 for mclass
in mmodule
.intro_mclasses
do
172 if mclass
.kind
== interface_kind
then
173 if mclass
.in_hierarchy
(mainmodule
).greaters
.length
> 2 then count
+= 1
177 if mmodule
.intro_mclasses
.is_empty
then
178 values
[mmodule
] = 0.0
180 values
[mmodule
] = count
.to_f
/ nb
.to_f
186 # Module metric: proportion of MClass Inherited From
188 # Count classes that have at least a child
192 redef fun name
do return "mif"
193 redef fun desc
do return "proportion of mclass inherited from"
195 redef fun collect
(mmodules
) do
196 for mmodule
in mmodules
do
198 for mclass
in mmodule
.intro_mclasses
do
199 if mclass
.in_hierarchy
(mainmodule
).direct_smallers
.length
> 0 then count
+= 1
201 if mmodule
.intro_mclasses
.is_empty
then
202 values
[mmodule
] = 0.0
204 values
[mmodule
] = count
.to_f
/ mmodule
.intro_mclasses
.length
.to_f
210 # Module metric: proportion of abstract, concrete and extern Class Inherited From
212 # Count classes that have at least a child
216 redef fun name
do return "mifc"
217 redef fun desc
do return "proportion of class_kind inherited from"
219 redef fun collect
(mmodules
) do
220 for mmodule
in mmodules
do
223 for mclass
in mmodule
.intro_mclasses
do
224 if mclass
.kind
== abstract_kind
or mclass
.kind
== concrete_kind
or mclass
.kind
== extern_kind
then
225 if mclass
.in_hierarchy
(mainmodule
).direct_smallers
.length
> 0 then count
+= 1
229 if mmodule
.intro_mclasses
.is_empty
then
230 values
[mmodule
] = 0.0
232 values
[mmodule
] = count
.to_f
/ nb
.to_f
238 # Module metric: proportion of Interface Inherited From
240 # Count interfaces that have at least a child
244 redef fun name
do return "mifi"
245 redef fun desc
do return "proportion of interface_kind inherited from"
247 redef fun collect
(mmodules
) do
248 for mmodule
in mmodules
do
251 for mclass
in mmodule
.intro_mclasses
do
252 if mclass
.kind
== interface_kind
then
253 if mclass
.in_hierarchy
(mainmodule
).direct_smallers
.length
> 0 then count
+= 1
257 if mmodule
.intro_mclasses
.is_empty
then
258 values
[mmodule
] = 0.0
260 values
[mmodule
] = count
.to_f
/ nb
.to_f
266 # MClass metric: Number of Class Ancestors
268 # Count only absrtract, concrete and extern classes
272 redef fun name
do return "cnoac"
273 redef fun desc
do return "number of class_kind ancestor"
275 redef fun collect
(mclasses
) do
276 for mclass
in mclasses
do
278 for parent
in mclass
.in_hierarchy
(mainmodule
).greaters
do
279 if parent
== mclass
then continue
280 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
284 values
[mclass
] = count
289 # MClass metric: Number of Class Parents
291 # Count only absrtract, concrete and extern classes
295 redef fun name
do return "cnopc"
296 redef fun desc
do return "number of class_kind parent"
298 redef fun collect
(mclasses
) do
299 for mclass
in mclasses
do
301 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_greaters
do
302 if parent
== mclass
then continue
303 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
307 values
[mclass
] = count
312 # MClass metric: Number of Class Children
314 # Count only absrtract, concrete and extern classes
318 redef fun name
do return "cnocc"
319 redef fun desc
do return "number of class_kind children"
321 redef fun collect
(mclasses
) do
322 for mclass
in mclasses
do
324 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_smallers
do
325 if parent
== mclass
then continue
326 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
330 values
[mclass
] = count
335 # MClass metric: Number of Class Descendants
337 # Count only absrtract, concrete and extern classes
341 redef fun name
do return "cnodc"
342 redef fun desc
do return "number of class_kind descendants"
344 redef fun collect
(mclasses
) do
345 for mclass
in mclasses
do
347 for parent
in mclass
.in_hierarchy
(mainmodule
).smallers
do
348 if parent
== mclass
then continue
349 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
353 values
[mclass
] = count
358 # MClass metric: Number of Abstract Class Ancestors
360 # Count only absrtract classes
364 redef fun name
do return "cnoaa"
365 redef fun desc
do return "number of abstract class ancestors"
367 redef fun collect
(mclasses
) do
368 for mclass
in mclasses
do
370 for parent
in mclass
.in_hierarchy
(mainmodule
).greaters
do
371 if parent
== mclass
then continue
372 if parent
.kind
== abstract_kind
then
376 values
[mclass
] = count
381 # MClass metric: Number of Interface Ancestors
383 # Count only interfaces
387 redef fun name
do return "cnoai"
388 redef fun desc
do return "number of interface_kind ancestor"
390 redef fun collect
(mclasses
) do
391 for mclass
in mclasses
do
393 for parent
in mclass
.in_hierarchy
(mainmodule
).greaters
do
394 if parent
== mclass
then continue
395 if parent
.kind
== interface_kind
then
399 values
[mclass
] = count
404 # MClass metric: Number of Interface Parents
406 # Count only interfaces
410 redef fun name
do return "cnopi"
411 redef fun desc
do return "number of interface_kind parent"
413 redef fun collect
(mclasses
) do
414 for mclass
in mclasses
do
416 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_greaters
do
417 if parent
== mclass
then continue
418 if parent
.kind
== interface_kind
then
422 values
[mclass
] = count
427 # MClass metric: Number of Interface Children
429 # Count only interfaces
433 redef fun name
do return "cnoci"
434 redef fun desc
do return "number of interface_kind children"
436 redef fun collect
(mclasses
) do
437 for mclass
in mclasses
do
439 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_smallers
do
440 if parent
== mclass
then continue
441 if parent
.kind
== interface_kind
then
445 values
[mclass
] = count
450 # MClass metric: Number of Interface Descendants
452 # Count only interfaces
456 redef fun name
do return "cnodi"
457 redef fun desc
do return "number of interface_kind descendants"
459 redef fun collect
(mclasses
) do
460 for mclass
in mclasses
do
462 for parent
in mclass
.in_hierarchy
(mainmodule
).smallers
do
463 if parent
== mclass
then continue
464 if parent
.kind
== interface_kind
then
468 values
[mclass
] = count
473 # MClass metric: Class Depth in Inheritance Tree
475 # Following the longest path composed only of extends edges from self to Object
479 redef fun name
do return "cditc"
480 redef fun desc
do return "depth in class tree following only class, abstract, extern kind"
482 redef fun collect
(mclasses
) do
483 for mclass
in mclasses
do
484 values
[mclass
] = mclass
.ditc
(mainmodule
)
489 # MClass metric: Interface Depth in Inheritance Tree
491 # Following the longest path composed only of implements edges from self to Object
495 redef fun name
do return "cditi"
496 redef fun desc
do return "depth in class tree following only interface_kind"
498 redef fun collect
(mclasses
) do
499 for mclass
in mclasses
do
500 values
[mclass
] = mclass
.diti
(mainmodule
)
509 # Class Depth in Inheritance Tree
511 # Following the longest path composed only of extends edges from self to Object
512 fun ditc
(mainmodule
: MModule): Int do
513 if in_hierarchy
(mainmodule
).direct_greaters
.is_empty
then
517 for p
in in_hierarchy
(mainmodule
).direct_greaters
do
518 if p
.kind
!= abstract_kind
and p
.kind
!= concrete_kind
and p
.kind
!= extern_kind
then continue
519 var d
= p
.ditc
(mainmodule
) + 1
520 if min
== -1 or d
< min
then
524 if min
== -1 then min
= 0
528 # Interface Depth in Inheritance Tree
530 # Following the longest path composed only of implements edges from self to Object
531 fun diti
(mainmodule
: MModule): Int do
532 if in_hierarchy
(mainmodule
).direct_greaters
.is_empty
then
536 for p
in in_hierarchy
(mainmodule
).direct_greaters
do
537 if p
.kind
!= interface_kind
then continue
538 var d
= p
.diti
(mainmodule
) + 1
539 if min
== -1 or d
< min
then
543 if min
== -1 then min
= 0