2 * SonarQube, open source software quality management tool.
3 * Copyright (C) 2008-2013 SonarSource
4 * mailto:contact AT sonarsource DOT com
6 * SonarQube 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 * SonarQube 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.plugins.core.issue;
22 import com.google.common.annotations.VisibleForTesting;
23 import com.google.common.base.Predicate;
24 import com.google.common.collect.*;
25 import org.apache.commons.lang.time.DateUtils;
26 import org.sonar.api.batch.*;
27 import org.sonar.api.component.ResourcePerspectives;
28 import org.sonar.api.issue.Issuable;
29 import org.sonar.api.issue.Issue;
30 import org.sonar.api.measures.*;
31 import org.sonar.api.resources.Project;
32 import org.sonar.api.resources.Resource;
33 import org.sonar.api.resources.ResourceUtils;
34 import org.sonar.api.rules.Rule;
35 import org.sonar.api.rules.RuleFinder;
36 import org.sonar.api.rules.RulePriority;
37 import org.sonar.batch.components.PastSnapshot;
38 import org.sonar.batch.components.TimeMachineConfiguration;
40 import javax.annotation.Nullable;
44 import static com.google.common.collect.Lists.newArrayList;
47 * Computes metrics related to number of issues.
51 @DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING)
52 public class CountUnresolvedIssuesDecorator implements Decorator {
54 private final ResourcePerspectives perspectives;
55 private final RuleFinder rulefinder;
56 private final TimeMachineConfiguration timeMachineConfiguration;
58 public CountUnresolvedIssuesDecorator(ResourcePerspectives perspectives, RuleFinder rulefinder, TimeMachineConfiguration timeMachineConfiguration) {
59 this.perspectives = perspectives;
60 this.rulefinder = rulefinder;
61 this.timeMachineConfiguration = timeMachineConfiguration;
64 public boolean shouldExecuteOnProject(Project project) {
69 public List<Metric> generatesIssuesMetrics() {
70 return ImmutableList.of(
71 CoreMetrics.VIOLATIONS,
72 CoreMetrics.BLOCKER_VIOLATIONS,
73 CoreMetrics.CRITICAL_VIOLATIONS,
74 CoreMetrics.MAJOR_VIOLATIONS,
75 CoreMetrics.MINOR_VIOLATIONS,
76 CoreMetrics.INFO_VIOLATIONS,
77 CoreMetrics.NEW_VIOLATIONS,
78 CoreMetrics.NEW_BLOCKER_VIOLATIONS,
79 CoreMetrics.NEW_CRITICAL_VIOLATIONS,
80 CoreMetrics.NEW_MAJOR_VIOLATIONS,
81 CoreMetrics.NEW_MINOR_VIOLATIONS,
82 CoreMetrics.NEW_INFO_VIOLATIONS,
83 CoreMetrics.OPEN_ISSUES,
84 CoreMetrics.REOPENED_ISSUES,
85 CoreMetrics.CONFIRMED_ISSUES
89 public void decorate(Resource resource, DecoratorContext context) {
90 Issuable issuable = perspectives.as(Issuable.class, resource);
91 if (issuable != null) {
92 Collection<Issue> issues = issuable.unresolvedIssues();
93 boolean shouldSaveNewMetrics = shouldSaveNewMetrics(context);
95 Multiset<RulePriority> severityBag = HashMultiset.create();
96 Map<RulePriority, Multiset<Rule>> rulesPerSeverity = Maps.newHashMap();
97 ListMultimap<RulePriority, Issue> issuesPerSeverity = ArrayListMultimap.create();
99 int countReopened = 0;
100 int countConfirmed = 0;
102 for (Issue issue : issues) {
103 severityBag.add(RulePriority.valueOf(issue.severity()));
104 Multiset<Rule> rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity()));
105 rulesBag.add(rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule()));
106 issuesPerSeverity.put(RulePriority.valueOf(issue.severity()), issue);
108 if (Issue.STATUS_OPEN.equals(issue.status())){
111 if (Issue.STATUS_REOPENED.equals(issue.status())){
114 if (Issue.STATUS_CONFIRMED.equals(issue.status())){
119 for (RulePriority ruleSeverity : RulePriority.values()) {
120 saveIssuesForSeverity(context, ruleSeverity, severityBag);
121 saveIssuesPerRules(context, ruleSeverity, rulesPerSeverity);
122 saveNewIssuesForSeverity(context, ruleSeverity, issuesPerSeverity, shouldSaveNewMetrics);
123 saveNewIssuesPerRule(context, ruleSeverity, issues, shouldSaveNewMetrics);
126 saveTotalIssues(context, issues);
127 saveNewIssues(context, issues, shouldSaveNewMetrics);
129 saveMeasure(context, CoreMetrics.OPEN_ISSUES, countOpen);
130 saveMeasure(context, CoreMetrics.REOPENED_ISSUES, countReopened);
131 saveMeasure(context, CoreMetrics.CONFIRMED_ISSUES, countConfirmed);
135 private void saveTotalIssues(DecoratorContext context, Collection<Issue> issues) {
136 if (context.getMeasure(CoreMetrics.VIOLATIONS) == null) {
137 Collection<Measure> childrenIssues = context.getChildrenMeasures(CoreMetrics.VIOLATIONS);
138 Double sum = MeasureUtils.sum(true, childrenIssues);
139 context.saveMeasure(CoreMetrics.VIOLATIONS, sum + issues.size());
143 private void saveNewIssues(DecoratorContext context, Collection<Issue> issues, boolean shouldSaveNewMetrics) {
144 if (shouldSaveNewMetrics) {
145 Measure measure = new Measure(CoreMetrics.NEW_VIOLATIONS);
146 saveNewIssues(context, measure, issues);
150 private void saveIssuesForSeverity(DecoratorContext context, RulePriority ruleSeverity, Multiset<RulePriority> severitiesBag) {
151 Metric metric = SeverityUtils.severityToIssueMetric(ruleSeverity);
152 if (context.getMeasure(metric) == null) {
153 Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric));
154 int sum = MeasureUtils.sum(true, children).intValue() + severitiesBag.count(ruleSeverity);
155 context.saveMeasure(metric, (double) sum);
159 private void saveNewIssuesForSeverity(DecoratorContext context, RulePriority severity, ListMultimap<RulePriority, Issue> issuesPerSeverities, boolean shouldSaveNewMetrics) {
160 if (shouldSaveNewMetrics) {
161 Metric metric = SeverityUtils.severityToNewMetricIssue(severity);
162 Measure measure = new Measure(metric);
163 saveNewIssues(context, measure, issuesPerSeverities.get(severity));
167 private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map<RulePriority, Multiset<Rule>> rulesPerSeverity) {
168 Metric metric = SeverityUtils.severityToIssueMetric(severity);
170 Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
171 for (Measure child : children) {
172 RuleMeasure childRuleMeasure = (RuleMeasure) child;
173 Rule rule = childRuleMeasure.getRule();
174 if (rule != null && MeasureUtils.hasValue(childRuleMeasure)) {
175 Multiset<Rule> rulesBag = initRules(rulesPerSeverity, severity);
176 rulesBag.add(rule, childRuleMeasure.getIntValue());
180 Multiset<Rule> rulesBag = rulesPerSeverity.get(severity);
181 if (rulesBag != null) {
182 for (Multiset.Entry<Rule> entry : rulesBag.entrySet()) {
183 RuleMeasure measure = RuleMeasure.createForRule(metric, entry.getElement(), (double) entry.getCount());
184 measure.setSeverity(severity);
185 context.saveMeasure(measure);
190 private void saveNewIssuesPerRule(DecoratorContext context, RulePriority severity, Collection<Issue> issues, boolean shouldSaveNewMetrics) {
191 if (shouldSaveNewMetrics) {
192 Metric metric = SeverityUtils.severityToNewMetricIssue(severity);
193 ListMultimap<Rule, Measure> childMeasuresPerRule = ArrayListMultimap.create();
194 ListMultimap<Rule, Issue> issuesPerRule = ArrayListMultimap.create();
195 Set<Rule> rules = Sets.newHashSet();
197 Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
198 for (Measure child : children) {
199 RuleMeasure childRuleMeasure = (RuleMeasure) child;
200 Rule rule = childRuleMeasure.getRule();
202 childMeasuresPerRule.put(rule, childRuleMeasure);
207 for (Issue issue : issues) {
208 if (RulePriority.valueOf(issue.severity()).equals(severity)) {
209 Rule rule = rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule());
211 issuesPerRule.put(rule, issue);
215 for (Rule rule : rules) {
216 RuleMeasure measure = RuleMeasure.createForRule(metric, rule, null);
217 measure.setSeverity(severity);
218 for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
219 int variationIndex = pastSnapshot.getIndex();
220 int count = countIssuesAfterDate(issuesPerRule.get(rule), pastSnapshot.getTargetDate());
221 double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRule.get(rule)) + count;
222 measure.setVariation(variationIndex, sum);
224 context.saveMeasure(measure);
229 private void saveNewIssues(DecoratorContext context, Measure measure, Collection<Issue> issues) {
230 for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
231 int variationIndex = pastSnapshot.getIndex();
232 Collection<Measure> children = context.getChildrenMeasures(measure.getMetric());
233 Date targetDatePlusOneSecond = pastSnapshot.getTargetDate() != null ? DateUtils.addSeconds(pastSnapshot.getTargetDate(), 1) : null;
234 int count = countIssuesAfterDate(issues, targetDatePlusOneSecond);
235 double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + count;
236 measure.setVariation(variationIndex, sum);
238 context.saveMeasure(measure);
241 private void saveMeasure(DecoratorContext context, Metric metric, int value) {
242 context.saveMeasure(metric, (double) (value + sumChildren(context, metric)));
245 private int sumChildren(DecoratorContext context, Metric metric) {
247 if (!ResourceUtils.isFile(context.getResource())) {
248 sum = MeasureUtils.sum(true, context.getChildrenMeasures(metric)).intValue();
253 private Multiset<Rule> initRules(Map<RulePriority, Multiset<Rule>> rulesPerSeverity, RulePriority severity) {
254 Multiset<Rule> rulesBag = rulesPerSeverity.get(severity);
255 if (rulesBag == null) {
256 rulesBag = HashMultiset.create();
257 rulesPerSeverity.put(severity, rulesBag);
263 int countIssuesAfterDate(Collection<Issue> issues, @Nullable Date targetDate) {
264 if (issues == null) {
268 for (Issue issue : issues) {
269 if (isAfter(issue, targetDate)) {
276 private boolean isAfter(Issue issue, @Nullable Date date) {
277 return date == null || (issue.creationDate() != null && issue.creationDate().after(date));
280 private boolean shouldSaveNewMetrics(DecoratorContext context) {
281 return context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null;
285 public String toString() {
286 return getClass().getSimpleName();