Merge remote-tracking branch 'alexandre/nitmetrics'
[nit.git] / src / metrics / mmodules_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 common metrics about modules
18 module mmodules_metrics
19
20 import model
21 import metrics_base
22 import phase
23 import frontend
24
25 redef class ToolContext
26 var mmodules_metrics_phase = new MModulesMetricsPhase(self, null)
27 end
28
29 # Extract metrics about modules from the model.
30 private class MModulesMetricsPhase
31 super Phase
32 redef fun process_mainmodule(mainmodule)
33 do
34 if not toolcontext.opt_mmodules.value and not toolcontext.opt_all.value then return
35
36 print toolcontext.format_h1("\n# MModules metrics")
37
38 var metrics = new MModuleMetricSet
39 metrics.register(new MNOA, new MNOP, new MNOC, new MNOD, new MDIT)
40 metrics.register(new MNBI, new MNBR, new MNBCC, new MNBAC, new MNBIC)
41
42 var model = toolcontext.modelbuilder.model
43 var mmodules = new HashSet[MModule]
44 for mproject in model.mprojects do
45
46 print toolcontext.format_h2("\n ## project {mproject}")
47
48 for mgroup in mproject.mgroups do
49 if mgroup.mmodules.is_empty then continue
50
51 # Scalar metrics
52 print toolcontext.format_h3(" `- group {mgroup.full_name}")
53
54 mmodules.add_all(mgroup.mmodules)
55 metrics.collect(new HashSet[MModule].from(mgroup.mmodules), mainmodule)
56 for name, metric in metrics.metrics do
57 print toolcontext.format_h4("\t{name}: {metric.desc}")
58 print toolcontext.format_p("\t avg: {metric.avg}")
59 var max = metric.max
60 print toolcontext.format_p("\t max: {max.first} ({max.second})")
61 var min = metric.min
62 print toolcontext.format_p("\t min: {min.first} ({min.second})")
63 end
64 end
65 end
66 if not mmodules.is_empty then
67 # Global metrics
68 print toolcontext.format_h2("\n ## global metrics")
69
70 metrics.collect(mmodules, mainmodule)
71 for name, metric in metrics.metrics do
72 print toolcontext.format_h4( "\t{name}: {metric.desc}")
73 print toolcontext.format_p("\t avg: {metric.avg}")
74 var max = metric.max
75 print toolcontext.format_p("\t max: {max.first} ({max.second})")
76 var min = metric.min
77 print toolcontext.format_p("\t min: {min.first} ({min.second})")
78 end
79 end
80 end
81 end
82
83 # A MetricSet for metrics about MModules
84 class MModuleMetricSet
85 super MetricSet
86 redef type METRIC: MModuleMetric
87
88 # Collect all the metrics on the set of MModules
89 fun collect(mmodules: Set[MModule], mainmodule: MModule) do
90 clear
91 for metric in metrics.values do
92 for mmodule in mmodules do
93 metric.collect(mmodule, mainmodule)
94 end
95 end
96 end
97 end
98
99 # An abstract Metric on MModules
100 abstract class MModuleMetric
101 super IntMetric[MModule]
102 # Collect the metric on the MModule
103 #
104 # Results are stored in the property `values`
105 fun collect(mmodule: MModule, mainmodule: MModule) is abstract
106 end
107
108 # Module Metric: Number of Ancestors
109 class MNOA
110 super MModuleMetric
111 redef fun name do return "mnoa"
112 redef fun desc do return "number of ancestor modules"
113
114 redef fun collect(mmodule, main) do
115 values[mmodule] = mmodule.in_importation.greaters.length - 1
116 end
117 end
118
119 # Module Metric: Number of Parents
120 class MNOP
121 super MModuleMetric
122 redef fun name do return "mnop"
123 redef fun desc do return "number of parent modules"
124
125 redef fun collect(mmodule, main) do
126 values[mmodule] = mmodule.in_importation.direct_greaters.length
127 end
128 end
129
130 # Module Metric: Number of Children
131 class MNOC
132 super MModuleMetric
133 redef fun name do return "mnoc"
134 redef fun desc do return "number of child modules"
135
136 redef fun collect(mmodule, main) do
137 values[mmodule] = mmodule.in_importation.direct_smallers.length
138 end
139 end
140
141 # Module Metric: Number of Descendants
142 class MNOD
143 super MModuleMetric
144 redef fun name do return "mnod"
145 redef fun desc do return "number of descendant modules"
146
147 redef fun collect(mmodule, main) do
148 values[mmodule] = mmodule.in_importation.smallers.length - 1
149 end
150 end
151
152 # Module Metric: Depth in Tree
153 class MDIT
154 super MModuleMetric
155 redef fun name do return "mdit"
156 redef fun desc do return "depth in module tree"
157
158 redef fun collect(mmodule, main) do
159 values[mmodule] = mmodule.in_importation.depth
160 end
161 end
162
163 # Module Metric: Number of Introduction (of all kind)
164 #
165 # count all mclasses introduced by the module
166 class MNBI
167 super MModuleMetric
168 redef fun name do return "mnbi"
169 redef fun desc do return "number of introduction in module"
170
171 redef fun collect(mmodule, main) do
172 values[mmodule] = mmodule.intro_mclasses.length
173 end
174 end
175
176 # Module Metric: Number of Refinement
177 #
178 # count all mclasses refined in the module
179 class MNBR
180 super MModuleMetric
181 redef fun name do return "mnbr"
182 redef fun desc do return "number of refinement in module"
183
184 redef fun collect(mmodule, main) do
185 values[mmodule] = 0
186 for mclassdef in mmodule.mclassdefs do
187 if not mclassdef.is_intro then values.inc(mmodule)
188 end
189 end
190 end
191
192 # Module Metric: Number of Concrete Class in module (intro + redef)
193 class MNBCC
194 super MModuleMetric
195 redef fun name do return "mnbcc"
196 redef fun desc do return "number of concrete class in module (intro + redef)"
197
198 redef fun collect(mmodule, main) do
199 values[mmodule] = 0
200 for mclassdef in mmodule.mclassdefs do
201 if mclassdef.mclass.kind == concrete_kind then values.inc(mmodule)
202 end
203 end
204 end
205
206 # Module Metric: Number of Abstract Class in module (intro + redef)
207 class MNBAC
208 super MModuleMetric
209 redef fun name do return "mnbac"
210 redef fun desc do return "number of abstract class in module (intro + redef)"
211
212 redef fun collect(mmodule, main) do
213 values[mmodule] = 0
214 for mclassdef in mmodule.mclassdefs do
215 if mclassdef.mclass.kind == abstract_kind then values.inc(mmodule)
216 end
217 end
218 end
219
220 # Module Metric: Number of Interface in module (intro + redef)
221 class MNBIC
222 super MModuleMetric
223 redef fun name do return "mnbic"
224 redef fun desc do return "number of interface in module (intro + redef)"
225
226 redef fun collect(mmodule, main) do
227 values[mmodule] = 0
228 for mclassdef in mmodule.mclassdefs do
229 if mclassdef.mclass.kind == interface_kind then values.inc(mmodule)
230 end
231 end
232 end
233
234 # Module Metric: Number of Enum in module (intro + redef)
235 class MNBEC
236 super MModuleMetric
237 redef fun name do return "mnbec"
238 redef fun desc do return "number of enum in module (intro + redef)"
239
240 redef fun collect(mmodule, main) do
241 values[mmodule] = 0
242 for mclassdef in mmodule.mclassdefs do
243 if mclassdef.mclass.kind == enum_kind then values.inc(mmodule)
244 end
245 end
246 end
247
248