metrics: extract mclasses_metrics in its own nit module
[nit.git] / src / metrics / inheritance_metrics.nit
index f9b0b15..015dbe1 100644 (file)
@@ -19,72 +19,81 @@ module inheritance_metrics
 
 import model
 private import metrics_base
+import phase
+import frontend
 
-redef class Model
+redef class ToolContext
+       var inheritance_metrics_phase = new InheritanceMetricsPhase(self, null)
+end
+
+# Extract metrics about module and class hierarchies.
+private class InheritanceMetricsPhase
+       super Phase
+       redef fun process_mainmodule(mainmodule)
+       do
+               if not toolcontext.opt_inheritance.value and not toolcontext.opt_all.value then return
+               compute_inheritance_metrics(toolcontext, toolcontext.modelbuilder.model)
+       end
+end
 
+redef class Model
        # Extract the subset of classes from a set of mclass
-       fun extract_classes(mclasses: Collection[MClass]): Set[MClass] do
+       private fun extract_classes(mclasses: Collection[MClass]): Set[MClass] do
                var lst = new HashSet[MClass]
                for mclass in mclasses do if mclass.is_class then lst.add(mclass)
                return lst
        end
 
        # Extract the subset of interfaces from a set of mclass
-       fun extract_interfaces(mclasses: Collection[MClass]): Set[MClass] do
+       private fun extract_interfaces(mclasses: Collection[MClass]): Set[MClass] do
                var lst = new HashSet[MClass]
                for mclass in mclasses do if mclass.is_interface then lst.add(mclass)
                return lst
        end
 
        # Extract the subset of generic classes/interfaces from a set of mclass
-       fun extract_generics(mclasses: Collection[MClass]): Set[MClass] do
+       private fun extract_generics(mclasses: Collection[MClass]): Set[MClass] do
                var lst = new HashSet[MClass]
                for mclass in mclasses do if mclass.arity > 0 then lst.add(mclass)
                return lst
        end
 
        # Extract the subset of abstract classes from a set of mclass
-       fun extract_abstracts(mclasses: Collection[MClass]): Set[MClass] do
+       private fun extract_abstracts(mclasses: Collection[MClass]): Set[MClass] do
                var lst = new HashSet[MClass]
                for mclass in mclasses do if mclass.is_abstract then lst.add(mclass)
                return lst
        end
 
        # Extract the subset of user defined classes/interfaces from a set of mclass
-       fun extract_user_defined(mclasses: Collection[MClass]): Set[MClass] do
+       private fun extract_user_defined(mclasses: Collection[MClass]): Set[MClass] do
                var lst = new HashSet[MClass]
                for mclass in mclasses do if mclass.is_user_defined then lst.add(mclass)
                return lst
        end
 
        # Extract the subset of user defined modules from a set of mmodules
-       fun extract_user_defined_modules(mmodules: Collection[MModule]): Set[MModule] do
+       private fun extract_user_defined_modules(mmodules: Collection[MModule]): Set[MModule] do
                var lst = new HashSet[MModule]
                for mmodule in mmodules do if mmodule.is_user_defined then lst.add(mmodule)
                return lst
        end
 
        # Extract the subset of classes/interfaces from sl lib
-       fun extract_stdlib(mclasses: Collection[MClass]): Set[MClass] do
+       private fun extract_stdlib(mclasses: Collection[MClass]): Set[MClass] do
                var lst = new HashSet[MClass]
                for mclass in mclasses do if not mclass.is_user_defined then lst.add(mclass)
                return lst
        end
 
        # Extract the subset of user defined modules from std lib
-       fun extract_stdlib_modules(mmodules: Collection[MModule]): Set[MModule] do
+       private fun extract_stdlib_modules(mmodules: Collection[MModule]): Set[MModule] do
                var lst = new HashSet[MModule]
                for mmodule in mmodules do if not mmodule.is_user_defined then lst.add(mmodule)
                return lst
        end
 end
 
