Package org.sonar.plugins.core.issue

Source Code of org.sonar.plugins.core.issue.CountUnresolvedIssuesDecorator

/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package org.sonar.plugins.core.issue;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multiset;
import com.google.common.collect.Sets;
import org.apache.commons.lang.time.DateUtils;
import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorBarriers;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.DependedUpon;
import org.sonar.api.batch.DependsUpon;
import org.sonar.api.component.ResourcePerspectives;
import org.sonar.api.issue.Issuable;
import org.sonar.api.issue.Issue;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasureUtils;
import org.sonar.api.measures.MeasuresFilters;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.RuleMeasure;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleFinder;
import org.sonar.api.rules.RulePriority;
import org.sonar.batch.components.Period;
import org.sonar.batch.components.TimeMachineConfiguration;

import javax.annotation.Nullable;

import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Computes metrics related to number of issues.
*
* @since 3.6
*/
@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
public class CountUnresolvedIssuesDecorator implements Decorator {

  private final ResourcePerspectives perspectives;
  private final RuleFinder rulefinder;
  private final TimeMachineConfiguration timeMachineConfiguration;

  public CountUnresolvedIssuesDecorator(ResourcePerspectives perspectives, RuleFinder rulefinder, TimeMachineConfiguration timeMachineConfiguration) {
    this.perspectives = perspectives;
    this.rulefinder = rulefinder;
    this.timeMachineConfiguration = timeMachineConfiguration;
  }

  @Override
  public boolean shouldExecuteOnProject(Project project) {
    return true;
  }

  @DependedUpon
  public List<Metric> generatesIssuesMetrics() {
    return ImmutableList.<Metric>of(
      CoreMetrics.VIOLATIONS,
      CoreMetrics.BLOCKER_VIOLATIONS,
      CoreMetrics.CRITICAL_VIOLATIONS,
      CoreMetrics.MAJOR_VIOLATIONS,
      CoreMetrics.MINOR_VIOLATIONS,
      CoreMetrics.INFO_VIOLATIONS,
      CoreMetrics.NEW_VIOLATIONS,
      CoreMetrics.NEW_BLOCKER_VIOLATIONS,
      CoreMetrics.NEW_CRITICAL_VIOLATIONS,
      CoreMetrics.NEW_MAJOR_VIOLATIONS,
      CoreMetrics.NEW_MINOR_VIOLATIONS,
      CoreMetrics.NEW_INFO_VIOLATIONS,
      CoreMetrics.OPEN_ISSUES,
      CoreMetrics.REOPENED_ISSUES,
      CoreMetrics.CONFIRMED_ISSUES
      );
  }

  @Override
  public void decorate(Resource resource, DecoratorContext context) {
    Issuable issuable = perspectives.as(Issuable.class, resource);
    if (issuable != null) {
      Collection<Issue> issues = issuable.issues();
      boolean shouldSaveNewMetrics = shouldSaveNewMetrics(context);

      Multiset<RulePriority> severityBag = HashMultiset.create();
      Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity = Maps.newHashMap();
      ListMultimap<RulePriority, Issue> issuesPerSeverity = ArrayListMultimap.create();
      int countOpen = 0;
      int countReopened = 0;
      int countConfirmed = 0;

      for (Issue issue : issues) {
        severityBag.add(RulePriority.valueOf(issue.severity()));
        Multiset<RuleKey> rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity()));
        rulesBag.add(issue.ruleKey());
        issuesPerSeverity.put(RulePriority.valueOf(issue.severity()), issue);

        if (Issue.STATUS_OPEN.equals(issue.status())) {
          countOpen++;
        } else if (Issue.STATUS_REOPENED.equals(issue.status())) {
          countReopened++;
        } else if (Issue.STATUS_CONFIRMED.equals(issue.status())) {
          countConfirmed++;
        }
      }

      for (RulePriority ruleSeverity : RulePriority.values()) {
        saveIssuesForSeverity(context, ruleSeverity, severityBag);
        saveIssuesPerRules(context, ruleSeverity, rulesPerSeverity);
        saveNewIssuesForSeverity(context, ruleSeverity, issuesPerSeverity, shouldSaveNewMetrics);
        saveNewIssuesPerRule(context, ruleSeverity, issues, shouldSaveNewMetrics);
      }

      saveTotalIssues(context, issues);
      saveNewIssues(context, issues, shouldSaveNewMetrics);

