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)
48 import mclasses_metrics
52 redef class ToolContext
53 var mendel_metrics_phase
: Phase = new MendelMetricsPhase(self, null)
56 private class MendelMetricsPhase
58 redef fun process_mainmodule
(mainmodule
, given_mmodules
)
60 if not toolcontext
.opt_mendel
.value
and not toolcontext
.opt_all
.value
then return
61 var csv
= toolcontext
.opt_csv
.value
62 var out
= "{toolcontext.opt_dir.value or else "metrics"}/mendel"
65 print toolcontext
.format_h1
("\n# Mendel metrics")
67 var vis
= protected_visibility
68 var model
= toolcontext
.modelbuilder
.model
70 var mclasses
= new HashSet[MClass]
71 for mclass
in model
.mclasses
do
72 if mclass
.visibility
< vis
then continue
73 if mclass
.is_interface
then continue
77 var cnblp
= new CNBLP(mainmodule
, vis
)
78 var cnvi
= new CNVI(mainmodule
)
79 var cnvs
= new CNVS(mainmodule
)
81 var metrics
= new MetricSet
82 metrics
.register
(cnblp
, cnvi
, cnvs
)
83 metrics
.collect
(mclasses
)
84 if csv
then metrics
.to_csv
.save
("{out}/mendel.csv")
86 print toolcontext
.format_h4
("\tlarge mclasses (threshold: {cnblp.threshold})")
87 for mclass
in cnblp
.above_threshold
do
88 print toolcontext
.format_p
("\t {mclass.name}: {cnblp.values[mclass]}")
91 print toolcontext
.format_h4
("\tbudding mclasses (threshold: {cnvi.threshold})")
92 for mclass
in cnvi
.above_threshold
do
93 print toolcontext
.format_p
("\t {mclass.name}: {cnvi.values[mclass]}")
96 print toolcontext
.format_h4
("\tblooming mclasses (threshold: {cnvs.threshold})")
97 for mclass
in cnvs
.above_threshold
do
98 print toolcontext
.format_p
("\t {mclass.name}: {cnvs.values[mclass]}")
101 print toolcontext
.format_h4
("\tblooming mclasses (threshold: {cnvs.threshold})")
102 for mclass
in cnvs
.above_threshold
do
103 print toolcontext
.format_p
("\t {mclass.name}: {cnvs.values[mclass]}")
107 var csvh
= new CSVDocument
108 csvh
.header
= ["povr", "ovr", "pext", "ext", "pspe", "spe", "prep", "rep", "eq"]
109 for mclass
in mclasses
do
110 var povr
= mclass
.is_pure_overrider
(vis
).object_id
111 var ovr
= mclass
.is_overrider
(vis
).object_id
112 var pext
= mclass
.is_pure_extender
(vis
).object_id
113 var ext
= mclass
.is_extender
(vis
).object_id
114 var pspe
= mclass
.is_pure_specializer
(vis
).object_id
115 var spe
= mclass
.is_pure_specializer
(vis
).object_id
116 var prep
= mclass
.is_pure_replacer
(vis
).object_id
117 var rep
= mclass
.is_replacer
(vis
).object_id
118 var eq
= mclass
.is_equal
(vis
).object_id
119 csvh
.add_line
(povr
, ovr
, pext
, ext
, pspe
, spe
, prep
, rep
, eq
)
121 csvh
.save
("{out}/inheritance_behaviour.csv")
126 # Class Branch Mean Size
127 # cbms(class) = |TotS(class)| / (DIT(class) + 1)
131 redef fun name
do return "cbms"
132 redef fun desc
do return "branch mean size, mean number of introduction available among ancestors"
134 var mainmodule
: MModule
135 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
137 redef fun collect
(mclasses
) do
138 for mclass
in mclasses
do
139 var totc
= mclass
.all_mproperties
(mainmodule
, protected_visibility
).length
140 var ditc
= mclass
.in_hierarchy
(mainmodule
).depth
141 values
[mclass
] = totc
.to_f
/ (ditc
+ 1).to_f
146 # Class Novelty Index
147 # cnvi = |LocS(class)| / cbms(parents(class))
151 redef fun name
do return "cnvi"
152 redef fun desc
do return "class novelty index, contribution of the class to its branch in term of introductions"
154 var mainmodule
: MModule
155 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
157 redef fun collect
(mclasses
) do
158 var cbms
= new CBMS(mainmodule
)
159 for mclass
in mclasses
do
160 # compute branch mean size
161 var parents
= mclass
.in_hierarchy
(mainmodule
).direct_greaters
162 if parents
.length
> 0 then
164 cbms
.collect
(new HashSet[MClass].from
(parents
))
165 # compute class novelty index
166 var locc
= mclass
.local_mproperties
(protected_visibility
).length
167 values
[mclass
] = locc
.to_f
/ cbms
.avg
175 # Class Novelty Score
176 # cnvs = |LocS(class)| x nvi
180 redef fun name
do return "cnvs"
181 redef fun desc
do return "class novelty score, importance of the contribution of the class to its branch"
183 var mainmodule
: MModule
184 init(mainmodule
: MModule) do self.mainmodule
= mainmodule
186 redef fun collect
(mclasses
) do
187 var cnvi
= new CNVI(mainmodule
)
188 cnvi
.collect
(mclasses
)
189 for mclass
in mclasses
do
190 var locc
= mclass
.local_mproperties
(protected_visibility
).length
191 values
[mclass
] = cnvi
.values
[mclass
] * locc
.to_f
197 # the set of redefition that call to super
198 fun extended_mproperties
(min_visibility
: MVisibility): Set[MProperty] do
199 var set
= new HashSet[MProperty]
200 for mclassdef
in mclassdefs
do
201 for mpropdef
in mclassdef
.mpropdefs
do
202 if mpropdef
.mproperty
.visibility
< min_visibility
then continue
203 if not mpropdef
.has_supercall
then continue
204 if mpropdef
.mproperty
.intro_mclassdef
.mclass
!= self then set
.add
(mpropdef
.mproperty
)
210 # the set of redefition that do not call to super
211 fun overriden_mproperties
(min_visibility
: MVisibility): Set[MProperty] do
212 var set
= new HashSet[MProperty]
213 for mclassdef
in mclassdefs
do
214 for mpropdef
in mclassdef
.mpropdefs
do
215 if mpropdef
.mproperty
.visibility
< min_visibility
then continue
216 if mpropdef
.has_supercall
then continue
217 if mpropdef
.mproperty
.intro_mclassdef
.mclass
!= self then set
.add
(mpropdef
.mproperty
)
223 # pure overriders contain only redefinitions
224 private fun is_pure_overrider
(min_visibility
: MVisibility): Bool do
225 var news
= intro_mproperties
(min_visibility
).length
226 var locs
= local_mproperties
(min_visibility
).length
227 if news
== 0 and locs
> 0 then return true
231 # overriders contain more definitions than introductions
232 private fun is_overrider
(min_visibility
: MVisibility): Bool do
233 var rdfs
= redef_mproperties
(min_visibility
).length
234 var news
= intro_mproperties
(min_visibility
).length
235 var locs
= local_mproperties
(min_visibility
).length
236 if rdfs
>= news
and locs
> 0 then return true
240 # pure extenders contain only introductions
241 private fun is_pure_extender
(min_visibility
: MVisibility): Bool do
242 var rdfs
= redef_mproperties
(min_visibility
).length
243 var locs
= local_mproperties
(min_visibility
).length
244 if rdfs
== 0 and locs
> 0 then return true
248 # extenders contain more introduction than redefinitions
249 private fun is_extender
(min_visibility
: MVisibility): Bool do
250 var rdfs
= redef_mproperties
(min_visibility
).length
251 var news
= intro_mproperties
(min_visibility
).length
252 var locs
= local_mproperties
(min_visibility
).length
253 if news
> rdfs
and locs
> 0 then return true
257 # pure specializers always call to super in its redefinitions
258 private fun is_pure_specializer
(min_visibility
: MVisibility): Bool do
259 var ovrs
= overriden_mproperties
(min_visibility
).length
260 var rdfs
= redef_mproperties
(min_visibility
).length
261 if ovrs
== 0 and rdfs
> 0 then return true
265 # specializers have more redefinitions that call super than not calling it
266 private fun is_specializer
(min_visibility
: MVisibility): Bool do
267 var spcs
= extended_mproperties
(min_visibility
).length
268 var ovrs
= overriden_mproperties
(min_visibility
).length
269 var rdfs
= redef_mproperties
(min_visibility
).length
270 if spcs
> ovrs
and rdfs
> 0 then return true
274 # pure replacers never call to super in its redefinitions
275 private fun is_pure_replacer
(min_visibility
: MVisibility): Bool do
276 var spcs
= extended_mproperties
(min_visibility
).length
277 var rdfs
= redef_mproperties
(min_visibility
).length
278 if spcs
== 0 and rdfs
> 0 then return true
282 # replacers have less redefinitions that call super than not calling it
283 private fun is_replacer
(min_visibility
: MVisibility): Bool do
284 var spcs
= extended_mproperties
(min_visibility
).length
285 var ovrs
= overriden_mproperties
(min_visibility
).length
286 var rdfs
= redef_mproperties
(min_visibility
).length
287 if ovrs
> spcs
and rdfs
> 0 then return true
291 # equals contain as redifinition than introduction
292 private fun is_equal
(min_visibility
: MVisibility): Bool do
293 var spcs
= extended_mproperties
(min_visibility
).length
294 var ovrs
= overriden_mproperties
(min_visibility
).length
295 var rdfs
= redef_mproperties
(min_visibility
).length
296 if spcs
== ovrs
and rdfs
> 0 then return true