-redef class MModule
-       private fun is_user_defined: Bool do
-               return self.public_owner == null or self.public_owner.name != "standard"
-       end
-end
-
 redef class MClass
        # Inheritance
        private var nop: Int = 0                # (NOP) Number of parents (direct superclasses)
@@ -120,7 +129,7 @@ redef class MClass
        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_scalar_metrics(model: Model) do
+       private fun compute_class_inheritance_metrics(model: Model) do
                # inheritance metrics
                self.nop = parents.length
                self.nopc = model.extract_classes(parents).length
@@ -128,15 +137,15 @@ redef class MClass
                self.noa = ancestors.length
                self.noac = model.extract_classes(ancestors).length
                self.noai = model.extract_interfaces(ancestors).length
-               self.noc = children(model).length
-               self.nocc = model.extract_classes(children(model)).length
-               self.noci = model.extract_interfaces(children(model)).length
+               self.noc = children.length
+               self.nocc = model.extract_classes(children).length
+               self.noci = model.extract_interfaces(children).length
                self.nod = descendants.length
                self.nodc = model.extract_classes(descendants).length
                self.nodi = model.extract_interfaces(descendants).length
-               self.dit = depth_from_object
-               self.ditc = model.extract_classes(path_to_object).length
-               self.diti = model.extract_interfaces(path_to_object).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
@@ -145,119 +154,155 @@ redef class MClass
                self.noaud = model.extract_user_defined(ancestors).length
                self.noacud = model.extract_user_defined(model.extract_classes(ancestors)).length
                self.noaiud = model.extract_user_defined(model.extract_interfaces(ancestors)).length
-               self.nocud = model.extract_user_defined(children(model)).length
-               self.noccud = model.extract_user_defined(model.extract_classes(children(model))).length
-               self.nociud = model.extract_user_defined(model.extract_interfaces(children(model))).length
+               self.nocud = model.extract_user_defined(children).length
+               self.noccud = model.extract_user_defined(model.extract_classes(children)).length
+               self.nociud = model.extract_user_defined(model.extract_interfaces(children)).length
                self.nodud = model.extract_user_defined(descendants).length
                self.nodcud = model.extract_user_defined(model.extract_classes(descendants)).length
                self.nodiud = model.extract_user_defined(model.extract_interfaces(descendants)).length
-               self.ditud = model.extract_user_defined(path_to_object).length
-               self.ditcud = model.extract_user_defined(model.extract_classes(path_to_object)).length
-               self.ditiud = model.extract_user_defined(model.extract_interfaces(path_to_object)).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
 
-       private fun is_class: Bool do
-               return self.kind == concrete_kind or self.kind == abstract_kind
-       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
 
-       private fun is_interface: Bool do
-               return self.kind == interface_kind
-       end
+               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
+               end
 
-       private fun is_abstract: Bool do
-               return self.kind == abstract_kind
-       end
+               if max_parent != null and parent_path != null then
+                       path.add(max_parent)
+                       path.add_all(parent_path)
+               end
 
-       private fun is_user_defined: Bool do
-               var mod = self.intro_mmodule.public_owner
-               return mod == null or mod.name != "standard"
+               return path
        end
 
-       # Get parents of the class (direct super classes only)
-       private fun parents: Set[MClass] do
-               var lst = new HashSet[MClass]
-               # explore all definitions of the class (refinement)
-               for mclassdef in self.mclassdefs do
-                       for parent in mclassdef.supertypes do
-                               lst.add(parent.mclass)
+       # 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
+
+               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
                        end
                end
-               return lst
-       end
 
-       # Get ancestors of the class (all super classes)
-       private fun ancestors: Set[MClass] do
-               var lst = new HashSet[MClass]
-               for mclassdef in self.mclassdefs do
-                       for super_mclassdef in mclassdef.in_hierarchy.greaters do
-                               if super_mclassdef == mclassdef then continue  # skip self
-                               lst.add(super_mclassdef.mclass)
-                       end
+               if max_parent != null and parent_path != null then
+                       path.add(max_parent)
+                       path.add_all(parent_path)
                end
