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 # The mndel model helps to understand class hierarchies
19 # It provides metrics to extract interesting classes:
21 # * Large classes that have a lot of local mproperties
22 # * Budding classes that provide more mproperties than their superclasses
23 # * Blooming classes that are both large and budding
25 # Also, this model helps to understand inheritance behviours between classes.
26 # It provide metrics to categorize classes as:
28 # * pure overriders that contain only redefinitions
29 # * overriders that contain more definitions than introductions
30 # * pure extenders that contain only introductions
31 # * extenders that contain more introduction than redefinitions
33 # Finally, this model can characterize overriding behaviors
35 # * pure specializers that always call to super in its redefinitions
36 # * specializers that have more redefinitions that call super than not calling it
37 # * pure replacers that never call to super in its redefinitions
38 # * replacers that have less redefinitions that call super than not calling it
40 # For more details see
41 # Mendel: A Model, Metrics and Rules to Understan Class Hierarchies
42 # S. Denier and Y. Gueheneuc
43 # in Proceedings of the 16th IEEE International Conference on Program Comprehension (OCPC'08)
47 import mclasses_metrics
50 redef class ToolContext
51 # Compute MENDEL metrics.
53 # See `mendel_metrics` module documentation.
54 var mendel_metrics_phase
: Phase = new MendelMetricsPhase(self, null)
57 private class MendelMetricsPhase
59 redef fun process_mainmodule
(mainmodule
, given_mmodules
)
61 if not toolcontext
.opt_mendel
.value
and not toolcontext
.opt_all
.value
then return
62 var csv
= toolcontext
.opt_csv
.value
63 var out
= "{toolcontext.opt_dir.value or else "metrics"}/mendel"
66 print toolcontext
.format_h1
("\n# Mendel metrics")
68 var vis
= protected_visibility
69 var model
= toolcontext
.modelbuilder
.model
71 var mclasses
= new HashSet[MClass]
72 for mclass
in model
.mclasses
do
73 if mclass
.visibility
< vis
then continue
74 if mclass
.is_interface
then continue
78 var cnblp
= new CNBLP(mainmodule
, vis
)
79 var cnvi
= new CNVI(mainmodule
)
80 var cnvs
= new CNVS(mainmodule
)
82 var metrics
= new MetricSet
83 metrics
.register
(cnblp
, cnvi
, cnvs
)
84 metrics
.collect
(mclasses
)
85 if csv
then metrics
.to_csv
.save
("{out}/mendel.csv")
87 var threshold
= cnblp
.threshold
88 print toolcontext
.format_h4
("\tlarge mclasses (threshold: {threshold})")
89 for mclass
in cnblp
.sort
do
90 var val
= cnblp
.values
[mclass
]
91 if val
.to_f
< threshold
then break
92 print toolcontext
.format_p
("\t {mclass.name}: {val}")
95 threshold
= cnvi
.threshold
96 print toolcontext
.format_h4
("\tbudding mclasses (threshold: {threshold})")
97 for mclass
in cnvi
.sort
do
98 var val
= cnvi
.values
[mclass
]
99 if val
.to_f
< threshold
then break
100 print toolcontext
.format_p
("\t {mclass.name}: {val}")
103 threshold
= cnvs
.threshold
104 print toolcontext
.format_h4
("\tblooming mclasses (threshold: {threshold})")
105 for mclass
in cnvs
.sort
do
106 var val
= cnvs
.values
[mclass
]
107 if val
.to_f
< threshold
then break
108 print toolcontext
.format_p
("\t {mclass.name}: {val}")
112 var csvh
= new CsvDocument
113 csvh
.format
= new CsvFormat('"', ';', "\n")
114 csvh
.header
= ["povr", "ovr", "pext", "ext", "pspe", "spe", "prep", "rep", "eq"]
115 for mclass
in mclasses
do
116 var povr
= mclass
.is_pure_overrider
(vis
).object_id
117 var ovr
= mclass
.is_overrider
(vis
).object_id
118 var pext
= mclass
.is_pure_extender
(vis
).object_id
119 var ext
= mclass
.is_extender
(vis
).object_id
120 var pspe
= mclass
.is_pure_specializer
(vis
).object_id
121 var spe
= mclass
.is_pure_specializer
(vis
).object_id
122 var prep
= mclass
.is_pure_replacer
(vis
).object_id
123 var rep
= mclass
.is_replacer
(vis
).object_id
124 var eq
= mclass
.is_equal
(vis
).object_id
125 csvh
.add_record
(povr
, ovr
, pext
, ext
, pspe
, spe
, prep
, rep
, eq
)
127 csvh
.save
("{out}/inheritance_behaviour.csv")
132 # Class Branch Mean Size
133 # cbms(class) = |TotS(class)| / (DIT(class) + 1)
137 redef fun name
do return "cbms"
138 redef fun desc
do return "branch mean size, mean number of introduction available among ancestors"
140 # Mainmodule used to compute class hierarchy.
141 var mainmodule
: MModule
143 redef fun collect
(mclasses
) do
144 for mclass
in mclasses
do
145 var totc
= mclass
.all_mproperties
(mainmodule
, protected_visibility
).length
146 var ditc
= mclass
.in_hierarchy
(mainmodule
).depth
147 values
[mclass
] = totc
.to_f
/ (ditc
+ 1).to_f
152 # Class Novelty Index
153 # cnvi = |LocS(class)| / cbms(parents(class))
157 redef fun name
do return "cnvi"
158 redef fun desc
do return "class novelty index, contribution of the class to its branch in term of introductions"
160 # Mainmodule used to compute class hierarchy.
161 var mainmodule
: MModule
163 redef fun collect
(mclasses
) do
164 var cbms
= new CBMS(mainmodule
)
165 for mclass
in mclasses
do
166 # compute branch mean size
167 var parents
= mclass
.in_hierarchy
(mainmodule
).direct_greaters
168 if parents
.length
> 0 then
170 cbms
.collect
(new HashSet[MClass].from
(parents
))
171 # compute class novelty index
172 var locc
= mclass
.local_mproperties
(protected_visibility
).length
173 values
[mclass
] = locc
.to_f
/ cbms
.avg
181 # Class Novelty Score
182 # cnvs = |LocS(class)| x nvi
186 redef fun name
do return "cnvs"
187 redef fun desc
do return "class novelty score, importance of the contribution of the class to its branch"
189 # Mainmodule used to compute class hierarchy.
190 var mainmodule
: MModule
192 redef fun collect
(mclasses
) do
193 var cnvi
= new CNVI(mainmodule
)
194 cnvi
.collect
(mclasses
)
195 for mclass
in mclasses
do
196 var locc
= mclass
.local_mproperties
(protected_visibility
).length
197 values
[mclass
] = cnvi
.values
[mclass
] * locc
.to_f
203 # the set of redefition that call to super
204 fun extended_mproperties
(min_visibility
: MVisibility): Set[MProperty] do
205 var set
= new HashSet[MProperty]
206 for mclassdef
in mclassdefs
do
207 for mpropdef
in mclassdef
.mpropdefs
do
208 if mpropdef
.mproperty
.visibility
< min_visibility
then continue
209 if not mpropdef
.has_supercall
then continue
210 if mpropdef
.mproperty
.intro_mclassdef
.mclass
!= self then set
.add
(mpropdef
.mproperty
)
216 # the set of redefition that do not call to super
217 fun overriden_mproperties
(min_visibility
: MVisibility): Set[MProperty] do
218 var set
= new HashSet[MProperty]
219 for mclassdef
in mclassdefs
do
220 for mpropdef
in mclassdef
.mpropdefs
do
221 if mpropdef
.mproperty
.visibility
< min_visibility
then continue
222 if mpropdef
.has_supercall
then continue
223 if mpropdef
.mproperty
.intro_mclassdef
.mclass
!= self then set
.add
(mpropdef
.mproperty
)
229 # pure overriders contain only redefinitions
230 private fun is_pure_overrider
(min_visibility
: MVisibility): Bool do
231 var news
= intro_mproperties
(min_visibility
).length
232 var locs
= local_mproperties
(min_visibility
).length
233 if news
== 0 and locs
> 0 then return true
237 # overriders contain more definitions than introductions
238 private fun is_overrider
(min_visibility
: MVisibility): Bool do
239 var rdfs
= redef_mproperties
(min_visibility
).length
240 var news
= intro_mproperties
(min_visibility
).length
241 var locs
= local_mproperties
(min_visibility
).length
242 if rdfs
>= news
and locs
> 0 then return true
246 # pure extenders contain only introductions
247 private fun is_pure_extender
(min_visibility
: MVisibility): Bool do
248 var rdfs
= redef_mproperties
(min_visibility
).length
249 var locs
= local_mproperties
(min_visibility
).length
250 if rdfs
== 0 and locs
> 0 then return true
254 # extenders contain more introduction than redefinitions
255 private fun is_extender
(min_visibility
: MVisibility): Bool do
256 var rdfs
= redef_mproperties
(min_visibility
).length
257 var news
= intro_mproperties
(min_visibility
).length
258 var locs
= local_mproperties
(min_visibility
).length
259 if news
> rdfs
and locs
> 0 then return true
263 # pure specializers always call to super in its redefinitions
264 private fun is_pure_specializer
(min_visibility
: MVisibility): Bool do
265 var ovrs
= overriden_mproperties
(min_visibility
).length
266 var rdfs
= redef_mproperties
(min_visibility
).length
267 if ovrs
== 0 and rdfs
> 0 then return true
271 # specializers have more redefinitions that call super than not calling it
272 private fun is_specializer
(min_visibility
: MVisibility): Bool do
273 var spcs
= extended_mproperties
(min_visibility
).length
274 var ovrs
= overriden_mproperties
(min_visibility
).length
275 var rdfs
= redef_mproperties
(min_visibility
).length
276 if spcs
> ovrs
and rdfs
> 0 then return true
280 # pure replacers never call to super in its redefinitions
281 private fun is_pure_replacer
(min_visibility
: MVisibility): Bool do
282 var spcs
= extended_mproperties
(min_visibility
).length
283 var rdfs
= redef_mproperties
(min_visibility
).length
284 if spcs
== 0 and rdfs
> 0 then return true
288 # replacers have less redefinitions that call super than not calling it
289 private fun is_replacer
(min_visibility
: MVisibility): Bool do
290 var spcs
= extended_mproperties
(min_visibility
).length
291 var ovrs
= overriden_mproperties
(min_visibility
).length
292 var rdfs
= redef_mproperties
(min_visibility
).length
293 if ovrs
> spcs
and rdfs
> 0 then return true
297 # equals contain as redifinition than introduction
298 private fun is_equal
(min_visibility
: MVisibility): Bool do
299 var spcs
= extended_mproperties
(min_visibility
).length
300 var ovrs
= overriden_mproperties
(min_visibility
).length
301 var rdfs
= redef_mproperties
(min_visibility
).length
302 if spcs
== ovrs
and rdfs
> 0 then return true