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
25 var inheritance_metrics_phase
: Phase = new InheritanceMetricsPhase(self, null)
28 # Extract metrics about inheritance from model.
29 private class InheritanceMetricsPhase
31 redef fun process_mainmodule
(mainmodule
, given_mmodules
)
33 if not toolcontext
.opt_inheritance
.value
and not toolcontext
.opt_all
.value
then return
34 var csv
= toolcontext
.opt_csv
.value
35 var out
= "{toolcontext.opt_dir.value or else "metrics"}/inheritance"
38 print toolcontext
.format_h1
("\n# Inheritance metrics")
40 var hmetrics
= new MetricSet
41 hmetrics
.register
(new MDUI(mainmodule
))
42 hmetrics
.register
(new MDUIC(mainmodule
))
43 hmetrics
.register
(new MDUII(mainmodule
))
44 hmetrics
.register
(new MIF(mainmodule
))
45 hmetrics
.register
(new MIFC(mainmodule
))
46 hmetrics
.register
(new MIFI(mainmodule
))
48 var cmetrics
= new MetricSet
49 cmetrics
.register
(new CNOAC(mainmodule
))
50 cmetrics
.register
(new CNOPC(mainmodule
))
51 cmetrics
.register
(new CNOCC(mainmodule
))
52 cmetrics
.register
(new CNODC(mainmodule
))
53 cmetrics
.register
(new CNOPI(mainmodule
))
54 cmetrics
.register
(new CNOCI(mainmodule
))
55 cmetrics
.register
(new CNODI(mainmodule
))
56 cmetrics
.register
(new CDITC(mainmodule
))
57 cmetrics
.register
(new CDITI(mainmodule
))
59 var model
= toolcontext
.modelbuilder
.model
60 var mmodules
= new HashSet[MModule]
61 var mclasses
= new HashSet[MClass]
62 for mpackage
in model
.mpackages
do
64 print toolcontext
.format_h2
("\n ## package {mpackage}")
66 for mgroup
in mpackage
.mgroups
do
67 if mgroup
.mmodules
.is_empty
then continue
70 print toolcontext
.format_h3
(" `- group {mgroup.full_name}")
72 var mod_mclasses
= new HashSet[MClass]
73 for mmodule
in mgroup
.mmodules
do mod_mclasses
.add_all
(mmodule
.intro_mclasses
)
74 if mod_mclasses
.is_empty
then continue
75 mmodules
.add_all
(mgroup
.mmodules
)
76 mclasses
.add_all
(mod_mclasses
)
78 cmetrics
.collect
(new HashSet[MClass].from
(mod_mclasses
))
79 cmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
80 if csv
then cmetrics
.to_csv
.write_to_file
("{out}/{mgroup}_classes.csv")
82 hmetrics
.collect
(new HashSet[MModule].from
(mgroup
.mmodules
))
83 hmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
84 if csv
then hmetrics
.to_csv
.write_to_file
("{out}/{mgroup}_inheritance.csv")
87 if not mclasses
.is_empty
then
89 print toolcontext
.format_h2
("\n ## global metrics")
91 cmetrics
.collect
(mclasses
)
92 cmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
93 if csv
then cmetrics
.to_csv
.write_to_file
("{out}/summary_classes.csv")
95 hmetrics
.collect
(mmodules
)
96 hmetrics
.to_console
(1, not toolcontext
.opt_nocolors
.value
)
97 if csv
then hmetrics
.to_csv
.write_to_file
("{out}/summary_inheritance.csv")
102 # Module metric: proportion of MClasses Defined Using Inheritance
104 # Count MClasses that have another parents than Object
108 redef fun name
do return "mdui"
109 redef fun desc
do return "proportion of mclass defined using inheritance (has other parent than Object)"
111 var mainmodule
: MModule
112 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
114 redef fun collect
(mmodules
) do
115 for mmodule
in mmodules
do
117 for mclass
in mmodule
.intro_mclasses
do
118 if mclass
.in_hierarchy
(mainmodule
).greaters
.length
> 2 then count
+= 1
120 if mmodule
.intro_mclasses
.is_empty
then
121 values
[mmodule
] = 0.0
123 values
[mmodule
] = count
.to_f
/ mmodule
.intro_mclasses
.length
.to_f
129 # Module metric: proportion of abstract, concrete and extern Classes Defined Using Inheritance
131 # Count classes that have another parents than Object
135 redef fun name
do return "mduic"
136 redef fun desc
do return "proportion of class_kind defined using inheritance"
138 var mainmodule
: MModule
139 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
141 redef fun collect
(mmodules
) do
142 for mmodule
in mmodules
do
145 for mclass
in mmodule
.intro_mclasses
do
146 if mclass
.kind
== abstract_kind
or mclass
.kind
== concrete_kind
or mclass
.kind
== extern_kind
then
147 if mclass
.in_hierarchy
(mainmodule
).greaters
.length
> 2 then count
+= 1
151 if mmodule
.intro_mclasses
.is_empty
then
152 values
[mmodule
] = 0.0
154 values
[mmodule
] = count
.to_f
/ nb
.to_f
160 # Module metric: proportion of Interface Defined Using Inheritance
162 # Count interface that have another parents than Object
166 redef fun name
do return "mduii"
167 redef fun desc
do return "proportion of interface_kind defined using inheritance"
169 var mainmodule
: MModule
170 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
172 redef fun collect
(mmodules
) do
173 for mmodule
in mmodules
do
176 for mclass
in mmodule
.intro_mclasses
do
177 if mclass
.kind
== interface_kind
then
178 if mclass
.in_hierarchy
(mainmodule
).greaters
.length
> 2 then count
+= 1
182 if mmodule
.intro_mclasses
.is_empty
then
183 values
[mmodule
] = 0.0
185 values
[mmodule
] = count
.to_f
/ nb
.to_f
191 # Module metric: proportion of MClass Inherited From
193 # Count classes that have at least a child
197 redef fun name
do return "mif"
198 redef fun desc
do return "proportion of mclass inherited from"
200 var mainmodule
: MModule
201 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
203 redef fun collect
(mmodules
) do
204 for mmodule
in mmodules
do
206 for mclass
in mmodule
.intro_mclasses
do
207 if mclass
.in_hierarchy
(mainmodule
).direct_smallers
.length
> 0 then count
+= 1
209 if mmodule
.intro_mclasses
.is_empty
then
210 values
[mmodule
] = 0.0
212 values
[mmodule
] = count
.to_f
/ mmodule
.intro_mclasses
.length
.to_f
218 # Module metric: proportion of abstract, concrete and extern Class Inherited From
220 # Count classes that have at least a child
224 redef fun name
do return "mifc"
225 redef fun desc
do return "proportion of class_kind inherited from"
227 var mainmodule
: MModule
228 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
230 redef fun collect
(mmodules
) do
231 for mmodule
in mmodules
do
234 for mclass
in mmodule
.intro_mclasses
do
235 if mclass
.kind
== abstract_kind
or mclass
.kind
== concrete_kind
or mclass
.kind
== extern_kind
then
236 if mclass
.in_hierarchy
(mainmodule
).direct_smallers
.length
> 0 then count
+= 1
240 if mmodule
.intro_mclasses
.is_empty
then
241 values
[mmodule
] = 0.0
243 values
[mmodule
] = count
.to_f
/ nb
.to_f
249 # Module metric: proportion of Interface Inherited From
251 # Count interfaces that have at least a child
255 redef fun name
do return "mifi"
256 redef fun desc
do return "proportion of interface_kind inherited from"
258 var mainmodule
: MModule
259 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
261 redef fun collect
(mmodules
) do
262 for mmodule
in mmodules
do
265 for mclass
in mmodule
.intro_mclasses
do
266 if mclass
.kind
== interface_kind
then
267 if mclass
.in_hierarchy
(mainmodule
).direct_smallers
.length
> 0 then count
+= 1
271 if mmodule
.intro_mclasses
.is_empty
then
272 values
[mmodule
] = 0.0
274 values
[mmodule
] = count
.to_f
/ nb
.to_f
280 # MClass metric: Number of Class Ancestors
282 # Count only absrtract, concrete and extern classes
286 redef fun name
do return "cnoac"
287 redef fun desc
do return "number of class_kind ancestor"
289 var mainmodule
: MModule
290 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
292 redef fun collect
(mclasses
) do
293 for mclass
in mclasses
do
295 for parent
in mclass
.in_hierarchy
(mainmodule
).greaters
do
296 if parent
== mclass
then continue
297 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
301 values
[mclass
] = count
306 # MClass metric: Number of Class Parents
308 # Count only absrtract, concrete and extern classes
312 redef fun name
do return "cnopc"
313 redef fun desc
do return "number of class_kind parent"
315 var mainmodule
: MModule
316 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
318 redef fun collect
(mclasses
) do
319 for mclass
in mclasses
do
321 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_greaters
do
322 if parent
== mclass
then continue
323 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
327 values
[mclass
] = count
332 # MClass metric: Number of Class Children
334 # Count only absrtract, concrete and extern classes
338 redef fun name
do return "cnocc"
339 redef fun desc
do return "number of class_kind children"
341 var mainmodule
: MModule
342 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
344 redef fun collect
(mclasses
) do
345 for mclass
in mclasses
do
347 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_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 Class Descendants
360 # Count only absrtract, concrete and extern classes
364 redef fun name
do return "cnodc"
365 redef fun desc
do return "number of class_kind descendants"
367 var mainmodule
: MModule
368 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
370 redef fun collect
(mclasses
) do
371 for mclass
in mclasses
do
373 for parent
in mclass
.in_hierarchy
(mainmodule
).smallers
do
374 if parent
== mclass
then continue
375 if parent
.kind
== abstract_kind
or parent
.kind
== concrete_kind
or parent
.kind
== extern_kind
then
379 values
[mclass
] = count
384 # MClass metric: Number of Interface Ancestors
386 # Count only interfaces
390 redef fun name
do return "cnoai"
391 redef fun desc
do return "number of interface_kind ancestor"
393 var mainmodule
: MModule
394 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
396 redef fun collect
(mclasses
) do
397 for mclass
in mclasses
do
399 for parent
in mclass
.in_hierarchy
(mainmodule
).greaters
do
400 if parent
== mclass
then continue
401 if parent
.kind
== interface_kind
then
405 values
[mclass
] = count
410 # MClass metric: Number of Interface Parents
412 # Count only interfaces
416 redef fun name
do return "cnopi"
417 redef fun desc
do return "number of interface_kind parent"
419 var mainmodule
: MModule
420 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
422 redef fun collect
(mclasses
) do
423 for mclass
in mclasses
do
425 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_greaters
do
426 if parent
== mclass
then continue
427 if parent
.kind
== interface_kind
then
431 values
[mclass
] = count
436 # MClass metric: Number of Interface Children
438 # Count only interfaces
442 redef fun name
do return "cnoci"
443 redef fun desc
do return "number of interface_kind children"
445 var mainmodule
: MModule
446 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
448 redef fun collect
(mclasses
) do
449 for mclass
in mclasses
do
451 for parent
in mclass
.in_hierarchy
(mainmodule
).direct_smallers
do
452 if parent
== mclass
then continue
453 if parent
.kind
== interface_kind
then
457 values
[mclass
] = count
462 # MClass metric: Number of Interface Descendants
464 # Count only interfaces
468 redef fun name
do return "cnodi"
469 redef fun desc
do return "number of interface_kind descendants"
471 var mainmodule
: MModule
472 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
474 redef fun collect
(mclasses
) do
475 for mclass
in mclasses
do
477 for parent
in mclass
.in_hierarchy
(mainmodule
).smallers
do
478 if parent
== mclass
then continue
479 if parent
.kind
== interface_kind
then
483 values
[mclass
] = count
488 # MClass metric: Class Depth in Inheritance Tree
490 # Following the longest path composed only of extends edges from self to Object
494 redef fun name
do return "cditc"
495 redef fun desc
do return "depth in class tree following only class, abstract, extern kind"
497 var mainmodule
: MModule
498 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
500 redef fun collect
(mclasses
) do
501 for mclass
in mclasses
do
502 values
[mclass
] = mclass
.ditc
(mainmodule
)
507 # MClass metric: Interface Depth in Inheritance Tree
509 # Following the longest path composed only of implements edges from self to Object
513 redef fun name
do return "cditi"
514 redef fun desc
do return "depth in class tree following only interface_kind"
516 var mainmodule
: MModule
517 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
519 redef fun collect
(mclasses
) do
520 for mclass
in mclasses
do
521 values
[mclass
] = mclass
.diti
(mainmodule
)
530 # Class Depth in Inheritance Tree
532 # Following the longest path composed only of extends edges from self to Object
533 fun ditc
(mainmodule
: MModule): Int do
534 if in_hierarchy
(mainmodule
).direct_greaters
.is_empty
then
538 for p
in in_hierarchy
(mainmodule
).direct_greaters
do
539 if p
.kind
!= abstract_kind
and p
.kind
!= concrete_kind
and p
.kind
!= extern_kind
then continue
540 var d
= p
.ditc
(mainmodule
) + 1
541 if min
== -1 or d
< min
then
545 if min
== -1 then min
= 0
549 # Interface Depth in Inheritance Tree
551 # Following the longest path composed only of implements edges from self to Object
552 fun diti
(mainmodule
: MModule): Int do
553 if in_hierarchy
(mainmodule
).direct_greaters
.is_empty
then
557 for p
in in_hierarchy
(mainmodule
).direct_greaters
do
558 if p
.kind
!= interface_kind
then continue
559 var d
= p
.diti
(mainmodule
) + 1
560 if min
== -1 or d
< min
then
564 if min
== -1 then min
= 0