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.*; 023 import org.apache.commons.lang.StringUtils; 024 import org.sonar.api.batch.*; 025 import org.sonar.api.measures.*; 026 import org.sonar.api.resources.Project; 027 import org.sonar.api.resources.Resource; 028 import org.sonar.api.resources.ResourceUtils; 029 import org.sonar.api.resources.Scopes; 030 import org.sonar.api.rules.Rule; 031 import org.sonar.api.rules.RulePriority; 032 import org.sonar.api.rules.Violation; 033 import org.sonar.batch.components.PastSnapshot; 034 import org.sonar.batch.components.TimeMachineConfiguration; 035 036 import java.util.*; 037 038 @DependsUpon(DecoratorBarriers.END_OF_VIOLATION_TRACKING) 039 public class NewViolationsDecorator implements Decorator { 040 041 private TimeMachineConfiguration timeMachineConfiguration; 042 043 // temporary data for current resource 044 private Map<Rule, RulePriority> ruleToLevel = Maps.newHashMap(); 045 private Multimap<RulePriority, Violation> violationsBySeverity = ArrayListMultimap.create(); 046 private Multimap<Rule, Violation> violationsByRule = ArrayListMultimap.create(); 047 048 public NewViolationsDecorator(TimeMachineConfiguration timeMachineConfiguration) { 049 this.timeMachineConfiguration = timeMachineConfiguration; 050 } 051 052 public boolean shouldExecuteOnProject(Project project) { 053 return project.isLatestAnalysis(); 054 } 055 056 @DependedUpon 057 public List<Metric> generatesMetric() { 058 return Arrays.asList( 059 CoreMetrics.NEW_VIOLATIONS, CoreMetrics.NEW_BLOCKER_VIOLATIONS, CoreMetrics.NEW_CRITICAL_VIOLATIONS, 060 CoreMetrics.NEW_MAJOR_VIOLATIONS, CoreMetrics.NEW_MINOR_VIOLATIONS, CoreMetrics.NEW_INFO_VIOLATIONS); 061 } 062 063 public void decorate(Resource resource, DecoratorContext context) { 064 if (shouldDecorateResource(resource, context)) { 065 prepareCurrentResourceViolations(context); 066 saveNewViolations(context); 067 saveNewViolationsBySeverity(context); 068 saveNewViolationsByRule(context); 069 clearCache(); 070 } 071 } 072 073 private boolean shouldDecorateResource(Resource resource, DecoratorContext context) { 074 return 075 (StringUtils.equals(Scopes.PROJECT, resource.getScope()) || StringUtils.equals(Scopes.DIRECTORY, resource.getScope()) || StringUtils.equals(Scopes.FILE, resource.getScope())) 076 && !ResourceUtils.isUnitTestClass(resource) && context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null; 077 } 078 079 080 private void clearCache() { 081 ruleToLevel.clear(); 082 violationsBySeverity.clear(); 083 violationsByRule.clear(); 084 } 085 086 private void prepareCurrentResourceViolations(DecoratorContext context) { 087 for (Violation violation : context.getViolations()) { 088 violationsBySeverity.put(violation.getSeverity(), violation); 089 violationsByRule.put(violation.getRule(), violation); 090 ruleToLevel.put(violation.getRule(), violation.getSeverity()); 091 } 092 } 093 094 private void saveNewViolations(DecoratorContext context) { 095 Measure measure = new Measure(CoreMetrics.NEW_VIOLATIONS); 096 for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) { 097 int variationIndex = pastSnapshot.getIndex(); 098 Collection<Measure> children = context.getChildrenMeasures(CoreMetrics.NEW_VIOLATIONS); 099 int count = countViolations(context.getViolations(), pastSnapshot.getTargetDate()); 100 double sum = sumChildren(variationIndex, children) + count; 101 measure.setVariation(variationIndex, sum); 102 } 103 context.saveMeasure(measure); 104 } 105 106 private void saveNewViolationsBySeverity(DecoratorContext context) { 107 for (RulePriority priority : RulePriority.values()) { 108 Metric metric = getMetricForSeverity(priority); 109 Measure measure = new Measure(metric); 110 for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) { 111 int variationIndex = pastSnapshot.getIndex(); 112 int count = countViolations(violationsBySeverity.get(priority), pastSnapshot.getTargetDate()); 113 Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric)); 114 double sum = sumChildren(variationIndex, children) + count; 115 measure.setVariation(variationIndex, sum); 116 } 117 context.saveMeasure(measure); 118 } 119 } 120 121 private void saveNewViolationsByRule(DecoratorContext context) { 122 ListMultimap<Rule, Measure> childrenByRule = ArrayListMultimap.create(); 123 Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(CoreMetrics.NEW_VIOLATIONS)); 124 for (Measure childMeasure : children) { 125 RuleMeasure childRuleMeasure = (RuleMeasure) childMeasure; 126 Rule rule = childRuleMeasure.getRule(); 127 if (rule != null) { 128 childrenByRule.put(rule, childMeasure); 129 ruleToLevel.put(childRuleMeasure.getRule(), childRuleMeasure.getRulePriority()); 130 } 131 } 132 133 Set<Rule> rules = Sets.newHashSet(violationsByRule.keys()); 134 rules.addAll(childrenByRule.keys()); 135 136 for (Rule rule : rules) { 137 RuleMeasure measure = RuleMeasure.createForRule(CoreMetrics.NEW_VIOLATIONS, rule, null); 138 measure.setRulePriority(ruleToLevel.get(rule)); 139 for (PastSnapshot pastSnapshot : timeMachineConfiguration.getProjectPastSnapshots()) { 140 int variationIndex = pastSnapshot.getIndex(); 141 int count = countViolations(violationsByRule.get(rule), pastSnapshot.getTargetDate()); 142 double sum = sumChildren(variationIndex, childrenByRule.get(rule)) + count; 143 measure.setVariation(variationIndex, sum); 144 } 145 context.saveMeasure(measure); 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 getMetricForSeverity(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("Not supported severity: " + severity); 194 } 195 return metric; 196 } 197 198 @Override 199 public String toString() { 200 return getClass().getSimpleName(); 201 } 202 }