001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.plugins.core.timemachine; 021 022 import com.google.common.collect.ArrayListMultimap; 023 import com.google.common.collect.ListMultimap; 024 import com.google.common.collect.Sets; 025 import org.apache.commons.lang.StringUtils; 026 import org.sonar.api.batch.*; 027 import org.sonar.api.measures.*; 028 import org.sonar.api.resources.Project; 029 import org.sonar.api.resources.Resource; 030 import org.sonar.api.resources.ResourceUtils; 031 import org.sonar.api.resources.Scopes; 032 import org.sonar.api.rules.Rule; 033 import org.sonar.api.rules.RulePriority; 034 import org.sonar.api.rules.Violation; 035 import org.sonar.batch.components.PastSnapshot; 036 import org.sonar.batch.components.TimeMachineConfiguration; 037 038 import java.util.*; 039 040 @DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING) 041 public class NewViolationsDecorator implements Decorator { 042 043 private TimeMachineConfiguration timeMachineConfiguration; 044 045 public NewViolationsDecorator(TimeMachineConfiguration timeMachineConfiguration) { 046 this.timeMachineConfiguration = timeMachineConfiguration; 047 } 048 049 public boolean shouldExecuteOnProject(Project project) { 050 return project.isLatestAnalysis(); 051 } 052 053 @DependedUpon 054 public List<Metric> generatesMetric() { 055 return Arrays.asList( 056 CoreMetrics.NEW_VIOLATIONS, 057 CoreMetrics.NEW_BLOCKER_VIOLATIONS, 058 CoreMetrics.NEW_CRITICAL_VIOLATIONS, 059 CoreMetrics.NEW_MAJOR_VIOLATIONS, 060 CoreMetrics.NEW_MINOR_VIOLATIONS, 061 CoreMetrics.NEW_INFO_VIOLATIONS); 062 } 063 064 public void decorate(Resource resource, DecoratorContext context) { 065 if (shouldDecorateResource(resource, context)) { 066 computeNewViolations(context); 067 computeNewViolationsPerSeverity(context); 068 computeNewViolationsPerRule(context); 069 } 070 } 071 072 private boolean shouldDecorateResource(Resource resource, DecoratorContext context) { 073 return 074 (StringUtils.equals(Scopes.PROJECT, resource.getScope()) || StringUtils.equals(Scopes.DIRECTORY, resource.getScope()) || StringUtils.equals(Scopes.FILE, resource.getScope())) 075 && !ResourceUtils.isUnitTestClass(resource) 076 && context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null; 077 } 078 079 private void computeNewViolations(DecoratorContext context) { 080 Measure measure = new Measure(CoreMetrics.NEW_VIOLATIONS); 081 for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) { 082 int variationIndex = pastSnapshot.getIndex(); 083 Collection<Measure> children = context.getChildrenMeasures(CoreMetrics.NEW_VIOLATIONS); 084 int count = countViolations(context.getViolations(), pastSnapshot.getTargetDate()); 085 double sum = sumChildren(variationIndex, children) + count; 086 measure.setVariation(variationIndex, sum); 087 } 088 context.saveMeasure(measure); 089 } 090 091 private void computeNewViolationsPerSeverity(DecoratorContext context) { 092 ListMultimap<RulePriority, Violation> violationsPerSeverities = ArrayListMultimap.create(); 093 for (Violation violation : context.getViolations()) { 094 violationsPerSeverities.put(violation.getSeverity(), violation); 095 } 096 097 for (RulePriority severity : RulePriority.values()) { 098 Metric metric = severityToMetric(severity); 099 Measure measure = new Measure(metric); 100 for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) { 101 int variationIndex = pastSnapshot.getIndex(); 102 int count = countViolations(violationsPerSeverities.get(severity), pastSnapshot.getTargetDate()); 103 Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric)); 104 double sum = sumChildren(variationIndex, children) + count; 105 measure.setVariation(variationIndex, sum); 106 } 107 context.saveMeasure(measure); 108 } 109 } 110 111 private void computeNewViolationsPerRule(DecoratorContext context) { 112 for (RulePriority severity : RulePriority.values()) { 113 Metric metric = severityToMetric(severity); 114 ListMultimap<Rule, Measure> childMeasuresPerRule = ArrayListMultimap.create(); 115 ListMultimap<Rule, Violation> violationsPerRule = ArrayListMultimap.create(); 116 Set<Rule> rules = Sets.newHashSet(); 117 118 Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric)); 119 for (Measure child : children) { 120 RuleMeasure childRuleMeasure = (RuleMeasure) child; 121 Rule rule = childRuleMeasure.getRule(); 122 if (rule != null) { 123 childMeasuresPerRule.put(rule, childRuleMeasure); 124 rules.add(rule); 125 } 126 } 127 128 for (Violation violation : context.getViolations()) { 129 if (violation.getSeverity().equals(severity)) { 130 rules.add(violation.getRule()); 131 violationsPerRule.put(violation.getRule(), violation); 132 } 133 } 134 135 for (Rule rule : rules) { 136 RuleMeasure measure = RuleMeasure.createForRule(metric, rule, null); 137 measure.setRulePriority(severity); 138 for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) { 139 int variationIndex = pastSnapshot.getIndex(); 140 int count = countViolations(violationsPerRule.get(rule), pastSnapshot.getTargetDate()); 141 double sum = sumChildren(variationIndex, childMeasuresPerRule.get(rule)) + count; 142 measure.setVariation(variationIndex, sum); 143 } 144 context.saveMeasure(measure); 145 } 146 } 147 } 148 149 int sumChildren(int variationIndex, Collection<Measure> measures) { 150 int sum = 0; 151 for (Measure measure : measures) { 152 Double var = measure.getVariation(variationIndex); 153 if (var != null) { 154 sum += var.intValue(); 155 } 156 } 157 return sum; 158 } 159 160 int countViolations(Collection<Violation> violations, Date targetDate) { 161 if (violations == null) { 162 return 0; 163 } 164 int count = 0; 165 for (Violation violation : violations) { 166 if (isAfter(violation, targetDate)) { 167 count++; 168 } 169 } 170 return count; 171 } 172 173 private boolean isAfter(Violation violation, Date date) { 174 if (date == null) { 175 return true; 176 } 177 return violation.getCreatedAt() != null && violation.getCreatedAt().after(date); 178 } 179 180 private Metric severityToMetric(RulePriority severity) { 181 Metric metric; 182 if (severity.equals(RulePriority.BLOCKER)) { 183 metric = CoreMetrics.NEW_BLOCKER_VIOLATIONS; 184 } else if (severity.equals(RulePriority.CRITICAL)) { 185 metric = CoreMetrics.NEW_CRITICAL_VIOLATIONS; 186 } else if (severity.equals(RulePriority.MAJOR)) { 187 metric = CoreMetrics.NEW_MAJOR_VIOLATIONS; 188 } else if (severity.equals(RulePriority.MINOR)) { 189 metric = CoreMetrics.NEW_MINOR_VIOLATIONS; 190 } else if (severity.equals(RulePriority.INFO)) { 191 metric = CoreMetrics.NEW_INFO_VIOLATIONS; 192 } else { 193 throw new IllegalArgumentException("Unsupported severity: " + severity); 194 } 195 return metric; 196 } 197 198 @Override 199 public String toString() { 200 return getClass().getSimpleName(); 201 } 202 }