-               return lst
+
+               return path
        end
 
-       # Get children of the class (direct subclasses only)
-       private fun children(model: Model): Set[MClass] do
-               var lst = new HashSet[MClass]
-               for other in model.mclasses do
-                       if other == self then continue # skip self
-                       if other.parents.has(self) then
-                               lst.add(other)
+       # 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
+
+               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
                        end
                end
-               return lst
+
+               if max_parent != null and parent_path != null then
+                       path.add(max_parent)
+                       path.add_all(parent_path)
+               end
+
+               return path
        end
 
-       # Get children of the class (direct subclasses only)
-       private fun descendants: Set[MClass] do
-               var lst = new HashSet[MClass]
-               for mclassdef in self.mclassdefs do
-                       for sub_mclassdef in mclassdef.in_hierarchy.smallers do
-                               if sub_mclassdef == mclassdef then continue  # skip self
-                               lst.add(sub_mclassdef.mclass)
+       # 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
+
+               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
                end
-               return lst
+
+               if max_parent != null and parent_path != null then
+                       path.add(max_parent)
+                       path.add_all(parent_path)
+               end
+
+               return path
        end
 
-       # Get the length of the longest path to Object
-       private fun depth_from_object: Int do
-               var max_depth: nullable Int = null
-
-               for parent in self.parents do
-                       # direct parent is root
-                       if parent.parents.is_empty then
-                               return 1
-                       else
-                               var depth = parent.depth_from_object + 1
-                               if max_depth == null then
-                                       max_depth = depth
-                               else
-                                       if depth > max_depth then max_depth = depth
-                               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
+
+               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
                        end
                end
-               if max_depth == null then
-                       return 0
-               else
-                       return max_depth
+
+               if max_parent != null and parent_path != null then
+                       path.add(max_parent)
+                       path.add_all(parent_path)
                end
+
+               return path
        end
 
-       # Return the longest path from class to root hierarchy
-       private fun path_to_object: Array[MClass] do
+       # 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
 
                for p in parents do
-                       var dit = p.depth_from_object
+                       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.path_to_object
+                               parent_path = p.ud_interface_path_to_object
                        end
                end
 
@@ -290,20 +335,20 @@ redef class MClass
                for parent in parents do if parent.name != "Object" and parent.is_interface then return true
                return false
        end
-       private fun is_if_eligible(model: Model): Bool do return not children(model).is_empty
+       private fun is_if_eligible(model: Model): Bool do return not children.is_empty
        private fun is_ccif_eligible(model: Model): Bool do
                if not is_class then return false
-               for child in children(model) do if child.is_class then return true
+               for child in children do if child.is_class then return true
                return false
        end
        private fun is_icif_eligible(model: Model): Bool do
                if not is_interface then return false
-               for child in children(model) do if child.is_class then return true
+               for child in children do if child.is_class then return true
                return false
        end
        private fun is_iiif_eligible(model: Model): Bool do
                if not is_interface then return false
-               for child in children(model) do if child.is_interface then return true
+               for child in children do if child.is_interface then return true
                return false
        end
 
@@ -334,24 +379,24 @@ redef class MClass
        end
        private fun is_slif_eligible(model: Model): Bool do
                if is_user_defined then return false
-               return not children(model).is_empty
+               return not children.is_empty
        end
        private fun is_slccif_eligible(model: Model): Bool do
                if is_user_defined then return false
                if not is_class then return false
-               for child in children(model) do if child.is_class then return true
+               for child in children do if child.is_class then return true
                return false
        end
        private fun is_slicif_eligible(model: Model): Bool do
                if is_user_defined then return false
                if not is_interface then return false
