model: add `MEntity::is_broken`
[nit.git] / src / metrics / mclasses_metrics.nit
index 3651aa7..69b5b40 100644 (file)
 # Collect common metrics about mclasses
 module mclasses_metrics
 
 # Collect common metrics about mclasses
 module mclasses_metrics
 
-import model
 import metrics_base
 import metrics_base
-import phase
-import frontend
+import model::model_collect
 
 redef class ToolContext
 
 redef class ToolContext
-       var mclasses_metrics_phase = new MClassesMetricsPhase(self, null)
+       var mclasses_metrics_phase: Phase = new MClassesMetricsPhase(self, null)
 end
 
 # Extract metrics about mclasses from model.
 private class MClassesMetricsPhase
        super Phase
 end
 
 # Extract metrics about mclasses from model.
 private class MClassesMetricsPhase
        super Phase
-       redef fun process_mainmodule(mainmodule)
+       redef fun process_mainmodule(mainmodule, given_mmodules)
        do
                if not toolcontext.opt_mclasses.value and not toolcontext.opt_all.value then return
        do
                if not toolcontext.opt_mclasses.value and not toolcontext.opt_all.value then return
-
-               print "\n# MClasses metrics".yellow.bold
-
-               var metrics = new MClassMetricSet
-               metrics.register(new CNOA, new CNOP, new CNOC, new CNOD, new CDIT)
-               metrics.register(new CNBIP, new CNBRP, new CNBHP)
+               var csv = toolcontext.opt_csv.value
+               var out = "{toolcontext.opt_dir.value or else "metrics"}/mclasses"
+               out.mkdir
+
+
+               print toolcontext.format_h1("\n# MClasses metrics")
+
+               var metrics = new MetricSet
+               var min_vis = private_visibility
+               metrics.register(new CNOA(mainmodule))
+               metrics.register(new CNOP(mainmodule))
+               metrics.register(new CNOC(mainmodule))
+               metrics.register(new CNOD(mainmodule))
+               metrics.register(new CDIT(mainmodule))
+               metrics.register(new CNBP(mainmodule, min_vis))
+               metrics.register(new CNBA(mainmodule, min_vis))
+               metrics.register(new CNBIP(mainmodule, min_vis))
+               metrics.register(new CNBRP(mainmodule, min_vis))
+               metrics.register(new CNBHP(mainmodule, min_vis))
                #TODO metrics.register(new CNBI) # nb init
                #TODO metrics.register(new CNBI) # nb init
-               #TODO metrics.register(new CNBA) # nb attrs
                #TODO metrics.register(new CNBM) # nb methods
                #TODO metrics.register(new CNBV) # nb vtypes
 
                var model = toolcontext.modelbuilder.model
                var mclasses = new HashSet[MClass]
                #TODO metrics.register(new CNBM) # nb methods
                #TODO metrics.register(new CNBV) # nb vtypes
 
                var model = toolcontext.modelbuilder.model
                var mclasses = new HashSet[MClass]
-               for mproject in model.mprojects do
+               for mpackage in model.mpackages do
 
 
-                       print "\n ## project {mproject}".bold
+                       print toolcontext.format_h2("\n ## package {mpackage}")
 
 
-                       for mgroup in mproject.mgroups do
+                       for mgroup in mpackage.mgroups do
                                if mgroup.mmodules.is_empty then continue
                                if mgroup.mmodules.is_empty then continue
+                               metrics.clear
 
                                # Scalar metrics
 
                                # Scalar metrics
-                               print "  `- group {mgroup.full_name}"
-
+                               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
                                mclasses.add_all(mod_mclasses)
                                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
                                mclasses.add_all(mod_mclasses)
-                               metrics.collect(new HashSet[MClass].from(mod_mclasses), mainmodule)
-                               for name, metric in metrics.metrics do
-                                       print "\t{name}: {metric.desc}".green
-                                       print "\t    avg: {metric.avg}".light_gray
-                                       var max = metric.max
-                                       print "\t    max: {max.first} ({max.second})".light_gray
-                                       var min = metric.min
-                                       print "\t    min: {min.first} ({min.second})".light_gray
-                               end
+                               metrics.collect(new HashSet[MClass].from(mod_mclasses))
+                               metrics.to_console(1, not toolcontext.opt_nocolors.value)
+                               if csv then metrics.to_csv.save("{out}/{mgroup}.csv")
                        end
                end
                if not mclasses.is_empty then
                        end
                end
                if not mclasses.is_empty then
+                       metrics.clear
                        # Global metrics
                        # Global metrics
