package Analizo::GlobalMetrics;
use strict;
use parent qw(Class::Accessor::Fast);
use Analizo::GlobalMetric::TotalAbstractClasses;
use Analizo::GlobalMetric::MethodsPerAbstractClass;
use Analizo::GlobalMetric::ChangeCost;
use Statistics::Descriptive;
__PACKAGE__->mk_accessors(qw(
model
calculators
metric_report
values_lists
module_metrics_list
));
sub new {
my ($package, %args) = @_;
my @instance_variables = (
model => $args{model},
calculators => _initialize_calculators($args{model}),
metric_report => _initialize_metric_report(),
values_lists => {},
);
return bless { @instance_variables }, $package;
}
sub _initialize_calculators {
my ($model) = @_;
my %calculators = (
total_abstract_classes => Analizo::GlobalMetric::TotalAbstractClasses->new(model => $model),
total_methods_per_abstract_class => Analizo::GlobalMetric::MethodsPerAbstractClass->new(model => $model),
change_cost => Analizo::GlobalMetric::ChangeCost->new(model => $model),
);
return \%calculators;
}
sub _initialize_metric_report {
my %metric_report = (
total_modules => 0,
total_modules_with_defined_methods => 0,
total_modules_with_defined_attributes => 0,
total_nom => 0,
total_loc => 0,
total_cof => 0
);
return \%metric_report;
}
sub list {
my ($self) = @_;
my %list = (
total_cof => "Total Coupling Factor",
total_modules => "Total Number of Modules",
total_nom => "Total Number of Methods",
total_loc => "Total Lines of Code",
total_modules_with_defined_methods => "Total number of modules with at least one defined method",
total_modules_with_defined_attributes => "Total number of modules with at least one defined attributes"
);
for my $metric (keys %{$self->calculators}) {
$list{$metric} = $self->calculators->{$metric}->description;
}
return %list;
}
sub add_module_values {
my ($self, $values) = @_;
$self->_update_metric_report($values);
$self->_add_values_to_values_lists($values);
}
sub _update_metric_report {
my ($self, $values) = @_;
$self->metric_report->{'total_modules'} += 1;
$self->metric_report->{'total_modules_with_defined_methods'} += 1 if $values->{'nom'} > 0;
$self->metric_report->{'total_modules_with_defined_attributes'} += 1 if $values->{'noa'} > 0;
$self->metric_report->{'total_nom'} += $values->{'nom'};
$self->metric_report->{'total_loc'} += $values->{'loc'};
}
sub _add_values_to_values_lists {
my ($self, $values) = @_;
for my $metric (keys %{$values}) {
$self->_add_metric_value_to_values_list($metric, $values->{$metric});
}
}
sub _add_metric_value_to_values_list {
my ($self, $metric, $metric_value) = @_;
if( $metric ne '_module' && $metric ne '_filename' ) {
$self->values_lists->{$metric} = [] unless ($self->values_lists->{$metric});
push @{$self->values_lists->{$metric}}, $metric_value;
}
}
sub report {
my ($self) = @_;
$self->_include_metrics_from_calculators;
$self->_add_statistics;
$self->_add_total_coupling_factor;
return \%{$self->metric_report};
}
sub _include_metrics_from_calculators {
my ($self) = @_;
for my $metric (keys %{$self->calculators}) {
$self->metric_report->{$metric} = $self->calculators->{$metric}->calculate();
}
}
sub _add_statistics {
my ($self) = @_;
for my $metric (keys %{$self->values_lists}) {
my $statistics = Statistics::Descriptive::Full->new();
$statistics->add_data(@{$self->values_lists->{$metric}});
$self->_add_descriptive_statistics($metric, $statistics);
$self->_add_distributions_statistics($metric, $statistics);
}
}
sub _add_descriptive_statistics {
my ($self, $metric, $statistics) = @_;
$self->metric_report->{$metric . "_mean"} = $statistics->mean();
$self->metric_report->{$metric . "_mode"} = $statistics->mode();
$self->metric_report->{$metric . "_standard_deviation"} = $statistics->standard_deviation();
$self->metric_report->{$metric . "_sum"} = $statistics->sum();
$self->metric_report->{$metric . "_variance"} = $statistics->variance();
$self->metric_report->{$metric . "_quantile_min"} = $statistics->min(); #minimum
$self->metric_report->{$metric . "_quantile_lower"} = $statistics->quantile(1); #lower quartile
$self->metric_report->{$metric . "_quantile_median"} = $statistics->median(); #median
$self->metric_report->{$metric . "_quantile_upper"} = $statistics->quantile(3); #upper quartile
$self->metric_report->{$metric . "_quantile_ninety_five"} = $statistics->percentile(95); #95th percentile
$self->metric_report->{$metric . "_quantile_max"} = $statistics->max(); #maximum
}
sub _add_distributions_statistics {
my ($self, $metric, $statistics) = @_;
if (($statistics->count >= 4) && ($statistics->variance() > 0)) {
$self->metric_report->{$metric . "_kurtosis"} = $statistics->kurtosis();
$self->metric_report->{$metric . "_skewness"} = $statistics->skewness();
}
else {
$self->metric_report->{$metric . "_kurtosis"} = 0;
$self->metric_report->{$metric . "_skewness"} = 0;
}
}
sub _add_total_coupling_factor {
my ($self) = @_;
my $total_modules = $self->metric_report->{'total_modules'};
my $total_acc = $self->metric_report->{'acc_sum'};
$self->metric_report->{"total_cof"} = $self->coupling_factor($total_acc, $total_modules);
}
sub coupling_factor {
my ($self, $total_acc, $total_modules) = @_;
return ($total_modules > 1) ? $total_acc / _number_of_combinations($total_modules) : 1;
}
sub _number_of_combinations {
my ($total_modules) = @_;
return $total_modules * ($total_modules - 1);
}
1;