2 * Sonar, open source software quality management tool.
3 * Copyright (C) 2008-2012 SonarSource
4 * mailto:contact AT sonarsource DOT com
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.
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.
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
20 package org.sonar.plugins.core.timemachine;
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;
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;
54 import com.google.common.collect.ArrayListMultimap;
55 import com.google.common.collect.ListMultimap;
56 import com.google.common.collect.Sets;
58 @DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING)
59 public class NewViolationsDecorator implements Decorator {
61 private TimeMachineConfiguration timeMachineConfiguration;
62 private NotificationManager notificationManager;
64 public NewViolationsDecorator(TimeMachineConfiguration timeMachineConfiguration, NotificationManager notificationManager) {
65 this.timeMachineConfiguration = timeMachineConfiguration;
66 this.notificationManager = notificationManager;
69 public boolean shouldExecuteOnProject(Project project) {
70 return project.isLatestAnalysis();
74 public List<Metric> generatesMetric() {
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);
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);
91 if (ResourceUtils.isRootProject(resource)) {
92 notifyNewViolations((Project) resource, context);
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;
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);
112 context.saveMeasure(measure);
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);
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);
131 context.saveMeasure(measure);
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();
142 Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
143 for (Measure child : children) {
144 RuleMeasure childRuleMeasure = (RuleMeasure) child;
145 Rule rule = childRuleMeasure.getRule();
147 childMeasuresPerRule.put(rule, childRuleMeasure);
152 for (Violation violation : context.getViolations()) {
153 if (violation.getSeverity().equals(severity)) {
154 rules.add(violation.getRule());
155 violationsPerRule.put(violation.getRule(), violation);
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);
168 context.saveMeasure(measure);
173 int countViolations(Collection<Violation> violations, Date targetDate) {
174 if (violations == null) {
178 for (Violation violation : violations) {
179 if (isAfter(violation, targetDate)) {
186 private boolean isAfter(Violation violation, Date date) {
190 return violation.getCreatedAt() != null && violation.getCreatedAt().after(date);
193 private Metric severityToMetric(RulePriority severity) {
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;
206 throw new IllegalArgumentException("Unsupported severity: " + severity);
211 protected void notifyNewViolations(Project project, DecoratorContext context) {
212 Integer lastAnalysisPeriodIndex = timeMachineConfiguration.getLastAnalysisPeriodIndex();
213 if (lastAnalysisPeriodIndex != null) {
214 PastSnapshot pastSnapshot = timeMachineConfiguration.getProjectPastSnapshots().get(lastAnalysisPeriodIndex - 1);
215 Double newViolationsCount = context.getMeasure(CoreMetrics.NEW_VIOLATIONS).getVariation(lastAnalysisPeriodIndex);
216 if (newViolationsCount != null && newViolationsCount > 0) {
217 // Maybe we should check if this is the first analysis or not?
218 DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
219 Notification notification = new Notification("new-violations")
220 .setFieldValue("count", String.valueOf(newViolationsCount.intValue()))
221 .setFieldValue("projectName", project.getLongName())
222 .setFieldValue("projectKey", project.getKey())
223 .setFieldValue("projectId", String.valueOf(project.getId()))
224 .setFieldValue("period", lastAnalysisPeriodIndex.toString())
225 .setFieldValue("fromDate", dateformat.format(pastSnapshot.getTargetDate()))
226 .setFieldValue("toDate", dateformat.format(new Date()));
227 notificationManager.scheduleForSending(notification);
233 public String toString() {
234 return getClass().getSimpleName();