-                       print "\n ## global metrics".bold
-
-                       metrics.collect(mclasses, mainmodule)
-                       for name, metric in metrics.metrics do
-                               print "\t{name}: {metric.desc}".green
-                               print "\t    avg: {metric.avg}".light_gray
-                               var max = metric.max
-                               print "\t    max: {max.first} ({max.second})".light_gray
-                               var min = metric.min
-                               print "\t    min: {min.first} ({min.second})".light_gray
-                       end
-               end
-       end
-end
-
-# An MetricSet for MClasses metrics
-class MClassMetricSet
-       super MetricSet
-       redef type METRIC: MClassMetric
-
-       # Collect all the metrics on the set of MClasses
-       fun collect(mclasses: Set[MClass], mainmodule: MModule) do
-               clear
-               for metric in metrics.values do
-                       for mclass in mclasses do
-                               metric.collect(mclass, mainmodule)
-                       end
+                       print toolcontext.format_h2("\n ## global metrics")
+                       metrics.collect(mclasses)
+                       metrics.to_console(1, not toolcontext.opt_nocolors.value)
+                       if csv then metrics.to_csv.save("{out}/summary.csv")
                end
        end
 end
 
                end
        end
 end
 
-# An abstract metric about MClass
-abstract class MClassMetric
-       super IntMetric[MClass]
-       # Collect the metric value for this mclass
-       fun collect(mclass: MClass, mainmodule: MModule) is abstract
+# A metric about MClass
+interface MClassMetric
+       super Metric
+       redef type ELM: MClass
 end
 
 # Class Metric: Number of Ancestors
 class CNOA
        super MClassMetric
 end
 
 # Class Metric: Number of Ancestors
 class CNOA
        super MClassMetric
+       super IntMetric
        redef fun name do return "cnoa"
        redef fun desc do return "number of ancestor classes"
 
        redef fun name do return "cnoa"
        redef fun desc do return "number of ancestor classes"
 
-       redef fun collect(mclass, mainmodule) do
-               values[mclass] = mclass.in_hierarchy(mainmodule).greaters.length - 1
+       var mainmodule: MModule
+       init(mainmodule: MModule) do self.mainmodule = mainmodule
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.in_hierarchy(mainmodule).greaters.length - 1
+               end
        end
 end
 
 # Class Metric: Number of Parents
 class CNOP
        super MClassMetric
        end
 end
 
 # Class Metric: Number of Parents
 class CNOP
        super MClassMetric
+       super IntMetric
        redef fun name do return "cnop"
        redef fun desc do return "number of parent classes"
 
        redef fun name do return "cnop"
        redef fun desc do return "number of parent classes"
 
-       redef fun collect(mclass, mainmodule) do
-               values[mclass] = mclass.in_hierarchy(mainmodule).direct_greaters.length
+       var mainmodule: MModule
+       init(mainmodule: MModule) do self.mainmodule = mainmodule
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.in_hierarchy(mainmodule).direct_greaters.length
+               end
        end
 end
 
 # Class Metric: Number of Children
 class CNOC
        super MClassMetric
        end
 end
 
 # Class Metric: Number of Children
 class CNOC
        super MClassMetric
+       super IntMetric
        redef fun name do return "cnoc"
        redef fun desc do return "number of child classes"
 
        redef fun name do return "cnoc"
        redef fun desc do return "number of child classes"
 
-       redef fun collect(mclass, mainmodule) do
-               values[mclass] = mclass.in_hierarchy(mainmodule).direct_smallers.length
+       var mainmodule: MModule
+       init(mainmodule: MModule) do self.mainmodule = mainmodule
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.in_hierarchy(mainmodule).direct_smallers.length
+               end
        end
 end
 
 # Class Metric: Number of Descendants
 class CNOD
        super MClassMetric
        end
 end
 
 # Class Metric: Number of Descendants
 class CNOD
        super MClassMetric
+       super IntMetric
        redef fun name do return "cnod"
        redef fun desc do return "number of descendant classes"
 
        redef fun name do return "cnod"
        redef fun desc do return "number of descendant classes"
 
-       redef fun collect(mclass, mainmodule) do
-               values[mclass] = mclass.in_hierarchy(mainmodule).smallers.length - 1
+       var mainmodule: MModule
+       init(mainmodule: MModule) do self.mainmodule = mainmodule
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.in_hierarchy(mainmodule).smallers.length - 1
+               end
        end
 end
 
 # Class Metric: Depth in Inheritance Tree
 class CDIT
        super MClassMetric
        end
 end
 
 # Class Metric: Depth in Inheritance Tree
 class CDIT
        super MClassMetric
+       super IntMetric
        redef fun name do return "cdit"
        redef fun desc do return "depth in class tree"
 
        redef fun name do return "cdit"
        redef fun desc do return "depth in class tree"
 
