]> source.dussan.org Git - sonarqube.git/blob
f8495296c027efd08992bf07e730fc6e7f572ab7
[sonarqube.git] /
1 /*
2  * SonarQube, open source software quality management tool.
3  * Copyright (C) 2008-2013 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
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.
10  *
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.
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.plugins.core.issue;
21
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;
39
40 import javax.annotation.Nullable;
41
42 import java.util.*;
43
44 import static com.google.common.collect.Lists.newArrayList;
45
46 /**
47  * Computes metrics related to number of issues.
48  *
49  * @since 3.6
50  */
51 @DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING)
52 public class CountUnresolvedIssuesDecorator implements Decorator {
53
54   private final ResourcePerspectives perspectives;
55   private final RuleFinder rulefinder;
56   private final TimeMachineConfiguration timeMachineConfiguration;
57
58   public CountUnresolvedIssuesDecorator(ResourcePerspectives perspectives, RuleFinder rulefinder, TimeMachineConfiguration timeMachineConfiguration) {
59     this.perspectives = perspectives;
60     this.rulefinder = rulefinder;
61     this.timeMachineConfiguration = timeMachineConfiguration;
62   }
63
64   public boolean shouldExecuteOnProject(Project project) {
65     return true;
66   }
67
68   @DependedUpon
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
86     );
87   }
88
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);
94
95       Multiset<RulePriority> severityBag = HashMultiset.create();
96       Map<RulePriority, Multiset<Rule>> rulesPerSeverity = Maps.newHashMap();
97       ListMultimap<RulePriority, Issue> issuesPerSeverity = ArrayListMultimap.create();
98       int countOpen = 0;
99       int countReopened = 0;
100       int countConfirmed = 0;
101
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);
107
108         if (Issue.STATUS_OPEN.equals(issue.status())){
109           countOpen++;
110         }
111         if (Issue.STATUS_REOPENED.equals(issue.status())){
112           countReopened++;
113         }
114         if (Issue.STATUS_CONFIRMED.equals(issue.status())){
115           countConfirmed++;
116         }
117       }
118
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);
124       }
125
126       saveTotalIssues(context, issues);
127       saveNewIssues(context, issues, shouldSaveNewMetrics);
128
129       saveMeasure(context, CoreMetrics.OPEN_ISSUES, countOpen);
130       saveMeasure(context, CoreMetrics.REOPENED_ISSUES, countReopened);
131       saveMeasure(context, CoreMetrics.CONFIRMED_ISSUES, countConfirmed);
132     }
133   }
134
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());
140     }
141   }
142
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);
147     }
148   }
149
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);
156     }
157   }
158
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));
164     }
165   }
166
167   private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map<RulePriority, Multiset<Rule>> rulesPerSeverity) {
168     Metric metric = SeverityUtils.severityToIssueMetric(severity);
169
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());
177       }
178     }
179
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);
186       }
187     }
188   }
189
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();
196
197       Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
198       for (Measure child : children) {
199         RuleMeasure childRuleMeasure = (RuleMeasure) child;
200         Rule rule = childRuleMeasure.getRule();
201         if (rule != null) {
202           childMeasuresPerRule.put(rule, childRuleMeasure);
203           rules.add(rule);
204         }
205       }
206
207       for (Issue issue : issues) {
208         if (RulePriority.valueOf(issue.severity()).equals(severity)) {
209           Rule rule = rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule());
210           rules.add(rule);
211           issuesPerRule.put(rule, issue);
212         }
213       }
214
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);
223         }
224         context.saveMeasure(measure);
225       }
226     }
227   }
228
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);
237     }
238     context.saveMeasure(measure);
239   }
240
241   private void saveMeasure(DecoratorContext context, Metric metric, int value) {
242     context.saveMeasure(metric, (double) (value + sumChildren(context, metric)));
243   }
244
245   private int sumChildren(DecoratorContext context, Metric metric) {
246     int sum = 0;
247     if (!ResourceUtils.isFile(context.getResource())) {
248       sum = MeasureUtils.sum(true, context.getChildrenMeasures(metric)).intValue();
249     }
250     return sum;
251   }
252
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);
258     }
259     return rulesBag;
260   }
261
262   @VisibleForTesting
263   int countIssuesAfterDate(Collection<Issue> issues, @Nullable Date targetDate) {
264     if (issues == null) {
265       return 0;
266     }
267     int count = 0;
268     for (Issue issue : issues) {
269       if (isAfter(issue, targetDate)) {
270         count++;
271       }
272     }
273     return count;
274   }
275
276   private boolean isAfter(Issue issue, @Nullable Date date) {
277     return date == null || (issue.creationDate() != null && issue.creationDate().after(date));
278   }
279
280   private boolean shouldSaveNewMetrics(DecoratorContext context) {
281     return context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null;
282   }
283
284   @Override
285   public String toString() {
286     return getClass().getSimpleName();
287   }
288 }