Package org.sonar.batch.debt

Source Code of org.sonar.batch.debt.DebtDecorator$SumMap

/*
* 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.batch.debt;

import com.google.common.annotations.VisibleForTesting;
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.batch.rule.Rule;
import org.sonar.api.batch.rule.Rules;
import org.sonar.api.component.ResourcePerspectives;
import org.sonar.api.issue.Issuable;
import org.sonar.api.issue.Issue;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasuresFilters;
import org.sonar.api.measures.Metric;
import org.sonar.api.measures.PersistenceMode;
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.technicaldebt.batch.Characteristic;
import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;

/**
* Decorator that computes the technical debt metric
*/
@DependsUpon(DecoratorBarriers.ISSUES_TRACKED)
public final class DebtDecorator implements Decorator {

  private final ResourcePerspectives perspectives;
  private final TechnicalDebtModel model;
  private final Rules rules;

  /**
   * ruleFinder is needed to load "old" rule in order to persist rule measure
   */
  private final RuleFinder ruleFinder;

  public DebtDecorator(ResourcePerspectives perspectives, TechnicalDebtModel model, Rules rules, RuleFinder ruleFinder) {
    this.perspectives = perspectives;
    this.model = model;
    this.rules = rules;
    this.ruleFinder = ruleFinder;
  }

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

  @DependedUpon
  public List<Metric> generatesMetrics() {
    return Arrays.<Metric>asList(CoreMetrics.TECHNICAL_DEBT);
  }

  @Override
  public void decorate(Resource resource, DecoratorContext context) {
    Issuable issuable = perspectives.as(Issuable.class, resource);
    if (issuable != null && shouldSaveMeasure(context)) {
      List<Issue> issues = newArrayList(issuable.issues());
      saveMeasures(context, issues);
    }
  }

  private void saveMeasures(DecoratorContext context, List<Issue> issues) {
    Long total = 0L;
    SumMap<RuleKey> ruleDebts = new SumMap<RuleKey>();
    SumMap<Characteristic> characteristicDebts = new SumMap<Characteristic>();

    // Aggregate rules debt from current issues (and populate current characteristic debt)
    for (Issue issue : issues) {
      Long debt = ((DefaultIssue) issue).debtInMinutes();
      total += computeDebt(debt, issue.ruleKey(), ruleDebts, characteristicDebts);
    }

    // Aggregate rules debt from children (and populate children characteristics debt)
    for (Measure measure : context.getChildrenMeasures(MeasuresFilters.rules(CoreMetrics.TECHNICAL_DEBT))) {
      Long debt = measure.getValue().longValue();
      RuleMeasure ruleMeasure = (RuleMeasure) measure;
      total += computeDebt(debt, ruleMeasure.ruleKey(), ruleDebts, characteristicDebts);
    }

    context.saveMeasure(CoreMetrics.TECHNICAL_DEBT, total.doubleValue());
    saveOnRule(context, ruleDebts);
    for (Characteristic characteristic : model.characteristics()) {
      Long debt = characteristicDebts.get(characteristic);
      saveCharacteristicMeasure(context, characteristic, debt != null ? debt.doubleValue() : 0d, false);
    }
  }

  private Long computeDebt(@Nullable Long debt, RuleKey ruleKey, SumMap<RuleKey> ruleDebts, SumMap<Characteristic> characteristicDebts) {
    if (debt != null) {
      Rule rule = rules.find(ruleKey);
      if (rule != null) {
        String characteristicKey = rule.debtSubCharacteristic();
        if (characteristicKey != null) {
          Characteristic characteristic = model.characteristicByKey(characteristicKey);
          if (characteristic != null) {
            ruleDebts.add(ruleKey, debt);
            characteristicDebts.add(characteristic, debt);
            propagateTechnicalDebtInParents(characteristic.parent(), debt, characteristicDebts);
            return debt;
          }
        }
      }
    }
    return 0L;
  }

  private void propagateTechnicalDebtInParents(@Nullable Characteristic characteristic, long value, SumMap<Characteristic> characteristicDebts) {
    if (characteristic != null) {
      characteristicDebts.add(characteristic, value);
      propagateTechnicalDebtInParents(characteristic.parent(), value, characteristicDebts);
    }
  }

  private void saveOnRule(DecoratorContext context, SumMap<RuleKey> ruleDebts) {
    for (Map.Entry<RuleKey, Long> entry : ruleDebts.entrySet()) {
      org.sonar.api.rules.Rule oldRule = ruleFinder.findByKey(entry.getKey());
      if (oldRule != null) {
        saveRuleMeasure(context, oldRule, entry.getValue().doubleValue(), ResourceUtils.isEntity(context.getResource()));
      }
    }
  }

  @VisibleForTesting
  void saveCharacteristicMeasure(DecoratorContext context, Characteristic characteristic, Double value, boolean inMemory) {
    // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
    // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
    if (value > 0.0 || (ResourceUtils.isProject(context.getResource()) && characteristic.isRoot())) {
      Measure measure = new Measure(CoreMetrics.TECHNICAL_DEBT);
      measure.setCharacteristic(characteristic);
      saveMeasure(context, measure, value, inMemory);
    }
  }

  @VisibleForTesting
  void saveRuleMeasure(DecoratorContext context, org.sonar.api.rules.Rule rule, Double value, boolean inMemory) {
    // we need the value on projects (root or module) even if value==0 in order to display correctly the SQALE history chart (see SQALE-122)
    // BUT we don't want to save zero-values for non top-characteristics (see SQALE-147)
    if (value > 0.0) {
      RuleMeasure measure = new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, rule, null, null);
      saveMeasure(context, measure, value, inMemory);
    }
  }

  private void saveMeasure(DecoratorContext context, Measure measure, Double value, boolean inMemory) {
    measure.setValue(value);
    if (inMemory) {
      measure.setPersistenceMode(PersistenceMode.MEMORY);
    }
    context.saveMeasure(measure);
  }

  private boolean shouldSaveMeasure(DecoratorContext context) {
    return context.getMeasure(CoreMetrics.TECHNICAL_DEBT) == null;
  }

  private static class SumMap<E> {
    private Map<E, Long> sumByKeys;

    public SumMap() {
      sumByKeys = newHashMap();
    }

    public void add(@Nullable E key, Long value) {
      if (key != null) {
        Long currentValue = sumByKeys.get(key);
        sumByKeys.put(key, currentValue != null ? currentValue + value : value);
      }
    }

    @CheckForNull
    public Long get(E key) {
      return sumByKeys.get(key);
    }

    public Set<Map.Entry<E, Long>> entrySet() {
      return sumByKeys.entrySet();
    }
  }
}
TOP

Related Classes of org.sonar.batch.debt.DebtDecorator$SumMap

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.