metrics: refactor inheritance metrics
authorAlexandre Terrasa <alexandre@moz-code.org>
Tue, 25 Feb 2014 04:50:16 +0000 (23:50 -0500)
committerAlexandre Terrasa <alexandre@moz-code.org>
Fri, 28 Feb 2014 18:35:16 +0000 (13:35 -0500)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

src/metrics/inheritance_metrics.nit

index 015dbe1..94bbd5f 100644 (file)
@@ -1,6 +1,6 @@
 # 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.
@@ -18,7 +18,8 @@
 module inheritance_metrics
 
 import model
-private import metrics_base
+import mmodules_metrics
+import mclasses_metrics
 import phase
 import frontend
 
@@ -26,1049 +27,476 @@ redef class ToolContext
        var inheritance_metrics_phase = new InheritanceMetricsPhase(self, null)
 end
 
-# Extract metrics about module and class hierarchies.
+# Extract metrics about inheritance from model.
 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
-end
-
-redef class Model
-       # Extract the subset of classes from a set of mclass
-       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
-       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
-       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
-       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
-       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
-       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
-       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
+               print "\n# Inheritance metrics".yellow.bold
+
+               var hmetrics = new InheritanceMetricSet
+               hmetrics.register(new MDUI, new MDUIC, new MDUII, new MIF, new MIFC, new MIFI)
+               
+               var cmetrics = new MClassMetricSet
+               cmetrics.register(new CNOA, new CNOP, new CNOC, new CNODC)
+               cmetrics.register(new CNOA, new CNOP, new CNOC, new CNODI)
+               cmetrics.register(new CDIT, new CDITI)
+
+               var model = toolcontext.modelbuilder.model
+               var mmodules = new HashSet[MModule]
+               var mclasses = new HashSet[MClass]
+               for mproject in model.mprojects do
+
+                       print "\n ## project {mproject}".bold
+
+                       for mgroup in mproject.mgroups do
+                               if mgroup.mmodules.is_empty then continue
+
+                               # Scalar metrics
+                               print "  `- 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.collect(new HashSet[MClass].from(mod_mclasses), mainmodule)
+                               for name, metric in cmetrics.metrics do
+                                       print "\t{name}: {metric.desc}".green
+                                       print "\t    avg: {metric.avg}".light_gray
+                                       var max = metric.max
+                                       print "\t    max: {max.first} ({max.second})".light_gray
+                                       var min = metric.min
+                                       print "\t    min: {min.first} ({min.second})".light_gray
+                               end
+                               hmetrics.collect(new HashSet[MModule].from(mgroup.mmodules), mainmodule)
+                               for name, metric in hmetrics.metrics do
+                                       print "\t{name}: {metric.desc}".green
+                                       print "\t    avg: {metric.avg}".light_gray
+                                       var max = metric.max
+                                       print "\t    max: {max.first} ({max.second})".light_gray
+                                       var min = metric.min
+                                       print "\t    min: {min.first} ({min.second})".light_gray
+                               end
+                       end
+               end
+               if not mclasses.is_empty then
+                       # Global metrics
+                       print "\n ## global metrics".bold
+                       cmetrics.collect(mclasses, mainmodule)
+                       for name, metric in cmetrics.metrics do
+                               print "\t{name}: {metric.desc}".green
+                               print "\t    avg: {metric.avg}".light_gray
+                               var max = metric.max
+                               print "\t    max: {max.first} ({max.second})".light_gray
+                               var min = metric.min
+                               print "\t    min: {min.first} ({min.second})".light_gray
+                       end
+                       hmetrics.collect(mmodules, mainmodule)
+                       for name, metric in hmetrics.metrics do
+                               print "\t{name}: {metric.desc}".green
+                               print "\t    avg: {metric.avg}".light_gray
+                               var max = metric.max
+                               print "\t    max: {max.first} ({max.second})".light_gray
+                               var min = metric.min
+                               print "\t    min: {min.first} ({min.second})".light_gray
+                       end
+               end
        end
