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