X-Git-Url: http://nitlanguage.org diff --git a/src/metrics/inheritance_metrics.nit b/src/metrics/inheritance_metrics.nit index 42a1220..527ece8 100644 --- a/src/metrics/inheritance_metrics.nit +++ b/src/metrics/inheritance_metrics.nit @@ -1,6 +1,6 @@ # This file is part of NIT ( http://www.nitlanguage.org ). # -# Copyright 2012 Jean Privat +# Copyright 2014 Alexandre Terrasa # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,1034 +17,530 @@ # Collect metrics about inheritance usage module inheritance_metrics -import model -private import metrics_base +import metrics_base +import mmodules_metrics +import mclasses_metrics -redef class Model +redef class ToolContext - # List of modules in std lib - # FIXME this is quite ugly, find a dynamic way... - fun std_modules: Set[String] do - if self.std_modules_cache == null then - self.std_modules_cache = new HashSet[String] - self.std_modules_cache.add("collection") - self.std_modules_cache.add("abstract_collection") - self.std_modules_cache.add("array") - self.std_modules_cache.add("hash_collection") - self.std_modules_cache.add("list") - self.std_modules_cache.add("range") - self.std_modules_cache.add("sorter") - self.std_modules_cache.add("environ") - self.std_modules_cache.add("exec") - self.std_modules_cache.add("file") - self.std_modules_cache.add("gc") - self.std_modules_cache.add("hash") - self.std_modules_cache.add("kernel") - self.std_modules_cache.add("math") - self.std_modules_cache.add("standard") - self.std_modules_cache.add("stream") - self.std_modules_cache.add("string") - self.std_modules_cache.add("string_search") - self.std_modules_cache.add("time") - end - return self.std_modules_cache.as(not null) - end - private var std_modules_cache: nullable Set[String] - - # Extract the subset of classes from a set of mclass - fun extract_classes(mclasses: Collection[MClass]): Set[MClass] do - var lst = new HashSet[MClass] - for mclass in mclasses do if mclass.is_class then lst.add(mclass) - return lst - end - - # Extract the subset of interfaces from a set of mclass - fun extract_interfaces(mclasses: Collection[MClass]): Set[MClass] do - var lst = new HashSet[MClass] - for mclass in mclasses do if mclass.is_interface then lst.add(mclass) - return lst - end - - # Extract the subset of generic classes/interfaces from a set of mclass - fun extract_generics(mclasses: Collection[MClass]): Set[MClass] do - var lst = new HashSet[MClass] - for mclass in mclasses do if mclass.arity > 0 then lst.add(mclass) - return lst - end - - # Extract the subset of abstract classes from a set of mclass - fun extract_abstracts(mclasses: Collection[MClass]): Set[MClass] do - var lst = new HashSet[MClass] - for mclass in mclasses do if mclass.is_abstract then lst.add(mclass) - return lst - end - - # Extract the subset of user defined classes/interfaces from a set of mclass - fun extract_user_defined(mclasses: Collection[MClass]): Set[MClass] do - var lst = new HashSet[MClass] - for mclass in mclasses do if mclass.is_user_defined then lst.add(mclass) - return lst - end - - # Extract the subset of user defined modules from a set of mmodules - fun extract_user_defined_modules(mmodules: Collection[MModule]): Set[MModule] do - var lst = new HashSet[MModule] - for mmodule in mmodules do if mmodule.is_user_defined then lst.add(mmodule) - return lst - end - - # Extract the subset of classes/interfaces from sl lib - fun extract_stdlib(mclasses: Collection[MClass]): Set[MClass] do - var lst = new HashSet[MClass] - for mclass in mclasses do if not mclass.is_user_defined then lst.add(mclass) - return lst - end - - # Extract the subset of user defined modules from std lib - fun extract_stdlib_modules(mmodules: Collection[MModule]): Set[MModule] do - var lst = new HashSet[MModule] - for mmodule in mmodules do if not mmodule.is_user_defined then lst.add(mmodule) - return lst - end + # Inheritance related metrics phase + var inheritance_metrics_phase: Phase = new InheritanceMetricsPhase(self, null) end -redef class MClass - # Inheritance - private var nop: Int = 0 # (NOP) Number of parents (direct superclasses) - private var nopc: Int = 0 # (NOPC) Number of class parents - private var nopi: Int = 0 # (NOPI) Number of interface parents - private var noa: Int = 0 # (NOA) Number of ancestors (direct and indirect) - private var noac: Int = 0 # (NOAC) Number of class ancestors - private var noai: Int = 0 # (NOAI) Number of interface ancestors - private var noc: Int = 0 # (NOC) Number of children (direct subclasses) - private var nocc: Int = 0 # (NOCC) Number of class children - private var noci: Int = 0 # (NOCI) Number of interface children - private var nod: Int = 0 # (NOD) Number of descendants (direct and indirect) - private var nodc: Int = 0 # (NODC) Number of class descendants - private var nodi: Int = 0 # (NODI) Number of interface descendants - private var dit: Int = 0 # (DIT) Depth in Inheritance Tree (maximum distance to root of the hierarchy) - private var ditc: Int = 0 # (DITC) Length of longest path to the root hierarchy and consisting only of extends edges - private var diti: Int = 0 # (DITI) Length of longest path to the root hierarchy and consisting only of extends implements - - # User Defined inheritance - private var nopud: Int = 0 # (NOPUD) Number of parents (direct superclasses) - private var nopcud: Int = 0 # (NOPCUD) Number of class parents - private var nopiud: Int = 0 # (NOPIUD) Number of interface parents - private var noaud: Int = 0 # (NOAUD) Number of ancestors (direct and indirect) - private var noacud: Int = 0 # (NOACUD) Number of class ancestors - private var noaiud: Int = 0 # (NOAIUD) Number of interface ancestors - private var nocud: Int = 0 # (NOCUD) Number of children (direct subclasses) - private var noccud: Int = 0 # (NOCCUD) Number of class children - private var nociud: Int = 0 # (NOCIUD) Number of interface children - private var nodud: Int = 0 # (NODUD) Number of descendants (direct and indirect) - private var nodcud: Int = 0 # (NODCUD) Number of class descendants - private var nodiud: Int = 0 # (NODIUD) Number of interface descendants - private var ditud: Int = 0 # (DITUD) Depth in Inheritance Tree (maximum distance to root of the hierarchy) - private var ditcud: Int = 0 # (DITCUD) Length of longest path to the root hierarchy and consisting only of extends edges - private var ditiud: Int = 0 # (DITIUD) Length of longest path to the root hierarchy and consisting only of extends implements - - private fun compute_class_inheritance_metrics(model: Model) do - # inheritance metrics - self.nop = parents.length - self.nopc = model.extract_classes(parents).length - self.nopi = model.extract_interfaces(parents).length - self.noa = ancestors.length - self.noac = model.extract_classes(ancestors).length - self.noai = model.extract_interfaces(ancestors).length - self.noc = children(model).length - self.nocc = model.extract_classes(children(model)).length - self.noci = model.extract_interfaces(children(model)).length - self.nod = descendants.length - self.nodc = model.extract_classes(descendants).length - self.nodi = model.extract_interfaces(descendants).length - self.dit = depth_from_object - self.ditc = model.extract_classes(path_to_object).length - self.diti = model.extract_interfaces(path_to_object).length - - # used defined metrics - self.nopud = model.extract_user_defined(parents).length - self.nopcud = model.extract_user_defined(model.extract_classes(parents)).length - self.nopiud = model.extract_user_defined(model.extract_interfaces(parents)).length - self.noaud = model.extract_user_defined(ancestors).length - self.noacud = model.extract_user_defined(model.extract_classes(ancestors)).length - self.noaiud = model.extract_user_defined(model.extract_interfaces(ancestors)).length - self.nocud = model.extract_user_defined(children(model)).length - self.noccud = model.extract_user_defined(model.extract_classes(children(model))).length - self.nociud = model.extract_user_defined(model.extract_interfaces(children(model))).length - self.nodud = model.extract_user_defined(descendants).length - self.nodcud = model.extract_user_defined(model.extract_classes(descendants)).length - self.nodiud = model.extract_user_defined(model.extract_interfaces(descendants)).length - self.ditud = 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 - end - - private fun is_class: Bool do - return self.kind == concrete_kind or self.kind == abstract_kind - end - - private fun is_interface: Bool do - return self.kind == interface_kind - end - - private fun is_abstract: Bool do - return self.kind == abstract_kind - end - - private fun is_user_defined: Bool do - return self.intro_mmodule.is_user_defined - end - - # Get parents of the class (direct super classes only) - private fun parents: Set[MClass] do - var lst = new HashSet[MClass] - # explore all definitions of the class (refinement) - for mclassdef in self.mclassdefs do - for parent in mclassdef.supertypes do - lst.add(parent.mclass) +# Extract metrics about inheritance from model. +private class InheritanceMetricsPhase + super Phase + redef fun process_mainmodule(mainmodule, given_mmodules) + do + if not toolcontext.opt_inheritance.value and not toolcontext.opt_all.value then return + var csv = toolcontext.opt_csv.value + var out = "{toolcontext.opt_dir.value or else "metrics"}/inheritance" + out.mkdir + + var model = toolcontext.modelbuilder.model + var filter = new ModelFilter(min_visibility = private_visibility) + + print toolcontext.format_h1("\n# Inheritance metrics") + + var hmetrics = new MetricSet + hmetrics.register(new MDUI(model, mainmodule)) + hmetrics.register(new MDUIC(model, mainmodule)) + hmetrics.register(new MDUII(model, mainmodule)) + hmetrics.register(new MIF(model, mainmodule)) + hmetrics.register(new MIFC(model, mainmodule)) + hmetrics.register(new MIFI(model, mainmodule)) + + var cmetrics = new MetricSet + cmetrics.register(new CNOAC(model, mainmodule, filter)) + cmetrics.register(new CNOPC(model, mainmodule, filter)) + cmetrics.register(new CNOCC(model, mainmodule, filter)) + cmetrics.register(new CNODC(model, mainmodule, filter)) + cmetrics.register(new CNOPI(model, mainmodule, filter)) + cmetrics.register(new CNOCI(model, mainmodule, filter)) + cmetrics.register(new CNODI(model, mainmodule, filter)) + cmetrics.register(new CDITC(model, mainmodule, filter)) + cmetrics.register(new CDITI(model, mainmodule, filter)) + + var mmodules = new HashSet[MModule] + var mclasses = new HashSet[MClass] + for mpackage in model.mpackages do + + print toolcontext.format_h2("\n ## package {mpackage}") + + for mgroup in mpackage.mgroups do + if mgroup.mmodules.is_empty then continue + + # Scalar metrics + print toolcontext.format_h3(" `- group {mgroup.full_name}") + + var mod_mclasses = new HashSet[MClass] + for mmodule in mgroup.mmodules do mod_mclasses.add_all(mmodule.intro_mclasses) + if mod_mclasses.is_empty then continue + mmodules.add_all(mgroup.mmodules) + mclasses.add_all(mod_mclasses) + cmetrics.clear + cmetrics.collect(new HashSet[MClass].from(mod_mclasses)) + cmetrics.to_console(1, not toolcontext.opt_nocolors.value) + if csv then cmetrics.to_csv.write_to_file("{out}/{mgroup}_classes.csv") + hmetrics.clear + hmetrics.collect(new HashSet[MModule].from(mgroup.mmodules)) + hmetrics.to_console(1, not toolcontext.opt_nocolors.value) + if csv then hmetrics.to_csv.write_to_file("{out}/{mgroup}_inheritance.csv") end end - return lst + if not mclasses.is_empty then + # Global metrics + print toolcontext.format_h2("\n ## global metrics") + cmetrics.clear + cmetrics.collect(mclasses) + cmetrics.to_console(1, not toolcontext.opt_nocolors.value) + if csv then cmetrics.to_csv.write_to_file("{out}/summary_classes.csv") + hmetrics.clear + hmetrics.collect(mmodules) + hmetrics.to_console(1, not toolcontext.opt_nocolors.value) + if csv then hmetrics.to_csv.write_to_file("{out}/summary_inheritance.csv") + end end +end - # Get ancestors of the class (all super classes) - 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) +# Module metric: proportion of MClasses Defined Using Inheritance +# +# Count MClasses that have another parents than Object +class MDUI + super MModuleMetric + super FloatMetric + redef fun name do return "mdui" + redef fun desc do return "proportion of mclass defined using inheritance (has other parent than Object)" + + redef fun collect(mmodules) do + for mmodule in mmodules do + var count = 0 + for mclass in mmodule.intro_mclasses do + if mclass.in_hierarchy(mainmodule).greaters.length > 2 then count += 1 + end + if mmodule.intro_mclasses.is_empty then + values[mmodule] = 0.0 + else + values[mmodule] = count.to_f / mmodule.intro_mclasses.length.to_f end end - return lst end +end - # Get children of the class (direct subclasses only) - 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) +# Module metric: proportion of abstract, concrete and extern Classes Defined Using Inheritance +# +# Count classes that have another parents than Object +class MDUIC + super MModuleMetric + super FloatMetric + redef fun name do return "mduic" + redef fun desc do return "proportion of class_kind defined using inheritance" + + redef fun collect(mmodules) do + for mmodule in mmodules do + var count = 0 + var nb = 0 + for mclass in mmodule.intro_mclasses do + if mclass.kind == abstract_kind or mclass.kind == concrete_kind or mclass.kind == extern_kind then + if mclass.in_hierarchy(mainmodule).greaters.length > 2 then count += 1 + end + nb += 1 + end + if mmodule.intro_mclasses.is_empty then + values[mmodule] = 0.0 + else + values[mmodule] = count.to_f / nb.to_f end end - return lst end +end - # Get children of the class (direct subclasses only) - 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) +# Module metric: proportion of Interface Defined Using Inheritance +# +# Count interface that have another parents than Object +class MDUII + super MModuleMetric + super FloatMetric + redef fun name do return "mduii" + redef fun desc do return "proportion of interface_kind defined using inheritance" + + redef fun collect(mmodules) do + for mmodule in mmodules do + var count = 0 + var nb = 0 + for mclass in mmodule.intro_mclasses do + if mclass.kind == interface_kind then + if mclass.in_hierarchy(mainmodule).greaters.length > 2 then count += 1 + end + nb += 1 + end + if mmodule.intro_mclasses.is_empty then + values[mmodule] = 0.0 + else + values[mmodule] = count.to_f / nb.to_f end end - return lst end +end - # 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 +# Module metric: proportion of MClass Inherited From +# +# Count classes that have at least a child +class MIF + super MModuleMetric + super FloatMetric + redef fun name do return "mif" + redef fun desc do return "proportion of mclass inherited from" + + redef fun collect(mmodules) do + for mmodule in mmodules do + var count = 0 + for mclass in mmodule.intro_mclasses do + if mclass.in_hierarchy(mainmodule).direct_smallers.length > 0 then count += 1 + end + if mmodule.intro_mclasses.is_empty then + values[mmodule] = 0.0 else - 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 + values[mmodule] = count.to_f / mmodule.intro_mclasses.length.to_f end end - if max_depth == null then - return 0 - else - return max_depth - end end +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 - - for p in parents do - var dit = p.depth_from_object - if max_dit == null or dit >= max_dit then - max_dit = dit - max_parent = p - parent_path = p.path_to_object +# Module metric: proportion of abstract, concrete and extern Class Inherited From +# +# Count classes that have at least a child +class MIFC + super MModuleMetric + super FloatMetric + redef fun name do return "mifc" + redef fun desc do return "proportion of class_kind inherited from" + + redef fun collect(mmodules) do + for mmodule in mmodules do + var count = 0 + var nb = 0 + for mclass in mmodule.intro_mclasses do + if mclass.kind == abstract_kind or mclass.kind == concrete_kind or mclass.kind == extern_kind then + if mclass.in_hierarchy(mainmodule).direct_smallers.length > 0 then count += 1 + end + nb += 1 + end + if mmodule.intro_mclasses.is_empty then + values[mmodule] = 0.0 + else + values[mmodule] = count.to_f / nb.to_f end end - - if max_parent != null and parent_path != null then - path.add(max_parent) - path.add_all(parent_path) - end - - return path end +end - # * -> * DUI - - 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(model).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 - 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 - 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 - return false +# Module metric: proportion of Interface Inherited From +# +# Count interfaces that have at least a child +class MIFI + super MModuleMetric + super FloatMetric + redef fun name do return "mifi" + redef fun desc do return "proportion of interface_kind inherited from" + + redef fun collect(mmodules) do + for mmodule in mmodules do + var count = 0 + var nb = 0 + for mclass in mmodule.intro_mclasses do + if mclass.kind == interface_kind then + if mclass.in_hierarchy(mainmodule).direct_smallers.length > 0 then count += 1 + end + nb += 1 + end + if mmodule.intro_mclasses.is_empty then + values[mmodule] = 0.0 + else + values[mmodule] = count.to_f / nb.to_f + end + end end +end - # SL -> * DUI - - 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(model).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 - 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 - 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 - return false +# MClass metric: Number of Class Ancestors +# +# Count only absrtract, concrete and extern classes +class CNOAC + super MClassMetric + super IntMetric + redef fun name do return "cnoac" + redef fun desc do return "number of class_kind ancestor" + + redef fun collect(mclasses) do + for mclass in mclasses do + var count = 0 + for parent in mclass.in_hierarchy(mainmodule).greaters do + if parent == mclass then continue + if parent.kind == abstract_kind or parent.kind == concrete_kind or parent.kind == extern_kind then + count += 1 + end + end + values[mclass] = count + end end +end - # SL -> SL - - 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 - 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 - 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 - 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 - return false +# MClass metric: Number of Class Parents +# +# Count only absrtract, concrete and extern classes +class CNOPC + super MClassMetric + super IntMetric + redef fun name do return "cnopc" + redef fun desc do return "number of class_kind parent" + + redef fun collect(mclasses) do + for mclass in mclasses do + var count = 0 + for parent in mclass.in_hierarchy(mainmodule).direct_greaters do + if parent == mclass then continue + if parent.kind == abstract_kind or parent.kind == concrete_kind or parent.kind == extern_kind then + count += 1 + end + end + values[mclass] = count + end end +end - # SL -> UD - - 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 - 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 - 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 - 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 - return false +# MClass metric: Number of Class Children +# +# Count only absrtract, concrete and extern classes +class CNOCC + super MClassMetric + super IntMetric + redef fun name do return "cnocc" + redef fun desc do return "number of class_kind children" + + redef fun collect(mclasses) do + for mclass in mclasses do + var count = 0 + for parent in mclass.in_hierarchy(mainmodule).direct_smallers do + if parent == mclass then continue + if parent.kind == abstract_kind or parent.kind == concrete_kind or parent.kind == extern_kind then + count += 1 + end + end + values[mclass] = count + end end +end - # UD -> * - - 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(model).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 - 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 - 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 - return false +# MClass metric: Number of Class Descendants +# +# Count only absrtract, concrete and extern classes +class CNODC + super MClassMetric + super IntMetric + redef fun name do return "cnodc" + redef fun desc do return "number of class_kind descendants" + + redef fun collect(mclasses) do + for mclass in mclasses do + var count = 0 + for parent in mclass.in_hierarchy(mainmodule).smallers do + if parent == mclass then continue + if parent.kind == abstract_kind or parent.kind == concrete_kind or parent.kind == extern_kind then + count += 1 + end + end + values[mclass] = count + end end +end - # UD -> SL - - 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 +# MClass metric: Number of Abstract Class Ancestors +# +# Count only absrtract classes +class CNOAA + super MClassMetric + super IntMetric + redef fun name do return "cnoaa" + redef fun desc do return "number of abstract class ancestors" + + redef fun collect(mclasses) do + for mclass in mclasses do + var count = 0 + for parent in mclass.in_hierarchy(mainmodule).greaters do + if parent == mclass then continue + if parent.kind == abstract_kind then + count += 1 + end + end + values[mclass] = count + end end +end - # UD -> UD - - 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(model).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 - 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 - 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 - return false +# MClass metric: Number of Interface Ancestors +# +# Count only interfaces +class CNOAI + super MClassMetric + super IntMetric + redef fun name do return "cnoai" + redef fun desc do return "number of interface_kind ancestor" + + redef fun collect(mclasses) do + for mclass in mclasses do + var count = 0 + for parent in mclass.in_hierarchy(mainmodule).greaters do + if parent == mclass then continue + if parent.kind == interface_kind then + count += 1 + end + end + values[mclass] = count + end end - end -redef class MModule - - private var 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 - - for mmodule in self.in_nesting.greaters do - for mclass in mmodule.intro_mclasses do - if mclass.is_class then nc += 1 - if mclass.is_class and mclass.arity > 0 then ngc += 1 - if mclass.is_class and mclass.is_abstract then nac += 1 - if mclass.is_interface then ni += 1 - if mclass.is_interface and mclass.arity > 0 then ngi += 1 - - ditsum += mclass.depth_from_object - if mclass.is_dui_eligible then dui_count += 1 - if mclass.is_ccdui_eligible then ccdui_count += 1 - if mclass.is_cidui_eligible then cidui_count += 1 - if mclass.is_iidui_eligible then iidui_count += 1 - if mclass.is_if_eligible(model) then if_count += 1 - if mclass.is_ccif_eligible(model) then ccif_count += 1 - if mclass.is_icif_eligible(model) then icif_count += 1 - if mclass.is_iiif_eligible(model) then iiif_count += 1 +# MClass metric: Number of Interface Parents +# +# Count only interfaces +class CNOPI + super MClassMetric + super IntMetric + redef fun name do return "cnopi" + redef fun desc do return "number of interface_kind parent" + + redef fun collect(mclasses) do + for mclass in mclasses do + var count = 0 + for parent in mclass.in_hierarchy(mainmodule).direct_greaters do + if parent == mclass then continue + if parent.kind == interface_kind then + count += 1 + end end + values[mclass] = count end - - dit = div(ditsum, nc + ni) - dui = div(dui_count * 100, nc + ni) - ccdui = div(ccdui_count * 100, nc) - cidui = div(cidui_count * 100, nc) - iidui = div(iidui_count * 100, ni) - inhf = div(if_count * 100, nc + ni) - ccif = div(ccif_count * 100, nc) - icif = div(icif_count * 100, ni) - iiif = div(iiif_count * 100, ni) end +end - private fun is_user_defined: Bool do - return not self.model.std_modules.has(self.name) +# MClass metric: Number of Interface Children +# +# Count only interfaces +class CNOCI + super MClassMetric + super IntMetric + redef fun name do return "cnoci" + redef fun desc do return "number of interface_kind children" + + redef fun collect(mclasses) do + for mclass in mclasses do + var count = 0 + for parent in mclass.in_hierarchy(mainmodule).direct_smallers do + if parent == mclass then continue + if parent.kind == interface_kind then + count += 1 + end + end + values[mclass] = count + end end end -# Print inheritance usage metrics -fun compute_inheritance_metrics(toolcontext: ToolContext, model: Model) -do - # global summary metrics - var nmd: Int = 0 # (NMD) Number of Modules - var nc: Int = 0 # (NC) Number of Classes - var ni: Int = 0 # (NI) Number of Interfaces - var nac : Int = 0 # (NAC) Number of Abstract Classes - var ngc : Int = 0 # (NGC) Number of Generic Classes - var ngi : Int = 0 # (NGI) Number of Generic Interfaces - # (SL) Std-Lib summary metrics - var nmdsl: Int = 0 # (NMDSL) Number of Modules in Std-Lib - var ncsl: Int = 0 # (NCSL) Number of Classes in Std-Lib - var nisl: Int = 0 # (NISL) Number of Interfaces in Std-Lib - var nacsl : Int = 0 # (NACSL) Number of Abstract Classes in Std-Lib - var ngcsl : Int = 0 # (NGCSL) Number of Generic Classes in Std-Lib - var ngisl : Int = 0 # (NGISL) Number of Generic Interfaces in Std-Lib - # (UD) User-Defined summary metrics - var nmdud: Int = 0 # (NMDUD) Number of Modules User Defined - var ncud: Int = 0 # (NCUD) Number of Classes User Defined - var niud: Int = 0 # (NIUD) Number of Interfaces User Defined - var nacud : Int = 0 # (NACUD) Number of Abstract Classes User Defined - var ngcud : Int = 0 # (NGCUD) Number of Generic Classes User Defined - var ngiud : Int = 0 # (NGIUD) Number of Generic Interfaces User Defined - - # global summary inheritance metrics - var dit = "" # (DIT) Global Depth in Inheritance Tree - var dui = "" # (DUI) Proportion of types that either implement an interface or extend another type other than Object - var ccdui = "" # (CCDUI) Proportion of classes that extend some other class. - var cidui = "" # (CIDUI) Proportion of classes that implement some other interface. - var iidui = "" # (IIDUI) Proportion of interfaces that extend some other interface. - var inhf = "" # (IF) Proportion of types Inherited From, that is, those types that are either extended or implemented - var ccif = "" # (CCIF) Proportion of classes extended by some other class. - var icif = "" # (ICIF) Proportion of interfaces implemented by some other class. - var iiif = "" # (IIIF) Proportion of interfaces extended by some other interface. - - # (SL -> *) Std-Lib summary inheritance metrics - var sldui = "" # (SLDUI) Proportion of std-lib types that either implement an interface or extend another std-lib type other than Object - var slccdui = "" # (SLCCDUI) Proportion of std-lib classes that extend some other std-lib class. - var slcidui = "" # (SLCIDUI) Proportion of std-lib classes that implement some other std-lib interface. - var sliidui = "" # (SLIIDUI) Proportion of std-lib interfaces that extend some other std-lib interface. - var slinhf = "" # (SLIF) Proportion of SL types Inherited From, that is, those types that are either extended or implemented - var slccif = "" # (SLCCIF) Proportion of SL classes extended by some other class. - var slicif = "" # (SLICIF) Proportion of SL interfaces implemented by some other class. - var sliiif = "" # (SLIIIF) Proportion of SL interfaces extended by some other interface. - - # (SL -> SL) Std-Lib summary inheritance metrics - var slinhfsl = "" # (SLIFSL) Proportion of SL types Inherited From, that is, those types that are either extended or implemented by a SL type - var slccifsl = "" # (SLCCIFSL) Proportion of SL classes extended by some other SL class. - var slicifsl = "" # (SLICIFSL) Proportion of SL interfaces implemented by some other SL class. - var sliiifsl = "" # (SLIIIFSL) Proportion of SL interfaces extended by some other SL interface. - - # (SL -> UD) Std-Lib summary inheritance metrics - var slinhfud = "" # (SLIFUD) Proportion of SL types Inherited From, that is, those types that are either extended or implemented by a UD type - var slccifud = "" # (SLCCIFUD) Proportion of SL classes extended by some other UD class. - var slicifud = "" # (SLICIFUD) Proportion of SL interfaces implemented by some other UD class. - var sliiifud = "" # (SLIIIFUD) Proportion of SL interfaces extended by some other UD interface. - - # (UD -> *) User-defined summary inheritance metrics - var uddui = "" # (UDDUI) Proportion user-defined of types that either implement an interface or extend another type - var udccdui = "" # (UDCCDUI) Proportion of user-defined classes that extend some other class. - var udcidui = "" # (UDCIDUI) Proportion of user-defined classes that implement some other interface. - var udiidui = "" # (UDIIDUI) Proportion of user-defined interfaces that extend some other interface. - var udinhf = "" # (UDIF) Proportion of UD types Inherited From, that is, those types that are either extended or implemented - var udccif = "" # (UDCCIF) Proportion of UD classes extended by some other class. - var udicif = "" # (UDICIF) Proportion of UD interfaces implemented by some other class. - var udiiif = "" # (UDIIIF) Proportion of UD interfaces extended by some other interface. - - # (UD -> SL) User-defined summary inheritance metrics - var udduisl = "" # (UDDUISL) Proportion user-defined of types that either implement an interface or extend another type SL - var udccduisl = "" # (UDCCDUISL) Proportion of user-defined classes that extend some other SL class. - var udciduisl = "" # (UDCIDUISL) Proportion of user-defined classes that implement some other SL interface. - var udiiduisl = "" # (UDIIDUISL) Proportion of user-defined interfaces that extend some other SL interface. - - # (UD -> UD) User-defined summary inheritance metrics - var udduiud = "" # (UDDUIUD) Proportion user-defined of types that either implement an interface or extend another type user-defined - var udccduiud = "" # (UDCCDUIUD) Proportion of user-defined classes that extend some other user-defined class. - var udciduiud = "" # (UDCIDUIUD) Proportion of user-defined classes that implement some other user-defined interface. - var udiiduiud = "" # (UDIIDUIUD) Proportion of user-defined interfaces that extend some other user-defined interface. - var udinhfud = "" # (UDIFUD) Proportion of UD types Inherited From, that is, those types that are either extended or implemented by another UD type - var udccifud = "" # (UDCCIFUD) Proportion of UD classes extended by some other UD class. - var udicifud = "" # (UDICIFUD) Proportion of UD interfaces implemented by some other UD class. - var udiiifud = "" # (UDIIIFUD) Proportion of UD interfaces extended by some other UD interface. - - # compute modules scalar metrics - for mmodule in model.mmodules do - mmodule.compute_module_inheritance_metrics(model) +# MClass metric: Number of Interface Descendants +# +# Count only interfaces +class CNODI + super MClassMetric + super IntMetric + redef fun name do return "cnodi" + redef fun desc do return "number of interface_kind descendants" + + redef fun collect(mclasses) do + for mclass in mclasses do + var count = 0 + for parent in mclass.in_hierarchy(mainmodule).smallers do + if parent == mclass then continue + if parent.kind == interface_kind then + count += 1 + end + end + values[mclass] = count + end end +end - # compute class scalar metrics - for mclass in model.mclasses do - mclass.compute_class_inheritance_metrics(model) +# MClass metric: Class Depth in Inheritance Tree +# +# Following the longest path composed only of extends edges from self to Object +class CDITC + super MClassMetric + super IntMetric + redef fun name do return "cditc" + redef fun desc do return "depth in class tree following only class, abstract, extern kind" + + redef fun collect(mclasses) do + for mclass in mclasses do + values[mclass] = mclass.ditc(mainmodule) + end end +end - # compute summary metrics - - # compute global summary metrics - nmd = model.mmodules.length - nc = model.extract_classes(model.mclasses).length - ni = model.extract_interfaces(model.mclasses).length - nac = model.extract_abstracts(model.mclasses).length - ngc = model.extract_generics(model.extract_classes(model.mclasses)).length - ngi = model.extract_generics(model.extract_interfaces(model.mclasses)).length - # compute std-lib summary metrics - nmdsl = model.extract_stdlib_modules(model.mmodules).length - ncsl = model.extract_stdlib(model.extract_classes(model.mclasses)).length - nisl = model.extract_stdlib(model.extract_interfaces(model.mclasses)).length - nacsl = model.extract_stdlib(model.extract_abstracts(model.mclasses)).length - ngcsl = model.extract_stdlib(model.extract_generics(model.extract_classes(model.mclasses))).length - ngisl = model.extract_stdlib(model.extract_generics(model.extract_interfaces(model.mclasses))).length - # compute user-defined summary metrics - nmdud = model.extract_user_defined_modules(model.mmodules).length - ncud = model.extract_user_defined(model.extract_classes(model.mclasses)).length - niud = model.extract_user_defined(model.extract_interfaces(model.mclasses)).length - nacud = model.extract_user_defined(model.extract_abstracts(model.mclasses)).length - ngcud = model.extract_user_defined(model.extract_generics(model.extract_classes(model.mclasses))).length - ngiud = model.extract_user_defined(model.extract_generics(model.extract_interfaces(model.mclasses))).length - - # compute inheritance summary metrics - - # * -> * - var ditsum = 0 - var dui_count = 0 - var ccdui_count = 0 - var cidui_count = 0 - var iidui_count = 0 - var if_count = 0 - var ccif_count = 0 - var icif_count = 0 - var iiif_count = 0 - - # SL -> * - var sldui_count = 0 - var slccdui_count = 0 - var slcidui_count = 0 - var sliidui_count = 0 - var slif_count = 0 - var slccif_count = 0 - var slicif_count = 0 - var sliiif_count = 0 - - # SL -> SL - var slifsl_count = 0 - var slccifsl_count = 0 - var slicifsl_count = 0 - var sliiifsl_count = 0 - - # SL -> UD - var slifud_count = 0 - var slccifud_count = 0 - var slicifud_count = 0 - var sliiifud_count = 0 - - # UD -> * - var uddui_count = 0 - var udccdui_count = 0 - var udcidui_count = 0 - var udiidui_count = 0 - var udif_count = 0 - var udccif_count = 0 - var udicif_count = 0 - var udiiif_count = 0 - - # UD -> SL - var udduisl_count = 0 - var udccduisl_count = 0 - var udciduisl_count = 0 - var udiiduisl_count = 0 - - # UD -> UD - var udduiud_count = 0 - var udccduiud_count = 0 - var udciduiud_count = 0 - var udiiduiud_count = 0 - var udifud_count = 0 - var udccifud_count = 0 - var udicifud_count = 0 - var udiiifud_count = 0 - - for mclass in model.mclasses do - ditsum += mclass.dit - - # * -> * - if mclass.is_dui_eligible then dui_count += 1 - if mclass.is_ccdui_eligible then ccdui_count += 1 - if mclass.is_cidui_eligible then cidui_count += 1 - if mclass.is_iidui_eligible then iidui_count += 1 - if mclass.is_if_eligible(model) then if_count += 1 - if mclass.is_ccif_eligible(model) then ccif_count += 1 - if mclass.is_icif_eligible(model) then icif_count += 1 - if mclass.is_iiif_eligible(model) then iiif_count += 1 - - # SL -> * - if mclass.is_sldui_eligible then sldui_count += 1 - if mclass.is_slccdui_eligible then slccdui_count += 1 - if mclass.is_slcidui_eligible then slcidui_count += 1 - if mclass.is_sliidui_eligible then sliidui_count += 1 - if mclass.is_slif_eligible(model) then slif_count += 1 - if mclass.is_slccif_eligible(model) then slccif_count += 1 - if mclass.is_slicif_eligible(model) then slicif_count += 1 - if mclass.is_sliiif_eligible(model) then sliiif_count += 1 - - # SL -> SL - if mclass.is_slifsl_eligible(model) then slifsl_count += 1 - if mclass.is_slccifsl_eligible(model) then slccifsl_count += 1 - if mclass.is_slicifsl_eligible(model) then slicifsl_count += 1 - if mclass.is_sliiifsl_eligible(model) then sliiifsl_count += 1 - - # SL -> UD - if mclass.is_slifud_eligible(model) then slifud_count += 1 - if mclass.is_slccifud_eligible(model) then slccifud_count += 1 - if mclass.is_slicifud_eligible(model) then slicifud_count += 1 - if mclass.is_sliiifud_eligible(model) then sliiifud_count += 1 - - # UD -> * - if mclass.is_uddui_eligible then uddui_count += 1 - if mclass.is_udccdui_eligible then udccdui_count += 1 - if mclass.is_udcidui_eligible then udcidui_count += 1 - if mclass.is_udiidui_eligible then udiidui_count += 1 - if mclass.is_udif_eligible(model) then udif_count += 1 - if mclass.is_udccif_eligible(model) then udccif_count += 1 - if mclass.is_udicif_eligible(model) then udicif_count += 1 - if mclass.is_udiiif_eligible(model) then udiiif_count += 1 - - # UD -> SL - if mclass.is_udduisl_eligible then udduisl_count += 1 - if mclass.is_udccduisl_eligible then udccduisl_count += 1 - if mclass.is_udciduisl_eligible then udciduisl_count += 1 - if mclass.is_udiiduisl_eligible then udiiduisl_count += 1 - - # UD -> UD - if mclass.is_udduiud_eligible then udduiud_count += 1 - if mclass.is_udccduiud_eligible then udccduiud_count += 1 - if mclass.is_udciduiud_eligible then udciduiud_count += 1 - if mclass.is_udiiduiud_eligible then udiiduiud_count += 1 - if mclass.is_udifud_eligible(model) then udifud_count += 1 - if mclass.is_udccifud_eligible(model) then udccifud_count += 1 - if mclass.is_udicifud_eligible(model) then udicifud_count += 1 - if mclass.is_udiiifud_eligible(model) then udiiifud_count += 1 +# MClass metric: Interface Depth in Inheritance Tree +# +# Following the longest path composed only of implements edges from self to Object +class CDITI + super MClassMetric + super IntMetric + redef fun name do return "cditi" + redef fun desc do return "depth in class tree following only interface_kind" + + redef fun collect(mclasses) do + for mclass in mclasses do + values[mclass] = mclass.diti(mainmodule) + end end +end - # * -> * - dit = div(ditsum, model.mclasses.length) - dui = div(dui_count * 100, nc + ni) - ccdui = div(ccdui_count * 100, nc) - cidui = div(cidui_count * 100, nc) - iidui = div(iidui_count * 100, ni) - inhf = div(if_count * 100, nc + ni) - ccif = div(ccif_count * 100, nc) - icif = div(icif_count * 100, ni) - iiif = div(iiif_count * 100, ni) - - # SL -> * - sldui = div(sldui_count * 100, ncsl + nisl) - slccdui = div(slccdui_count * 100, ncsl) - slcidui = div(slcidui_count * 100, ncsl) - sliidui = div(sliidui_count * 100, nisl) - slinhf = div(slif_count * 100, ncsl + nisl) - slccif = div(slccif_count * 100, ncsl) - slicif = div(slicif_count * 100, nisl) - sliiif = div(sliiif_count * 100, nisl) - - # SL -> SL - slinhfsl = div(slifsl_count * 100, ncsl + nisl) - slccifsl = div(slccifsl_count * 100, ncsl) - slicifsl = div(slicifsl_count * 100, nisl) - sliiifsl = div(sliiifsl_count * 100, nisl) - - # SL -> UD - slinhfud = div(slifud_count * 100, ncsl + nisl) - slccifud = div(slccifud_count * 100, ncsl) - slicifud = div(slicifud_count * 100, nisl) - sliiifud = div(sliiifud_count * 100, nisl) - - # UD -> * - uddui = div(uddui_count * 100, ncud + niud) - udccdui = div(udccdui_count * 100, ncud) - udcidui = div(udcidui_count * 100, ncud) - udiidui = div(udiidui_count * 100, niud) - udinhf = div(if_count * 100, ncud + niud) - udccif = div(ccif_count * 100, ncud) - udicif = div(icif_count * 100, niud) - udiiif = div(iiif_count * 100, niud) - - # UD -> SL - udduisl = div(udduisl_count * 100, ncud + niud) - udccduisl = div(udccduisl_count * 100, ncud) - udciduisl = div(udciduisl_count * 100, ncud) - udiiduisl = div(udiiduisl_count * 100, niud) +# model redef - # UD -> UD - udduiud = div(udduiud_count * 100, ncud + niud) - udccduiud = div(udccduiud_count * 100, ncud) - udciduiud = div(udciduiud_count * 100, ncud) - udiiduiud = div(udiiduiud_count * 100, niud) - udinhfud = div(udifud_count * 100, ncud + niud) - udccifud = div(udccifud_count * 100, ncud) - udicifud = div(udicifud_count * 100, niud) - udiiifud = div(udiiifud_count * 100, niud) +redef class MClass - # CSV generation - if toolcontext.opt_generate_csv.value then - # summary_metrics - var summaryCSV = new CSVDocument(toolcontext.output_dir.join_path("summary_metrics.csv")) - summaryCSV.set_header("scope", "NMD", "NC", "NI", "NAC", "NGC", "NGI") - summaryCSV.add_line("global", nmd, nc, ni, nac, ngc, ngi) - summaryCSV.add_line("std-lib", nmdsl, ncsl, nisl, nacsl, ngcsl, ngisl) - summaryCSV.add_line("user-defined", nmdud, ncud, niud, nacud, ngcud, ngiud) - for m in model.mmodules do - summaryCSV.add_line(m.name, 1, m.nc, m.ni, m.nac, m.ngc, m.ngi) + # Class Depth in Inheritance Tree + # + # Following the longest path composed only of extends edges from self to Object + fun ditc(mainmodule: MModule): Int do + if in_hierarchy(mainmodule).direct_greaters.is_empty then + return 0 end - summaryCSV.save - - # inheritance metrics - var inheritanceCSV = new CSVDocument(toolcontext.output_dir.join_path("inheritance_metrics.csv")) - inheritanceCSV.set_header("scope", "DIT", "DUI", "CCDUI", "CIDUI", "IIDUI", "IF", "CCIF", "ICIF", "IIIF") - inheritanceCSV.add_line("global", dit, dui, ccdui, cidui, iidui, inhf, ccif, icif, iiif) - inheritanceCSV.add_line("SL -> *", "", sldui, slccdui, slcidui, sliidui, slinhf, slccif, slicif, sliiif) - inheritanceCSV.add_line("SL -> SL", "", sldui, slccdui, slcidui, sliidui, slinhfsl, slccifsl, slicifsl, sliiifsl) - inheritanceCSV.add_line("SL -> UD", "", 0, 0, 0, 0, slinhfud, slccifud, slicifud, sliiifud) - inheritanceCSV.add_line("UD -> *", "", uddui, udccdui, udcidui, udiidui, udinhf, udccif, udicif, udiiif) - inheritanceCSV.add_line("UD -> SL", "", udduisl, udccduisl, udciduisl, udiiduisl, 0, 0, 0, 0) - inheritanceCSV.add_line("UD -> UD", "", udduiud, udccduiud, udciduiud, udiiduiud, udinhfud, udccifud, udicifud, udiiifud) - for m in model.mmodules do - if m.intro_mclasses.is_empty and m.in_nesting.greaters.length == 1 then continue - inheritanceCSV.add_line(m.name, m.dit, m.dui, m.ccdui, m.cidui, m.iidui, m.inhf, m.ccif, m.icif, m.iiif) + var min = -1 + for p in in_hierarchy(mainmodule).direct_greaters do + if p.kind != abstract_kind and p.kind != concrete_kind and p.kind != extern_kind then continue + var d = p.ditc(mainmodule) + 1 + if min == -1 or d < min then + min = d + end end - inheritanceCSV.save + if min == -1 then min = 0 + return min + end - # scalar metrics - var scalarCSV = new CSVDocument(toolcontext.output_dir.join_path("global_scalar_metrics.csv")) - var udscalarCSV = new CSVDocument(toolcontext.output_dir.join_path("ud_scalar_metrics.csv")) - scalarCSV.set_header("mclass", "type", "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") - 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) + # Interface Depth in Inheritance Tree + # + # Following the longest path composed only of implements edges from self to Object + fun diti(mainmodule: MModule): Int do + if in_hierarchy(mainmodule).direct_greaters.is_empty then + return 0 + end + var min = -1 + for p in in_hierarchy(mainmodule).direct_greaters do + if p.kind != interface_kind then continue + var d = p.diti(mainmodule) + 1 + if min == -1 or d < min then + min = d + end end - scalarCSV.save - udscalarCSV.save + if min == -1 then min = 0 + return min end - - print "--- Global Summary metrics ---" - print "(NMD) Number of Modules: {nmd}" - print "(NC) Number of Classes: {nc}" - print "(NI) Number of Interfaces: {ni}" - print "(NAC) Number of Abstract Classes: {nac}" - print "(NGC) Number of Generic Classes: {ngc}" - print "(NGI) Number of Generic Interfaces: {ngi}" - print "--- (SL) Std-Lib Summary metrics ---" - print "(NMDSL) Number of Modules: {nmdsl}" - print "(NCSL) Number of Classes: {ncsl}" - print "(NISL) Number of Interfaces: {nisl}" - print "(NACSL) Number of Abstract Classes: {nacsl}" - print "(NGCSL) Number of Generic Classes: {ngcsl}" - print "(NGISL) Number of Generic Interfaces: {ngisl}" - print "--- (UD) User-Defined Summary metrics ---" - print "(NMDUD) Number of Modules: {nmdud}" - print "(NCUD) Number of Classes: {ncud}" - print "(NIUD) Number of Interfaces: {niud}" - print "(NACUD) Number of Abstract Classes: {nacud}" - print "(NGCUD) Number of Generic Classes: {ngcud}" - print "(NGIUD) Number of Generic Interfaces: {ngiud}" - print "" - print "--- Global Inheritance metrics ---" - print "(DIT) Global Depth in Inheritance Tree: {dit}" - print "(DUI) Proportion of types inheriting another type other than Object: {dui}%" - print "(CCDUI) Proportion of classes that extend some other class: {ccdui}%" - print "(CIDUI) Proportion of classes that implement some other interface: {cidui}%" - print "(IIDUI) Proportion of interfaces that extend some other interface: {iidui}%" - print "(IF) Proportion of types Inherited From: {inhf}%" - print "(CCIF) Proportion of classes extended by class: {ccif}%" - print "(ICIF) Proportion of interfaces implemented by class: {icif}%" - print "(IIIF) Proportion of interfaces extended by interface: {iiif}%" - print "" - print "--- (SL -> *) Std-Lib Inheritance metrics ---" - print "(SLDUI) Proportion of SL types inheriting another type other than Object: {sldui}%" - print "(SLCCDUI) Proportion of SL classes that extend some other class: {slccdui}%" - print "(SLCIDUI) Proportion of SL classes that implement some other interface: {slcidui}%" - print "(SLIIDUI) Proportion of SL interfaces that extend some other interface: {sliidui}%" - print "(SLIF) Proportion of SL types Inherited From: {slinhf}%" - print "(SLCCIF) Proportion of SL classes extended by class: {slccif}%" - print "(SLICIF) Proportion of SL interfaces implemented by class: {slicif}%" - print "(SLIIIF) Proportion of SL interfaces extended by interface: {sliiif}%" - print "" - print "--- (SL -> SL) Std-Lib Inheritance metrics ---" - print "(SLIFSL) Proportion of SL types Inherited From by SL type: {slinhfsl}%" - print "(SLCCIFSL) Proportion of SL classes extended by SL class: {slccifsl}%" - print "(SLICIFSL) Proportion of SL interfaces implemented by SL class: {slicifsl}%" - print "(SLIIIFSL) Proportion of SL interfaces extended by SL interface: {sliiifsl}%" - print "" - print "--- (SL->UD) Std-Lib Inheritance metrics ---" - print "(SLIFUD) Proportion of SL types Inherited From by UD type: {slinhfud}%" - print "(SLCCIFUD) Proportion of SL classes extended by UD class: {slccifud}%" - print "(SLICIFUD) Proportion of SL interfaces implemented by UD class: {slicifud}%" - print "(SLIIIFUD) Proportion of SL interfaces extended by UD interface: {sliiifud}%" - print "" - print "--- (UD->*) User-Defined Inheritance metrics ---" - print "(UDDUI) Proportion of UD types inheriting another type other than Object: {uddui}%" - print "(UDCCDUI) Proportion of UD classes that extend some other class: {udccdui}%" - print "(UDCIDUI) Proportion of UD classes that implement some other interface: {udcidui}%" - print "(UDIIDUI) Proportion of UD interfaces that extend some other interface: {udiidui}%" - print "(UDIF) Proportion of UD types Inherited From: {udinhf}%" - print "(UDCCIF) Proportion of UD classes extended by class: {udccif}%" - print "(UDICIF) Proportion of UD interfaces implemented by class: {udicif}%" - print "(UDIIIF) Proportion of UD interfaces extended by interface: {udiiif}%" - print "" - print "--- (UD -> SL) User-Defined Inheritance metrics ---" - print "(UDDUISL) Proportion of UD types inheriting another type other SL type: {udduisl}%" - print "(UDCCDUISL) Proportion of UD classes that extend some other SL class: {udccduisl}%" - print "(UDCIDUISL) Proportion of UD classes that implement some other SL interface: {udciduisl}%" - print "(UDIIDUISL) Proportion of UD interfaces that extend some other SL interface: {udiiduisl}%" - print "" - print "--- (UD -> UD) User-Defined Inheritance metrics ---" - print "(UDDUIUD) Proportion of UD types inheriting another type other UD type: {udduiud}%" - print "(UDCCDUIUD) Proportion of UD classes that extend some other UD class: {udccduiud}%" - print "(UDCIDUIUD) Proportion of UD classes that implement some other UD interface: {udciduiud}%" - print "(UDIIDUIUD) Proportion of UD interfaces that extend some other UD interface: {udiiduiud}%" - print "(UDIFUD) Proportion of UD types Inherited From: {udinhfud}%" - print "(UDCCIFUD) Proportion of UD classes extended by UD class: {udccifud}%" - print "(UDICIFUD) Proportion of UD interfaces implemented by UD class: {udicifud}%" - print "(UDIIIFUD) Proportion of UD interfaces extended by UD interface: {udiiifud}%" end - -# TODO Third-Party metrics -# TODO CSV generation -# TODO gnu-plot generation