nitc :: InheritanceMetricsPhase
Extract metrics about inheritance from model.nitc $ InheritanceMetricsPhase
Extract metrics about inheritance from model.Serializable::inspect
to show more useful information
nitc :: modelbuilder
more_collections :: more_collections
Highly specific, but useful, collections-related classes.serialization :: serialization_core
Abstract services to serialize Nit objects to different formatsnitc :: toolcontext
Common command-line tool infrastructure than handle options and error messagescore :: union_find
union–find algorithm using an efficient disjoint-set data structurenitc :: api_metrics
nitc :: nitmetrics
A program that collects various metrics on nit programs and libraries
# Collect metrics about inheritance usage
module inheritance_metrics
import metrics_base
import mmodules_metrics
import mclasses_metrics
redef class ToolContext
# Inheritance related metrics phase
var inheritance_metrics_phase: Phase = new InheritanceMetricsPhase(self, null)
end
# Extract metrics about inheritance from model.
private class InheritanceMetricsPhase
super Phase
redef fun process_mainmodule(mainmodule, given_mmodules)
do
if not toolcontext.opt_inheritance.value and not toolcontext.opt_all.value then return
var csv = toolcontext.opt_csv.value
var out = "{toolcontext.opt_dir.value or else "metrics"}/inheritance"
out.mkdir
var model = toolcontext.modelbuilder.model
var filter = new ModelFilter(min_visibility = private_visibility)
print toolcontext.format_h1("\n# Inheritance metrics")
var hmetrics = new MetricSet
hmetrics.register(new MDUI(model, mainmodule))
hmetrics.register(new MDUIC(model, mainmodule))
hmetrics.register(new MDUII(model, mainmodule))
hmetrics.register(new MIF(model, mainmodule))
hmetrics.register(new MIFC(model, mainmodule))
hmetrics.register(new MIFI(model, mainmodule))
var cmetrics = new MetricSet
cmetrics.register(new CNOAC(model, mainmodule, filter))
cmetrics.register(new CNOPC(model, mainmodule, filter))
cmetrics.register(new CNOCC(model, mainmodule, filter))
cmetrics.register(new CNODC(model, mainmodule, filter))
cmetrics.register(new CNOPI(model, mainmodule, filter))
cmetrics.register(new CNOCI(model, mainmodule, filter))
cmetrics.register(new CNODI(model, mainmodule, filter))
cmetrics.register(new CDITC(model, mainmodule, filter))
cmetrics.register(new CDITI(model, mainmodule, filter))
var mmodules = new HashSet[MModule]
var mclasses = new HashSet[MClass]
for mpackage in model.mpackages do
print toolcontext.format_h2("\n ## package {mpackage}")
for mgroup in mpackage.mgroups do
if mgroup.mmodules.is_empty then continue
# Scalar metrics
print toolcontext.format_h3(" `- group {mgroup.full_name}")
var mod_mclasses = new HashSet[MClass]
for mmodule in mgroup.mmodules do mod_mclasses.add_all(mmodule.intro_mclasses)
if mod_mclasses.is_empty then continue
mmodules.add_all(mgroup.mmodules)
mclasses.add_all(mod_mclasses)
cmetrics.clear
cmetrics.collect(new HashSet[MClass].from(mod_mclasses))
cmetrics.to_console(1, not toolcontext.opt_nocolors.value)
if csv then cmetrics.to_csv.write_to_file("{out}/{mgroup}_classes.csv")
hmetrics.clear
hmetrics.collect(new HashSet[MModule].from(mgroup.mmodules))
hmetrics.to_console(1, not toolcontext.opt_nocolors.value)
if csv then hmetrics.to_csv.write_to_file("{out}/{mgroup}_inheritance.csv")
end
end
if not mclasses.is_empty then
# Global metrics
print toolcontext.format_h2("\n ## global metrics")
cmetrics.clear
cmetrics.collect(mclasses)
cmetrics.to_console(1, not toolcontext.opt_nocolors.value)
if csv then cmetrics.to_csv.write_to_file("{out}/summary_classes.csv")
hmetrics.clear
hmetrics.collect(mmodules)
hmetrics.to_console(1, not toolcontext.opt_nocolors.value)
if csv then hmetrics.to_csv.write_to_file("{out}/summary_inheritance.csv")
end
end
end
# Module metric: proportion of MClasses Defined Using Inheritance
#
# Count MClasses that have another parents than Object
class MDUI
super MModuleMetric
super FloatMetric
redef fun name do return "mdui"
redef fun desc do return "proportion of mclass defined using inheritance (has other parent than Object)"
redef fun collect(mmodules) do
for mmodule in mmodules do
var count = 0
for mclass in mmodule.intro_mclasses do
if mclass.in_hierarchy(mainmodule).greaters.length > 2 then count += 1
end
if mmodule.intro_mclasses.is_empty then
values[mmodule] = 0.0
else
values[mmodule] = count.to_f / mmodule.intro_mclasses.length.to_f
end
end
end
end
# Module metric: proportion of abstract, concrete and extern Classes Defined Using Inheritance
#
# Count classes that have another parents than Object
class MDUIC
super MModuleMetric
super FloatMetric
redef fun name do return "mduic"
redef fun desc do return "proportion of class_kind defined using inheritance"
redef fun collect(mmodules) do
for mmodule in mmodules do
var count = 0
var nb = 0
for mclass in mmodule.intro_mclasses do
if mclass.kind == abstract_kind or mclass.kind == concrete_kind or mclass.kind == extern_kind then
if mclass.in_hierarchy(mainmodule).greaters.length > 2 then count += 1
end
nb += 1
end
if mmodule.intro_mclasses.is_empty then
values[mmodule] = 0.0
else
values[mmodule] = count.to_f / nb.to_f
end
end
end
end
# Module metric: proportion of Interface Defined Using Inheritance
#
# Count interface that have another parents than Object
class MDUII
super MModuleMetric
super FloatMetric
redef fun name do return "mduii"
redef fun desc do return "proportion of interface_kind defined using inheritance"
redef fun collect(mmodules) do
for mmodule in mmodules do
var count = 0
var nb = 0
for mclass in mmodule.intro_mclasses do
if mclass.kind == interface_kind then
if mclass.in_hierarchy(mainmodule).greaters.length > 2 then count += 1
end
nb += 1
end
if mmodule.intro_mclasses.is_empty then
values[mmodule] = 0.0
else
values[mmodule] = count.to_f / nb.to_f
end
end
end
end
# Module metric: proportion of MClass Inherited From
#
# Count classes that have at least a child
class MIF
super MModuleMetric
super FloatMetric
redef fun name do return "mif"
redef fun desc do return "proportion of mclass inherited from"
redef fun collect(mmodules) do
for mmodule in mmodules do
var count = 0
for mclass in mmodule.intro_mclasses do
if mclass.in_hierarchy(mainmodule).direct_smallers.length > 0 then count += 1
end
if mmodule.intro_mclasses.is_empty then
values[mmodule] = 0.0
else
values[mmodule] = count.to_f / mmodule.intro_mclasses.length.to_f
end
end
end
end
# Module metric: proportion of abstract, concrete and extern Class Inherited From
#
# Count classes that have at least a child
class MIFC
super MModuleMetric
super FloatMetric
redef fun name do return "mifc"
redef fun desc do return "proportion of class_kind inherited from"
redef fun collect(mmodules) do
for mmodule in mmodules do
var count = 0
var nb = 0
for mclass in mmodule.intro_mclasses do
if mclass.kind == abstract_kind or mclass.kind == concrete_kind or mclass.kind == extern_kind then
if mclass.in_hierarchy(mainmodule).direct_smallers.length > 0 then count += 1
end
nb += 1
end
if mmodule.intro_mclasses.is_empty then
values[mmodule] = 0.0
else
values[mmodule] = count.to_f / nb.to_f
end
end
end
end
# Module metric: proportion of Interface Inherited From
#
# Count interfaces that have at least a child
class MIFI
super MModuleMetric
super FloatMetric
redef fun name do return "mifi"
redef fun desc do return "proportion of interface_kind inherited from"
redef fun collect(mmodules) do
for mmodule in mmodules do
var count = 0
var nb = 0
for mclass in mmodule.intro_mclasses do
if mclass.kind == interface_kind then
if mclass.in_hierarchy(mainmodule).direct_smallers.length > 0 then count += 1
end
nb += 1
end
if mmodule.intro_mclasses.is_empty then
values[mmodule] = 0.0
else
values[mmodule] = count.to_f / nb.to_f
end
end
end
end
# MClass metric: Number of Class Ancestors
#
# Count only absrtract, concrete and extern classes
class CNOAC
super MClassMetric
super IntMetric
redef fun name do return "cnoac"
redef fun desc do return "number of class_kind ancestor"
redef fun collect(mclasses) do
for mclass in mclasses do
var count = 0
for parent in mclass.in_hierarchy(mainmodule).greaters do
if parent == mclass then continue
if parent.kind == abstract_kind or parent.kind == concrete_kind or parent.kind == extern_kind then
count += 1
end
end
values[mclass] = count
end
end
end
# MClass metric: Number of Class Parents
#
# Count only absrtract, concrete and extern classes
class CNOPC
super MClassMetric
super IntMetric
redef fun name do return "cnopc"
redef fun desc do return "number of class_kind parent"
redef fun collect(mclasses) do
for mclass in mclasses do
var count = 0
for parent in mclass.in_hierarchy(mainmodule).direct_greaters do
if parent == mclass then continue
if parent.kind == abstract_kind or parent.kind == concrete_kind or parent.kind == extern_kind then
count += 1
end
end
values[mclass] = count
end
end
end
# MClass metric: Number of Class Children
#
# Count only absrtract, concrete and extern classes
class CNOCC
super MClassMetric
super IntMetric
redef fun name do return "cnocc"
redef fun desc do return "number of class_kind children"
redef fun collect(mclasses) do
for mclass in mclasses do
var count = 0
for parent in mclass.in_hierarchy(mainmodule).direct_smallers do
if parent == mclass then continue
if parent.kind == abstract_kind or parent.kind == concrete_kind or parent.kind == extern_kind then
count += 1
end
end
values[mclass] = count
end
end
end
# MClass metric: Number of Class Descendants
#
# Count only absrtract, concrete and extern classes
class CNODC
super MClassMetric
super IntMetric
redef fun name do return "cnodc"
redef fun desc do return "number of class_kind descendants"
redef fun collect(mclasses) do
for mclass in mclasses do
var count = 0
for parent in mclass.in_hierarchy(mainmodule).smallers do
if parent == mclass then continue
if parent.kind == abstract_kind or parent.kind == concrete_kind or parent.kind == extern_kind then
count += 1
end
end
values[mclass] = count
end
end
end
# MClass metric: Number of Abstract Class Ancestors
#
# Count only absrtract classes
class CNOAA
super MClassMetric
super IntMetric
redef fun name do return "cnoaa"
redef fun desc do return "number of abstract class ancestors"
redef fun collect(mclasses) do
for mclass in mclasses do
var count = 0
for parent in mclass.in_hierarchy(mainmodule).greaters do
if parent == mclass then continue
if parent.kind == abstract_kind then
count += 1
end
end
values[mclass] = count
end
end
end
# MClass metric: Number of Interface Ancestors
#
# Count only interfaces
class CNOAI
super MClassMetric
super IntMetric
redef fun name do return "cnoai"
redef fun desc do return "number of interface_kind ancestor"
redef fun collect(mclasses) do
for mclass in mclasses do
var count = 0
for parent in mclass.in_hierarchy(mainmodule).greaters do
if parent == mclass then continue
if parent.kind == interface_kind then
count += 1
end
end
values[mclass] = count
end
end
end
# MClass metric: Number of Interface Parents
#
# Count only interfaces
class CNOPI
super MClassMetric
super IntMetric
redef fun name do return "cnopi"
redef fun desc do return "number of interface_kind parent"
redef fun collect(mclasses) do
for mclass in mclasses do
var count = 0
for parent in mclass.in_hierarchy(mainmodule).direct_greaters do
if parent == mclass then continue
if parent.kind == interface_kind then
count += 1
end
end
values[mclass] = count
end
end
end
# MClass metric: Number of Interface Children
#
# Count only interfaces
class CNOCI
super MClassMetric
super IntMetric
redef fun name do return "cnoci"
redef fun desc do return "number of interface_kind children"
redef fun collect(mclasses) do
for mclass in mclasses do
var count = 0
for parent in mclass.in_hierarchy(mainmodule).direct_smallers do
if parent == mclass then continue
if parent.kind == interface_kind then
count += 1
end
end
values[mclass] = count
end
end
end
# MClass metric: Number of Interface Descendants
#
# Count only interfaces
class CNODI
super MClassMetric
super IntMetric
redef fun name do return "cnodi"
redef fun desc do return "number of interface_kind descendants"
redef fun collect(mclasses) do
for mclass in mclasses do
var count = 0
for parent in mclass.in_hierarchy(mainmodule).smallers do
if parent == mclass then continue
if parent.kind == interface_kind then
count += 1
end
end
values[mclass] = count
end
end
end
# MClass metric: Class Depth in Inheritance Tree
#
# Following the longest path composed only of extends edges from self to Object
class CDITC
super MClassMetric
super IntMetric
redef fun name do return "cditc"
redef fun desc do return "depth in class tree following only class, abstract, extern kind"
redef fun collect(mclasses) do
for mclass in mclasses do
values[mclass] = mclass.ditc(mainmodule)
end
end
end
# MClass metric: Interface Depth in Inheritance Tree
#
# Following the longest path composed only of implements edges from self to Object
class CDITI
super MClassMetric
super IntMetric
redef fun name do return "cditi"
redef fun desc do return "depth in class tree following only interface_kind"
redef fun collect(mclasses) do
for mclass in mclasses do
values[mclass] = mclass.diti(mainmodule)
end
end
end
# model redef
redef class MClass
# Class Depth in Inheritance Tree
#
# Following the longest path composed only of extends edges from self to Object
fun ditc(mainmodule: MModule): Int do
if in_hierarchy(mainmodule).direct_greaters.is_empty then
return 0
end
var min = -1
for p in in_hierarchy(mainmodule).direct_greaters do
if p.kind != abstract_kind and p.kind != concrete_kind and p.kind != extern_kind then continue
var d = p.ditc(mainmodule) + 1
if min == -1 or d < min then
min = d
end
end
if min == -1 then min = 0
return min
end
# Interface Depth in Inheritance Tree
#
# Following the longest path composed only of implements edges from self to Object
fun diti(mainmodule: MModule): Int do
if in_hierarchy(mainmodule).direct_greaters.is_empty then
return 0
end
var min = -1
for p in in_hierarchy(mainmodule).direct_greaters do
if p.kind != interface_kind then continue
var d = p.diti(mainmodule) + 1
if min == -1 or d < min then
min = d
end
end
if min == -1 then min = 0
return min
end
end
src/metrics/inheritance_metrics.nit:17,1--546,3