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    }