      saveMeasure(context, CoreMetrics.OPEN_ISSUES, countOpen);
      saveMeasure(context, CoreMetrics.REOPENED_ISSUES, countReopened);
      saveMeasure(context, CoreMetrics.CONFIRMED_ISSUES, countConfirmed);
    }
  }

  private void saveTotalIssues(DecoratorContext context, Collection<Issue> issues) {
    if (context.getMeasure(CoreMetrics.VIOLATIONS) == null) {
      Collection<Measure> childrenIssues = context.getChildrenMeasures(CoreMetrics.VIOLATIONS);
      Double sum = MeasureUtils.sum(true, childrenIssues);
      context.saveMeasure(CoreMetrics.VIOLATIONS, sum + issues.size());
    }
  }

  private void saveNewIssues(DecoratorContext context, Collection<Issue> issues, boolean shouldSaveNewMetrics) {
    if (shouldSaveNewMetrics) {
      Measure measure = new Measure(CoreMetrics.NEW_VIOLATIONS);
      saveNewIssues(context, measure, issues);
    }
  }

  private void saveIssuesForSeverity(DecoratorContext context, RulePriority ruleSeverity, Multiset<RulePriority> severitiesBag) {
    Metric metric = SeverityUtils.severityToIssueMetric(ruleSeverity);
    if (context.getMeasure(metric) == null) {
      Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.metric(metric));
      int sum = MeasureUtils.sum(true, children).intValue() + severitiesBag.count(ruleSeverity);
      context.saveMeasure(metric, (double) sum);
    }
  }

  private void saveNewIssuesForSeverity(DecoratorContext context, RulePriority severity, ListMultimap<RulePriority, Issue> issuesPerSeverities, boolean shouldSaveNewMetrics) {
    if (shouldSaveNewMetrics) {
      Metric metric = SeverityUtils.severityToNewMetricIssue(severity);
      Measure measure = new Measure(metric);
      saveNewIssues(context, measure, issuesPerSeverities.get(severity));
    }
  }

  private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity) {
    Metric metric = SeverityUtils.severityToIssueMetric(severity);

    Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
    for (Measure child : children) {
      RuleMeasure childRuleMeasure = (RuleMeasure) child;
      RuleKey ruleKey = childRuleMeasure.ruleKey();
      if (ruleKey != null && MeasureUtils.hasValue(childRuleMeasure)) {
        Multiset<RuleKey> rulesBag = initRules(rulesPerSeverity, severity);
        rulesBag.add(ruleKey, childRuleMeasure.getIntValue());
      }
    }

    Multiset<RuleKey> rulesBag = rulesPerSeverity.get(severity);
    if (rulesBag != null) {
      for (Multiset.Entry<RuleKey> entry : rulesBag.entrySet()) {
        RuleMeasure measure = RuleMeasure.createForRule(metric, entry.getElement(), (double) entry.getCount());
        measure.setSeverity(severity);
        context.saveMeasure(measure);
      }
    }
  }

  private void saveNewIssuesPerRule(DecoratorContext context, RulePriority severity, Collection<Issue> issues, boolean shouldSaveNewMetrics) {
    if (shouldSaveNewMetrics) {
      Metric metric = SeverityUtils.severityToNewMetricIssue(severity);
      ListMultimap<RuleKey, Measure> childMeasuresPerRuleKeys = ArrayListMultimap.create();
      ListMultimap<RuleKey, Issue> issuesPerRuleKeys = ArrayListMultimap.create();
      Set<RuleKey> ruleKeys = Sets.newHashSet();

      Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
      for (Measure child : children) {
        RuleMeasure childRuleMeasure = (RuleMeasure) child;
        RuleKey ruleKey = childRuleMeasure.ruleKey();
        if (ruleKey != null) {
          childMeasuresPerRuleKeys.put(ruleKey, childRuleMeasure);
          ruleKeys.add(ruleKey);
        }
      }

      for (Issue issue : issues) {
        if (RulePriority.valueOf(issue.severity()).equals(severity)) {
          ruleKeys.add(issue.ruleKey());
          issuesPerRuleKeys.put(issue.ruleKey(), issue);
        }
      }

      for (RuleKey ruleKey : ruleKeys) {
        RuleMeasure measure = RuleMeasure.createForRule(metric, ruleKey, null);
        measure.setSeverity(severity);
        for (Period period : timeMachineConfiguration.periods()) {
          int variationIndex = period.getIndex();
          double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRuleKeys.get(ruleKey)) + countIssues(issuesPerRuleKeys.get(ruleKey), period);
          measure.setVariation(variationIndex, sum);
        }
        context.saveMeasure(measure);
      }
    }
  }

  private void saveNewIssues(DecoratorContext context, Measure measure, Collection<Issue> issues) {
    for (Period period : timeMachineConfiguration.periods()) {
      int variationIndex = period.getIndex();
      Collection<Measure> children = context.getChildrenMeasures(measure.getMetric());
      double sum = MeasureUtils.sumOnVariation(true, variationIndex, children) + countIssues(issues, period);
      measure.setVariation(variationIndex, sum);
    }
    context.saveMeasure(measure);
  }

  private void saveMeasure(DecoratorContext context, Metric metric, int value) {
    context.saveMeasure(metric, (double) (value + sumChildren(context, metric)));
  }

  private int sumChildren(DecoratorContext context, Metric metric) {
    int sum = 0;
    if (!ResourceUtils.isFile(context.getResource())) {
      sum = MeasureUtils.sum(true, context.getChildrenMeasures(metric)).intValue();
    }
    return sum;
  }

  private Multiset<RuleKey> initRules(Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity, RulePriority severity) {
    Multiset<RuleKey> rulesBag = rulesPerSeverity.get(severity);
    if (rulesBag == null) {
      rulesBag = HashMultiset.create();
      rulesPerSeverity.put(severity, rulesBag);
    }
    return rulesBag;
  }

  private int countIssues(Collection<Issue> issues, Period period) {
    // SONAR-3647 Use real snapshot date and not target date in order to stay consistent with other measure variations
    Date datePlusOneSecond = period.getDate() != null ? DateUtils.addSeconds(period.getDate(), 1) : null;
    return countIssuesAfterDate(issues, datePlusOneSecond);
  }

  @VisibleForTesting
  int countIssuesAfterDate(Collection<Issue> issues, @Nullable Date date) {
    if (issues == null) {
      return 0;
    }
    int count = 0;
    for (Issue issue : issues) {
      if (isAfter(issue, date)) {
        count++;
      }
    }
    return count;
  }

  private boolean isAfter(Issue issue, @Nullable Date date) {
    return date == null || (issue.creationDate() != null && DateUtils.truncatedCompareTo(issue.creationDate(), date, Calendar.SECOND) > 0);
  }

  private boolean shouldSaveNewMetrics(DecoratorContext context) {
    return context.getMeasure(CoreMetrics.NEW_VIOLATIONS) == null;
  }

  @Override
  public String toString() {
    return getClass().getSimpleName();
  }
}
TOP

Related Classes of org.sonar.plugins.core.issue.CountUnresolvedIssuesDecorator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.