123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- /*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.ce.task.projectanalysis.step;
-
- import java.util.Arrays;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Optional;
- import java.util.Set;
- import java.util.stream.IntStream;
- import org.sonar.ce.task.projectanalysis.component.Component;
- import org.sonar.ce.task.projectanalysis.component.PathAwareCrawler;
- import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
- import org.sonar.ce.task.projectanalysis.duplication.Duplication;
- import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepository;
- import org.sonar.ce.task.projectanalysis.duplication.InnerDuplicate;
- import org.sonar.ce.task.projectanalysis.duplication.TextBlock;
- import org.sonar.ce.task.projectanalysis.formula.Counter;
- import org.sonar.ce.task.projectanalysis.formula.CounterInitializationContext;
- import org.sonar.ce.task.projectanalysis.formula.CreateMeasureContext;
- import org.sonar.ce.task.projectanalysis.formula.Formula;
- import org.sonar.ce.task.projectanalysis.formula.FormulaExecutorComponentVisitor;
- import org.sonar.ce.task.projectanalysis.formula.counter.IntValue;
- import org.sonar.ce.task.projectanalysis.measure.Measure;
- import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
- import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
- import org.sonar.ce.task.projectanalysis.source.NewLinesRepository;
- import org.sonar.ce.task.step.ComputationStep;
-
- import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKS_DUPLICATED_KEY;
- import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY;
- import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_KEY;
- import static org.sonar.api.measures.CoreMetrics.NEW_LINES_KEY;
-
- /**
- * Computes measures on new code related to the size
- */
- public class NewSizeMeasuresStep implements ComputationStep {
- private final TreeRootHolder treeRootHolder;
- private final MetricRepository metricRepository;
- private final MeasureRepository measureRepository;
- private final NewDuplicationFormula duplicationFormula;
-
- public NewSizeMeasuresStep(TreeRootHolder treeRootHolder, MetricRepository metricRepository, MeasureRepository measureRepository,
- NewLinesRepository newLinesRepository, DuplicationRepository duplicationRepository) {
- this.treeRootHolder = treeRootHolder;
- this.metricRepository = metricRepository;
- this.measureRepository = measureRepository;
- this.duplicationFormula = new NewDuplicationFormula(newLinesRepository, duplicationRepository);
- }
-
- @Override
- public String getDescription() {
- return "Compute size measures on new code";
- }
-
- @Override
- public void execute(ComputationStep.Context context) {
- new PathAwareCrawler<>(
- FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
- .buildFor(List.of(duplicationFormula)))
- .visit(treeRootHolder.getRoot());
- }
-
- private static class NewSizeCounter implements Counter<NewSizeCounter> {
- private final DuplicationRepository duplicationRepository;
- private final NewLinesRepository newLinesRepository;
-
- private final IntValue newLines = new IntValue();
- private final IntValue newDuplicatedLines = new IntValue();
- private final IntValue newDuplicatedBlocks = new IntValue();
-
- private NewSizeCounter(DuplicationRepository duplicationRepository, NewLinesRepository newLinesRepository) {
- this.duplicationRepository = duplicationRepository;
- this.newLinesRepository = newLinesRepository;
- }
-
- @Override
- public void aggregate(NewSizeCounter counter) {
- this.newDuplicatedLines.increment(counter.newDuplicatedLines);
- this.newDuplicatedBlocks.increment(counter.newDuplicatedBlocks);
- this.newLines.increment(counter.newLines);
- }
-
- @Override
- public void initialize(CounterInitializationContext context) {
- Component leaf = context.getLeaf();
- if (leaf.getType() != Component.Type.FILE) {
- return;
- }
- Optional<Set<Integer>> changedLines = newLinesRepository.getNewLines(leaf);
- if (!changedLines.isPresent()) {
- return;
- }
-
- newLines.increment(0);
- if (leaf.getType() != Component.Type.FILE) {
- newDuplicatedLines.increment(0);
- newDuplicatedBlocks.increment(0);
- } else {
- initNewLines(changedLines.get());
- initNewDuplicated(leaf, changedLines.get());
- }
- }
-
- private void initNewLines(Set<Integer> changedLines) {
- newLines.increment(changedLines.size());
- }
-
- private void initNewDuplicated(Component component, Set<Integer> changedLines) {
- DuplicationCounters duplicationCounters = new DuplicationCounters(changedLines);
- Iterable<Duplication> duplications = duplicationRepository.getDuplications(component);
- for (Duplication duplication : duplications) {
- duplicationCounters.addBlock(duplication.getOriginal());
- Arrays.stream(duplication.getDuplicates())
- .filter(InnerDuplicate.class::isInstance)
- .map(duplicate -> (InnerDuplicate) duplicate)
- .forEach(duplicate -> duplicationCounters.addBlock(duplicate.getTextBlock()));
- }
-
- newDuplicatedLines.increment(duplicationCounters.getNewLinesDuplicated());
- newDuplicatedBlocks.increment(duplicationCounters.getNewBlocksDuplicated());
- }
- }
-
- private static class DuplicationCounters {
- private final Set<Integer> changedLines;
- private final Set<Integer> lineCounts;
- private int blockCounts;
-
- private DuplicationCounters(Set<Integer> changedLines) {
- this.changedLines = changedLines;
- this.lineCounts = new HashSet<>(changedLines.size());
- }
-
- void addBlock(TextBlock textBlock) {
- Boolean[] newBlock = new Boolean[] {false};
- IntStream.rangeClosed(textBlock.getStart(), textBlock.getEnd())
- .filter(changedLines::contains)
- .forEach(line -> {
- lineCounts.add(line);
- newBlock[0] = true;
- });
- if (newBlock[0]) {
- blockCounts++;
- }
- }
-
- int getNewLinesDuplicated() {
- return lineCounts.size();
- }
-
- int getNewBlocksDuplicated() {
- return blockCounts;
- }
- }
-
- private static final class NewDuplicationFormula implements Formula<NewSizeCounter> {
- private final DuplicationRepository duplicationRepository;
- private final NewLinesRepository newLinesRepository;
-
- private NewDuplicationFormula(NewLinesRepository newLinesRepository, DuplicationRepository duplicationRepository) {
- this.duplicationRepository = duplicationRepository;
- this.newLinesRepository = newLinesRepository;
- }
-
- @Override
- public NewSizeCounter createNewCounter() {
- return new NewSizeCounter(duplicationRepository, newLinesRepository);
- }
-
- @Override
- public Optional<Measure> createMeasure(NewSizeCounter counter, CreateMeasureContext context) {
- String metricKey = context.getMetric().getKey();
- switch (metricKey) {
- case NEW_LINES_KEY:
- return createMeasure(counter.newLines);
- case NEW_DUPLICATED_LINES_KEY:
- return createMeasure(counter.newDuplicatedLines);
- case NEW_DUPLICATED_LINES_DENSITY_KEY:
- return createNewDuplicatedLinesDensityMeasure(counter);
- case NEW_BLOCKS_DUPLICATED_KEY:
- return createMeasure(counter.newDuplicatedBlocks);
- default:
- throw new IllegalArgumentException("Unsupported metric " + context.getMetric());
- }
- }
-
- private static Optional<Measure> createMeasure(IntValue intValue) {
- return intValue.isSet()
- ? Optional.of(Measure.newMeasureBuilder().setVariation(intValue.getValue()).createNoValue())
- : Optional.empty();
- }
-
- private static Optional<Measure> createNewDuplicatedLinesDensityMeasure(NewSizeCounter counter) {
- IntValue newLines = counter.newLines;
- IntValue newDuplicatedLines = counter.newDuplicatedLines;
- if (newLines.isSet() && newDuplicatedLines.isSet()) {
- int newLinesVariations = newLines.getValue();
- int newDuplicatedLinesVariations = newDuplicatedLines.getValue();
- if (newLinesVariations > 0D) {
- double density = Math.min(100D, 100D * newDuplicatedLinesVariations / newLinesVariations);
- return Optional.of(Measure.newMeasureBuilder().setVariation(density).createNoValue());
- }
- }
- return Optional.empty();
- }
-
- @Override
- public String[] getOutputMetricKeys() {
- return new String[] {NEW_LINES_KEY, NEW_DUPLICATED_LINES_KEY, NEW_DUPLICATED_LINES_DENSITY_KEY, NEW_BLOCKS_DUPLICATED_KEY};
- }
- }
- }
|