]> source.dussan.org Git - sonarqube.git/blob
ec9221056c131bb5f4f7f763c120d94e552437cf
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2024 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.ce.task.projectanalysis.qualitymodel;
21
22 import java.util.Optional;
23 import org.sonar.api.measures.CoreMetrics;
24 import org.sonar.ce.task.projectanalysis.component.Component;
25 import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
26 import org.sonar.ce.task.projectanalysis.component.PathAwareVisitorAdapter;
27 import org.sonar.ce.task.projectanalysis.measure.Measure;
28 import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
29 import org.sonar.ce.task.projectanalysis.measure.RatingMeasures;
30 import org.sonar.ce.task.projectanalysis.metric.Metric;
31 import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
32 import org.sonar.server.measure.Rating;
33 import org.sonar.server.metric.SoftwareQualitiesMetrics;
34
35 import static org.sonar.api.measures.CoreMetrics.DEVELOPMENT_COST_KEY;
36 import static org.sonar.api.measures.CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY;
37 import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
38 import static org.sonar.api.measures.CoreMetrics.SQALE_DEBT_RATIO_KEY;
39 import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY;
40 import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
41 import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
42 import static org.sonar.server.metric.SoftwareQualitiesMetrics.EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A_KEY;
43 import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO_KEY;
44 import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY;
45 import static org.sonar.server.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY;
46
47 /**
48  * Compute measures related to maintainability for projects and descendants :
49  * {@link CoreMetrics#DEVELOPMENT_COST_KEY}
50  * {@link CoreMetrics#SQALE_DEBT_RATIO_KEY}
51  * {@link CoreMetrics#SQALE_RATING_KEY}
52  * {@link CoreMetrics#EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY}
53  * {@link SoftwareQualitiesMetrics#SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO_KEY}
54  * {@link SoftwareQualitiesMetrics#SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY}
55  * {@link SoftwareQualitiesMetrics#EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A_KEY}
56  */
57 public class MaintainabilityMeasuresVisitor extends PathAwareVisitorAdapter<MaintainabilityMeasuresVisitor.Counter> {
58   private final MeasureRepository measureRepository;
59   private final RatingSettings ratingSettings;
60
61   private final Metric nclocMetric;
62   private final Metric developmentCostMetric;
63
64   // Maintainability metrics based on RuleType
65   private final Metric maintainabilityRemediationEffortMetric;
66   private final Metric debtRatioMetric;
67   private final Metric maintainabilityRatingMetric;
68   private final Metric effortToMaintainabilityRatingAMetric;
69
70   // Maintainability metrics based on Software Quality
71   private final Metric softwareQualityMaintainabilityRemediationEffortMetric;
72   private final Metric softwareQualityDebtRatioMetric;
73   private final Metric softwareQualityEffortToMaintainabilityRatingAMetric;
74   private final Metric softwareQualityMaintainabilityRatingMetric;
75
76   public MaintainabilityMeasuresVisitor(MetricRepository metricRepository, MeasureRepository measureRepository, RatingSettings ratingSettings) {
77     super(CrawlerDepthLimit.FILE, Order.POST_ORDER, CounterFactory.INSTANCE);
78     this.measureRepository = measureRepository;
79     this.ratingSettings = ratingSettings;
80
81     // Input metrics
82     this.nclocMetric = metricRepository.getByKey(NCLOC_KEY);
83     this.maintainabilityRemediationEffortMetric = metricRepository.getByKey(TECHNICAL_DEBT_KEY);
84     this.softwareQualityMaintainabilityRemediationEffortMetric = metricRepository.getByKey(SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY);
85
86     // Output metrics
87     this.developmentCostMetric = metricRepository.getByKey(DEVELOPMENT_COST_KEY);
88
89     this.debtRatioMetric = metricRepository.getByKey(SQALE_DEBT_RATIO_KEY);
90     this.maintainabilityRatingMetric = metricRepository.getByKey(SQALE_RATING_KEY);
91     this.effortToMaintainabilityRatingAMetric = metricRepository.getByKey(EFFORT_TO_REACH_MAINTAINABILITY_RATING_A_KEY);
92
93     this.softwareQualityDebtRatioMetric = metricRepository.getByKey(SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO_KEY);
94     this.softwareQualityMaintainabilityRatingMetric = metricRepository.getByKey(SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY);
95     this.softwareQualityEffortToMaintainabilityRatingAMetric = metricRepository.getByKey(EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A_KEY);
96   }
97
98   @Override
99   public void visitProject(Component project, Path<Counter> path) {
100     computeAndSaveMeasures(project, path);
101   }
102
103   @Override
104   public void visitDirectory(Component directory, Path<Counter> path) {
105     computeAndSaveMeasures(directory, path);
106   }
107
108   @Override
109   public void visitFile(Component file, Path<Counter> path) {
110     path.current().addDevCosts(computeDevelopmentCost(file));
111     computeAndSaveMeasures(file, path);
112   }
113
114   private long computeDevelopmentCost(Component file) {
115     Optional<Measure> measure = measureRepository.getRawMeasure(file, nclocMetric);
116     long ncloc = measure.map(Measure::getIntValue).orElse(0);
117     return ncloc * ratingSettings.getDevCost();
118   }
119
120   private void computeAndSaveMeasures(Component component, Path<Counter> path) {
121     addDevelopmentCostMeasure(component, path.current());
122
123     double density = computeDensity(maintainabilityRemediationEffortMetric, component, path.current());
124     addDebtRatioMeasure(debtRatioMetric, component, density);
125     addMaintainabilityRatingMeasure(component, density);
126     addEffortToMaintainabilityRatingAMeasure(maintainabilityRemediationEffortMetric, effortToMaintainabilityRatingAMetric, component, path);
127
128     double densityBasedOnSoftwareQuality = computeDensity(softwareQualityMaintainabilityRemediationEffortMetric, component, path.current());
129     addDebtRatioMeasure(softwareQualityDebtRatioMetric, component, densityBasedOnSoftwareQuality);
130     addSoftwareQualityMaintainabilityRatingMeasure(component, densityBasedOnSoftwareQuality);
131     addEffortToMaintainabilityRatingAMeasure(softwareQualityMaintainabilityRemediationEffortMetric, softwareQualityEffortToMaintainabilityRatingAMetric, component, path);
132
133     addToParent(path);
134   }
135
136   private double computeDensity(Metric remediationEffortMetric, Component component, Counter developmentCost) {
137     Optional<Measure> measure = measureRepository.getRawMeasure(component, remediationEffortMetric);
138     double maintainabilityRemediationEffort = measure.isPresent() ? measure.get().getLongValue() : 0L;
139     if (Double.doubleToRawLongBits(developmentCost.devCosts) != 0L) {
140       return maintainabilityRemediationEffort / developmentCost.devCosts;
141     }
142     return 0D;
143   }
144
145   private void addDevelopmentCostMeasure(Component component, Counter developmentCost) {
146     // the value of this measure is stored as a string because it can exceed the size limit of number storage on some DB
147     measureRepository.add(component, developmentCostMetric, newMeasureBuilder().create(Long.toString(developmentCost.devCosts)));
148   }
149
150   private void addDebtRatioMeasure(Metric debtRatioMetric, Component component, double density) {
151     measureRepository.add(component, debtRatioMetric, newMeasureBuilder().create(100.0 * density, debtRatioMetric.getDecimalScale()));
152   }
153
154   private void addMaintainabilityRatingMeasure(Component component, double density) {
155     Rating rating = ratingSettings.getDebtRatingGrid().getRatingForDensity(density);
156     measureRepository.add(component, maintainabilityRatingMetric, RatingMeasures.get(rating));
157   }
158
159   private void addSoftwareQualityMaintainabilityRatingMeasure(Component component, double density) {
160     Rating rating = ratingSettings.getDebtRatingGrid().getAToDRatingForDensity(density);
161     measureRepository.add(component, softwareQualityMaintainabilityRatingMetric, RatingMeasures.get(rating));
162   }
163
164   private void addEffortToMaintainabilityRatingAMeasure(Metric maintainabilityRemediationEffortMetric, Metric effortToMaintainabilityRatingAMetric,
165     Component component, Path<Counter> path) {
166     long developmentCostValue = path.current().devCosts;
167     Optional<Measure> effortMeasure = measureRepository.getRawMeasure(component, maintainabilityRemediationEffortMetric);
168     long effort = effortMeasure.isPresent() ? effortMeasure.get().getLongValue() : 0L;
169     long upperGradeCost = ((Double) (ratingSettings.getDebtRatingGrid().getGradeLowerBound(Rating.B) * developmentCostValue)).longValue();
170     long effortToRatingA = upperGradeCost < effort ? (effort - upperGradeCost) : 0L;
171     measureRepository.add(component, effortToMaintainabilityRatingAMetric, Measure.newMeasureBuilder().create(effortToRatingA));
172   }
173
174   private static void addToParent(Path<Counter> path) {
175     if (!path.isRoot()) {
176       path.parent().add(path.current());
177     }
178   }
179
180   public static final class Counter {
181     private long devCosts = 0;
182
183     private Counter() {
184       // prevents instantiation
185     }
186
187     void add(Counter otherCounter) {
188       addDevCosts(otherCounter.devCosts);
189     }
190
191     void addDevCosts(long developmentCosts) {
192       this.devCosts += developmentCosts;
193     }
194   }
195
196   private static final class CounterFactory extends SimpleStackElementFactory<Counter> {
197     public static final CounterFactory INSTANCE = new CounterFactory();
198
199     private CounterFactory() {
200       // prevents instantiation
201     }
202
203     @Override
204     public Counter createForAny(Component component) {
205       return new Counter();
206     }
207   }
208 }