Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

NewSizeMeasuresStep.java 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.ce.task.projectanalysis.step;
  21. import java.util.Arrays;
  22. import java.util.HashSet;
  23. import java.util.List;
  24. import java.util.Optional;
  25. import java.util.Set;
  26. import java.util.stream.IntStream;
  27. import org.sonar.ce.task.projectanalysis.component.Component;
  28. import org.sonar.ce.task.projectanalysis.component.PathAwareCrawler;
  29. import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
  30. import org.sonar.ce.task.projectanalysis.duplication.Duplication;
  31. import org.sonar.ce.task.projectanalysis.duplication.DuplicationRepository;
  32. import org.sonar.ce.task.projectanalysis.duplication.InnerDuplicate;
  33. import org.sonar.ce.task.projectanalysis.duplication.TextBlock;
  34. import org.sonar.ce.task.projectanalysis.formula.Counter;
  35. import org.sonar.ce.task.projectanalysis.formula.CounterInitializationContext;
  36. import org.sonar.ce.task.projectanalysis.formula.CreateMeasureContext;
  37. import org.sonar.ce.task.projectanalysis.formula.Formula;
  38. import org.sonar.ce.task.projectanalysis.formula.FormulaExecutorComponentVisitor;
  39. import org.sonar.ce.task.projectanalysis.formula.counter.IntValue;
  40. import org.sonar.ce.task.projectanalysis.measure.Measure;
  41. import org.sonar.ce.task.projectanalysis.measure.MeasureRepository;
  42. import org.sonar.ce.task.projectanalysis.metric.MetricRepository;
  43. import org.sonar.ce.task.projectanalysis.source.NewLinesRepository;
  44. import org.sonar.ce.task.step.ComputationStep;
  45. import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKS_DUPLICATED_KEY;
  46. import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY;
  47. import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_KEY;
  48. import static org.sonar.api.measures.CoreMetrics.NEW_LINES_KEY;
  49. /**
  50. * Computes measures on new code related to the size
  51. */
  52. public class NewSizeMeasuresStep implements ComputationStep {
  53. private final TreeRootHolder treeRootHolder;
  54. private final MetricRepository metricRepository;
  55. private final MeasureRepository measureRepository;
  56. private final NewDuplicationFormula duplicationFormula;
  57. public NewSizeMeasuresStep(TreeRootHolder treeRootHolder, MetricRepository metricRepository, MeasureRepository measureRepository,
  58. NewLinesRepository newLinesRepository, DuplicationRepository duplicationRepository) {
  59. this.treeRootHolder = treeRootHolder;
  60. this.metricRepository = metricRepository;
  61. this.measureRepository = measureRepository;
  62. this.duplicationFormula = new NewDuplicationFormula(newLinesRepository, duplicationRepository);
  63. }
  64. @Override
  65. public String getDescription() {
  66. return "Compute size measures on new code";
  67. }
  68. @Override
  69. public void execute(ComputationStep.Context context) {
  70. new PathAwareCrawler<>(
  71. FormulaExecutorComponentVisitor.newBuilder(metricRepository, measureRepository)
  72. .buildFor(List.of(duplicationFormula)))
  73. .visit(treeRootHolder.getRoot());
  74. }
  75. private static class NewSizeCounter implements Counter<NewSizeCounter> {
  76. private final DuplicationRepository duplicationRepository;
  77. private final NewLinesRepository newLinesRepository;
  78. private final IntValue newLines = new IntValue();
  79. private final IntValue newDuplicatedLines = new IntValue();
  80. private final IntValue newDuplicatedBlocks = new IntValue();
  81. private NewSizeCounter(DuplicationRepository duplicationRepository, NewLinesRepository newLinesRepository) {
  82. this.duplicationRepository = duplicationRepository;
  83. this.newLinesRepository = newLinesRepository;
  84. }
  85. @Override
  86. public void aggregate(NewSizeCounter counter) {
  87. this.newDuplicatedLines.increment(counter.newDuplicatedLines);
  88. this.newDuplicatedBlocks.increment(counter.newDuplicatedBlocks);
  89. this.newLines.increment(counter.newLines);
  90. }
  91. @Override
  92. public void initialize(CounterInitializationContext context) {
  93. Component leaf = context.getLeaf();
  94. if (leaf.getType() != Component.Type.FILE) {
  95. return;
  96. }
  97. Optional<Set<Integer>> changedLines = newLinesRepository.getNewLines(leaf);
  98. if (!changedLines.isPresent()) {
  99. return;
  100. }
  101. newLines.increment(0);
  102. if (leaf.getType() != Component.Type.FILE) {
  103. newDuplicatedLines.increment(0);
  104. newDuplicatedBlocks.increment(0);
  105. } else {
  106. initNewLines(changedLines.get());
  107. initNewDuplicated(leaf, changedLines.get());
  108. }
  109. }
  110. private void initNewLines(Set<Integer> changedLines) {
  111. newLines.increment(changedLines.size());
  112. }
  113. private void initNewDuplicated(Component component, Set<Integer> changedLines) {
  114. DuplicationCounters duplicationCounters = new DuplicationCounters(changedLines);
  115. Iterable<Duplication> duplications = duplicationRepository.getDuplications(component);
  116. for (Duplication duplication : duplications) {
  117. duplicationCounters.addBlock(duplication.getOriginal());
  118. Arrays.stream(duplication.getDuplicates())
  119. .filter(InnerDuplicate.class::isInstance)
  120. .map(duplicate -> (InnerDuplicate) duplicate)
  121. .forEach(duplicate -> duplicationCounters.addBlock(duplicate.getTextBlock()));
  122. }
  123. newDuplicatedLines.increment(duplicationCounters.getNewLinesDuplicated());
  124. newDuplicatedBlocks.increment(duplicationCounters.getNewBlocksDuplicated());
  125. }
  126. }
  127. private static class DuplicationCounters {
  128. private final Set<Integer> changedLines;
  129. private final Set<Integer> lineCounts;
  130. private int blockCounts;
  131. private DuplicationCounters(Set<Integer> changedLines) {
  132. this.changedLines = changedLines;
  133. this.lineCounts = new HashSet<>(changedLines.size());
  134. }
  135. void addBlock(TextBlock textBlock) {
  136. Boolean[] newBlock = new Boolean[] {false};
  137. IntStream.rangeClosed(textBlock.getStart(), textBlock.getEnd())
  138. .filter(changedLines::contains)
  139. .forEach(line -> {
  140. lineCounts.add(line);
  141. newBlock[0] = true;
  142. });
  143. if (newBlock[0]) {
  144. blockCounts++;
  145. }
  146. }
  147. int getNewLinesDuplicated() {
  148. return lineCounts.size();
  149. }
  150. int getNewBlocksDuplicated() {
  151. return blockCounts;
  152. }
  153. }
  154. private static final class NewDuplicationFormula implements Formula<NewSizeCounter> {
  155. private final DuplicationRepository duplicationRepository;
  156. private final NewLinesRepository newLinesRepository;
  157. private NewDuplicationFormula(NewLinesRepository newLinesRepository, DuplicationRepository duplicationRepository) {
  158. this.duplicationRepository = duplicationRepository;
  159. this.newLinesRepository = newLinesRepository;
  160. }
  161. @Override
  162. public NewSizeCounter createNewCounter() {
  163. return new NewSizeCounter(duplicationRepository, newLinesRepository);
  164. }
  165. @Override
  166. public Optional<Measure> createMeasure(NewSizeCounter counter, CreateMeasureContext context) {
  167. String metricKey = context.getMetric().getKey();
  168. switch (metricKey) {
  169. case NEW_LINES_KEY:
  170. return createMeasure(counter.newLines);
  171. case NEW_DUPLICATED_LINES_KEY:
  172. return createMeasure(counter.newDuplicatedLines);
  173. case NEW_DUPLICATED_LINES_DENSITY_KEY:
  174. return createNewDuplicatedLinesDensityMeasure(counter);
  175. case NEW_BLOCKS_DUPLICATED_KEY:
  176. return createMeasure(counter.newDuplicatedBlocks);
  177. default:
  178. throw new IllegalArgumentException("Unsupported metric " + context.getMetric());
  179. }
  180. }
  181. private static Optional<Measure> createMeasure(IntValue intValue) {
  182. return intValue.isSet()
  183. ? Optional.of(Measure.newMeasureBuilder().setVariation(intValue.getValue()).createNoValue())
  184. : Optional.empty();
  185. }
  186. private static Optional<Measure> createNewDuplicatedLinesDensityMeasure(NewSizeCounter counter) {
  187. IntValue newLines = counter.newLines;
  188. IntValue newDuplicatedLines = counter.newDuplicatedLines;
  189. if (newLines.isSet() && newDuplicatedLines.isSet()) {
  190. int newLinesVariations = newLines.getValue();
  191. int newDuplicatedLinesVariations = newDuplicatedLines.getValue();
  192. if (newLinesVariations > 0D) {
  193. double density = Math.min(100D, 100D * newDuplicatedLinesVariations / newLinesVariations);
  194. return Optional.of(Measure.newMeasureBuilder().setVariation(density).createNoValue());
  195. }
  196. }
  197. return Optional.empty();
  198. }
  199. @Override
  200. public String[] getOutputMetricKeys() {
  201. return new String[] {NEW_LINES_KEY, NEW_DUPLICATED_LINES_KEY, NEW_DUPLICATED_LINES_DENSITY_KEY, NEW_BLOCKS_DUPLICATED_KEY};
  202. }
  203. }
  204. }