+end
 
-       # Extract the subset of user defined modules from std lib
-       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
+# Metric Set used to collect data about inheritance in each module
+class InheritanceMetricSet
+       super MetricSet
+       redef type METRIC: InheritanceMetric
+       fun collect(mmodules: Set[MModule], mainmodule: MModule) do
+               clear
+               for metric in metrics.values do
+                       for mmodule in mmodules do
+                               metric.collect(mmodule, mainmodule)
+                       end
+               end
        end
 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.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.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).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.ditud = ud_path_to_object.length
-               self.ditcud = ud_class_path_to_object.length
-               self.ditiud = ud_interface_path_to_object.length
-       end
+# An abstract metric used to collect data about inheritance usage
+#
+# The metric is based on a module
+abstract class InheritanceMetric
+       super FloatMetric[MModule]
+       fun collect(mmodule: MModule, mainmodule: MModule) is abstract
+end
 
-       # Return the longest path from class to root hierarchy
-       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
-               var parent_path: nullable Array[MClass] = null
+# Module metric: proportion of MClasses Defined Using Inheritance
+#
+# Count MClasses that have another parents than Object
+class MDUI
+       super InheritanceMetric
+       redef fun name do return "mdui"
+       redef fun desc do return "proportion of mclass defined using inheritance (has other parent than Object)"
 
-               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
-                       end
+       redef fun collect(mmodule, mainmodule) do
+               var count = 0
+               for mclass in mmodule.intro_mclasses do
+                       if mclass.in_hierarchy(mainmodule).greaters.length > 2 then count += 1
                end
-
-               if max_parent != null and parent_path != null then
-                       path.add(max_parent)
-                       path.add_all(parent_path)
+               if mmodule.intro_mclasses.is_empty then
+                       values[mmodule] = 0.0
+               else
+                       values[mmodule] = count.to_f / mmodule.intro_mclasses.length.to_f
                end
-
-               return path
        end
+end
 
-       # Return the longest path from class to root hierarchy
-       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
-               var max_parent: nullable MClass = null
-               var parent_path: nullable Array[MClass] = null
+# Module metric: proportion of abstract, concrete and extern Classes Defined Using Inheritance
+#
+# Count classes that have another parents than Object
+class MDUIC
+       super InheritanceMetric
+       redef fun name do return "mduic"
+       redef fun desc do return "proportion of class_kind defined using inheritance"
 
-               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
+       redef fun collect(mmodule, mainmodule) 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 max_parent != null and parent_path != null then
-                       path.add(max_parent)
-                       path.add_all(parent_path)
+               if mmodule.intro_mclasses.is_empty then
+                       values[mmodule] = 0.0
+               else
+                       values[mmodule] = count.to_f / nb.to_f
                end
-
-               return path
        end
+end
 
-       # Return the longest path from class to root hierarchy following only classes relations
-       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
-               var max_parent: nullable MClass = null
-               var parent_path: nullable Array[MClass] = null
+# Module metric: proportion of Interface Defined Using Inheritance
+#
+# Count interface that have another parents than Object
+class MDUII
+       super InheritanceMetric
+       redef fun name do return "mduii"
+       redef fun desc do return "proportion of interface_kind defined using inheritance"
 
-               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
+       redef fun collect(mmodule, mainmodule) 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 max_parent != null and parent_path != null then
-                       path.add(max_parent)
-                       path.add_all(parent_path)
+               if mmodule.intro_mclasses.is_empty then
+                       values[mmodule] = 0.0
+               else
+                       values[mmodule] = count.to_f / nb.to_f
                end
-
-               return path
        end
+end
 
-       # Return the longest path from class to root hierarchy following only interfaces relations
-       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
-               var max_parent: nullable MClass = null
-               var parent_path: nullable Array[MClass] = null
+# Module metric: proportion of MClass Inherited From
+#
+# Count classes that have at least a child
+class MIF
+       super InheritanceMetric
+       redef fun name do return "mif"
+       redef fun desc do return "proportion of mclass inherited from"
 
