import model
private import metrics_base
+import phase
+import frontend
-redef class Model
+redef class ToolContext
+ var inheritance_metrics_phase = new InheritanceMetricsPhase(self, null)
+end
- # List of modules in std lib
- # FIXME this is quite ugly, find a dynamic way...
- fun std_modules: Set[String] do
- if self.std_modules_cache == null then
- self.std_modules_cache = new HashSet[String]
- self.std_modules_cache.add("collection")
- self.std_modules_cache.add("abstract_collection")
- self.std_modules_cache.add("array")
- self.std_modules_cache.add("hash_collection")
- self.std_modules_cache.add("list")
- self.std_modules_cache.add("range")
- self.std_modules_cache.add("sorter")
- self.std_modules_cache.add("environ")
- self.std_modules_cache.add("exec")
- self.std_modules_cache.add("file")
- self.std_modules_cache.add("gc")
- self.std_modules_cache.add("hash")
- self.std_modules_cache.add("kernel")
- self.std_modules_cache.add("math")
- self.std_modules_cache.add("standard")
- self.std_modules_cache.add("stream")
- self.std_modules_cache.add("string")
- self.std_modules_cache.add("string_search")
- self.std_modules_cache.add("time")
- end
- return self.std_modules_cache.as(not null)
+# Extract metrics about module and class hierarchies.
+private class InheritanceMetricsPhase
+ super Phase
+ redef fun process_mainmodule(mainmodule)
+ do
+ if not toolcontext.opt_inheritance.value and not toolcontext.opt_all.value then return
+ compute_inheritance_metrics(toolcontext, toolcontext.modelbuilder.model)
end
- private var std_modules_cache: nullable Set[String]
+end
+redef class Model
# Extract the subset of classes from a set of mclass
- fun extract_classes(mclasses: Collection[MClass]): Set[MClass] do
+ private fun extract_classes(mclasses: Collection[MClass]): Set[MClass] do
var lst = new HashSet[MClass]
for mclass in mclasses do if mclass.is_class then lst.add(mclass)
return lst
end
# Extract the subset of interfaces from a set of mclass
- fun extract_interfaces(mclasses: Collection[MClass]): Set[MClass] do
+ private fun extract_interfaces(mclasses: Collection[MClass]): Set[MClass] do
var lst = new HashSet[MClass]
for mclass in mclasses do if mclass.is_interface then lst.add(mclass)
return lst
end
# Extract the subset of generic classes/interfaces from a set of mclass
- fun extract_generics(mclasses: Collection[MClass]): Set[MClass] do
+ private fun extract_generics(mclasses: Collection[MClass]): Set[MClass] do
var lst = new HashSet[MClass]
for mclass in mclasses do if mclass.arity > 0 then lst.add(mclass)
return lst
end
# Extract the subset of abstract classes from a set of mclass
- fun extract_abstracts(mclasses: Collection[MClass]): Set[MClass] do
+ private fun extract_abstracts(mclasses: Collection[MClass]): Set[MClass] do
var lst = new HashSet[MClass]
for mclass in mclasses do if mclass.is_abstract then lst.add(mclass)
return lst
end
# Extract the subset of user defined classes/interfaces from a set of mclass
- fun extract_user_defined(mclasses: Collection[MClass]): Set[MClass] do
+ private fun extract_user_defined(mclasses: Collection[MClass]): Set[MClass] do
var lst = new HashSet[MClass]
for mclass in mclasses do if mclass.is_user_defined then lst.add(mclass)
return lst
end
# Extract the subset of user defined modules from a set of mmodules
- fun extract_user_defined_modules(mmodules: Collection[MModule]): Set[MModule] do
+ private fun extract_user_defined_modules(mmodules: Collection[MModule]): Set[MModule] do
var lst = new HashSet[MModule]
for mmodule in mmodules do if mmodule.is_user_defined then lst.add(mmodule)
return lst
end
# Extract the subset of classes/interfaces from sl lib
- fun extract_stdlib(mclasses: Collection[MClass]): Set[MClass] do
+ private fun extract_stdlib(mclasses: Collection[MClass]): Set[MClass] do
var lst = new HashSet[MClass]
for mclass in mclasses do if not mclass.is_user_defined then lst.add(mclass)
return lst
end
# Extract the subset of user defined modules from std lib
- fun extract_stdlib_modules(mmodules: Collection[MModule]): Set[MModule] do
+ private fun extract_stdlib_modules(mmodules: Collection[MModule]): Set[MModule] do
var lst = new HashSet[MModule]
for mmodule in mmodules do if not mmodule.is_user_defined then lst.add(mmodule)
return lst
self.noa = ancestors.length
self.noac = model.extract_classes(ancestors).length
self.noai = model.extract_interfaces(ancestors).length
- self.noc = children(model).length
- self.nocc = model.extract_classes(children(model)).length
- self.noci = model.extract_interfaces(children(model)).length
+ self.noc = children.length
+ self.nocc = model.extract_classes(children).length
+ self.noci = model.extract_interfaces(children).length
self.nod = descendants.length
self.nodc = model.extract_classes(descendants).length
self.nodi = model.extract_interfaces(descendants).length
self.noaud = model.extract_user_defined(ancestors).length
self.noacud = model.extract_user_defined(model.extract_classes(ancestors)).length
self.noaiud = model.extract_user_defined(model.extract_interfaces(ancestors)).length
- self.nocud = model.extract_user_defined(children(model)).length
- self.noccud = model.extract_user_defined(model.extract_classes(children(model))).length
- self.nociud = model.extract_user_defined(model.extract_interfaces(children(model))).length
+ self.nocud = model.extract_user_defined(children).length
+ self.noccud = model.extract_user_defined(model.extract_classes(children)).length
+ self.nociud = model.extract_user_defined(model.extract_interfaces(children)).length
self.nodud = model.extract_user_defined(descendants).length
self.nodcud = model.extract_user_defined(model.extract_classes(descendants)).length
self.nodiud = model.extract_user_defined(model.extract_interfaces(descendants)).length
self.ditiud = ud_interface_path_to_object.length
end
- private fun is_class: Bool do
- return self.kind == concrete_kind or self.kind == abstract_kind
- end
-
- private fun is_interface: Bool do
- return self.kind == interface_kind
- end
-
- private fun is_abstract: Bool do
- return self.kind == abstract_kind
- end
-
- private fun is_user_defined: Bool do
- return self.intro_mmodule.is_user_defined
- end
-
- # Get parents of the class (direct super classes only)
- private fun parents: Set[MClass] do
- var lst = new HashSet[MClass]
- # explore all definitions of the class (refinement)
- for mclassdef in self.mclassdefs do
- for parent in mclassdef.supertypes do
- lst.add(parent.mclass)
- end
- end
- return lst
- end
-
- # Get ancestors of the class (all super classes)
- private fun ancestors: Set[MClass] do
- var lst = new HashSet[MClass]
- for mclassdef in self.mclassdefs do
- for super_mclassdef in mclassdef.in_hierarchy.greaters do
- if super_mclassdef == mclassdef then continue # skip self
- lst.add(super_mclassdef.mclass)
- end
- end
- return lst
- end
-
- # Get children of the class (direct subclasses only)
- private fun children(model: Model): Set[MClass] do
- var lst = new HashSet[MClass]
- for other in model.mclasses do
- if other == self then continue # skip self
- if other.parents.has(self) then
- lst.add(other)
- end
- end
- return lst
- end
-
- # Get children of the class (direct subclasses only)
- private fun descendants: Set[MClass] do
- var lst = new HashSet[MClass]
- for mclassdef in self.mclassdefs do
- for sub_mclassdef in mclassdef.in_hierarchy.smallers do
- if sub_mclassdef == mclassdef then continue # skip self
- lst.add(sub_mclassdef.mclass)
- end
- end
- return lst
- end
-
# Return the longest path from class to root hierarchy
- fun path_to_object: Array[MClass] do
+ private fun path_to_object: Array[MClass] do
var path = new Array[MClass]
var max_dit: nullable Int = null
var max_parent: nullable MClass = null
return path
end
+
# Return the longest path from class to root hierarchy
- fun ud_path_to_object: Array[MClass] do
+ private fun ud_path_to_object: Array[MClass] do
var path = new Array[MClass]
if not self.is_user_defined then return path
var max_dit: nullable Int = null
end
# Return the longest path from class to root hierarchy following only classes relations
- fun class_path_to_object: Array[MClass] do
+ private fun class_path_to_object: Array[MClass] do
var path = new Array[MClass]
if not self.is_class then return path
var max_dit: nullable Int = null
end
# Return the longest path from class to root hierarchy following only interfaces relations
- fun interface_path_to_object: Array[MClass] do
+ private fun interface_path_to_object: Array[MClass] do
var path = new Array[MClass]
if not self.is_interface then return path
var max_dit: nullable Int = null
end
# Return the longest path from class to root hierarchy following only ud classes relations
- fun ud_class_path_to_object: Array[MClass] do
+ private fun ud_class_path_to_object: Array[MClass] do
var path = new Array[MClass]
if not self.is_class or not self.is_user_defined then return path
var max_dit: nullable Int = null
end
# Return the longest path from class to root hierarchy following only ud interfaces relations
- fun ud_interface_path_to_object: Array[MClass] do
+ private fun ud_interface_path_to_object: Array[MClass] do
var path = new Array[MClass]
if not self.is_interface or not self.is_user_defined then return path
var max_dit: nullable Int = null
for parent in parents do if parent.name != "Object" and parent.is_interface then return true
return false
end
- private fun is_if_eligible(model: Model): Bool do return not children(model).is_empty
+ private fun is_if_eligible(model: Model): Bool do return not children.is_empty
private fun is_ccif_eligible(model: Model): Bool do
if not is_class then return false
- for child in children(model) do if child.is_class then return true
+ for child in children do if child.is_class then return true
return false
end
private fun is_icif_eligible(model: Model): Bool do
if not is_interface then return false
- for child in children(model) do if child.is_class then return true
+ for child in children do if child.is_class then return true
return false
end
private fun is_iiif_eligible(model: Model): Bool do
if not is_interface then return false
- for child in children(model) do if child.is_interface then return true
+ for child in children do if child.is_interface then return true
return false
end
end
private fun is_slif_eligible(model: Model): Bool do
if is_user_defined then return false
- return not children(model).is_empty
+ return not children.is_empty
end
private fun is_slccif_eligible(model: Model): Bool do
if is_user_defined then return false
if not is_class then return false
- for child in children(model) do if child.is_class then return true
+ for child in children do if child.is_class then return true
return false
end
private fun is_slicif_eligible(model: Model): Bool do
if is_user_defined then return false
if not is_interface then return false
- for child in children(model) do if child.is_class then return true
+ for child in children do if child.is_class then return true
return false
end
private fun is_sliiif_eligible(model: Model): Bool do
if is_user_defined then return false
if not is_interface then return false
- for child in children(model) do if child.is_interface then return true
+ for child in children do if child.is_interface then return true
return false
end
private fun is_slifsl_eligible(model: Model): Bool do
if is_user_defined then return false
- for child in children(model) do if not child.is_user_defined then return true
+ for child in children do if not child.is_user_defined then return true
return false
end
private fun is_slccifsl_eligible(model: Model): Bool do
if is_user_defined then return false
if is_class then return false
- for child in children(model) do if not child.is_user_defined and child.is_class then return true
+ for child in children do if not child.is_user_defined and child.is_class then return true
return false
end
private fun is_slicifsl_eligible(model: Model): Bool do
if is_user_defined then return false
if not is_interface then return false
- for child in children(model) do if not child.is_user_defined and child.is_class then return true
+ for child in children do if not child.is_user_defined and child.is_class then return true
return false
end
private fun is_sliiifsl_eligible(model: Model): Bool do
if is_user_defined then return false
if not is_interface then return false
- for child in children(model) do if not child.is_user_defined and child.is_interface then return true
+ for child in children do if not child.is_user_defined and child.is_interface then return true
return false
end
private fun is_slifud_eligible(model: Model): Bool do
if is_user_defined then return false
- for child in children(model) do if child.is_user_defined then return true
+ for child in children do if child.is_user_defined then return true
return false
end
private fun is_slccifud_eligible(model: Model): Bool do
if is_user_defined then return false
if not is_class then return false
- for child in children(model) do if child.is_user_defined and child.is_class then return true
+ for child in children do if child.is_user_defined and child.is_class then return true
return false
end
private fun is_slicifud_eligible(model: Model): Bool do
if is_user_defined then return false
if not is_interface then return false
- for child in children(model) do if child.is_user_defined and child.is_class then return true
+ for child in children do if child.is_user_defined and child.is_class then return true
return false
end
private fun is_sliiifud_eligible(model: Model): Bool do
if is_user_defined then return false
if not is_interface then return false
- for child in children(model) do if child.is_user_defined and child.is_interface then return true
+ for child in children do if child.is_user_defined and child.is_interface then return true
return false
end
end
private fun is_udif_eligible(model: Model): Bool do
if not is_user_defined then return false
- return not children(model).is_empty
+ return not children.is_empty
end
private fun is_udccif_eligible(model: Model): Bool do
if not is_user_defined then return false
if not is_class then return false
- for child in children(model) do if child.is_class then return true
+ for child in children do if child.is_class then return true
return false
end
private fun is_udicif_eligible(model: Model): Bool do
if not is_user_defined then return false
if not is_interface then return false
- for child in children(model) do if child.is_class then return true
+ for child in children do if child.is_class then return true
return false
end
private fun is_udiiif_eligible(model: Model): Bool do
if not is_user_defined then return false
if not is_interface then return false
- for child in children(model) do if child.is_interface then return true
+ for child in children do if child.is_interface then return true
return false
end
end
private fun is_udifud_eligible(model: Model): Bool do
if not is_user_defined then return false
- return not children(model).is_empty
+ return not children.is_empty
end
private fun is_udccifud_eligible(model: Model): Bool do
if not is_user_defined then return false
if not is_class then return false
- for child in children(model) do if child.is_user_defined and child.is_class then return true
+ for child in children do if child.is_user_defined and child.is_class then return true
return false
end
private fun is_udicifud_eligible(model: Model): Bool do
if not is_user_defined then return false
if not is_interface then return false
- for child in children(model) do if child.is_user_defined and child.is_class then return true
+ for child in children do if child.is_user_defined and child.is_class then return true
return false
end
private fun is_udiiifud_eligible(model: Model): Bool do
if not is_user_defined then return false
if not is_interface then return false
- for child in children(model) do if child.is_user_defined and child.is_interface then return true
+ for child in children do if child.is_user_defined and child.is_interface then return true
return false
end
-
end
redef class MModule
+ private var nm: Int = 0 # (NC) Number of Modules
private var nc: Int = 0 # (NC) Number of Classes
private var ni: Int = 0 # (NI) Number of Interfaces
private var nac : Int = 0 # (NAC) Number of Abstract Classes
var ccif_count = 0
var icif_count = 0
var iiif_count = 0
-
+ var count = 0
for mmodule in self.in_nesting.greaters do
for mclass in mmodule.intro_mclasses do
+ count += 1
if mclass.is_class then nc += 1
if mclass.is_class and mclass.arity > 0 then ngc += 1
if mclass.is_class and mclass.is_abstract then nac += 1
if mclass.is_interface then ni += 1
if mclass.is_interface and mclass.arity > 0 then ngi += 1
-
ditsum += mclass.path_to_object.length
if mclass.is_dui_eligible then dui_count += 1
if mclass.is_ccdui_eligible then ccdui_count += 1
end
end
- dit = div(ditsum, nc + ni)
- dui = div(dui_count * 100, nc + ni)
+ self.nm = self.in_nesting.greaters.length
+ dit = div(ditsum, count)
+ dui = div(dui_count * 100, count)
ccdui = div(ccdui_count * 100, nc)
cidui = div(cidui_count * 100, nc)
iidui = div(iidui_count * 100, ni)
- inhf = div(if_count * 100, nc + ni)
+ inhf = div(if_count * 100, count)
ccif = div(ccif_count * 100, nc)
icif = div(icif_count * 100, ni)
iiif = div(iiif_count * 100, ni)
end
-
- private fun is_user_defined: Bool do
- return not self.model.std_modules.has(self.name)
- end
end
# Print inheritance usage metrics
var udiiduisl = "" # (UDIIDUISL) Proportion of user-defined interfaces that extend some other SL interface.
# (UD -> UD) User-defined summary inheritance metrics
+ var ditud = ""
var udduiud = "" # (UDDUIUD) Proportion user-defined of types that either implement an interface or extend another type user-defined
var udccduiud = "" # (UDCCDUIUD) Proportion of user-defined classes that extend some other user-defined class.
var udciduiud = "" # (UDCIDUIUD) Proportion of user-defined classes that implement some other user-defined interface.
# * -> *
var ditsum = 0
+ var ditudsum = 0
+
var dui_count = 0
var ccdui_count = 0
var cidui_count = 0
for mclass in model.mclasses do
ditsum += mclass.dit
+ ditudsum += mclass.ditud
# * -> *
if mclass.is_dui_eligible then dui_count += 1
# * -> *
dit = div(ditsum, model.mclasses.length)
- dui = div(dui_count * 100, nc + ni)
+ ditud = div(ditudsum, ncud + niud)
+ dui = div(dui_count * 100, model.mclasses.length)
ccdui = div(ccdui_count * 100, nc)
cidui = div(cidui_count * 100, nc)
iidui = div(iidui_count * 100, ni)
summaryCSV.add_line("std-lib", nmdsl, ncsl, nisl, nacsl, ngcsl, ngisl)
summaryCSV.add_line("user-defined", nmdud, ncud, niud, nacud, ngcud, ngiud)
for m in model.mmodules do
- summaryCSV.add_line(m.name, 1, m.nc, m.ni, m.nac, m.ngc, m.ngi)
+ summaryCSV.add_line(m.name, m.nm, m.nc, m.ni, m.nac, m.ngc, m.ngi)
end
summaryCSV.save
inheritanceCSV.add_line("SL -> UD", "", 0, 0, 0, 0, slinhfud, slccifud, slicifud, sliiifud)
inheritanceCSV.add_line("UD -> *", "", uddui, udccdui, udcidui, udiidui, udinhf, udccif, udicif, udiiif)
inheritanceCSV.add_line("UD -> SL", "", udduisl, udccduisl, udciduisl, udiiduisl, 0, 0, 0, 0)
- inheritanceCSV.add_line("UD -> UD", "", udduiud, udccduiud, udciduiud, udiiduiud, udinhfud, udccifud, udicifud, udiiifud)
+ inheritanceCSV.add_line("UD -> UD", ditud, udduiud, udccduiud, udciduiud, udiiduiud, udinhfud, udccifud, udicifud, udiiifud)
for m in model.mmodules do
if m.intro_mclasses.is_empty and m.in_nesting.greaters.length == 1 then continue
inheritanceCSV.add_line(m.name, m.dit, m.dui, m.ccdui, m.cidui, m.iidui, m.inhf, m.ccif, m.icif, m.iiif)
# scalar metrics
var scalarCSV = new CSVDocument(toolcontext.output_dir.join_path("global_scalar_metrics.csv"))
var udscalarCSV = new CSVDocument(toolcontext.output_dir.join_path("ud_scalar_metrics.csv"))
- scalarCSV.set_header("mclass", "type", "DIT", "DITC", "DITI", "NOP", "NOPC", "NOPI", "NOA", "NOAC", "NOAI", "NOC", "NOCC", "NOCI", "NOD", "NODC", "NODI")
- udscalarCSV.set_header("mclass", "type", "DITUD", "DITCUD", "DITIUD", "NOPUD", "NOPCUD", "NOPIUD", "NOAUD", "NOACUD", "NOAIUD", "NOCUD", "NOCCUD", "NOCIUD", "NODUD", "NODCUD", "NODIUD")
+ scalarCSV.set_header("mclass", "type", "FT", "DIT", "DITC", "DITI", "NOP", "NOPC", "NOPI", "NOA", "NOAC", "NOAI", "NOC", "NOCC", "NOCI", "NOD", "NODC", "NODI")
+ udscalarCSV.set_header("mclass", "type", "FT","DITUD", "DITCUD", "DITIUD", "NOPUD", "NOPCUD", "NOPIUD", "NOAUD", "NOACUD", "NOAIUD", "NOCUD", "NOCCUD", "NOCIUD", "NODUD", "NODCUD", "NODIUD")
for mclass in model.mclasses do
var name = mclass.name
var typ = "class"
if mclass.is_interface then typ = "interface"
- scalarCSV.add_line(name, typ, mclass.dit, mclass.ditc, mclass.diti, mclass.nop, mclass.nopc, mclass.nopi, mclass.noa, mclass.noac, mclass.noai, mclass.noc, mclass.nocc, mclass.noci, mclass.nod, mclass.nodc, mclass.nodi)
- udscalarCSV.add_line(name, typ, mclass.ditud, mclass.ditcud, mclass.ditiud, mclass.nopud, mclass.nopcud, mclass.nopiud, mclass.noaud, mclass.noacud, mclass.noaiud, mclass.nocud, mclass.noccud, mclass.nociud, mclass.nodud, mclass.nodcud, mclass.nodiud)
+ scalarCSV.add_line(name, typ, mclass.arity, mclass.dit, mclass.ditc, mclass.diti, mclass.nop, mclass.nopc, mclass.nopi, mclass.noa, mclass.noac, mclass.noai, mclass.noc, mclass.nocc, mclass.noci, mclass.nod, mclass.nodc, mclass.nodi)
+ udscalarCSV.add_line(name, typ, mclass.arity, mclass.ditud, mclass.ditcud, mclass.ditiud, mclass.nopud, mclass.nopcud, mclass.nopiud, mclass.noaud, mclass.noacud, mclass.noaiud, mclass.nocud, mclass.noccud, mclass.nociud, mclass.nodud, mclass.nodcud, mclass.nodiud)
end
scalarCSV.save
udscalarCSV.save
end
# TODO Third-Party metrics
-# TODO CSV generation
# TODO gnu-plot generation