3 * Copyright (C) 2009-2024 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.ce.task.projectanalysis.qualitymodel;
23 import org.sonar.api.ce.measure.Issue;
24 import org.sonar.api.issue.impact.Severity;
25 import org.sonar.api.issue.impact.SoftwareQuality;
26 import org.sonar.api.measures.CoreMetrics;
27 import org.sonar.ce.task.projectanalysis.component.Component;
28 import org.sonar.ce.task.projectanalysis.component.PathAwareVisitorAdapter;
29 import org.sonar.ce.task.projectanalysis.formula.counter.RatingValue;
30 import org.sonar.ce.task.projectanalysis.issue.ComponentIssuesRepository;
31 import org.sonar.ce.task.projectanalysis.issue.NewIssueClassifier;
32 import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
33 import org.sonar.ce.task.projectanalysis.metric.Metric;
34 import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
35 import org.sonar.core.issue.DefaultIssue;
36 import org.sonar.server.measure.Rating;
37 import org.sonar.server.metric.SoftwareQualitiesMetrics;
39 import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING_KEY;
40 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY;
41 import static org.sonar.api.rule.Severity.BLOCKER;
42 import static org.sonar.api.rule.Severity.CRITICAL;
43 import static org.sonar.api.rule.Severity.INFO;
44 import static org.sonar.api.rule.Severity.MAJOR;
45 import static org.sonar.api.rule.Severity.MINOR;
46 import static org.sonar.api.rules.RuleType.BUG;
47 import static org.sonar.api.rules.RuleType.VULNERABILITY;
48 import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
49 import static org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit.LEAVES;
50 import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
51 import static org.sonar.server.measure.Rating.A;
52 import static org.sonar.server.measure.Rating.B;
53 import static org.sonar.server.measure.Rating.C;
54 import static org.sonar.server.measure.Rating.D;
55 import static org.sonar.server.measure.Rating.E;
56 import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY;
57 import static org.sonar.server.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY;
60 * Compute following measures :
61 * {@link CoreMetrics#NEW_RELIABILITY_RATING_KEY}
62 * {@link CoreMetrics#NEW_SECURITY_RATING_KEY}
63 * {@link SoftwareQualitiesMetrics#NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY}
64 * {@link SoftwareQualitiesMetrics#NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY}
66 public class NewReliabilityAndSecurityRatingMeasuresVisitor extends PathAwareVisitorAdapter<NewReliabilityAndSecurityRatingMeasuresVisitor.Counter> {
68 private static final Map<String, Rating> RATING_BY_SEVERITY = Map.of(
75 private final MeasureRepository measureRepository;
76 private final ComponentIssuesRepository componentIssuesRepository;
77 private final Map<String, Metric> metricsByKey;
78 private final NewIssueClassifier newIssueClassifier;
80 public NewReliabilityAndSecurityRatingMeasuresVisitor(MetricRepository metricRepository, MeasureRepository measureRepository,
81 ComponentIssuesRepository componentIssuesRepository, NewIssueClassifier newIssueClassifier) {
82 super(LEAVES, POST_ORDER, new CounterFactory(newIssueClassifier));
83 this.measureRepository = measureRepository;
84 this.componentIssuesRepository = componentIssuesRepository;
87 this.metricsByKey = Map.of(
88 NEW_RELIABILITY_RATING_KEY, metricRepository.getByKey(NEW_RELIABILITY_RATING_KEY),
89 NEW_SECURITY_RATING_KEY, metricRepository.getByKey(NEW_SECURITY_RATING_KEY),
90 NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, metricRepository.getByKey(NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY),
91 NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, metricRepository.getByKey(NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY));
92 this.newIssueClassifier = newIssueClassifier;
96 public void visitProject(Component project, Path<Counter> path) {
97 computeAndSaveMeasures(project, path);
101 public void visitDirectory(Component directory, Path<Counter> path) {
102 computeAndSaveMeasures(directory, path);
106 public void visitFile(Component file, Path<Counter> path) {
107 computeAndSaveMeasures(file, path);
110 private void computeAndSaveMeasures(Component component, Path<Counter> path) {
111 if (!newIssueClassifier.isEnabled()) {
114 initRatingsToA(path);
115 processIssues(component, path);
116 processIssuesForSoftwareQuality(component, path);
117 path.current().newRatingValueByMetric.entrySet()
119 .filter(entry -> entry.getValue().isSet())
121 entry -> measureRepository.add(
123 metricsByKey.get(entry.getKey()),
124 newMeasureBuilder().create(entry.getValue().getValue().getIndex())));
128 private static void initRatingsToA(Path<Counter> path) {
129 path.current().newRatingValueByMetric.values().forEach(entry -> entry.increment(A));
132 private void processIssues(Component component, Path<Counter> path) {
133 componentIssuesRepository.getIssues(component)
135 .filter(issue -> issue.resolution() == null)
136 .filter(issue -> issue.type().equals(BUG) || issue.type().equals(VULNERABILITY))
137 .forEach(issue -> path.current().processIssue(issue));
140 private void processIssuesForSoftwareQuality(Component component, Path<Counter> path) {
141 componentIssuesRepository.getIssues(component)
143 .filter(issue -> issue.resolution() == null)
144 .forEach(issue -> path.current().processIssueForSoftwareQuality(issue));
147 private static void addToParent(Path<Counter> path) {
148 if (!path.isRoot()) {
149 path.parent().add(path.current());
153 static class Counter {
154 private final Map<String, RatingValue> newRatingValueByMetric = Map.of(
155 NEW_RELIABILITY_RATING_KEY, new RatingValue(),
156 NEW_SECURITY_RATING_KEY, new RatingValue(),
157 NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, new RatingValue(),
158 NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, new RatingValue());
159 private final NewIssueClassifier newIssueClassifier;
160 private final Component component;
162 public Counter(NewIssueClassifier newIssueClassifier, Component component) {
163 this.newIssueClassifier = newIssueClassifier;
164 this.component = component;
167 void add(Counter otherCounter) {
168 newRatingValueByMetric.forEach((metric, rating) -> rating.increment(otherCounter.newRatingValueByMetric.get(metric)));
171 void processIssue(Issue issue) {
172 if (newIssueClassifier.isNew(component, (DefaultIssue) issue)) {
173 Rating rating = RATING_BY_SEVERITY.get(issue.severity());
174 if (issue.type().equals(BUG)) {
175 newRatingValueByMetric.get(NEW_RELIABILITY_RATING_KEY).increment(rating);
176 } else if (issue.type().equals(VULNERABILITY)) {
177 newRatingValueByMetric.get(NEW_SECURITY_RATING_KEY).increment(rating);
182 void processIssueForSoftwareQuality(Issue issue) {
183 if (newIssueClassifier.isNew(component, (DefaultIssue) issue)) {
184 processSoftwareQualityRating(issue, SoftwareQuality.RELIABILITY, NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY);
185 processSoftwareQualityRating(issue, SoftwareQuality.SECURITY, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY);
189 private void processSoftwareQualityRating(Issue issue, SoftwareQuality softwareQuality, String metricKey) {
190 Severity severity = issue.impacts().get(softwareQuality);
191 if (severity != null) {
192 Rating rating = Rating.RATING_BY_SOFTWARE_QUALITY_SEVERITY.get(severity);
194 if (rating != null) {
195 newRatingValueByMetric.get(metricKey).increment(rating);
201 private static final class CounterFactory extends SimpleStackElementFactory<NewReliabilityAndSecurityRatingMeasuresVisitor.Counter> {
202 private final NewIssueClassifier newIssueClassifier;
204 private CounterFactory(NewIssueClassifier newIssueClassifier) {
205 this.newIssueClassifier = newIssueClassifier;
209 public Counter createForAny(Component component) {
210 return new Counter(newIssueClassifier, component);