-               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
-                       end
+       redef fun collect(mmodule, mainmodule) 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 max_parent != null and parent_path != null then
-                       path.add(max_parent)
-                       path.add_all(parent_path)
+               if mmodule.intro_mclasses.is_empty then
+                       values[mmodule] = 0.0
+               else
+                       values[mmodule] = count.to_f / mmodule.intro_mclasses.length.to_f
                end
-
-               return path
        end
+end
 
-       # Return the longest path from class to root hierarchy following only ud classes relations
-       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
-               var max_parent: nullable MClass = null
-               var parent_path: nullable Array[MClass] = null
+# Module metric: proportion of abstract, concrete and extern Class Inherited From
+#
+# Count classes that have at least a child
+class MIFC
+       super InheritanceMetric
+       redef fun name do return "mifc"
+       redef fun desc do return "proportion of class_kind inherited from"
 
-               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
+       redef fun collect(mmodule, mainmodule) 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 max_parent != null and parent_path != null then
-                       path.add(max_parent)
-                       path.add_all(parent_path)
+               if mmodule.intro_mclasses.is_empty then
+                       values[mmodule] = 0.0
+               else
+                       values[mmodule] = count.to_f / nb.to_f
                end
-
-               return path
        end
+end
 
-       # Return the longest path from class to root hierarchy following only ud interfaces relations
-       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
-               var max_parent: nullable MClass = null
-               var parent_path: nullable Array[MClass] = null
+# Module metric: proportion of Interface Inherited From
+#
+# Count interfaces that have at least a child
+class MIFI
+       super InheritanceMetric
+       redef fun name do return "mifi"
+       redef fun desc do return "proportion of interface_kind inherited from"
 
-               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
+       redef fun collect(mmodule, mainmodule) 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 max_parent != null and parent_path != null then
-                       path.add(max_parent)
-                       path.add_all(parent_path)
+               if mmodule.intro_mclasses.is_empty then
+                       values[mmodule] = 0.0
+               else
+                       values[mmodule] = count.to_f / nb.to_f
                end
-
-               return path
        end
+end
 
-       # * -> * DUI
+# MClass metric: Number of Class Ancestors
+#
+# Count only absrtract, concrete and extern classes
+class CNOAC
+       super MClassMetric
+       redef fun name do return "cnoac"
+       redef fun desc do return "number of class_kind ancestor"
 
-       private fun is_dui_eligible: Bool do
-               for parent in parents do if parent.name != "Object" then return true
-               return false
-       end
-       private 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
-       private 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
-       private 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
-       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 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 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 do if child.is_interface then return true
-               return false
+       redef fun collect(mclass, mainmodule) 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
 
-       # SL -> * DUI
+# MClass metric: Number of Class Parents
+#
+# Count only absrtract, concrete and extern classes
+class CNOPC
+       super MClassMetric
+       redef fun name do return "cnopc"
+       redef fun desc do return "number of class_kind parent"
 
-       private 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
-       private 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
-       private 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
-       private 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
-       private fun is_slif_eligible(model: Model): Bool do
-               if is_user_defined then return false
-               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 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 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 do if child.is_interface then return true
-               return false
+       redef fun collect(mclass, mainmodule) 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
 
-       # SL -> SL
+# MClass metric: Number of Class Children
+#
+# Count only absrtract, concrete and extern classes
+class CNOCC
+       super MClassMetric
+       redef fun name do return "cnocc"
+       redef fun desc do return "number of class_kind children"
 
-       private fun is_slifsl_eligible(model: Model): Bool do
-               if is_user_defined then return false
-               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 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 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 do if not child.is_user_defined and child.is_interface then return true
-               return false
+       redef fun collect(mclass, mainmodule) 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
 
-       # SL -> UD
+# MClass metric: Number of Class Descendants
+#
+# Count only absrtract, concrete and extern classes
+class CNODC
+       super MClassMetric
+       redef fun name do return "cnodc"
+       redef fun desc do return "number of class_kind descendants"
 