-               for child in children(model) do if child.is_class then return true
+               for child in children do if child.is_class then return true
                return false
        end
        private fun is_sliiif_eligible(model: Model): Bool do
                if is_user_defined then return false
                if not is_interface then return false
-               for child in children(model) do if child.is_interface then return true
+               for child in children do if child.is_interface then return true
                return false
        end
 
@@ -359,25 +404,25 @@ redef class MClass
 
        private fun is_slifsl_eligible(model: Model): Bool do
                if is_user_defined then return false
-               for child in children(model) do if not child.is_user_defined then return true
+               for child in children do if not child.is_user_defined then return true
                return false
        end
        private fun is_slccifsl_eligible(model: Model): Bool do
                if is_user_defined then return false
                if is_class then return false
-               for child in children(model) do if not child.is_user_defined and child.is_class then return true
+               for child in children do if not child.is_user_defined and child.is_class then return true
                return false
        end
        private fun is_slicifsl_eligible(model: Model): Bool do
                if is_user_defined then return false
                if not is_interface then return false
-               for child in children(model) do if not child.is_user_defined and child.is_class then return true
+               for child in children do if not child.is_user_defined and child.is_class then return true
                return false
        end
        private fun is_sliiifsl_eligible(model: Model): Bool do
                if is_user_defined then return false
                if not is_interface then return false
-               for child in children(model) do if not child.is_user_defined and child.is_interface then return true
+               for child in children do if not child.is_user_defined and child.is_interface then return true
                return false
        end
 
@@ -385,25 +430,25 @@ redef class MClass
 
        private fun is_slifud_eligible(model: Model): Bool do
                if is_user_defined then return false
-               for child in children(model) do if child.is_user_defined then return true
+               for child in children do if child.is_user_defined then return true
                return false
        end
        private fun is_slccifud_eligible(model: Model): Bool do
                if is_user_defined then return false
                if not is_class then return false
-               for child in children(model) do if child.is_user_defined and child.is_class then return true
+               for child in children do if child.is_user_defined and child.is_class then return true
                return false
        end
        private fun is_slicifud_eligible(model: Model): Bool do
                if is_user_defined then return false
                if not is_interface then return false
-               for child in children(model) do if child.is_user_defined and child.is_class then return true
+               for child in children do if child.is_user_defined and child.is_class then return true
                return false
        end
        private fun is_sliiifud_eligible(model: Model): Bool do
                if is_user_defined then return false
                if not is_interface then return false
-               for child in children(model) do if child.is_user_defined and child.is_interface then return true
+               for child in children do if child.is_user_defined and child.is_interface then return true
                return false
        end
 
@@ -434,24 +479,24 @@ redef class MClass
        end
        private fun is_udif_eligible(model: Model): Bool do
                if not is_user_defined then return false
-               return not children(model).is_empty
+               return not children.is_empty
        end
        private fun is_udccif_eligible(model: Model): Bool do
                if not is_user_defined then return false
                if not is_class then return false
-               for child in children(model) do if child.is_class then return true
+               for child in children do if child.is_class then return true
                return false
        end
        private fun is_udicif_eligible(model: Model): Bool do
                if not is_user_defined then return false
                if not is_interface then return false
-               for child in children(model) do if child.is_class then return true
+               for child in children do if child.is_class then return true
                return false
        end
        private fun is_udiiif_eligible(model: Model): Bool do
                if not is_user_defined then return false
                if not is_interface then return false
-               for child in children(model) do if child.is_interface then return true
+               for child in children do if child.is_interface then return true
                return false
        end
 
@@ -508,27 +553,89 @@ redef class MClass
        end
        private fun is_udifud_eligible(model: Model): Bool do
                if not is_user_defined then return false
-               return not children(model).is_empty
+               return not children.is_empty
        end
        private fun is_udccifud_eligible(model: Model): Bool do
                if not is_user_defined then return false
                if not is_class then return false