-       redef fun collect(mclass, mainmodule) do
-               values[mclass] = mclass.in_hierarchy(mainmodule).depth
+       var mainmodule: MModule
+       init(mainmodule: MModule) do self.mainmodule = mainmodule
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.in_hierarchy(mainmodule).depth
+               end
+       end
+end
+
+# Class Metric: Number of MProperties
+class CNBP
+       super MClassMetric
+       super IntMetric
+       redef fun name do return "cnbp"
+       redef fun desc do return "number of accessible properties (inherited + local)"
+
+       var mainmodule: MModule
+       var min_visibility: MVisibility
+
+       init(mainmodule: MModule, min_visibility: MVisibility) do
+               self.mainmodule = mainmodule
+               self.min_visibility = min_visibility
+       end
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.collect_accessible_mproperties(min_visibility).length
+               end
+       end
+end
+
+# Class Metric: Number of MAttributes
+class CNBA
+       super MClassMetric
+       super IntMetric
+       redef fun name do return "cnba"
+       redef fun desc do return "number of accessible attributes (inherited + local)"
+
+       var mainmodule: MModule
+       var min_visibility: MVisibility
+
+       init(mainmodule: MModule, min_visibility: MVisibility) do
+               self.mainmodule = mainmodule
+               self.min_visibility = min_visibility
+       end
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.collect_accessible_mattributes(min_visibility).length
+               end
        end
 end
 
 # Class Metric: Number of Introduced MProperties
 class CNBIP
        super MClassMetric
        end
 end
 
 # Class Metric: Number of Introduced MProperties
 class CNBIP
        super MClassMetric
+       super IntMetric
        redef fun name do return "cnbip"
        redef fun desc do return "number of introduced properties"
 
        redef fun name do return "cnbip"
        redef fun desc do return "number of introduced properties"
 
-       redef fun collect(mclass, mainmodule) do
-               values[mclass] = mclass.intro_mproperties.length
+       var mainmodule: MModule
+       var min_visibility: MVisibility
+
+       init(mainmodule: MModule, min_visibility: MVisibility) do
+               self.mainmodule = mainmodule
+               self.min_visibility = min_visibility
+       end
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.collect_intro_mproperties(min_visibility).length
+               end
        end
 end
 
 # Class Metric: Number of Refined MProperties
 class CNBRP
        super MClassMetric
        end
 end
 
 # Class Metric: Number of Refined MProperties
 class CNBRP
        super MClassMetric
+       super IntMetric
        redef fun name do return "cnbrp"
        redef fun desc do return "number of redefined properties"
 
        redef fun name do return "cnbrp"
        redef fun desc do return "number of redefined properties"
 
-       redef fun collect(mclass, mainmodule) do
-               values[mclass] = mclass.redef_mproperties.length
+       var mainmodule: MModule
+       var min_visibility: MVisibility
+
+       init(mainmodule: MModule, min_visibility: MVisibility) do
+               self.mainmodule = mainmodule
+               self.min_visibility = min_visibility
+       end
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.collect_redef_mproperties(min_visibility).length
+               end
        end
 end
 
 # Class Metric: Number of Inherited MProperties
 class CNBHP
        super MClassMetric
        end
 end
 
 # Class Metric: Number of Inherited MProperties
 class CNBHP
        super MClassMetric
+       super IntMetric
        redef fun name do return "cnbhp"
        redef fun desc do return "number of inherited properties"
 
        redef fun name do return "cnbhp"
        redef fun desc do return "number of inherited properties"
 
-       redef fun collect(mclass, mainmodule) do
-               values[mclass] = mclass.inherited_mproperties2(mainmodule).length
+       var mainmodule: MModule
+       var min_visibility: MVisibility
+
+       init(mainmodule: MModule, min_visibility: MVisibility) do
+               self.mainmodule = mainmodule
+               self.min_visibility = min_visibility
        end
        end
-end
 
 
-redef class MClass
-       # FIXME wait for cleaning in model_utils
-       redef fun intro_mproperties: Set[MProperty] do
-               var set = new HashSet[MProperty]
-               for mclassdef in mclassdefs do
-                       set.add_all(mclassdef.intro_mproperties)
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.collect_inherited_mproperties(min_visibility).length
                end
                end
-               return set
        end
        end
+end
 
 
-       # FIXME wait for cleaning in model_utils
-       redef fun redef_mproperties: Set[MProperty] do
-               var set = new HashSet[MProperty]
-               for mclassdef in mclassdefs do
-                       for mpropdef in mclassdef.mpropdefs do
-                               if mpropdef.mproperty.intro_mclassdef.mclass != self then set.add(mpropdef.mproperty)
-                       end
-               end
-               return set
+# Class Metric: Number of Local MProperties (Intro + Redef)
+class CNBLP
+       super MClassMetric
+       super IntMetric
+       redef fun name do return "cnblp"
+       redef fun desc do return "number of local properties (intro + redef)"
+
+       var mainmodule: MModule
+       var min_visibility: MVisibility
+
+       init(mainmodule: MModule, min_visibility: MVisibility) do
+               self.mainmodule = mainmodule
+               self.min_visibility = min_visibility
        end
 
        end
 
-       # FIXME wait for cleaning in model_utils
-       fun inherited_mproperties2(mainmodule: MModule): Set[MProperty] do
-               var set = new HashSet[MProperty]
-               for parent in in_hierarchy(mainmodule).direct_greaters do
-                       set.add_all(parent.intro_mproperties)
-                       set.add_all(parent.inherited_mproperties2(mainmodule))
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.collect_local_mproperties(min_visibility).length
                end
                end
-               return set
        end
 end
        end
 end