3 * Copyright (C) 2009-2020 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 package org.sonar.server.measure.live;
22 import java.util.List;
23 import java.util.Optional;
25 import org.sonar.api.issue.Issue;
26 import org.sonar.api.measures.CoreMetrics;
27 import org.sonar.api.measures.Metric;
28 import org.sonar.api.rule.Severity;
29 import org.sonar.api.rules.RuleType;
30 import org.sonar.server.measure.Rating;
32 import static java.util.Arrays.asList;
33 import static org.sonar.server.measure.Rating.RATING_BY_SEVERITY;
34 import static org.sonar.server.security.SecurityReviewRating.computePercent;
35 import static org.sonar.server.security.SecurityReviewRating.computeRating;
37 public class IssueMetricFormulaFactoryImpl implements IssueMetricFormulaFactory {
39 private static final List<IssueMetricFormula> FORMULAS = asList(
40 new IssueMetricFormula(CoreMetrics.CODE_SMELLS, false,
41 (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.CODE_SMELL, false))),
43 new IssueMetricFormula(CoreMetrics.BUGS, false,
44 (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.BUG, false))),
46 new IssueMetricFormula(CoreMetrics.VULNERABILITIES, false,
47 (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.VULNERABILITY, false))),
49 new IssueMetricFormula(CoreMetrics.SECURITY_HOTSPOTS, false,
50 (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.SECURITY_HOTSPOT, false))),
52 new IssueMetricFormula(CoreMetrics.VIOLATIONS, false,
53 (context, issues) -> context.setValue(issues.countUnresolved(false))),
55 new IssueMetricFormula(CoreMetrics.BLOCKER_VIOLATIONS, false,
56 (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.BLOCKER, false))),
58 new IssueMetricFormula(CoreMetrics.CRITICAL_VIOLATIONS, false,
59 (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.CRITICAL, false))),
61 new IssueMetricFormula(CoreMetrics.MAJOR_VIOLATIONS, false,
62 (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.MAJOR, false))),
64 new IssueMetricFormula(CoreMetrics.MINOR_VIOLATIONS, false,
65 (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.MINOR, false))),
67 new IssueMetricFormula(CoreMetrics.INFO_VIOLATIONS, false,
68 (context, issues) -> context.setValue(issues.countUnresolvedBySeverity(Severity.INFO, false))),
70 new IssueMetricFormula(CoreMetrics.FALSE_POSITIVE_ISSUES, false,
71 (context, issues) -> context.setValue(issues.countByResolution(Issue.RESOLUTION_FALSE_POSITIVE, false))),
73 new IssueMetricFormula(CoreMetrics.WONT_FIX_ISSUES, false,
74 (context, issues) -> context.setValue(issues.countByResolution(Issue.RESOLUTION_WONT_FIX, false))),
76 new IssueMetricFormula(CoreMetrics.OPEN_ISSUES, false,
77 (context, issues) -> context.setValue(issues.countByStatus(Issue.STATUS_OPEN, false))),
79 new IssueMetricFormula(CoreMetrics.REOPENED_ISSUES, false,
80 (context, issues) -> context.setValue(issues.countByStatus(Issue.STATUS_REOPENED, false))),
82 new IssueMetricFormula(CoreMetrics.CONFIRMED_ISSUES, false,
83 (context, issues) -> context.setValue(issues.countByStatus(Issue.STATUS_CONFIRMED, false))),
85 new IssueMetricFormula(CoreMetrics.TECHNICAL_DEBT, false,
86 (context, issues) -> context.setValue(issues.sumEffortOfUnresolved(RuleType.CODE_SMELL, false))),
88 new IssueMetricFormula(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, false,
89 (context, issues) -> context.setValue(issues.sumEffortOfUnresolved(RuleType.BUG, false))),
91 new IssueMetricFormula(CoreMetrics.SECURITY_REMEDIATION_EFFORT, false,
92 (context, issues) -> context.setValue(issues.sumEffortOfUnresolved(RuleType.VULNERABILITY, false))),
94 new IssueMetricFormula(CoreMetrics.SQALE_DEBT_RATIO, false,
95 (context, issues) -> context.setValue(100.0 * debtDensity(context)),
96 asList(CoreMetrics.TECHNICAL_DEBT, CoreMetrics.DEVELOPMENT_COST)),
98 new IssueMetricFormula(CoreMetrics.SQALE_RATING, false,
99 (context, issues) -> context
100 .setValue(context.getDebtRatingGrid().getRatingForDensity(debtDensity(context))),
101 asList(CoreMetrics.TECHNICAL_DEBT, CoreMetrics.DEVELOPMENT_COST)),
103 new IssueMetricFormula(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, false,
104 (context, issues) -> context.setValue(effortToReachMaintainabilityRatingA(context)), asList(CoreMetrics.TECHNICAL_DEBT, CoreMetrics.DEVELOPMENT_COST)),
106 new IssueMetricFormula(CoreMetrics.RELIABILITY_RATING, false,
107 (context, issues) -> context.setValue(RATING_BY_SEVERITY.get(issues.getHighestSeverityOfUnresolved(RuleType.BUG, false).orElse(Severity.INFO)))),
109 new IssueMetricFormula(CoreMetrics.SECURITY_RATING, false,
110 (context, issues) -> context.setValue(RATING_BY_SEVERITY.get(issues.getHighestSeverityOfUnresolved(RuleType.VULNERABILITY, false).orElse(Severity.INFO)))),
112 new IssueMetricFormula(CoreMetrics.SECURITY_REVIEW_RATING, false,
113 (context, issues) -> context
114 .setValue(computeRating(computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, false), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, false))))),
116 new IssueMetricFormula(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED, false,
117 (context, issues) -> context
118 .setValue(computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, false), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, false)))),
120 new IssueMetricFormula(CoreMetrics.NEW_CODE_SMELLS, true,
121 (context, issues) -> context.setLeakValue(issues.countUnresolvedByType(RuleType.CODE_SMELL, true))),
123 new IssueMetricFormula(CoreMetrics.NEW_BUGS, true,
124 (context, issues) -> context.setLeakValue(issues.countUnresolvedByType(RuleType.BUG, true))),
126 new IssueMetricFormula(CoreMetrics.NEW_VULNERABILITIES, true,
127 (context, issues) -> context.setLeakValue(issues.countUnresolvedByType(RuleType.VULNERABILITY, true))),
129 new IssueMetricFormula(CoreMetrics.NEW_SECURITY_HOTSPOTS, true,
130 (context, issues) -> context.setLeakValue(issues.countUnresolvedByType(RuleType.SECURITY_HOTSPOT, true))),
132 new IssueMetricFormula(CoreMetrics.NEW_VIOLATIONS, true,
133 (context, issues) -> context.setLeakValue(issues.countUnresolved(true))),
135 new IssueMetricFormula(CoreMetrics.NEW_BLOCKER_VIOLATIONS, true,
136 (context, issues) -> context.setLeakValue(issues.countUnresolvedBySeverity(Severity.BLOCKER, true))),
138 new IssueMetricFormula(CoreMetrics.NEW_CRITICAL_VIOLATIONS, true,
139 (context, issues) -> context.setLeakValue(issues.countUnresolvedBySeverity(Severity.CRITICAL, true))),
141 new IssueMetricFormula(CoreMetrics.NEW_MAJOR_VIOLATIONS, true,
142 (context, issues) -> context.setLeakValue(issues.countUnresolvedBySeverity(Severity.MAJOR, true))),
144 new IssueMetricFormula(CoreMetrics.NEW_MINOR_VIOLATIONS, true,
145 (context, issues) -> context.setLeakValue(issues.countUnresolvedBySeverity(Severity.MINOR, true))),
147 new IssueMetricFormula(CoreMetrics.NEW_INFO_VIOLATIONS, true,
148 (context, issues) -> context.setLeakValue(issues.countUnresolvedBySeverity(Severity.INFO, true))),
150 new IssueMetricFormula(CoreMetrics.NEW_TECHNICAL_DEBT, true,
151 (context, issues) -> context.setLeakValue(issues.sumEffortOfUnresolved(RuleType.CODE_SMELL, true))),
153 new IssueMetricFormula(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, true,
154 (context, issues) -> context.setLeakValue(issues.sumEffortOfUnresolved(RuleType.BUG, true))),
156 new IssueMetricFormula(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, true,
157 (context, issues) -> context.setLeakValue(issues.sumEffortOfUnresolved(RuleType.VULNERABILITY, true))),
159 new IssueMetricFormula(CoreMetrics.NEW_RELIABILITY_RATING, true,
160 (context, issues) -> {
161 String highestSeverity = issues.getHighestSeverityOfUnresolved(RuleType.BUG, true).orElse(Severity.INFO);
162 context.setLeakValue(RATING_BY_SEVERITY.get(highestSeverity));
165 new IssueMetricFormula(CoreMetrics.NEW_SECURITY_RATING, true,
166 (context, issues) -> {
167 String highestSeverity = issues.getHighestSeverityOfUnresolved(RuleType.VULNERABILITY, true).orElse(Severity.INFO);
168 context.setLeakValue(RATING_BY_SEVERITY.get(highestSeverity));
171 new IssueMetricFormula(CoreMetrics.NEW_SECURITY_REVIEW_RATING, true,
172 (context, issues) -> {
173 Rating rating = computeRating(computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, true), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, true)));
174 context.setLeakValue(rating);
177 new IssueMetricFormula(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED, true,
178 (context, issues) -> {
179 double percent = computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, true), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, true));
180 context.setLeakValue(percent);
183 new IssueMetricFormula(CoreMetrics.NEW_SQALE_DEBT_RATIO, true,
184 (context, issues) -> context.setLeakValue(100.0 * newDebtDensity(context)),
185 asList(CoreMetrics.NEW_TECHNICAL_DEBT, CoreMetrics.NEW_DEVELOPMENT_COST)),
187 new IssueMetricFormula(CoreMetrics.NEW_MAINTAINABILITY_RATING, true,
188 (context, issues) -> context.setLeakValue(context.getDebtRatingGrid().getRatingForDensity(
189 newDebtDensity(context))),
190 asList(CoreMetrics.NEW_TECHNICAL_DEBT, CoreMetrics.NEW_DEVELOPMENT_COST)));
192 private static final Set<Metric> FORMULA_METRICS = IssueMetricFormulaFactory.extractMetrics(FORMULAS);
194 private static double debtDensity(IssueMetricFormula.Context context) {
195 double debt = Math.max(context.getValue(CoreMetrics.TECHNICAL_DEBT).orElse(0.0), 0.0);
196 Optional<Double> devCost = context.getValue(CoreMetrics.DEVELOPMENT_COST);
197 if (devCost.isPresent() && Double.doubleToRawLongBits(devCost.get()) > 0L) {
198 return debt / devCost.get();
203 private static double newDebtDensity(IssueMetricFormula.Context context) {
204 double debt = Math.max(context.getLeakValue(CoreMetrics.NEW_TECHNICAL_DEBT).orElse(0.0), 0.0);
205 Optional<Double> devCost = context.getLeakValue(CoreMetrics.NEW_DEVELOPMENT_COST);
206 if (devCost.isPresent() && Double.doubleToRawLongBits(devCost.get()) > 0L) {
207 return debt / devCost.get();
212 private static double effortToReachMaintainabilityRatingA(IssueMetricFormula.Context context) {
213 double developmentCost = context.getValue(CoreMetrics.DEVELOPMENT_COST).orElse(0.0);
214 double effort = context.getValue(CoreMetrics.TECHNICAL_DEBT).orElse(0.0);
215 double upperGradeCost = context.getDebtRatingGrid().getGradeLowerBound(Rating.B) * developmentCost;
216 return upperGradeCost < effort ? (effort - upperGradeCost) : 0.0;
220 public List<IssueMetricFormula> getFormulas() {
225 public Set<Metric> getFormulaMetrics() {
226 return FORMULA_METRICS;