Merge: doc: fixed some typos and other misc. corrections
[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
26 # Inheritance related metrics phase
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 var model = toolcontext.modelbuilder.model
41 var filter = new ModelFilter(min_visibility = private_visibility)
42
43 print toolcontext.format_h1("\n# Inheritance metrics")
44
45 var hmetrics = new MetricSet
46 hmetrics.register(new MDUI(model, mainmodule))
47 hmetrics.register(new MDUIC(model, mainmodule))
48 hmetrics.register(new MDUII(model, mainmodule))
49 hmetrics.register(new MIF(model, mainmodule))
50 hmetrics.register(new MIFC(model, mainmodule))
51 hmetrics.register(new MIFI(model, mainmodule))
52
53 var cmetrics = new MetricSet
54 cmetrics.register(new CNOAC(model, mainmodule, filter))
55 cmetrics.register(new CNOPC(model, mainmodule, filter))
56 cmetrics.register(new CNOCC(model, mainmodule, filter))
57 cmetrics.register(new CNODC(model, mainmodule, filter))
58 cmetrics.register(new CNOPI(model, mainmodule, filter))
59 cmetrics.register(new CNOCI(model, mainmodule, filter))
60 cmetrics.register(new CNODI(model, mainmodule, filter))
61 cmetrics.register(new CDITC(model, mainmodule, filter))
62 cmetrics.register(new CDITI(model, mainmodule, filter))
63
64 var mmodules = new HashSet[MModule]
65 var mclasses = new HashSet[MClass]
66 for mpackage in model.mpackages do
67
68 print toolcontext.format_h2("\n ## package {mpackage}")
69
70 for mgroup in mpackage.mgroups do
71 if mgroup.mmodules.is_empty then continue
72
73 # Scalar metrics
74 print toolcontext.format_h3(" `- group {mgroup.full_name}")
75
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)
81 cmetrics.clear
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")
85 hmetrics.clear
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")
89 end
90 end
91 if not mclasses.is_empty then
92 # Global metrics
93 print toolcontext.format_h2("\n ## global metrics")
94 cmetrics.clear
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")
98 hmetrics.clear
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")
102 end
103 end
104 end
105
106 # Module metric: proportion of MClasses Defined Using Inheritance
107 #
108 # Count MClasses that have another parents than Object
109 class MDUI
110 super MModuleMetric
111 super FloatMetric
112 redef fun name do return "mdui"
113 redef fun desc do return "proportion of mclass defined using inheritance (has other parent than Object)"
114
115 redef fun collect(mmodules) do
116 for mmodule in mmodules do
117 var count = 0
118 for mclass in mmodule.intro_mclasses do
119 if mclass.in_hierarchy(mainmodule).greaters.length > 2 then count += 1
120 end
121 if mmodule.intro_mclasses.is_empty then
122 values[mmodule] = 0.0
123 else
124 values[mmodule] = count.to_f / mmodule.intro_mclasses.length.to_f
125 end
126 end
127 end
128 end
129
130 # Module metric: proportion of abstract, concrete and extern Classes Defined Using Inheritance
131 #
132 # Count classes that have another parents than Object
133 class MDUIC
134 super MModuleMetric
135 super FloatMetric
136 redef fun name do return "mduic"
137 redef fun desc do return "proportion of class_kind defined using inheritance"
138
139 redef fun collect(mmodules) do
140 for mmodule in mmodules do
141 var count = 0
142 var nb = 0
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
146 end
147 nb += 1
148 end
149 if mmodule.intro_mclasses.is_empty then
150 values[mmodule] = 0.0
151 else
152 values[mmodule] = count.to_f / nb.to_f
153 end
154 end
155 end
156 end
157
158 # Module metric: proportion of Interface Defined Using Inheritance
159 #
160 # Count interface that have another parents than Object
161 class MDUII
162 super MModuleMetric
163 super FloatMetric
164 redef fun name do return "mduii"
165 redef fun desc do return "proportion of interface_kind defined using inheritance"
166
167 redef fun collect(mmodules) do
168 for mmodule in mmodules do
169 var count = 0
170 var nb = 0
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
174 end
175 nb += 1
176 end
177 if mmodule.intro_mclasses.is_empty then
178 values[mmodule] = 0.0
179 else
180 values[mmodule] = count.to_f / nb.to_f
181 end
182 end
183 end
184 end
185
186 # Module metric: proportion of MClass Inherited From
187 #
188 # Count classes that have at least a child
189 class MIF
190 super MModuleMetric
191 super FloatMetric
192 redef fun name do return "mif"
193 redef fun desc do return "proportion of mclass inherited from"
194
195 redef fun collect(mmodules) do
196 for mmodule in mmodules do
197 var count = 0
198 for mclass in mmodule.intro_mclasses do
199 if mclass.in_hierarchy(mainmodule).direct_smallers.length > 0 then count += 1
200 end
201 if mmodule.intro_mclasses.is_empty then
202 values[mmodule] = 0.0
203 else
204 values[mmodule] = count.to_f / mmodule.intro_mclasses.length.to_f
205 end
206 end
207 end
208 end
209
210 # Module metric: proportion of abstract, concrete and extern Class Inherited From
211 #
212 # Count classes that have at least a child
213 class MIFC
214 super MModuleMetric
215 super FloatMetric
216 redef fun name do return "mifc"
217 redef fun desc do return "proportion of class_kind inherited from"
218
219 redef fun collect(mmodules) do
220 for mmodule in mmodules do
221 var count = 0
222 var nb = 0
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
226 end
227 nb += 1
228 end
229 if mmodule.intro_mclasses.is_empty then
230 values[mmodule] = 0.0
231 else
232 values[mmodule] = count.to_f / nb.to_f
233 end
234 end
235 end
236 end
237
238 # Module metric: proportion of Interface Inherited From
239 #
240 # Count interfaces that have at least a child
241 class MIFI
242 super MModuleMetric
243 super FloatMetric
244 redef fun name do return "mifi"
245 redef fun desc do return "proportion of interface_kind inherited from"
246
247 redef fun collect(mmodules) do
248 for mmodule in mmodules do
249 var count = 0
250 var nb = 0
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
254 end
255 nb += 1
256 end
257 if mmodule.intro_mclasses.is_empty then
258 values[mmodule] = 0.0
259 else
260 values[mmodule] = count.to_f / nb.to_f
261 end
262 end
263 end
264 end
265
266 # MClass metric: Number of Class Ancestors
267 #
268 # Count only absrtract, concrete and extern classes
269 class CNOAC
270 super MClassMetric
271 super IntMetric
272 redef fun name do return "cnoac"
273 redef fun desc do return "number of class_kind ancestor"
274
275 redef fun collect(mclasses) do
276 for mclass in mclasses do
277 var count = 0
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
281 count += 1
282 end
283 end
284 values[mclass] = count
285 end
286 end
287 end
288
289 # MClass metric: Number of Class Parents
290 #
291 # Count only absrtract, concrete and extern classes
292 class CNOPC
293 super MClassMetric
294 super IntMetric
295 redef fun name do return "cnopc"
296 redef fun desc do return "number of class_kind parent"
297
298 redef fun collect(mclasses) do
299 for mclass in mclasses do
300 var count = 0
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
304 count += 1
305 end
306 end
307 values[mclass] = count
308 end
309 end
310 end
311
312 # MClass metric: Number of Class Children
313 #
314 # Count only absrtract, concrete and extern classes
315 class CNOCC
316 super MClassMetric
317 super IntMetric
318 redef fun name do return "cnocc"
319 redef fun desc do return "number of class_kind children"
320
321 redef fun collect(mclasses) do
322 for mclass in mclasses do
323 var count = 0
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
327 count += 1
328 end
329 end
330 values[mclass] = count
331 end
332 end
333 end
334
335 # MClass metric: Number of Class Descendants
336 #
337 # Count only absrtract, concrete and extern classes
338 class CNODC
339 super MClassMetric
340 super IntMetric
341 redef fun name do return "cnodc"
342 redef fun desc do return "number of class_kind descendants"
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).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 Abstract Class Ancestors
359 #
360 # Count only absrtract classes
361 class CNOAA
362 super MClassMetric
363 super IntMetric
364 redef fun name do return "cnoaa"
365 redef fun desc do return "number of abstract class ancestors"
366
367 redef fun collect(mclasses) do
368 for mclass in mclasses do
369 var count = 0
370 for parent in mclass.in_hierarchy(mainmodule).greaters do
371 if parent == mclass then continue
372 if parent.kind == abstract_kind then
373 count += 1
374 end
375 end
376 values[mclass] = count
377 end
378 end
379 end
380
381 # MClass metric: Number of Interface Ancestors
382 #
383 # Count only interfaces
384 class CNOAI
385 super MClassMetric
386 super IntMetric
387 redef fun name do return "cnoai"
388 redef fun desc do return "number of interface_kind ancestor"
389
390 redef fun collect(mclasses) do
391 for mclass in mclasses do
392 var count = 0
393 for parent in mclass.in_hierarchy(mainmodule).greaters do
394 if parent == mclass then continue
395 if parent.kind == interface_kind then
396 count += 1
397 end
398 end
399 values[mclass] = count
400 end
401 end
402 end
403
404 # MClass metric: Number of Interface Parents
405 #
406 # Count only interfaces
407 class CNOPI
408 super MClassMetric
409 super IntMetric
410 redef fun name do return "cnopi"
411 redef fun desc do return "number of interface_kind parent"
412
413 redef fun collect(mclasses) do
414 for mclass in mclasses do
415 var count = 0
416 for parent in mclass.in_hierarchy(mainmodule).direct_greaters do
417 if parent == mclass then continue
418 if parent.kind == interface_kind then
419 count += 1
420 end
421 end
422 values[mclass] = count
423 end
424 end
425 end
426
427 # MClass metric: Number of Interface Children
428 #
429 # Count only interfaces
430 class CNOCI
431 super MClassMetric
432 super IntMetric
433 redef fun name do return "cnoci"
434 redef fun desc do return "number of interface_kind children"
435
436 redef fun collect(mclasses) do
437 for mclass in mclasses do
438 var count = 0
439 for parent in mclass.in_hierarchy(mainmodule).direct_smallers do
440 if parent == mclass then continue
441 if parent.kind == interface_kind then
442 count += 1
443 end
444 end
445 values[mclass] = count
446 end
447 end
448 end
449
450 # MClass metric: Number of Interface Descendants
451 #
452 # Count only interfaces
453 class CNODI
454 super MClassMetric
455 super IntMetric
456 redef fun name do return "cnodi"
457 redef fun desc do return "number of interface_kind descendants"
458
459 redef fun collect(mclasses) do
460 for mclass in mclasses do
461 var count = 0
462 for parent in mclass.in_hierarchy(mainmodule).smallers do
463 if parent == mclass then continue
464 if parent.kind == interface_kind then
465 count += 1
466 end
467 end
468 values[mclass] = count
469 end
470 end
471 end
472
473 # MClass metric: Class Depth in Inheritance Tree
474 #
475 # Following the longest path composed only of extends edges from self to Object
476 class CDITC
477 super MClassMetric
478 super IntMetric
479 redef fun name do return "cditc"
480 redef fun desc do return "depth in class tree following only class, abstract, extern kind"
481
482 redef fun collect(mclasses) do
483 for mclass in mclasses do
484 values[mclass] = mclass.ditc(mainmodule)
485 end
486 end
487 end
488
489 # MClass metric: Interface Depth in Inheritance Tree
490 #
491 # Following the longest path composed only of implements edges from self to Object
492 class CDITI
493 super MClassMetric
494 super IntMetric
495 redef fun name do return "cditi"
496 redef fun desc do return "depth in class tree following only interface_kind"
497
498 redef fun collect(mclasses) do
499 for mclass in mclasses do
500 values[mclass] = mclass.diti(mainmodule)
501 end
502 end
503 end
504
505 # model redef
506
507 redef class MClass
508
509 # Class Depth in Inheritance Tree
510 #
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
514 return 0
515 end
516 var min = -1
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
521 min = d
522 end
523 end
524 if min == -1 then min = 0
525 return min
526 end
527
528 # Interface Depth in Inheritance Tree
529 #
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
533 return 0
534 end
535 var min = -1
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
540 min = d
541 end
542 end
543 if min == -1 then min = 0
544 return min
545 end
546 end