-       private fun is_slifud_eligible(model: Model): Bool do
-               if is_user_defined then return false
-               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 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 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 do if child.is_user_defined and child.is_interface then return true
-               return false
+       redef fun collect(mclass, mainmodule) 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
 
-       # UD -> *
+# MClass metric: Number of Interface Ancestors
+#
+# Count only interfaces
+class CNOAI
+       super MClassMetric
+       redef fun name do return "cnoai"
+       redef fun desc do return "number of interface_kind ancestor"
 
-       private 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
-       private 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
-       private 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
-       private 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
-       private fun is_udif_eligible(model: Model): Bool do
-               if not is_user_defined then return false
-               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 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 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 do if child.is_interface then return true
-               return false
+       redef fun collect(mclass, mainmodule) 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
 
-       # UD -> SL
+# MClass metric: Number of Interface Parents
+#
+# Count only interfaces
+class CNOPI
+       super MClassMetric
+       redef fun name do return "cnopi"
+       redef fun desc do return "number of interface_kind parent"
 
-       private 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
-       private 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
-       private 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
-       private 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
+       redef fun collect(mclass, mainmodule) 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
 
-       # UD -> UD
+# MClass metric: Number of Interface Children
+#
+# Count only interfaces
+class CNOCI
+       super MClassMetric
+       redef fun name do return "cnoci"
+       redef fun desc do return "number of interface_kind children"
 
-       private 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
-       private 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
-       private 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
-       private 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
-       private fun is_udifud_eligible(model: Model): Bool do
-               if not is_user_defined then return false
-               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 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 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 do if child.is_user_defined and child.is_interface then return true
-               return false
+       redef fun collect(mclass, mainmodule) 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
 
-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
-       private var ngc : Int = 0               # (NGC) Number of Generic Classes
-       private 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.
+# MClass metric: Number of Interface Descendants
+#
+# Count only interfaces
+class CNODI
+       super MClassMetric
+       redef fun name do return "cnodi"
+       redef fun desc do return "number of interface_kind descendants"
 
-       private 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
+       redef fun collect(mclass, mainmodule) do
                var count = 0
-               for mmodule in self.in_nesting.greaters do
-                       for mclass in mmodule.intro_mclasses do
+               for parent in mclass.in_hierarchy(mainmodule).smallers do
+                       if parent == mclass then continue
+                       if parent.kind == interface_kind then
                                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
-                               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
                        end
                end
-
-               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, count)
-               ccif = div(ccif_count * 100, nc)
-               icif = div(icif_count * 100, ni)
-               iiif = div(iiif_count * 100, ni)
+               values[mclass] = count
        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 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 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)
-       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
+       redef fun name do return "cditc"
+       redef fun desc do return "depth in class tree following only class, abstract, extern kind"
 
-       # compute class scalar metrics
-       for mclass in model.mclasses do
-               mclass.compute_class_inheritance_metrics(model)
+       redef fun collect(mclass, mainmodule) do
+               values[mclass] = mclass.ditc(mainmodule)
        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 ditudsum = 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
-               ditudsum += mclass.ditud
-
-               # * -> *
-               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
+# MClass metric: Interface Depth in Inheritance Tree
+#
+# Following the longest path composed only of implements edges from self to Object
+class CDITI
+       super MClassMetric
+       redef fun name do return "cditi"
+       redef fun desc do return "depth in class tree following only interface_kind"
 
-               # 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
+       redef fun collect(mclass, mainmodule) do
+               values[mclass] = mclass.diti(mainmodule)
        end
+end
 
-       # * -> *
-       dit = div(ditsum, model.mclasses.length)
-       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)
-       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)
-
-       # 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)
+# model redef
 
-       # 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)
+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
-               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", 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)
+               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
-               scalarCSV.save
-               udscalarCSV.save
+               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
-
-       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 gnu-plot generation