]> source.dussan.org Git - sonarqube.git/blob
8b807b8ded6088cb6678a91ac027feef02cfe663
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 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.server.measure.live;
21
22 import java.util.List;
23 import java.util.Optional;
24 import java.util.OptionalInt;
25 import java.util.Set;
26 import java.util.function.BiConsumer;
27 import org.sonar.api.issue.Issue;
28 import org.sonar.api.measures.CoreMetrics;
29 import org.sonar.api.measures.Metric;
30 import org.sonar.api.rule.Severity;
31 import org.sonar.api.rules.RuleType;
32 import org.sonar.server.measure.Rating;
33
34 import static java.util.Arrays.asList;
35 import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS;
36 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED;
37 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS;
38 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
39 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED;
40 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS;
41 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS;
42 import static org.sonar.server.measure.Rating.RATING_BY_SEVERITY;
43 import static org.sonar.server.security.SecurityReviewRating.computePercent;
44 import static org.sonar.server.security.SecurityReviewRating.computeRating;
45
46 public class MeasureUpdateFormulaFactoryImpl implements MeasureUpdateFormulaFactory {
47   private static final List<MeasureUpdateFormula> FORMULAS = asList(
48     new MeasureUpdateFormula(CODE_SMELLS, false, new AddChildren(),
49       (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.CODE_SMELL, false))),
50
51     new MeasureUpdateFormula(CoreMetrics.BUGS, false, new AddChildren(),
52       (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.BUG, false))),
53
54     new MeasureUpdateFormula(CoreMetrics.VULNERABILITIES, false, new AddChildren(),
55       (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.VULNERABILITY, false))),
56
57     new MeasureUpdateFormula(CoreMetrics.SECURITY_HOTSPOTS, false, new AddChildren(),
58       (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.SECURITY_HOTSPOT, false))),
59
60     new MeasureUpdateFormula(CoreMetrics.VIOLATIONS, false, new AddChildren(),
61       (context, issues) -> context.setValue(issues.countUnresolved(false))),
62
63     new MeasureUpdateFormula(CoreMetrics.BLOCKER_VIOLATIONS, false, new AddChildren(),
64       (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.BLOCKER, false))),
65
66     new MeasureUpdateFormula(CoreMetrics.CRITICAL_VIOLATIONS, false, new AddChildren(),
67       (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.CRITICAL, false))),
68
69     new MeasureUpdateFormula(CoreMetrics.MAJOR_VIOLATIONS, false, new AddChildren(),
70       (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.MAJOR, false))),
71
72     new MeasureUpdateFormula(CoreMetrics.MINOR_VIOLATIONS, false, new AddChildren(),
73       (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.MINOR, false))),
74
75     new MeasureUpdateFormula(CoreMetrics.INFO_VIOLATIONS, false, new AddChildren(),
76       (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.INFO, false))),
77
78     new MeasureUpdateFormula(CoreMetrics.FALSE_POSITIVE_ISSUES, false, new AddChildren(),
79       (context, issues) -> context.setValue(issues.countByResolution(Issue.RESOLUTION_FALSE_POSITIVE, false))),
80
81     new MeasureUpdateFormula(CoreMetrics.WONT_FIX_ISSUES, false, new AddChildren(),
82       (context, issues) -> context.setValue(issues.countByResolution(Issue.RESOLUTION_WONT_FIX, false))),
83
84     new MeasureUpdateFormula(CoreMetrics.OPEN_ISSUES, false, new AddChildren(),
85       (context, issues) -> context.setValue(issues.countByStatus(Issue.STATUS_OPEN, false))),
86
87     new MeasureUpdateFormula(CoreMetrics.REOPENED_ISSUES, false, new AddChildren(),
88       (context, issues) -> context.setValue(issues.countByStatus(Issue.STATUS_REOPENED, false))),
89
90     new MeasureUpdateFormula(CoreMetrics.CONFIRMED_ISSUES, false, new AddChildren(),
91       (context, issues) -> context.setValue(issues.countByStatus(Issue.STATUS_CONFIRMED, false))),
92
93     new MeasureUpdateFormula(CoreMetrics.TECHNICAL_DEBT, false, new AddChildren(),
94       (context, issues) -> context.setValue(issues.sumEffortOfUnresolved(RuleType.CODE_SMELL, false))),
95
96     new MeasureUpdateFormula(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, false, new AddChildren(),
97       (context, issues) -> context.setValue(issues.sumEffortOfUnresolved(RuleType.BUG, false))),
98
99     new MeasureUpdateFormula(CoreMetrics.SECURITY_REMEDIATION_EFFORT, false, new AddChildren(),
100       (context, issues) -> context.setValue(issues.sumEffortOfUnresolved(RuleType.VULNERABILITY, false))),
101
102     new MeasureUpdateFormula(CoreMetrics.SQALE_DEBT_RATIO, false,
103       (context, formula) -> context.setValue(100.0 * debtDensity(context)),
104       (context, issues) -> context.setValue(100.0 * debtDensity(context)),
105       asList(CoreMetrics.TECHNICAL_DEBT, CoreMetrics.DEVELOPMENT_COST)),
106
107     new MeasureUpdateFormula(CoreMetrics.SQALE_RATING, false,
108       (context, issues) -> context.setValue(context.getDebtRatingGrid().getRatingForDensity(debtDensity(context))),
109       (context, issues) -> context.setValue(context.getDebtRatingGrid().getRatingForDensity(debtDensity(context))),
110       asList(CoreMetrics.TECHNICAL_DEBT, CoreMetrics.DEVELOPMENT_COST)),
111
112     new MeasureUpdateFormula(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, false,
113       (context, formula) -> context.setValue(effortToReachMaintainabilityRatingA(context)),
114       (context, issues) -> context.setValue(effortToReachMaintainabilityRatingA(context)), asList(CoreMetrics.TECHNICAL_DEBT, CoreMetrics.DEVELOPMENT_COST)),
115
116     new MeasureUpdateFormula(CoreMetrics.RELIABILITY_RATING, false, new MaxRatingChildren(),
117       (context, issues) -> context.setValue(RATING_BY_SEVERITY.get(issues.getHighestSeverityOfUnresolved(RuleType.BUG, false).orElse(Severity.INFO)))),
118
119     new MeasureUpdateFormula(CoreMetrics.SECURITY_RATING, false, new MaxRatingChildren(),
120       (context, issues) -> context.setValue(RATING_BY_SEVERITY.get(issues.getHighestSeverityOfUnresolved(RuleType.VULNERABILITY, false).orElse(Severity.INFO)))),
121
122     new MeasureUpdateFormula(SECURITY_HOTSPOTS_REVIEWED_STATUS, false,
123       (context, formula) -> context.setValue(context.getValue(SECURITY_HOTSPOTS_REVIEWED_STATUS).orElse(0D) + context.getChildrenHotspotsReviewed()),
124       (context, issues) -> context.setValue(issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, false))),
125
126     new MeasureUpdateFormula(SECURITY_HOTSPOTS_TO_REVIEW_STATUS, false,
127       (context, formula) -> context.setValue(context.getValue(SECURITY_HOTSPOTS_TO_REVIEW_STATUS).orElse(0D) + context.getChildrenHotspotsToReview()),
128       (context, issues) -> context.setValue(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, false))),
129
130     new MeasureUpdateFormula(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED, false,
131       (context, formula) -> {
132         Optional<Double> percent = computePercent(
133           context.getValue(SECURITY_HOTSPOTS_TO_REVIEW_STATUS).orElse(0D).longValue(),
134           context.getValue(SECURITY_HOTSPOTS_REVIEWED_STATUS).orElse(0D).longValue());
135         percent.ifPresent(context::setValue);
136       },
137       (context, issues) -> computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, false), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, false))
138         .ifPresent(context::setValue)),
139
140     new MeasureUpdateFormula(CoreMetrics.SECURITY_REVIEW_RATING, false,
141       (context, formula) -> context.setValue(computeRating(context.getValue(SECURITY_HOTSPOTS_REVIEWED).orElse(null))),
142       (context, issues) -> {
143         Optional<Double> percent = computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, false), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, false));
144         context.setValue(computeRating(percent.orElse(null)));
145       }),
146
147     new MeasureUpdateFormula(CoreMetrics.NEW_CODE_SMELLS, true, new AddChildren(),
148       (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.CODE_SMELL, true))),
149
150     new MeasureUpdateFormula(CoreMetrics.NEW_BUGS, true, new AddChildren(),
151       (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.BUG, true))),
152
153     new MeasureUpdateFormula(CoreMetrics.NEW_VULNERABILITIES, true, new AddChildren(),
154       (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.VULNERABILITY, true))),
155
156     new MeasureUpdateFormula(CoreMetrics.NEW_SECURITY_HOTSPOTS, true, new AddChildren(),
157       (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.SECURITY_HOTSPOT, true))),
158
159     new MeasureUpdateFormula(CoreMetrics.NEW_VIOLATIONS, true, new AddChildren(),
160       (context, issues) -> context.setValue(issues.countUnresolved(true))),
161
162     new MeasureUpdateFormula(CoreMetrics.NEW_BLOCKER_VIOLATIONS, true, new AddChildren(),
163       (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.BLOCKER, true))),
164
165     new MeasureUpdateFormula(CoreMetrics.NEW_CRITICAL_VIOLATIONS, true, new AddChildren(),
166       (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.CRITICAL, true))),
167
168     new MeasureUpdateFormula(CoreMetrics.NEW_MAJOR_VIOLATIONS, true, new AddChildren(),
169       (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.MAJOR, true))),
170
171     new MeasureUpdateFormula(CoreMetrics.NEW_MINOR_VIOLATIONS, true, new AddChildren(),
172       (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.MINOR, true))),
173
174     new MeasureUpdateFormula(CoreMetrics.NEW_INFO_VIOLATIONS, true, new AddChildren(),
175       (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.INFO, true))),
176
177     new MeasureUpdateFormula(CoreMetrics.NEW_TECHNICAL_DEBT, true, new AddChildren(),
178       (context, issues) -> context.setValue(issues.sumEffortOfUnresolved(RuleType.CODE_SMELL, true))),
179
180     new MeasureUpdateFormula(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, true, new AddChildren(),
181       (context, issues) -> context.setValue(issues.sumEffortOfUnresolved(RuleType.BUG, true))),
182
183     new MeasureUpdateFormula(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, true, new AddChildren(),
184       (context, issues) -> context.setValue(issues.sumEffortOfUnresolved(RuleType.VULNERABILITY, true))),
185
186     new MeasureUpdateFormula(CoreMetrics.NEW_RELIABILITY_RATING, true, new MaxRatingChildren(),
187       (context, issues) -> {
188         String highestSeverity = issues.getHighestSeverityOfUnresolved(RuleType.BUG, true).orElse(Severity.INFO);
189         context.setValue(RATING_BY_SEVERITY.get(highestSeverity));
190       }),
191
192     new MeasureUpdateFormula(CoreMetrics.NEW_SECURITY_RATING, true, new MaxRatingChildren(),
193       (context, issues) -> {
194         String highestSeverity = issues.getHighestSeverityOfUnresolved(RuleType.VULNERABILITY, true).orElse(Severity.INFO);
195         context.setValue(RATING_BY_SEVERITY.get(highestSeverity));
196       }),
197
198     new MeasureUpdateFormula(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, true,
199       (context, formula) -> context.setValue(context.getValue(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS).orElse(0D) + context.getChildrenNewHotspotsReviewed()),
200       (context, issues) -> context.setValue(issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, true))),
201
202     new MeasureUpdateFormula(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, true,
203       (context, formula) -> context.setValue(context.getValue(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS).orElse(0D) + context.getChildrenNewHotspotsToReview()),
204       (context, issues) -> context.setValue(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, true))),
205
206     new MeasureUpdateFormula(NEW_SECURITY_HOTSPOTS_REVIEWED, true,
207       (context, formula) -> {
208         Optional<Double> percent = computePercent(
209           context.getValue(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS).orElse(0D).longValue(),
210           context.getValue(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS).orElse(0D).longValue());
211         percent.ifPresent(context::setValue);
212       },
213       (context, issues) -> computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, true), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, true))
214         .ifPresent(context::setValue)),
215
216     new MeasureUpdateFormula(CoreMetrics.NEW_SECURITY_REVIEW_RATING, true,
217       (context, formula) -> context.setValue(computeRating(context.getValue(NEW_SECURITY_HOTSPOTS_REVIEWED).orElse(null))),
218       (context, issues) -> {
219         Optional<Double> percent = computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, true), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, true));
220         context.setValue(computeRating(percent.orElse(null)));
221       }),
222
223     new MeasureUpdateFormula(CoreMetrics.NEW_SQALE_DEBT_RATIO, true,
224       (context, formula) -> context.setValue(100.0D * newDebtDensity(context)),
225       (context, issues) -> context.setValue(100.0D * newDebtDensity(context)),
226       asList(CoreMetrics.NEW_TECHNICAL_DEBT, CoreMetrics.NEW_DEVELOPMENT_COST)),
227
228     new MeasureUpdateFormula(CoreMetrics.NEW_MAINTAINABILITY_RATING, true,
229       (context, formula) -> context.setValue(context.getDebtRatingGrid().getRatingForDensity(newDebtDensity(context))),
230       (context, issues) -> context.setValue(context.getDebtRatingGrid().getRatingForDensity(newDebtDensity(context))),
231       asList(CoreMetrics.NEW_TECHNICAL_DEBT, CoreMetrics.NEW_DEVELOPMENT_COST)));
232
233   private static final Set<Metric> FORMULA_METRICS = MeasureUpdateFormulaFactory.extractMetrics(FORMULAS);
234
235   private static double debtDensity(MeasureUpdateFormula.Context context) {
236     double debt = Math.max(context.getValue(CoreMetrics.TECHNICAL_DEBT).orElse(0.0D), 0.0D);
237     Optional<Double> devCost = context.getText(CoreMetrics.DEVELOPMENT_COST).map(Double::parseDouble);
238     if (devCost.isPresent() && Double.doubleToRawLongBits(devCost.get()) > 0L) {
239       return debt / devCost.get();
240     }
241     return 0.0D;
242   }
243
244   private static double newDebtDensity(MeasureUpdateFormula.Context context) {
245     double debt = Math.max(context.getValue(CoreMetrics.NEW_TECHNICAL_DEBT).orElse(0.0D), 0.0D);
246     Optional<Double> devCost = context.getValue(CoreMetrics.NEW_DEVELOPMENT_COST);
247     if (devCost.isPresent() && Double.doubleToRawLongBits(devCost.get()) > 0L) {
248       return debt / devCost.get();
249     }
250     return 0.0D;
251   }
252
253   private static double effortToReachMaintainabilityRatingA(MeasureUpdateFormula.Context context) {
254     double developmentCost = context.getText(CoreMetrics.DEVELOPMENT_COST).map(Double::parseDouble).orElse(0.0D);
255     double effort = context.getValue(CoreMetrics.TECHNICAL_DEBT).orElse(0.0D);
256     double upperGradeCost = context.getDebtRatingGrid().getGradeLowerBound(Rating.B) * developmentCost;
257     return upperGradeCost < effort ? (effort - upperGradeCost) : 0.0D;
258   }
259
260   static class AddChildren implements BiConsumer<MeasureUpdateFormula.Context, MeasureUpdateFormula> {
261     @Override
262     public void accept(MeasureUpdateFormula.Context context, MeasureUpdateFormula formula) {
263       double sum = context.getChildrenValues().stream().mapToDouble(x -> x).sum();
264       context.setValue(context.getValue(formula.getMetric()).orElse(0D) + sum);
265     }
266   }
267
268   private static class MaxRatingChildren implements BiConsumer<MeasureUpdateFormula.Context, MeasureUpdateFormula> {
269     @Override
270     public void accept(MeasureUpdateFormula.Context context, MeasureUpdateFormula formula) {
271       OptionalInt max = context.getChildrenValues().stream().mapToInt(Double::intValue).max();
272       if (max.isPresent()) {
273         int currentRating = context.getValue(formula.getMetric()).map(Double::intValue).orElse(Rating.A.getIndex());
274         context.setValue(Rating.valueOf(Math.max(currentRating, max.getAsInt())));
275       }
276     }
277   }
278
279   @Override
280   public List<MeasureUpdateFormula> getFormulas() {
281     return FORMULAS;
282   }
283
284   @Override
285   public Set<Metric> getFormulaMetrics() {
286     return FORMULA_METRICS;
287   }
288 }