]> source.dussan.org Git - sonarqube.git/blob
17ca21170d6d1ec9d422c465c6a5cd7fd8684a4c
[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.collect.*;
24 import org.apache.commons.lang.time.DateUtils;
25 import org.sonar.api.batch.*;
26 import org.sonar.api.component.ResourcePerspectives;
27 import org.sonar.api.issue.Issuable;
28 import org.sonar.api.issue.Issue;
29 import org.sonar.api.measures.*;
30 import org.sonar.api.resources.Project;
31 import org.sonar.api.resources.Resource;
32 import org.sonar.api.resources.ResourceUtils;
33 import org.sonar.api.rules.Rule;
34 import org.sonar.api.rules.RuleFinder;
35 import org.sonar.api.rules.RulePriority;
36 import org.sonar.batch.components.PastSnapshot;
37 import org.sonar.batch.components.TimeMachineConfiguration;
38
39 import javax.annotation.Nullable;
40 import java.util.*;
41
42 /**
43  * Computes metrics related to number of issues.
44  *
45  * @since 3.6
46  */
47 @DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING)
48 public class CountUnresolvedIssuesDecorator implements Decorator {
49
50   private final ResourcePerspectives perspectives;
51   private final RuleFinder rulefinder;
52   private final TimeMachineConfiguration timeMachineConfiguration;
53
54   public CountUnresolvedIssuesDecorator(ResourcePerspectives perspectives, RuleFinder rulefinder, TimeMachineConfiguration timeMachineConfiguration) {
55     this.perspectives = perspectives;
56     this.rulefinder = rulefinder;
57     this.timeMachineConfiguration = timeMachineConfiguration;
58   }
59
60   public boolean shouldExecuteOnProject(Project project) {
61     return true;
62   }
63
64   @DependedUpon
65   public List<Metric> generatesIssuesMetrics() {
66     return ImmutableList.of(
67       CoreMetrics.VIOLATIONS,
68       CoreMetrics.BLOCKER_VIOLATIONS,
69       CoreMetrics.CRITICAL_VIOLATIONS,
70       CoreMetrics.MAJOR_VIOLATIONS,
71       CoreMetrics.MINOR_VIOLATIONS,
72       CoreMetrics.INFO_VIOLATIONS,
73       CoreMetrics.NEW_VIOLATIONS,
74       CoreMetrics.NEW_BLOCKER_VIOLATIONS,
75       CoreMetrics.NEW_CRITICAL_VIOLATIONS,
76       CoreMetrics.NEW_MAJOR_VIOLATIONS,
77       CoreMetrics.NEW_MINOR_VIOLATIONS,
78       CoreMetrics.NEW_INFO_VIOLATIONS,
79       CoreMetrics.OPEN_ISSUES,
80       CoreMetrics.REOPENED_ISSUES,
81       CoreMetrics.CONFIRMED_ISSUES
82     );
83   }
84
85   public void decorate(Resource resource, DecoratorContext context) {
86     Issuable issuable = perspectives.as(Issuable.class, resource);
87     if (issuable != null) {
88       Collection<Issue> issues = issuable.issues();
89       boolean shouldSaveNewMetrics = shouldSaveNewMetrics(context);
90
91       Multiset<RulePriority> severityBag = HashMultiset.create();
92       Map<RulePriority, Multiset<Rule>> rulesPerSeverity = Maps.newHashMap();
93       ListMultimap<RulePriority, Issue> issuesPerSeverity = ArrayListMultimap.create();
94       int countOpen = 0;
95       int countReopened = 0;
96       int countConfirmed = 0;
97
98       for (Issue issue : issues) {
99         severityBag.add(RulePriority.valueOf(issue.severity()));
100         Multiset<Rule> rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity()));
101         rulesBag.add(rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule()));
102         issuesPerSeverity.put(RulePriority.valueOf(issue.severity()), issue);
103
104         if (Issue.STATUS_OPEN.equals(issue.status())) {
105           countOpen++;
106         }
107         if (Issue.STATUS_REOPENED.equals(issue.status())) {
108           countReopened++;
109         }
110         if (Issue.STATUS_CONFIRMED.equals(issue.status())) {
111           countConfirmed++;
112         }
113       }
114
115       for (RulePriority ruleSeverity : RulePriority.values()) {
116         saveIssuesForSeverity(context, ruleSeverity, severityBag);
117         saveIssuesPerRules(context, ruleSeverity, rulesPerSeverity);
118         saveNewIssuesForSeverity(context, ruleSeverity, issuesPerSeverity, shouldSaveNewMetrics);
119         saveNewIssuesPerRule(context, ruleSeverity, issues, shouldSaveNewMetrics);
120       }
121
122       saveTotalIssues(context, issues);
123       saveNewIssues(context, issues, shouldSaveNewMetrics);
124
125       saveMeasure(context, CoreMetrics.OPEN_ISSUES, countOpen);
126       saveMeasure(context, CoreMetrics.REOPENED_ISSUES, countReopened);
127       saveMeasure(context, CoreMetrics.CONFIRMED_ISSUES, countConfirmed);
128     }
129   }
130
131   private void saveTotalIssues(DecoratorContext context, Collection<Issue> issues) {
132     if (context.getMeasure(CoreMetrics.VIOLATIONS) == null) {
133       Collection<Measure> childrenIssues = context.getChildrenMeasures(CoreMetrics.VIOLATIONS);
134       Double sum = MeasureUtils.sum(true, childrenIssues);
135       context.saveMeasure(CoreMetrics.VIOLATIONS, sum + issues.size());
136     }
137   }
138
139   private void saveNewIssues(DecoratorContext context, Collection<Issue> issues, boolean shouldSaveNewMetrics) {
140     if (shouldSaveNewMetrics) {
141       Measure measure = new Measure(CoreMetrics.NEW_VIOLATIONS);
142       saveNewIssues(context, measure, issues);
143     }
144   }
145
146   private void saveIssuesForSeverity(DecoratorContext context, RulePriority ruleSeverity, Multiset<RulePriority> severitiesBag) {
147     Metric metric = SeverityUtils.severityToIssueMetric(ruleSeverity);
148     if (context.getMeasure(metric) == null) {
149       Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric));
150       int sum = MeasureUtils.sum(true, children).intValue() + severitiesBag.count(ruleSeverity);
151       context.saveMeasure(metric, (double) sum);
152     }
153   }
154
155   private void saveNewIssuesForSeverity(DecoratorContext context, RulePriority severity, ListMultimap<RulePriority, Issue> issuesPerSeverities, boolean shouldSaveNewMetrics) {
156     if (shouldSaveNewMetrics) {
157       Metric metric = SeverityUtils.severityToNewMetricIssue(severity);
158       Measure measure = new Measure(metric);
159       saveNewIssues(context, measure, issuesPerSeverities.get(severity));
160     }
161   }
162
163   private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map<RulePriority, Multiset<Rule>> rulesPerSeverity) {
164     Metric metric = SeverityUtils.severityToIssueMetric(severity);
165
166     Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
167     for (Measure child : children) {
168       RuleMeasure childRuleMeasure = (RuleMeasure) child;
169       Rule rule = childRuleMeasure.getRule();
170       if (rule != null && MeasureUtils.hasValue(childRuleMeasure)) {
171         Multiset<Rule> rulesBag = initRules(rulesPerSeverity, severity);
172         rulesBag.add(rule, childRuleMeasure.getIntValue());
173       }
174     }
175
176     Multiset<Rule> rulesBag = rulesPerSeverity.get(severity);
177     if (rulesBag != null) {
178       for (Multiset.Entry<Rule> entry : rulesBag.entrySet()) {
179         RuleMeasure measure = RuleMeasure.createForRule(metric, entry.getElement(), (double) entry.getCount());
180         measure.setSeverity(severity);
181         context.saveMeasure(measure);
182       }
183     }
184   }
185
186   private void saveNewIssuesPerRule(DecoratorContext context, RulePriority severity, Collection<Issue> issues, boolean shouldSaveNewMetrics) {
187     if (shouldSaveNewMetrics) {
188       Metric metric = SeverityUtils.severityToNewMetricIssue(severity);
189       ListMultimap<Rule, Measure> childMeasuresPerRule = ArrayListMultimap.create();
190       ListMultimap<Rule, Issue> issuesPerRule = ArrayListMultimap.create();
191       Set<Rule> rules = Sets.newHashSet();
192
193       Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
194       for (Measure child : children) {
195         RuleMeasure childRuleMeasure = (RuleMeasure) child;
196         Rule rule = childRuleMeasure.getRule();
197         if (rule != null) {
198           childMeasuresPerRule.put(rule, childRuleMeasure);
199           rules.add(rule);
200         }
201       }
202
203       for (Issue issue : issues) {
204         if (RulePriority.valueOf(issue.severity()).equals(severity)) {
205           Rule rule = rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule());
206           rules.add(rule);
207           issuesPerRule.put(rule, issue);
208         }
209       }
210
211       for (Rule rule : rules) {
212         RuleMeasure measure = RuleMeasure.createForRule(metric, rule, null);
213         measure.setSeverity(severity);
214         for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
215           int variationIndex = pastSnapshot.getIndex();
216           int count = countIssuesAfterDate(issuesPerRule.get(rule), pastSnapshot.getTargetDate());
217           double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRule.get(rule)) + count;
218           measure.setVariation(variationIndex, sum);
219         }
220         context.saveMeasure(measure);
221       }
222     }
223   }
224
225   private void saveNewIssues(DecoratorContext context, Measure measure, Collection<Issue> issues) {
226     for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
227       int variationIndex = pastSnapshot.getIndex();
228       Collection<Measure> children = context.getChildrenMeasures(measure.getMetric());
229       Date targetDatePlusOneSecond = pastSnapshot.getTargetDate() != null ? DateUtils.addSeconds(pastSnapshot.getTargetDate(), 1) : null;
230       int count = countIssuesAfterDate(issues, targetDatePlusOneSecond);
231       double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + count;
232       measure.setVariation(variationIndex, sum);
233     }
234     context.saveMeasure(measure);
235   }
236
237   private void saveMeasure(DecoratorContext context, Metric metric, int value) {
238     context.saveMeasure(metric, (double) (value + sumChildren(context, metric)));
239   }
240
241   private int sumChildren(DecoratorContext context, Metric metric) {
242     int sum = 0;
243     if (!ResourceUtils.isFile(context.getResource())) {
244       sum = MeasureUtils.sum(true, context.getChildrenMeasures(metric)).intValue();
245     }
246     return sum;
247   }
248
249   private Multiset<Rule> initRules(Map<RulePriority, Multiset<Rule>> rulesPerSeverity, RulePriority severity) {
250     Multiset<Rule> rulesBag = rulesPerSeverity.get(severity);
251     if (rulesBag == null) {
252       rulesBag = HashMultiset.create();
253       rulesPerSeverity.put(severity, rulesBag);
254     }
255     return rulesBag;
256   }
257
258   @VisibleForTesting
259   int countIssuesAfterDate(Collection<Issue> issues, @Nullable Date targetDate) {
260     if (issues == null) {
261       return 0;
262     }
263     int count = 0;
264     for (Issue issue : issues) {
265       if (isAfter(issue, targetDate)) {
266         count++;
267       }
268     }
269     return count;
270   }
271
272   private boolean isAfter(Issue issue, @Nullable Date date) {
273     return date == null || (issue.creationDate() != null && issue.creationDate().after(date));
274   }
275
276   private boolean shouldSaveNewMetrics(DecoratorContext context) {
277     return context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null;
278   }
279
280   @Override
281   public String toString() {
282     return getClass().getSimpleName();
283   }
284 }