/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.computation.task.projectanalysis.qualitymodel;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import org.sonar.api.ce.measure.Issue;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.PathAwareVisitorAdapter;
import org.sonar.server.computation.task.projectanalysis.formula.counter.RatingValue;
import org.sonar.server.computation.task.projectanalysis.issue.ComponentIssuesRepository;
import org.sonar.server.computation.task.projectanalysis.measure.Measure;
import org.sonar.server.computation.task.projectanalysis.measure.MeasureRepository;
import org.sonar.server.computation.task.projectanalysis.metric.Metric;
import org.sonar.server.computation.task.projectanalysis.metric.MetricRepository;
import static org.sonar.api.measures.CoreMetrics.RELIABILITY_RATING_KEY;
import static org.sonar.api.measures.CoreMetrics.SECURITY_RATING_KEY;
import static org.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.api.rule.Severity.CRITICAL;
import static org.sonar.api.rule.Severity.INFO;
import static org.sonar.api.rule.Severity.MAJOR;
import static org.sonar.api.rule.Severity.MINOR;
import static org.sonar.api.rules.RuleType.BUG;
import static org.sonar.api.rules.RuleType.VULNERABILITY;
import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
import static org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit.FILE;
import static org.sonar.server.computation.task.projectanalysis.measure.Measure.newMeasureBuilder;
import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating;
import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating.A;
import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating.B;
import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating.C;
import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating.D;
import static org.sonar.server.computation.task.projectanalysis.qualitymodel.RatingGrid.Rating.E;
/**
* Compute following measures for projects and descendants:
* {@link CoreMetrics#RELIABILITY_RATING_KEY}
* {@link CoreMetrics#SECURITY_RATING_KEY}
*/
public class ReliabilityAndSecurityRatingMeasuresVisitor extends PathAwareVisitorAdapter<ReliabilityAndSecurityRatingMeasuresVisitor.Counter> {
private static final Map<String, Rating> RATING_BY_SEVERITY = ImmutableMap.of(
BLOCKER, E,
CRITICAL, D,
MAJOR, C,
MINOR, B,
INFO, A);
private final MeasureRepository measureRepository;
private final ComponentIssuesRepository componentIssuesRepository;
// Output metrics
private final Metric reliabilityRatingMetric;
private final Metric securityRatingMetric;
private final Map<String, Metric> metricsByKey;
public ReliabilityAndSecurityRatingMeasuresVisitor(MetricRepository metricRepository, MeasureRepository measureRepository, ComponentIssuesRepository componentIssuesRepository) {
super(FILE, POST_ORDER, CounterFactory.INSTANCE);
this.measureRepository = measureRepository;
this.componentIssuesRepository = componentIssuesRepository;
// Output metrics
this.reliabilityRatingMetric = metricRepository.getByKey(RELIABILITY_RATING_KEY);
this.securityRatingMetric = metricRepository.getByKey(SECURITY_RATING_KEY);
this.metricsByKey = ImmutableMap.of(
RELIABILITY_RATING_KEY, reliabilityRatingMetric,
SECURITY_RATING_KEY, securityRatingMetric);
}
@Override
public void visitProject(Component project, Path<Counter> path) {
computeAndSaveMeasures(project, path);
}
@Override
public void visitDirectory(Component directory, Path<Counter> path) {
computeAndSaveMeasures(directory, path);
}
@Override
public void visitModule(Component module, Path<Counter> path) {
computeAndSaveMeasures(module, path);
}
@Override
public void visitFile(Component file, Path<Counter> path) {
computeAndSaveMeasures(file, path);
}
private void computeAndSaveMeasures(Component component, Path<Counter> path) {
processIssues(component, path);
path.current().ratingValueByMetric.entrySet().forEach(
entry -> measureRepository.add(component, metricsByKey.get(entry.getKey()), createRatingMeasure(entry.getValue().getValue())));
addToParent(path);
}
private void processIssues(Component component, Path<Counter> path) {
componentIssuesRepository.getIssues(component)
.stream()
.filter(issue -> issue.resolution() == null)
.filter(issue -> issue.type().equals(BUG) || issue.type().equals(VULNERABILITY))
.forEach(path.current()::processIssue);
}
private static void addToParent(Path<Counter> path) {
if (!path.isRoot()) {
path.parent().add(path.current());
}
}
private static Measure createRatingMeasure(Rating rating) {
return newMeasureBuilder().create(rating.getIndex(), rating.name());
}
static final class Counter {
private Map<String, RatingValue> ratingValueByMetric = ImmutableMap.of(
RELIABILITY_RATING_KEY, new RatingValue(),
SECURITY_RATING_KEY, new RatingValue());
private Counter() {
// prevents instantiation
}
void add(Counter otherCounter) {
ratingValueByMetric.entrySet().forEach(e -> e.getValue().increment(otherCounter.ratingValueByMetric.get(e.getKey())));
}
void processIssue(Issue issue) {
Rating rating = RATING_BY_SEVERITY.get(issue.severity());
if (issue.type().equals(BUG)) {
ratingValueByMetric.get(RELIABILITY_RATING_KEY).increment(rating);
} else if (issue.type().equals(VULNERABILITY)) {
ratingValueByMetric.get(SECURITY_RATING_KEY).increment(rating);
}
}
}
private static final class CounterFactory extends PathAwareVisitorAdapter.SimpleStackElementFactory<ReliabilityAndSecurityRatingMeasuresVisitor.Counter> {
public static final CounterFactory INSTANCE = new CounterFactory();
private CounterFactory() {
// prevents instantiation
}
@Override
public Counter createForAny(Component component) {
return new Counter();
}
}
}