modelbuilder: `resolve_mtype` require a MClassDef instead of a AClassdef
[nit.git] / src / metrics / inheritance_metrics.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Alexandre Terrasa <alexandre@moz-code.org>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # Collect metrics about inheritance usage
18 module inheritance_metrics
19
20 import model
21 import mmodules_metrics
22 import mclasses_metrics
23 import phase
24 import frontend
25
26 redef class ToolContext
27 var inheritance_metrics_phase: Phase = new InheritanceMetricsPhase(self, null)
28 end
29
30 # Extract metrics about inheritance from model.
31 private class InheritanceMetricsPhase
32 super Phase
33 redef fun process_mainmodule(mainmodule, given_mmodules)
34 do
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"
38 out.mkdir
39
40 print toolcontext.format_h1("\n# Inheritance metrics")
41
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))
49
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))
60
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
65
66 print toolcontext.format_h2("\n ## project {mproject}")
67
68 for mgroup in mproject.mgroups do
69 if mgroup.mmodules.is_empty then continue
70
71 # Scalar metrics
72 print toolcontext.format_h3(" `- group {mgroup.full_name}")
73
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)
79 cmetrics.clear
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")
83 hmetrics.clear
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")
87 end
88 end
89 if not mclasses.is_empty then
90 # Global metrics
91 print toolcontext.format_h2("\n ## global metrics")
92 cmetrics.clear
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")
96 hmetrics.clear
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")
100 end
101 end
102 end
103
104 # Module metric: proportion of MClasses Defined Using Inheritance
105 #
106 # Count MClasses that have another parents than Object
107 class MDUI
108 super MModuleMetric
109 super FloatMetric
110 redef fun name do return "mdui"
111 redef fun desc do return "proportion of mclass defined using inheritance (has other parent than Object)"
112
113 var mainmodule: MModule
114 init(mainmodule: MModule) do self.mainmodule = mainmodule
115
116 redef fun collect(mmodules) do
117 for mmodule in mmodules do
118 var count = 0
119 for mclass in mmodule.intro_mclasses do
120 if mclass.in_hierarchy(mainmodule).greaters.length > 2 then count += 1
121 end
122 if mmodule.intro_mclasses.is_empty then
123 values[mmodule] = 0.0
124 else
125 values[mmodule] = count.to_f / mmodule.intro_mclasses.length.to_f
126 end
127 end
128 end
129 end
130
131 # Module metric: proportion of abstract, concrete and extern Classes Defined Using Inheritance
132 #
133 # Count classes that have another parents than Object
134 class MDUIC
135 super MModuleMetric
136 super FloatMetric
137 redef fun name do return "mduic"
138 redef fun desc do return "proportion of class_kind defined using inheritance"
139
140 var mainmodule: MModule
141 init(mainmodule: MModule) do self.mainmodule = mainmodule
142
143 redef fun collect(mmodules) do
144 for mmodule in mmodules do
145 var count = 0
146 var nb = 0
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
150 end
151 nb += 1
152 end
153 if mmodule.intro_mclasses.is_empty then
154 values[mmodule] = 0.0
155 else
156 values[mmodule] = count.to_f / nb.to_f
157 end
158 end
159 end
160 end
161
162 # Module metric: proportion of Interface Defined Using Inheritance
163 #
164 # Count interface that have another parents than Object
165 class MDUII
166 super MModuleMetric
167 super FloatMetric
168 redef fun name do return "mduii"
169 redef fun desc do return "proportion of interface_kind defined using inheritance"
170
171 var mainmodule: MModule
172 init(mainmodule: MModule) do self.mainmodule = mainmodule
173
174 redef fun collect(mmodules) do
175 for mmodule in mmodules do
176 var count = 0
177 var nb = 0
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
181 end
182 nb += 1
183 end
184 if mmodule.intro_mclasses.is_empty then
185 values[mmodule] = 0.0
186 else
187 values[mmodule] = count.to_f / nb.to_f
188 end
189 end
190 end
191 end
192
193 # Module metric: proportion of MClass Inherited From
194 #
195 # Count classes that have at least a child
196 class MIF
197 super MModuleMetric
198 super FloatMetric
199 redef fun name do return "mif"
200 redef fun desc do return "proportion of mclass inherited from"
201
202 var mainmodule: MModule
203 init(mainmodule: MModule) do self.mainmodule = mainmodule
204
205 redef fun collect(mmodules) do
206 for mmodule in mmodules do
207 var count = 0
208 for mclass in mmodule.intro_mclasses do
209 if mclass.in_hierarchy(mainmodule).direct_smallers.length > 0 then count += 1
210 end
211 if mmodule.intro_mclasses.is_empty then
212 values[mmodule] = 0.0
213 else
214 values[mmodule] = count.to_f / mmodule.intro_mclasses.length.to_f
215 end
216 end
217 end
218 end
219
220 # Module metric: proportion of abstract, concrete and extern Class Inherited From
221 #
222 # Count classes that have at least a child
223 class MIFC
224 super MModuleMetric
225 super FloatMetric
226 redef fun name do return "mifc"
227 redef fun desc do return "proportion of class_kind inherited from"
228
229 var mainmodule: MModule
230 init(mainmodule: MModule) do self.mainmodule = mainmodule
231
232 redef fun collect(mmodules) do
233 for mmodule in mmodules do
234 var count = 0
235 var nb = 0
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
239 end
240 nb += 1
241 end
242 if mmodule.intro_mclasses.is_empty then
243 values[mmodule] = 0.0
244 else
245 values[mmodule] = count.to_f / nb.to_f
246 end
247 end
248 end
249 end
250
251 # Module metric: proportion of Interface Inherited From
252 #
253 # Count interfaces that have at least a child
254 class MIFI
255 super MModuleMetric
256 super FloatMetric
257 redef fun name do return "mifi"
258 redef fun desc do return "proportion of interface_kind inherited from"
259
260 var mainmodule: MModule
261 init(mainmodule: MModule) do self.mainmodule = mainmodule
262
263 redef fun collect(mmodules) do
264 for mmodule in mmodules do
265 var count = 0
266 var nb = 0
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
270 end
271 nb += 1
272 end
273 if mmodule.intro_mclasses.is_empty then
274 values[mmodule] = 0.0
275 else
276 values[mmodule] = count.to_f / nb.to_f
277 end
278 end
279 end
280 end
281
282 # MClass metric: Number of Class Ancestors
283 #
284 # Count only absrtract, concrete and extern classes
285 class CNOAC
286 super MClassMetric
287 super IntMetric
288 redef fun name do return "cnoac"
289 redef fun desc do return "number of class_kind ancestor"
290
291 var mainmodule: MModule
292 init(mainmodule: MModule) do self.mainmodule = mainmodule
293
294 redef fun collect(mclasses) do
295 for mclass in mclasses do
296 var count = 0
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
300 count += 1
301 end
302 end
303 values[mclass] = count
304 end
305 end
306 end
307
308 # MClass metric: Number of Class Parents
309 #
310 # Count only absrtract, concrete and extern classes
311 class CNOPC
312 super MClassMetric
313 super IntMetric
314 redef fun name do return "cnopc"
315 redef fun desc do return "number of class_kind parent"
316
317 var mainmodule: MModule
318 init(mainmodule: MModule) do self.mainmodule = mainmodule
319
320 redef fun collect(mclasses) do
321 for mclass in mclasses do
322 var count = 0
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
326 count += 1
327 end
328 end
329 values[mclass] = count
330 end
331 end
332 end
333
334 # MClass metric: Number of Class Children
335 #
336 # Count only absrtract, concrete and extern classes
337 class CNOCC
338 super MClassMetric
339 super IntMetric
340 redef fun name do return "cnocc"
341 redef fun desc do return "number of class_kind children"
342
343 var mainmodule: MModule
344 init(mainmodule: MModule) do self.mainmodule = mainmodule
345
346 redef fun collect(mclasses) do
347 for mclass in mclasses do
348 var count = 0
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
352 count += 1
353 end
354 end
355 values[mclass] = count
356 end
357 end
358 end
359
360 # MClass metric: Number of Class Descendants
361 #
362 # Count only absrtract, concrete and extern classes
363 class CNODC
364 super MClassMetric
365 super IntMetric
366 redef fun name do return "cnodc"
367 redef fun desc do return "number of class_kind descendants"
368
369 var mainmodule: MModule
370 init(mainmodule: MModule) do self.mainmodule = mainmodule
371
372 redef fun collect(mclasses) do
373 for mclass in mclasses do
374 var count = 0
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
378 count += 1
379 end
380 end
381 values[mclass] = count
382 end
383 end
384 end
385
386 # MClass metric: Number of Interface Ancestors
387 #
388 # Count only interfaces
389 class CNOAI
390 super MClassMetric
391 super IntMetric
392 redef fun name do return "cnoai"
393 redef fun desc do return "number of interface_kind ancestor"
394
395 var mainmodule: MModule
396 init(mainmodule: MModule) do self.mainmodule = mainmodule
397
398 redef fun collect(mclasses) do
399 for mclass in mclasses do
400 var count = 0
401 for parent in mclass.in_hierarchy(mainmodule).greaters do
402 if parent == mclass then continue
403 if parent.kind == interface_kind then
404 count += 1
405 end
406 end
407 values[mclass] = count
408 end
409 end
410 end
411
412 # MClass metric: Number of Interface Parents
413 #
414 # Count only interfaces
415 class CNOPI
416 super MClassMetric
417 super IntMetric
418 redef fun name do return "cnopi"
419 redef fun desc do return "number of interface_kind parent"
420
421 var mainmodule: MModule
422 init(mainmodule: MModule) do self.mainmodule = mainmodule
423
424 redef fun collect(mclasses) do
425 for mclass in mclasses do
426 var count = 0
427 for parent in mclass.in_hierarchy(mainmodule).direct_greaters do
428 if parent == mclass then continue
429 if parent.kind == interface_kind then
430 count += 1
431 end
432 end
433 values[mclass] = count
434 end
435 end
436 end
437
438 # MClass metric: Number of Interface Children
439 #
440 # Count only interfaces
441 class CNOCI
442 super MClassMetric
443 super IntMetric
444 redef fun name do return "cnoci"
445 redef fun desc do return "number of interface_kind children"
446
447 var mainmodule: MModule
448 init(mainmodule: MModule) do self.mainmodule = mainmodule
449
450 redef fun collect(mclasses) do
451 for mclass in mclasses do
452 var count = 0
453 for parent in mclass.in_hierarchy(mainmodule).direct_smallers do
454 if parent == mclass then continue
455 if parent.kind == interface_kind then
456 count += 1
457 end
458 end
459 values[mclass] = count
460 end
461 end
462 end
463
464 # MClass metric: Number of Interface Descendants
465 #
466 # Count only interfaces
467 class CNODI
468 super MClassMetric
469 super IntMetric
470 redef fun name do return "cnodi"
471 redef fun desc do return "number of interface_kind descendants"
472
473 var mainmodule: MModule
474 init(mainmodule: MModule) do self.mainmodule = mainmodule
475
476 redef fun collect(mclasses) do
477 for mclass in mclasses do
478 var count = 0
479 for parent in mclass.in_hierarchy(mainmodule).smallers do
480 if parent == mclass then continue
481 if parent.kind == interface_kind then
482 count += 1
483 end
484 end
485 values[mclass] = count
486 end
487 end
488 end
489
490 # MClass metric: Class Depth in Inheritance Tree
491 #
492 # Following the longest path composed only of extends edges from self to Object
493 class CDITC
494 super MClassMetric
495 super IntMetric
496 redef fun name do return "cditc"
497 redef fun desc do return "depth in class tree following only class, abstract, extern kind"
498
499 var mainmodule: MModule
500 init(mainmodule: MModule) do self.mainmodule = mainmodule
501
502 redef fun collect(mclasses) do
503 for mclass in mclasses do
504 values[mclass] = mclass.ditc(mainmodule)
505 end
506 end
507 end
508
509 # MClass metric: Interface Depth in Inheritance Tree
510 #
511 # Following the longest path composed only of implements edges from self to Object
512 class CDITI
513 super MClassMetric
514 super IntMetric
515 redef fun name do return "cditi"
516 redef fun desc do return "depth in class tree following only interface_kind"
517
518 var mainmodule: MModule
519 init(mainmodule: MModule) do self.mainmodule = mainmodule
520
521 redef fun collect(mclasses) do
522 for mclass in mclasses do
523 values[mclass] = mclass.diti(mainmodule)
524 end
525 end
526 end
527
528 # model redef
529
530 redef class MClass
531
532 # Class Depth in Inheritance Tree
533 #
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
537 return 0
538 end
539 var min = -1
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
544 min = d
545 end
546 end
547 if min == -1 then min = 0
548 return min
549 end
550
551 # Interface Depth in Inheritance Tree
552 #
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
556 return 0
557 end
558 var min = -1
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
563 min = d
564 end
565 end
566 if min == -1 then min = 0
567 return min
568 end
569 end
570