# This file is part of NIT ( http://www.nitlanguage.org ).
#
-# Copyright 2012 Jean Privat <jean@pryen.org>
+# Copyright 2014 Alexandre Terrasa <alexandre@moz-code.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# Collect metrics about inheritance usage
module inheritance_metrics
-import model
-private import metrics_base
+import metrics_base
+import mmodules_metrics
+import mclasses_metrics
-redef class Model
+redef class ToolContext
- # 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)
- end
- private var std_modules_cache: nullable Set[String]
-
- # Extract the subset of classes from a set of mclass
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- var lst = new HashSet[MModule]
- for mmodule in mmodules do if not mmodule.is_user_defined then lst.add(mmodule)
- return lst
- end
+ # Inheritance related metrics phase
+ var inheritance_metrics_phase: Phase = new InheritanceMetricsPhase(self, null)
end
-redef class MClass
- # Inheritance
- private var nop: Int = 0 # (NOP) Number of parents (direct superclasses)
- private var nopc: Int = 0 # (NOPC) Number of class parents
- private var nopi: Int = 0 # (NOPI) Number of interface parents
- private var noa: Int = 0 # (NOA) Number of ancestors (direct and indirect)
- private var noac: Int = 0 # (NOAC) Number of class ancestors
- private var noai: Int = 0 # (NOAI) Number of interface ancestors
- private var noc: Int = 0 # (NOC) Number of children (direct subclasses)
- private var nocc: Int = 0 # (NOCC) Number of class children
- private var noci: Int = 0 # (NOCI) Number of interface children
- private var nod: Int = 0 # (NOD) Number of descendants (direct and indirect)
- private var nodc: Int = 0 # (NODC) Number of class descendants
- private var nodi: Int = 0 # (NODI) Number of interface descendants
- private var dit: Int = 0 # (DIT) Depth in Inheritance Tree (maximum distance to root of the hierarchy)
- private var ditc: Int = 0 # (DITC) Length of longest path to the root hierarchy and consisting only of extends edges
- private var diti: Int = 0 # (DITI) Length of longest path to the root hierarchy and consisting only of extends implements
-
- # User Defined inheritance
- private var nopud: Int = 0 # (NOPUD) Number of parents (direct superclasses)
- private var nopcud: Int = 0 # (NOPCUD) Number of class parents
- private var nopiud: Int = 0 # (NOPIUD) Number of interface parents
- private var noaud: Int = 0 # (NOAUD) Number of ancestors (direct and indirect)
- private var noacud: Int = 0 # (NOACUD) Number of class ancestors
- private var noaiud: Int = 0 # (NOAIUD) Number of interface ancestors
- private var nocud: Int = 0 # (NOCUD) Number of children (direct subclasses)
- private var noccud: Int = 0 # (NOCCUD) Number of class children
- private var nociud: Int = 0 # (NOCIUD) Number of interface children
- private var nodud: Int = 0 # (NODUD) Number of descendants (direct and indirect)
- private var nodcud: Int = 0 # (NODCUD) Number of class descendants
- private var nodiud: Int = 0 # (NODIUD) Number of interface descendants
- private var ditud: Int = 0 # (DITUD) Depth in Inheritance Tree (maximum distance to root of the hierarchy)
- private var ditcud: Int = 0 # (DITCUD) Length of longest path to the root hierarchy and consisting only of extends edges
- private var ditiud: Int = 0 # (DITIUD) Length of longest path to the root hierarchy and consisting only of extends implements
-
- private fun compute_class_inheritance_metrics(model: Model) do
- # inheritance metrics
- self.nop = parents.length
- self.nopc = model.extract_classes(parents).length
- self.nopi = model.extract_interfaces(parents).length
- 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.nod = descendants.length
- self.nodc = model.extract_classes(descendants).length
- self.nodi = model.extract_interfaces(descendants).length
- self.dit = path_to_object.length
- self.ditc = class_path_to_object.length
- self.diti = interface_path_to_object.length
-
- # used defined metrics
- self.nopud = model.extract_user_defined(parents).length
- self.nopcud = model.extract_user_defined(model.extract_classes(parents)).length
- self.nopiud = model.extract_user_defined(model.extract_interfaces(parents)).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.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.ditud = ud_path_to_object.length
- self.ditcud = ud_class_path_to_object.length
- self.ditiud = ud_interface_path_to_object.length
- end
-
- fun is_class: Bool do
- return self.kind == concrete_kind or self.kind == abstract_kind
- end
-
- fun is_interface: Bool do
- return self.kind == interface_kind
- end
-
- fun is_abstract: Bool do
- return self.kind == abstract_kind
- end
-
- fun is_user_defined: Bool do
- return self.intro_mmodule.is_user_defined
- end
-
- # Get parents of the class (direct super classes only)
- 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)
+# 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
- return lst
+ 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
- # Get ancestors of the class (all super classes)
- 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)
+# 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
- return lst
end
+end
- # Get children of the class (direct subclasses only)
- 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)
+# 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
- return lst
end
+end
- # Get children of the class (direct subclasses only)
- 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)
+# 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
- return lst
end
+end
- # Return the longest path from class to root hierarchy
- fun path_to_object: Array[MClass] do
- var path = new Array[MClass]
- var max_dit: nullable Int = null
- var max_parent: nullable MClass = null
- var parent_path: nullable Array[MClass] = null
-
- for p in parents do
- var dit = p.path_to_object.length
- if max_dit == null or dit >= max_dit then
- max_dit = dit
- max_parent = p
- parent_path = p.path_to_object
+# 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
-
- if max_parent != null and parent_path != null then
- path.add(max_parent)
- path.add_all(parent_path)
- end
-
- return path
end
+end
- # Return the longest path from class to root hierarchy
- 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
- var max_parent: nullable MClass = null
- var parent_path: nullable Array[MClass] = null
-
- for p in parents do
- var dit = p.ud_path_to_object.length
- if max_dit == null or dit >= max_dit then
- max_dit = dit
- max_parent = p
- parent_path = p.ud_path_to_object
+# 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
-
- if max_parent != null and parent_path != null then
- path.add(max_parent)
- path.add_all(parent_path)
- end
-
- return path
end
+end
- # Return the longest path from class to root hierarchy following only classes relations
- 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
- var max_parent: nullable MClass = null
- var parent_path: nullable Array[MClass] = null
-
- for p in parents do
- var dit = p.class_path_to_object.length
- if max_dit == null or dit >= max_dit then
- max_dit = dit
- max_parent = p
- parent_path = p.class_path_to_object
+# 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
-
- if max_parent != null and parent_path != null then
- path.add(max_parent)
- path.add_all(parent_path)
- end
-
- return path
end
+end
- # Return the longest path from class to root hierarchy following only interfaces relations
- 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
- var max_parent: nullable MClass = null
- var parent_path: nullable Array[MClass] = null
-
- for p in parents do
- var dit = p.interface_path_to_object.length
- if max_dit == null or dit >= max_dit then
- max_dit = dit
- max_parent = p
- parent_path = p.interface_path_to_object
+# 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
-
- if max_parent != null and parent_path != null then
- path.add(max_parent)
- path.add_all(parent_path)
- end
-
- return path
end
+end
- # Return the longest path from class to root hierarchy following only ud classes relations
- 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
- var max_parent: nullable MClass = null
- var parent_path: nullable Array[MClass] = null
-
- for p in parents do
- var dit = p.ud_class_path_to_object.length
- if max_dit == null or dit >= max_dit then
- max_dit = dit
- max_parent = p
- parent_path = p.ud_class_path_to_object
+# 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
-
- if max_parent != null and parent_path != null then
- path.add(max_parent)
- path.add_all(parent_path)
- end
-
- return path
end
+end
- # Return the longest path from class to root hierarchy following only ud interfaces relations
- 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
- var max_parent: nullable MClass = null
- var parent_path: nullable Array[MClass] = null
-
- for p in parents do
- var dit = p.ud_interface_path_to_object.length
- if max_dit == null or dit >= max_dit then
- max_dit = dit
- max_parent = p
- parent_path = p.ud_interface_path_to_object
+# 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
-
- if max_parent != null and parent_path != null then
- path.add(max_parent)
- path.add_all(parent_path)
- end
-
- return path
- end
-
- # * -> * DUI
-
- fun is_dui_eligible: Bool do
- for parent in parents do if parent.name != "Object" then return true
- return false
- end
- fun is_ccdui_eligible: Bool do
- if not is_class then return false
- for parent in parents do if parent.name != "Object" and parent.is_class then return true
- return false
- end
- fun is_cidui_eligible: Bool do
- if not is_class then return false
- for parent in parents do if parent.name != "Object" and parent.is_interface then return true
- return false
- end
- fun is_iidui_eligible: Bool do
- if not is_interface then return false
- for parent in parents do if parent.name != "Object" and parent.is_interface then return true
- return false
- end
- fun is_if_eligible(model: Model): Bool do return not children(model).is_empty
- 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
- return false
- end
- 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
- return false
- end
- 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
- return false
- end
-
- # SL -> * DUI
-
- fun is_sldui_eligible: Bool do
- if is_user_defined then return false
- for parent in parents do if parent.name != "Object" then return true
- return false
- end
- fun is_slccdui_eligible: Bool do
- if is_user_defined then return false
- if not is_class then return false
- for parent in parents do if parent.name != "Object" and parent.is_class then return true
- return false
- end
- fun is_slcidui_eligible: Bool do
- if is_user_defined then return false
- if not is_class then return false
- for parent in parents do if parent.name != "Object" and parent.is_interface then return true
- return false
- end
- fun is_sliidui_eligible: Bool do
- if is_user_defined then return false
- if not is_interface then return false
- for parent in parents do if parent.name != "Object" and parent.is_interface then return true
- return false
- end
- fun is_slif_eligible(model: Model): Bool do
- if is_user_defined then return false
- return not children(model).is_empty
- end
- 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
- return false
- end
- 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
- return false
- end
- 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
- return false
end
+end
- # SL -> SL
-
- 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
- return false
- end
- 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
- return false
- end
- 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
- return false
- end
- 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
- return false
- end
-
- # SL -> UD
-
- 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
- return false
- end
- 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
- return false
- end
- 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
- return false
- end
- 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
- return false
- end
-
- # UD -> *
-
- fun is_uddui_eligible: Bool do
- if not is_user_defined then return false
- for parent in parents do if parent.name != "Object" then return true
- return false
- end
- fun is_udccdui_eligible: Bool do
- if not is_user_defined then return false
- if not is_class then return false
- for parent in parents do if parent.name != "Object" and parent.is_class then return true
- return false
- end
- fun is_udcidui_eligible: Bool do
- if not is_user_defined then return false
- if not is_class then return false
- for parent in parents do if parent.name != "Object" and parent.is_interface then return true
- return false
- end
- fun is_udiidui_eligible: Bool do
- if not is_user_defined then return false
- if not is_interface then return false
- for parent in parents do if parent.name != "Object" and parent.is_interface then return true
- return false
- end
- fun is_udif_eligible(model: Model): Bool do
- if not is_user_defined then return false
- return not children(model).is_empty
- end
- 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
- return false
- end
- 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
- return false
- end
- 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
- return false
+# 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
- # UD -> SL
-
- fun is_udduisl_eligible: Bool do
- if not is_user_defined then return false
- for parent in parents do if not parent.is_user_defined and parent.name != "Object" then return true
- return false
- end
- fun is_udccduisl_eligible: Bool do
- if not is_user_defined then return false
- if not is_class then return false
- for parent in parents do if not parent.is_user_defined and parent.name != "Object" and parent.is_class then return true
- return false
- end
- fun is_udciduisl_eligible: Bool do
- if not is_user_defined then return false
- if not is_class then return false
- for parent in parents do if not parent.is_user_defined and parent.name != "Object" and parent.is_interface then return true
- return false
- end
- fun is_udiiduisl_eligible: Bool do
- if not is_user_defined then return false
- if not is_interface then return false
- for parent in parents do if not parent.is_user_defined and parent.name != "Object" and parent.is_interface then return true
- return false
+# 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
- # UD -> UD
-
- fun is_udduiud_eligible: Bool do
- if not is_user_defined then return false
- for parent in parents do if parent.name != "Object" and parent.is_user_defined then return true
- return false
- end
- fun is_udccduiud_eligible: Bool do
- if not is_user_defined then return false
- if not is_class then return false
- for parent in parents do if parent.name != "Object" and parent.is_class and parent.is_user_defined then return true
- return false
- end
- fun is_udciduiud_eligible: Bool do
- if not is_user_defined then return false
- if not is_class then return false
- for parent in parents do if parent.name != "Object" and parent.is_interface and parent.is_user_defined then return true
- return false
- end
- fun is_udiiduiud_eligible: Bool do
- if not is_user_defined then return false
- if not is_interface then return false
- for parent in parents do if parent.name != "Object" and parent.is_interface and parent.is_user_defined then return true
- return false
- end
- fun is_udifud_eligible(model: Model): Bool do
- if not is_user_defined then return false
- return not children(model).is_empty
- end
- 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
- return false
- end
- 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
- return false
- end
- 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
- return false
+# 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
-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
- protected var ngc : Int = 0 # (NGC) Number of Generic Classes
- protected var ngi : Int = 0 # (NGI) Number of Generic Interfaces
-
- private var dit = "" # (DIT) Global Depth in Inheritance Tree
- private var dui = "" # (DUI) Proportion of types that either implement an interface or extend another type other than Object
- private var ccdui = "" # (CCDUI) Proportion of classes that extend some other class.
- private var cidui = "" # (CIDUI) Proportion of classes that implement some other interface.
- private var iidui = "" # (IIDUI) Proportion of interfaces that extend some other interface.
- private var inhf = "" # (IF) Proportion of types Inherited From, that is, those types that are either extended or implemented
- private var ccif = "" # (CCIF) Proportion of classes extended by some other class.
- private var icif = "" # (ICIF) Proportion of interfaces implemented by some other class.
- private var iiif = "" # (IIIF) Proportion of interfaces extended by some other interface.
-
- fun compute_module_inheritance_metrics(model: Model) do
- var ditsum = 0
- var dui_count = 0
- var ccdui_count = 0
- var cidui_count = 0
- var iidui_count = 0
- var if_count = 0
- var ccif_count = 0
- var icif_count = 0
- var iiif_count = 0
-
- for mmodule in self.in_nesting.greaters do
- for mclass in mmodule.intro_mclasses do
- 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
- if mclass.is_cidui_eligible then cidui_count += 1
- if mclass.is_iidui_eligible then iidui_count += 1
- if mclass.is_if_eligible(model) then if_count += 1
- if mclass.is_ccif_eligible(model) then ccif_count += 1
- if mclass.is_icif_eligible(model) then icif_count += 1
- if mclass.is_iiif_eligible(model) then iiif_count += 1
+# 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
-
- self.nm = self.in_nesting.greaters.length
- dit = div(ditsum, nc + ni)
- dui = div(dui_count * 100, nc + ni)
- 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)
- ccif = div(ccif_count * 100, nc)
- icif = div(icif_count * 100, ni)
- iiif = div(iiif_count * 100, ni)
end
+end
- fun is_user_defined: Bool do
- return not self.model.std_modules.has(self.name)
+# 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
-# Print inheritance usage metrics
-fun compute_inheritance_metrics(toolcontext: ToolContext, model: Model)
-do
- # global summary metrics
- var nmd: Int = 0 # (NMD) Number of Modules
- var nc: Int = 0 # (NC) Number of Classes
- var ni: Int = 0 # (NI) Number of Interfaces
- var nac : Int = 0 # (NAC) Number of Abstract Classes
- var ngc : Int = 0 # (NGC) Number of Generic Classes
- var ngi : Int = 0 # (NGI) Number of Generic Interfaces
- # (SL) Std-Lib summary metrics
- var nmdsl: Int = 0 # (NMDSL) Number of Modules in Std-Lib
- var ncsl: Int = 0 # (NCSL) Number of Classes in Std-Lib
- var nisl: Int = 0 # (NISL) Number of Interfaces in Std-Lib
- var nacsl : Int = 0 # (NACSL) Number of Abstract Classes in Std-Lib
- var ngcsl : Int = 0 # (NGCSL) Number of Generic Classes in Std-Lib
- var ngisl : Int = 0 # (NGISL) Number of Generic Interfaces in Std-Lib
- # (UD) User-Defined summary metrics
- var nmdud: Int = 0 # (NMDUD) Number of Modules User Defined
- var ncud: Int = 0 # (NCUD) Number of Classes User Defined
- var niud: Int = 0 # (NIUD) Number of Interfaces User Defined
- var nacud : Int = 0 # (NACUD) Number of Abstract Classes User Defined
- var ngcud : Int = 0 # (NGCUD) Number of Generic Classes User Defined
- var ngiud : Int = 0 # (NGIUD) Number of Generic Interfaces User Defined
-
- # global summary inheritance metrics
- var dit = "" # (DIT) Global Depth in Inheritance Tree
- var dui = "" # (DUI) Proportion of types that either implement an interface or extend another type other than Object
- var ccdui = "" # (CCDUI) Proportion of classes that extend some other class.
- var cidui = "" # (CIDUI) Proportion of classes that implement some other interface.
- var iidui = "" # (IIDUI) Proportion of interfaces that extend some other interface.
- var inhf = "" # (IF) Proportion of types Inherited From, that is, those types that are either extended or implemented
- var ccif = "" # (CCIF) Proportion of classes extended by some other class.
- var icif = "" # (ICIF) Proportion of interfaces implemented by some other class.
- var iiif = "" # (IIIF) Proportion of interfaces extended by some other interface.
-
- # (SL -> *) Std-Lib summary inheritance metrics
- var sldui = "" # (SLDUI) Proportion of std-lib types that either implement an interface or extend another std-lib type other than Object
- var slccdui = "" # (SLCCDUI) Proportion of std-lib classes that extend some other std-lib class.
- var slcidui = "" # (SLCIDUI) Proportion of std-lib classes that implement some other std-lib interface.
- var sliidui = "" # (SLIIDUI) Proportion of std-lib interfaces that extend some other std-lib interface.
- var slinhf = "" # (SLIF) Proportion of SL types Inherited From, that is, those types that are either extended or implemented
- var slccif = "" # (SLCCIF) Proportion of SL classes extended by some other class.
- var slicif = "" # (SLICIF) Proportion of SL interfaces implemented by some other class.
- var sliiif = "" # (SLIIIF) Proportion of SL interfaces extended by some other interface.
-
- # (SL -> SL) Std-Lib summary inheritance metrics
- var slinhfsl = "" # (SLIFSL) Proportion of SL types Inherited From, that is, those types that are either extended or implemented by a SL type
- var slccifsl = "" # (SLCCIFSL) Proportion of SL classes extended by some other SL class.
- var slicifsl = "" # (SLICIFSL) Proportion of SL interfaces implemented by some other SL class.
- var sliiifsl = "" # (SLIIIFSL) Proportion of SL interfaces extended by some other SL interface.
-
- # (SL -> UD) Std-Lib summary inheritance metrics
- var slinhfud = "" # (SLIFUD) Proportion of SL types Inherited From, that is, those types that are either extended or implemented by a UD type
- var slccifud = "" # (SLCCIFUD) Proportion of SL classes extended by some other UD class.
- var slicifud = "" # (SLICIFUD) Proportion of SL interfaces implemented by some other UD class.
- var sliiifud = "" # (SLIIIFUD) Proportion of SL interfaces extended by some other UD interface.
-
- # (UD -> *) User-defined summary inheritance metrics
- var uddui = "" # (UDDUI) Proportion user-defined of types that either implement an interface or extend another type
- var udccdui = "" # (UDCCDUI) Proportion of user-defined classes that extend some other class.
- var udcidui = "" # (UDCIDUI) Proportion of user-defined classes that implement some other interface.
- var udiidui = "" # (UDIIDUI) Proportion of user-defined interfaces that extend some other interface.
- var udinhf = "" # (UDIF) Proportion of UD types Inherited From, that is, those types that are either extended or implemented
- var udccif = "" # (UDCCIF) Proportion of UD classes extended by some other class.
- var udicif = "" # (UDICIF) Proportion of UD interfaces implemented by some other class.
- var udiiif = "" # (UDIIIF) Proportion of UD interfaces extended by some other interface.
-
- # (UD -> SL) User-defined summary inheritance metrics
- var udduisl = "" # (UDDUISL) Proportion user-defined of types that either implement an interface or extend another type SL
- var udccduisl = "" # (UDCCDUISL) Proportion of user-defined classes that extend some other SL class.
- var udciduisl = "" # (UDCIDUISL) Proportion of user-defined classes that implement some other SL interface.
- var udiiduisl = "" # (UDIIDUISL) Proportion of user-defined interfaces that extend some other SL interface.
-
- # (UD -> UD) User-defined summary inheritance metrics
- 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 udiiduiud = "" # (UDIIDUIUD) Proportion of user-defined interfaces that extend some other user-defined interface.
- var udinhfud = "" # (UDIFUD) Proportion of UD types Inherited From, that is, those types that are either extended or implemented by another UD type
- var udccifud = "" # (UDCCIFUD) Proportion of UD classes extended by some other UD class.
- var udicifud = "" # (UDICIFUD) Proportion of UD interfaces implemented by some other UD class.
- var udiiifud = "" # (UDIIIFUD) Proportion of UD interfaces extended by some other UD interface.
-
- # compute modules scalar metrics
- for mmodule in model.mmodules do
- mmodule.compute_module_inheritance_metrics(model)
+# 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
- # compute class scalar metrics
- for mclass in model.mclasses do
- mclass.compute_class_inheritance_metrics(model)
+# 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
- # compute summary metrics
-
- # compute global summary metrics
- nmd = model.mmodules.length
- nc = model.extract_classes(model.mclasses).length
- ni = model.extract_interfaces(model.mclasses).length
- nac = model.extract_abstracts(model.mclasses).length
- ngc = model.extract_generics(model.extract_classes(model.mclasses)).length
- ngi = model.extract_generics(model.extract_interfaces(model.mclasses)).length
- # compute std-lib summary metrics
- nmdsl = model.extract_stdlib_modules(model.mmodules).length
- ncsl = model.extract_stdlib(model.extract_classes(model.mclasses)).length
- nisl = model.extract_stdlib(model.extract_interfaces(model.mclasses)).length
- nacsl = model.extract_stdlib(model.extract_abstracts(model.mclasses)).length
- ngcsl = model.extract_stdlib(model.extract_generics(model.extract_classes(model.mclasses))).length
- ngisl = model.extract_stdlib(model.extract_generics(model.extract_interfaces(model.mclasses))).length
- # compute user-defined summary metrics
- nmdud = model.extract_user_defined_modules(model.mmodules).length
- ncud = model.extract_user_defined(model.extract_classes(model.mclasses)).length
- niud = model.extract_user_defined(model.extract_interfaces(model.mclasses)).length
- nacud = model.extract_user_defined(model.extract_abstracts(model.mclasses)).length
- ngcud = model.extract_user_defined(model.extract_generics(model.extract_classes(model.mclasses))).length
- ngiud = model.extract_user_defined(model.extract_generics(model.extract_interfaces(model.mclasses))).length
-
- # compute inheritance summary metrics
-
- # * -> *
- var ditsum = 0
- var dui_count = 0
- var ccdui_count = 0
- var cidui_count = 0
- var iidui_count = 0
- var if_count = 0
- var ccif_count = 0
- var icif_count = 0
- var iiif_count = 0
-
- # SL -> *
- var sldui_count = 0
- var slccdui_count = 0
- var slcidui_count = 0
- var sliidui_count = 0
- var slif_count = 0
- var slccif_count = 0
- var slicif_count = 0
- var sliiif_count = 0
-
- # SL -> SL
- var slifsl_count = 0
- var slccifsl_count = 0
- var slicifsl_count = 0
- var sliiifsl_count = 0
-
- # SL -> UD
- var slifud_count = 0
- var slccifud_count = 0
- var slicifud_count = 0
- var sliiifud_count = 0
-
- # UD -> *
- var uddui_count = 0
- var udccdui_count = 0
- var udcidui_count = 0
- var udiidui_count = 0
- var udif_count = 0
- var udccif_count = 0
- var udicif_count = 0
- var udiiif_count = 0
-
- # UD -> SL
- var udduisl_count = 0
- var udccduisl_count = 0
- var udciduisl_count = 0
- var udiiduisl_count = 0
-
- # UD -> UD
- var udduiud_count = 0
- var udccduiud_count = 0
- var udciduiud_count = 0
- var udiiduiud_count = 0
- var udifud_count = 0
- var udccifud_count = 0
- var udicifud_count = 0
- var udiiifud_count = 0
-
- for mclass in model.mclasses do
- ditsum += mclass.dit
-
- # * -> *
- if mclass.is_dui_eligible then dui_count += 1
- if mclass.is_ccdui_eligible then ccdui_count += 1
- if mclass.is_cidui_eligible then cidui_count += 1
- if mclass.is_iidui_eligible then iidui_count += 1
- if mclass.is_if_eligible(model) then if_count += 1
- if mclass.is_ccif_eligible(model) then ccif_count += 1
- if mclass.is_icif_eligible(model) then icif_count += 1
- if mclass.is_iiif_eligible(model) then iiif_count += 1
-
- # SL -> *
- if mclass.is_sldui_eligible then sldui_count += 1
- if mclass.is_slccdui_eligible then slccdui_count += 1
- if mclass.is_slcidui_eligible then slcidui_count += 1
- if mclass.is_sliidui_eligible then sliidui_count += 1
- if mclass.is_slif_eligible(model) then slif_count += 1
- if mclass.is_slccif_eligible(model) then slccif_count += 1
- if mclass.is_slicif_eligible(model) then slicif_count += 1
- if mclass.is_sliiif_eligible(model) then sliiif_count += 1
-
- # SL -> SL
- if mclass.is_slifsl_eligible(model) then slifsl_count += 1
- if mclass.is_slccifsl_eligible(model) then slccifsl_count += 1
- if mclass.is_slicifsl_eligible(model) then slicifsl_count += 1
- if mclass.is_sliiifsl_eligible(model) then sliiifsl_count += 1
-
- # SL -> UD
- if mclass.is_slifud_eligible(model) then slifud_count += 1
- if mclass.is_slccifud_eligible(model) then slccifud_count += 1
- if mclass.is_slicifud_eligible(model) then slicifud_count += 1
- if mclass.is_sliiifud_eligible(model) then sliiifud_count += 1
-
- # UD -> *
- if mclass.is_uddui_eligible then uddui_count += 1
- if mclass.is_udccdui_eligible then udccdui_count += 1
- if mclass.is_udcidui_eligible then udcidui_count += 1
- if mclass.is_udiidui_eligible then udiidui_count += 1
- if mclass.is_udif_eligible(model) then udif_count += 1
- if mclass.is_udccif_eligible(model) then udccif_count += 1
- if mclass.is_udicif_eligible(model) then udicif_count += 1
- if mclass.is_udiiif_eligible(model) then udiiif_count += 1
-
- # UD -> SL
- if mclass.is_udduisl_eligible then udduisl_count += 1
- if mclass.is_udccduisl_eligible then udccduisl_count += 1
- if mclass.is_udciduisl_eligible then udciduisl_count += 1
- if mclass.is_udiiduisl_eligible then udiiduisl_count += 1
-
- # UD -> UD
- if mclass.is_udduiud_eligible then udduiud_count += 1
- if mclass.is_udccduiud_eligible then udccduiud_count += 1
- if mclass.is_udciduiud_eligible then udciduiud_count += 1
- if mclass.is_udiiduiud_eligible then udiiduiud_count += 1
- if mclass.is_udifud_eligible(model) then udifud_count += 1
- if mclass.is_udccifud_eligible(model) then udccifud_count += 1
- if mclass.is_udicifud_eligible(model) then udicifud_count += 1
- if mclass.is_udiiifud_eligible(model) then udiiifud_count += 1
+# 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
- # * -> *
- dit = div(ditsum, model.mclasses.length)
- dui = div(dui_count * 100, nc + ni)
- 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)
- ccif = div(ccif_count * 100, nc)
- icif = div(icif_count * 100, ni)
- iiif = div(iiif_count * 100, ni)
-
- # SL -> *
- sldui = div(sldui_count * 100, ncsl + nisl)
- slccdui = div(slccdui_count * 100, ncsl)
- slcidui = div(slcidui_count * 100, ncsl)
- sliidui = div(sliidui_count * 100, nisl)
- slinhf = div(slif_count * 100, ncsl + nisl)
- slccif = div(slccif_count * 100, ncsl)
- slicif = div(slicif_count * 100, nisl)
- sliiif = div(sliiif_count * 100, nisl)
-
- # SL -> SL
- slinhfsl = div(slifsl_count * 100, ncsl + nisl)
- slccifsl = div(slccifsl_count * 100, ncsl)
- slicifsl = div(slicifsl_count * 100, nisl)
- sliiifsl = div(sliiifsl_count * 100, nisl)
-
- # SL -> UD
- slinhfud = div(slifud_count * 100, ncsl + nisl)
- slccifud = div(slccifud_count * 100, ncsl)
- slicifud = div(slicifud_count * 100, nisl)
- sliiifud = div(sliiifud_count * 100, nisl)
-
- # UD -> *
- uddui = div(uddui_count * 100, ncud + niud)
- udccdui = div(udccdui_count * 100, ncud)
- udcidui = div(udcidui_count * 100, ncud)
- udiidui = div(udiidui_count * 100, niud)
- udinhf = div(if_count * 100, ncud + niud)
- udccif = div(ccif_count * 100, ncud)
- udicif = div(icif_count * 100, niud)
- udiiif = div(iiif_count * 100, niud)
-
- # UD -> SL
- udduisl = div(udduisl_count * 100, ncud + niud)
- udccduisl = div(udccduisl_count * 100, ncud)
- udciduisl = div(udciduisl_count * 100, ncud)
- udiiduisl = div(udiiduisl_count * 100, niud)
+# model redef
- # UD -> UD
- udduiud = div(udduiud_count * 100, ncud + niud)
- udccduiud = div(udccduiud_count * 100, ncud)
- udciduiud = div(udciduiud_count * 100, ncud)
- udiiduiud = div(udiiduiud_count * 100, niud)
- udinhfud = div(udifud_count * 100, ncud + niud)
- udccifud = div(udccifud_count * 100, ncud)
- udicifud = div(udicifud_count * 100, niud)
- udiiifud = div(udiiifud_count * 100, niud)
+redef class MClass
- # CSV generation
- if toolcontext.opt_generate_csv.value then
- # summary_metrics
- var summaryCSV = new CSVDocument(toolcontext.output_dir.join_path("summary_metrics.csv"))
- summaryCSV.set_header("scope", "NMD", "NC", "NI", "NAC", "NGC", "NGI")
- summaryCSV.add_line("global", nmd, nc, ni, nac, ngc, ngi)
- 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, m.nm, m.nc, m.ni, m.nac, m.ngc, m.ngi)
+ # 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
- summaryCSV.save
-
- # inheritance metrics
- var inheritanceCSV = new CSVDocument(toolcontext.output_dir.join_path("inheritance_metrics.csv"))
- inheritanceCSV.set_header("scope", "DIT", "DUI", "CCDUI", "CIDUI", "IIDUI", "IF", "CCIF", "ICIF", "IIIF")
- inheritanceCSV.add_line("global", dit, dui, ccdui, cidui, iidui, inhf, ccif, icif, iiif)
- inheritanceCSV.add_line("SL -> *", "", sldui, slccdui, slcidui, sliidui, slinhf, slccif, slicif, sliiif)
- inheritanceCSV.add_line("SL -> SL", "", sldui, slccdui, slcidui, sliidui, slinhfsl, slccifsl, slicifsl, sliiifsl)
- 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)
- 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)
+ 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
- inheritanceCSV.save
+ if min == -1 then min = 0
+ return min
+ end
- # 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", "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.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)
+ # 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
- scalarCSV.save
- udscalarCSV.save
+ if min == -1 then min = 0
+ return min
end
-
- print "--- Global Summary metrics ---"
- print "(NMD) Number of Modules: {nmd}"
- print "(NC) Number of Classes: {nc}"
- print "(NI) Number of Interfaces: {ni}"
- print "(NAC) Number of Abstract Classes: {nac}"
- print "(NGC) Number of Generic Classes: {ngc}"
- print "(NGI) Number of Generic Interfaces: {ngi}"
- print "--- (SL) Std-Lib Summary metrics ---"
- print "(NMDSL) Number of Modules: {nmdsl}"
- print "(NCSL) Number of Classes: {ncsl}"
- print "(NISL) Number of Interfaces: {nisl}"
- print "(NACSL) Number of Abstract Classes: {nacsl}"
- print "(NGCSL) Number of Generic Classes: {ngcsl}"
- print "(NGISL) Number of Generic Interfaces: {ngisl}"
- print "--- (UD) User-Defined Summary metrics ---"
- print "(NMDUD) Number of Modules: {nmdud}"
- print "(NCUD) Number of Classes: {ncud}"
- print "(NIUD) Number of Interfaces: {niud}"
- print "(NACUD) Number of Abstract Classes: {nacud}"
- print "(NGCUD) Number of Generic Classes: {ngcud}"
- print "(NGIUD) Number of Generic Interfaces: {ngiud}"
- print ""
- print "--- Global Inheritance metrics ---"
- print "(DIT) Global Depth in Inheritance Tree: {dit}"
- print "(DUI) Proportion of types inheriting another type other than Object: {dui}%"
- print "(CCDUI) Proportion of classes that extend some other class: {ccdui}%"
- print "(CIDUI) Proportion of classes that implement some other interface: {cidui}%"
- print "(IIDUI) Proportion of interfaces that extend some other interface: {iidui}%"
- print "(IF) Proportion of types Inherited From: {inhf}%"
- print "(CCIF) Proportion of classes extended by class: {ccif}%"
- print "(ICIF) Proportion of interfaces implemented by class: {icif}%"
- print "(IIIF) Proportion of interfaces extended by interface: {iiif}%"
- print ""
- print "--- (SL -> *) Std-Lib Inheritance metrics ---"
- print "(SLDUI) Proportion of SL types inheriting another type other than Object: {sldui}%"
- print "(SLCCDUI) Proportion of SL classes that extend some other class: {slccdui}%"
- print "(SLCIDUI) Proportion of SL classes that implement some other interface: {slcidui}%"
- print "(SLIIDUI) Proportion of SL interfaces that extend some other interface: {sliidui}%"
- print "(SLIF) Proportion of SL types Inherited From: {slinhf}%"
- print "(SLCCIF) Proportion of SL classes extended by class: {slccif}%"
- print "(SLICIF) Proportion of SL interfaces implemented by class: {slicif}%"
- print "(SLIIIF) Proportion of SL interfaces extended by interface: {sliiif}%"
- print ""
- print "--- (SL -> SL) Std-Lib Inheritance metrics ---"
- print "(SLIFSL) Proportion of SL types Inherited From by SL type: {slinhfsl}%"
- print "(SLCCIFSL) Proportion of SL classes extended by SL class: {slccifsl}%"
- print "(SLICIFSL) Proportion of SL interfaces implemented by SL class: {slicifsl}%"
- print "(SLIIIFSL) Proportion of SL interfaces extended by SL interface: {sliiifsl}%"
- print ""
- print "--- (SL->UD) Std-Lib Inheritance metrics ---"
- print "(SLIFUD) Proportion of SL types Inherited From by UD type: {slinhfud}%"
- print "(SLCCIFUD) Proportion of SL classes extended by UD class: {slccifud}%"
- print "(SLICIFUD) Proportion of SL interfaces implemented by UD class: {slicifud}%"
- print "(SLIIIFUD) Proportion of SL interfaces extended by UD interface: {sliiifud}%"
- print ""
- print "--- (UD->*) User-Defined Inheritance metrics ---"
- print "(UDDUI) Proportion of UD types inheriting another type other than Object: {uddui}%"
- print "(UDCCDUI) Proportion of UD classes that extend some other class: {udccdui}%"
- print "(UDCIDUI) Proportion of UD classes that implement some other interface: {udcidui}%"
- print "(UDIIDUI) Proportion of UD interfaces that extend some other interface: {udiidui}%"
- print "(UDIF) Proportion of UD types Inherited From: {udinhf}%"
- print "(UDCCIF) Proportion of UD classes extended by class: {udccif}%"
- print "(UDICIF) Proportion of UD interfaces implemented by class: {udicif}%"
- print "(UDIIIF) Proportion of UD interfaces extended by interface: {udiiif}%"
- print ""
- print "--- (UD -> SL) User-Defined Inheritance metrics ---"
- print "(UDDUISL) Proportion of UD types inheriting another type other SL type: {udduisl}%"
- print "(UDCCDUISL) Proportion of UD classes that extend some other SL class: {udccduisl}%"
- print "(UDCIDUISL) Proportion of UD classes that implement some other SL interface: {udciduisl}%"
- print "(UDIIDUISL) Proportion of UD interfaces that extend some other SL interface: {udiiduisl}%"
- print ""
- print "--- (UD -> UD) User-Defined Inheritance metrics ---"
- print "(UDDUIUD) Proportion of UD types inheriting another type other UD type: {udduiud}%"
- print "(UDCCDUIUD) Proportion of UD classes that extend some other UD class: {udccduiud}%"
- print "(UDCIDUIUD) Proportion of UD classes that implement some other UD interface: {udciduiud}%"
- print "(UDIIDUIUD) Proportion of UD interfaces that extend some other UD interface: {udiiduiud}%"
- print "(UDIFUD) Proportion of UD types Inherited From: {udinhfud}%"
- print "(UDCCIFUD) Proportion of UD classes extended by UD class: {udccifud}%"
- print "(UDICIFUD) Proportion of UD interfaces implemented by UD class: {udicifud}%"
- print "(UDIIIFUD) Proportion of UD interfaces extended by UD interface: {udiiifud}%"
end
-
-# TODO Third-Party metrics
-# TODO CSV generation
-# TODO gnu-plot generation