-               for child in children(model) do if child.is_user_defined and child.is_class then return true
+               for child in children do if child.is_user_defined and child.is_class then return true
                return false
        end
        private fun is_udicifud_eligible(model: Model): Bool do
                if not is_user_defined then return false
                if not is_interface then return false
-               for child in children(model) do if child.is_user_defined and child.is_class then return true
+               for child in children do if child.is_user_defined and child.is_class then return true
                return false
        end
        private fun is_udiiifud_eligible(model: Model): Bool do
                if not is_user_defined then return false
                if not is_interface then return false
-               for child in children(model) do if child.is_user_defined and child.is_interface then return true
+               for child in children do if child.is_user_defined and child.is_interface then return true
                return false
        end
+end
+
+redef class MModule
+
+       private var nm: Int = 0                 # (NC)  Number of Modules
+       private var nc: Int = 0                 # (NC)  Number of Classes
+       private var ni: Int = 0                 # (NI)  Number of Interfaces
+       private var nac : Int = 0               # (NAC) Number of Abstract Classes
+       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.
+
+       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
+               var count = 0
+               for mmodule in self.in_nesting.greaters do
+                       for mclass in mmodule.intro_mclasses do
+                               count += 1
+                               if mclass.is_class then nc += 1
+                               if mclass.is_class and mclass.arity > 0 then ngc += 1
+                               if mclass.is_class and mclass.is_abstract then nac += 1
+                               if mclass.is_interface then ni += 1
+                               if mclass.is_interface and mclass.arity > 0 then ngi += 1
+                               ditsum += mclass.path_to_object.length
+                               if mclass.is_dui_eligible then dui_count += 1
+                               if mclass.is_ccdui_eligible then ccdui_count += 1
+                               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)
+       end
 end
 
 # Print inheritance usage metrics
@@ -606,6 +713,7 @@ do
        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.
@@ -615,9 +723,14 @@ do
        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 scalar metrics
+       # compute modules scalar metrics
+       for mmodule in model.mmodules do
+               mmodule.compute_module_inheritance_metrics(model)
+       end
+
+       # compute class scalar metrics
        for mclass in model.mclasses do
-               mclass.compute_scalar_metrics(model)
+               mclass.compute_class_inheritance_metrics(model)
        end
 
        # compute summary metrics
@@ -648,6 +761,8 @@ do
 
        # * -> *
        var ditsum = 0
+       var ditudsum = 0
+
        var dui_count = 0
        var ccdui_count = 0
        var cidui_count = 0
@@ -707,6 +822,7 @@ do
 
        for mclass in model.mclasses do
                ditsum += mclass.dit
+               ditudsum += mclass.ditud
 
                # * -> *
                if mclass.is_dui_eligible then dui_count += 1
@@ -769,7 +885,8 @@ do
 
        # * -> *
        dit = div(ditsum, model.mclasses.length)
-       dui = div(dui_count * 100, nc + ni)
+       ditud = div(ditudsum, ncud + niud)
+       dui = div(dui_count * 100, model.mclasses.length)
        ccdui = div(ccdui_count * 100, nc)
        cidui = div(cidui_count * 100, nc)
        iidui = div(iidui_count * 100, ni)
@@ -834,6 +951,9 @@ do
                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)
+               end
                summaryCSV.save
 
                # inheritance metrics
@@ -845,20 +965,24 @@ do
                inheritanceCSV.add_line("SL -> UD", "", 0, 0, 0, 0, slinhfud, slccifud, slicifud, sliiifud)
                inheritanceCSV.add_line("UD -> *", "", uddui, udccdui, udcidui, udiidui, udinhf, udccif, udicif, udiiif)
                inheritanceCSV.add_line("UD -> SL", "", udduisl, udccduisl, udciduisl, udiiduisl, 0, 0, 0, 0)
