]> source.dussan.org Git - sonarqube.git/blob
4cd71f04f5d9254c3692b7d71a5176b24574e466
[sonarqube.git] /
1 /*
2  * Sonar, open source software quality management tool.
3  * Copyright (C) 2008-2012 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
6  * Sonar 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  * Sonar 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
17  * License along with Sonar; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
19  */
20 package org.sonar.plugins.core.timemachine;
21
22 import java.text.DateFormat;
23 import java.text.SimpleDateFormat;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Date;
27 import java.util.List;
28 import java.util.Set;
29
30 import org.apache.commons.lang.StringUtils;
31 import org.sonar.api.batch.Decorator;
32 import org.sonar.api.batch.DecoratorBarriers;
33 import org.sonar.api.batch.DecoratorContext;
34 import org.sonar.api.batch.DependedUpon;
35 import org.sonar.api.batch.DependsUpon;
36 import org.sonar.api.measures.CoreMetrics;
37 import org.sonar.api.measures.Measure;
38 import org.sonar.api.measures.MeasureUtils;
39 import org.sonar.api.measures.MeasuresFilters;
40 import org.sonar.api.measures.Metric;
41 import org.sonar.api.measures.RuleMeasure;
42 import org.sonar.api.notifications.Notification;
43 import org.sonar.api.notifications.NotificationManager;
44 import org.sonar.api.resources.Project;
45 import org.sonar.api.resources.Resource;
46 import org.sonar.api.resources.ResourceUtils;
47 import org.sonar.api.resources.Scopes;
48 import org.sonar.api.rules.Rule;
49 import org.sonar.api.rules.RulePriority;
50 import org.sonar.api.rules.Violation;
51 import org.sonar.batch.components.PastSnapshot;
52 import org.sonar.batch.components.TimeMachineConfiguration;
53
54 import com.google.common.collect.ArrayListMultimap;
55 import com.google.common.collect.ListMultimap;
56 import com.google.common.collect.Sets;
57
58 @DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING)
59 public class NewViolationsDecorator implements Decorator {
60
61   private TimeMachineConfiguration timeMachineConfiguration;
62   private NotificationManager notificationManager;
63
64   public NewViolationsDecorator(TimeMachineConfiguration timeMachineConfiguration, NotificationManager notificationManager) {
65     this.timeMachineConfiguration = timeMachineConfiguration;
66     this.notificationManager = notificationManager;
67   }
68
69   public boolean shouldExecuteOnProject(Project project) {
70     return project.isLatestAnalysis();
71   }
72
73   @DependedUpon
74   public List<Metric> generatesMetric() {
75     return Arrays.asList(
76         CoreMetrics.NEW_VIOLATIONS,
77         CoreMetrics.NEW_BLOCKER_VIOLATIONS,
78         CoreMetrics.NEW_CRITICAL_VIOLATIONS,
79         CoreMetrics.NEW_MAJOR_VIOLATIONS,
80         CoreMetrics.NEW_MINOR_VIOLATIONS,
81         CoreMetrics.NEW_INFO_VIOLATIONS);
82   }
83
84   @SuppressWarnings("rawtypes")
85   public void decorate(Resource resource, DecoratorContext context) {
86     if (shouldDecorateResource(resource, context)) {
87       computeNewViolations(context);
88       computeNewViolationsPerSeverity(context);
89       computeNewViolationsPerRule(context);
90     }
91     if (ResourceUtils.isRootProject(resource)) {
92       notifyNewViolations((Project) resource, context);
93     }
94   }
95
96   private boolean shouldDecorateResource(Resource<?> resource, DecoratorContext context) {
97     return (StringUtils.equals(Scopes.PROJECT, resource.getScope()) || StringUtils.equals(Scopes.DIRECTORY, resource.getScope()) || StringUtils
98         .equals(Scopes.FILE, resource.getScope()))
99       && !ResourceUtils.isUnitTestClass(resource)
100       && context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null;
101   }
102
103   private void computeNewViolations(DecoratorContext context) {
104     Measure measure = new Measure(CoreMetrics.NEW_VIOLATIONS);
105     for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
106       int variationIndex = pastSnapshot.getIndex();
107       Collection<Measure> children = context.getChildrenMeasures(CoreMetrics.NEW_VIOLATIONS);
108       int count = countViolations(context.getViolations(), pastSnapshot.getTargetDate());
109       double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + count;
110       measure.setVariation(variationIndex, sum);
111     }
112     context.saveMeasure(measure);
113   }
114
115   private void computeNewViolationsPerSeverity(DecoratorContext context) {
116     ListMultimap<RulePriority, Violation> violationsPerSeverities = ArrayListMultimap.create();
117     for (Violation violation : context.getViolations()) {
118       violationsPerSeverities.put(violation.getSeverity(), violation);
119     }
120
121     for (RulePriority severity : RulePriority.values()) {
122       Metric metric = severityToMetric(severity);
123       Measure measure = new Measure(metric);
124       for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
125         int variationIndex = pastSnapshot.getIndex();
126         int count = countViolations(violationsPerSeverities.get(severity), pastSnapshot.getTargetDate());
127         Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric));
128         double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + count;
129         measure.setVariation(variationIndex, sum);
130       }
131       context.saveMeasure(measure);
132     }
133   }
134
135   private void computeNewViolationsPerRule(DecoratorContext context) {
136     for (RulePriority severity : RulePriority.values()) {
137       Metric metric = severityToMetric(severity);
138       ListMultimap<Rule, Measure> childMeasuresPerRule = ArrayListMultimap.create();
139       ListMultimap<Rule, Violation> violationsPerRule = ArrayListMultimap.create();
140       Set<Rule> rules = Sets.newHashSet();
141
142       Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
143       for (Measure child : children) {
144         RuleMeasure childRuleMeasure = (RuleMeasure) child;
145         Rule rule = childRuleMeasure.getRule();
146         if (rule != null) {
147           childMeasuresPerRule.put(rule, childRuleMeasure);
148           rules.add(rule);
149         }
150       }
151
152       for (Violation violation : context.getViolations()) {
153         if (violation.getSeverity().equals(severity)) {
154           rules.add(violation.getRule());
155           violationsPerRule.put(violation.getRule(), violation);
156         }
157       }
158
159       for (Rule rule : rules) {
160         RuleMeasure measure = RuleMeasure.createForRule(metric, rule, null);
161         measure.setSeverity(severity);
162         for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) {
163           int variationIndex = pastSnapshot.getIndex();
164           int count = countViolations(violationsPerRule.get(rule), pastSnapshot.getTargetDate());
165           double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRule.get(rule)) + count;
166           measure.setVariation(variationIndex, sum);
167         }
168         context.saveMeasure(measure);
169       }
170     }
171   }
172
173   int countViolations(Collection<Violation> violations, Date targetDate) {
174     if (violations == null) {
175       return 0;
176     }
177     int count = 0;
178     for (Violation violation : violations) {
179       if (isAfter(violation, targetDate)) {
180         count++;
181       }
182     }
183     return count;
184   }
185
186   private boolean isAfter(Violation violation, Date date) {
187     if (date == null) {
188       return true;
189     }
190     return violation.getCreatedAt() != null && violation.getCreatedAt().after(date);
191   }
192
193   private Metric severityToMetric(RulePriority severity) {
194     Metric metric;
195     if (severity.equals(RulePriority.BLOCKER)) {
196       metric = CoreMetrics.NEW_BLOCKER_VIOLATIONS;
197     } else if (severity.equals(RulePriority.CRITICAL)) {
198       metric = CoreMetrics.NEW_CRITICAL_VIOLATIONS;
199     } else if (severity.equals(RulePriority.MAJOR)) {
200       metric = CoreMetrics.NEW_MAJOR_VIOLATIONS;
201     } else if (severity.equals(RulePriority.MINOR)) {
202       metric = CoreMetrics.NEW_MINOR_VIOLATIONS;
203     } else if (severity.equals(RulePriority.INFO)) {
204       metric = CoreMetrics.NEW_INFO_VIOLATIONS;
205     } else {
206       throw new IllegalArgumentException("Unsupported severity: " + severity);
207     }
208     return metric;
209   }
210
211   protected void notifyNewViolations(Project project, DecoratorContext context) {
212     List<PastSnapshot> projectPastSnapshots = timeMachineConfiguration.getProjectPastSnapshots();
213     if (projectPastSnapshots.size() >= 1) {
214       // we always check new violations against period1
215       PastSnapshot pastSnapshot = projectPastSnapshots.get(0);
216       Double newViolationsCount = context.getMeasure(CoreMetrics.NEW_VIOLATIONS).getVariation1();
217       // Do not send notification if this is the first analysis or if there's no violation
218       if (pastSnapshot.getTargetDate() != null && newViolationsCount != null && newViolationsCount > 0) {
219         // Maybe we should check if this is the first analysis or not?
220         DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
221         Notification notification = new Notification("new-violations")
222             .setFieldValue("count", String.valueOf(newViolationsCount.intValue()))
223             .setFieldValue("projectName", project.getLongName())
224             .setFieldValue("projectKey", project.getKey())
225             .setFieldValue("projectId", String.valueOf(project.getId()))
226             .setFieldValue("fromDate", dateformat.format(pastSnapshot.getTargetDate()));
227         notificationManager.scheduleForSending(notification);
228       }
229     }
230   }
231
232   @Override
233   public String toString() {
234     return getClass().getSimpleName();
235   }
236 }