-               inheritanceCSV.add_line("UD -> UD", "", udduiud, udccduiud, udciduiud, udiiduiud, udinhfud, udccifud, udicifud, udiiifud)
+               inheritanceCSV.add_line("UD -> UD", ditud, udduiud, udccduiud, udciduiud, udiiduiud, udinhfud, udccifud, udicifud, udiiifud)
+               for m in model.mmodules do
+                       if m.intro_mclasses.is_empty and m.in_nesting.greaters.length == 1 then continue
+                       inheritanceCSV.add_line(m.name, m.dit, m.dui, m.ccdui, m.cidui, m.iidui, m.inhf, m.ccif, m.icif, m.iiif)
+               end
                inheritanceCSV.save
 
                # scalar metrics
                var scalarCSV = new CSVDocument(toolcontext.output_dir.join_path("global_scalar_metrics.csv"))
                var udscalarCSV = new CSVDocument(toolcontext.output_dir.join_path("ud_scalar_metrics.csv"))
-               scalarCSV.set_header("mclass", "type", "DIT", "DITC", "DITI", "NOP", "NOPC", "NOPI", "NOA", "NOAC", "NOAI", "NOC", "NOCC", "NOCI", "NOD", "NODC", "NODI")
-               udscalarCSV.set_header("mclass", "type", "DITUD", "DITCUD", "DITIUD", "NOPUD", "NOPCUD", "NOPIUD", "NOAUD", "NOACUD", "NOAIUD", "NOCUD", "NOCCUD", "NOCIUD", "NODUD", "NODCUD", "NODIUD")
+               scalarCSV.set_header("mclass", "type", "FT", "DIT", "DITC", "DITI", "NOP", "NOPC", "NOPI", "NOA", "NOAC", "NOAI", "NOC", "NOCC", "NOCI", "NOD", "NODC", "NODI")
+               udscalarCSV.set_header("mclass", "type", "FT","DITUD", "DITCUD", "DITIUD", "NOPUD", "NOPCUD", "NOPIUD", "NOAUD", "NOACUD", "NOAIUD", "NOCUD", "NOCCUD", "NOCIUD", "NODUD", "NODCUD", "NODIUD")
                for mclass in model.mclasses do
                        var name = mclass.name
                        var typ = "class"
                        if mclass.is_interface then typ = "interface"
-                       scalarCSV.add_line(name, typ, mclass.dit, mclass.ditc, mclass.diti, mclass.nop, mclass.nopc, mclass.nopi, mclass.noa, mclass.noac, mclass.noai, mclass.noc, mclass.nocc, mclass.noci, mclass.nod, mclass.nodc, mclass.nodi)
-                       udscalarCSV.add_line(name, typ, mclass.ditud, mclass.ditcud, mclass.ditiud, mclass.nopud, mclass.nopcud, mclass.nopiud, mclass.noaud, mclass.noacud, mclass.noaiud, mclass.nocud, mclass.noccud, mclass.nociud, mclass.nodud, mclass.nodcud, mclass.nodiud)
+                       scalarCSV.add_line(name, typ, mclass.arity, mclass.dit, mclass.ditc, mclass.diti, mclass.nop, mclass.nopc, mclass.nopi, mclass.noa, mclass.noac, mclass.noai, mclass.noc, mclass.nocc, mclass.noci, mclass.nod, mclass.nodc, mclass.nodi)
+                       udscalarCSV.add_line(name, typ, mclass.arity, mclass.ditud, mclass.ditcud, mclass.ditiud, mclass.nopud, mclass.nopcud, mclass.nopiud, mclass.noaud, mclass.noacud, mclass.noaiud, mclass.nocud, mclass.noccud, mclass.nociud, mclass.nodud, mclass.nodcud, mclass.nodiud)
                end
                scalarCSV.save
                udscalarCSV.save
@@ -947,5 +1071,4 @@ do
 end
 
 # TODO Third-Party metrics
-# TODO CSV generation
 # TODO gnu-plot generation