You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LoadCrossProjectDuplicationsRepositoryStep.java 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2020 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.ArrayList;
  22. import java.util.Collection;
  23. import java.util.List;
  24. import java.util.function.Function;
  25. import java.util.stream.Collectors;
  26. import javax.annotation.Nonnull;
  27. import org.sonar.api.utils.log.Logger;
  28. import org.sonar.api.utils.log.Loggers;
  29. import org.sonar.ce.task.projectanalysis.analysis.Analysis;
  30. import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
  31. import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
  32. import org.sonar.ce.task.projectanalysis.component.Component;
  33. import org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit;
  34. import org.sonar.ce.task.projectanalysis.component.DepthTraversalTypeAwareCrawler;
  35. import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
  36. import org.sonar.ce.task.projectanalysis.component.TypeAwareVisitorAdapter;
  37. import org.sonar.ce.task.projectanalysis.duplication.CrossProjectDuplicationStatusHolder;
  38. import org.sonar.ce.task.projectanalysis.duplication.IntegrateCrossProjectDuplications;
  39. import org.sonar.ce.task.step.ComputationStep;
  40. import org.sonar.core.util.CloseableIterator;
  41. import org.sonar.db.DbClient;
  42. import org.sonar.db.DbSession;
  43. import org.sonar.db.duplication.DuplicationUnitDto;
  44. import org.sonar.duplications.block.Block;
  45. import org.sonar.duplications.block.ByteArray;
  46. import org.sonar.scanner.protocol.output.ScannerReport.CpdTextBlock;
  47. /**
  48. * Feed the duplications repository from the cross project duplication blocks computed with duplications blocks of the analysis report.
  49. * Blocks can be empty if :
  50. * - The file is excluded from the analysis using {@link org.sonar.api.CoreProperties#CPD_EXCLUSIONS}
  51. * - On Java, if the number of statements of the file is too small, nothing will be sent.
  52. */
  53. public class LoadCrossProjectDuplicationsRepositoryStep implements ComputationStep {
  54. private static final Logger LOGGER = Loggers.get(LoadCrossProjectDuplicationsRepositoryStep.class);
  55. private final TreeRootHolder treeRootHolder;
  56. private final BatchReportReader reportReader;
  57. private final AnalysisMetadataHolder analysisMetadataHolder;
  58. private final IntegrateCrossProjectDuplications integrateCrossProjectDuplications;
  59. private final CrossProjectDuplicationStatusHolder crossProjectDuplicationStatusHolder;
  60. private final DbClient dbClient;
  61. public LoadCrossProjectDuplicationsRepositoryStep(TreeRootHolder treeRootHolder, BatchReportReader reportReader,
  62. AnalysisMetadataHolder analysisMetadataHolder, CrossProjectDuplicationStatusHolder crossProjectDuplicationStatusHolder,
  63. IntegrateCrossProjectDuplications integrateCrossProjectDuplications, DbClient dbClient) {
  64. this.treeRootHolder = treeRootHolder;
  65. this.reportReader = reportReader;
  66. this.analysisMetadataHolder = analysisMetadataHolder;
  67. this.integrateCrossProjectDuplications = integrateCrossProjectDuplications;
  68. this.crossProjectDuplicationStatusHolder = crossProjectDuplicationStatusHolder;
  69. this.dbClient = dbClient;
  70. }
  71. @Override
  72. public void execute(ComputationStep.Context context) {
  73. if (crossProjectDuplicationStatusHolder.isEnabled()) {
  74. new DepthTraversalTypeAwareCrawler(new CrossProjectDuplicationVisitor()).visit(treeRootHolder.getRoot());
  75. }
  76. }
  77. @Override
  78. public String getDescription() {
  79. return "Compute cross project duplications";
  80. }
  81. private class CrossProjectDuplicationVisitor extends TypeAwareVisitorAdapter {
  82. private CrossProjectDuplicationVisitor() {
  83. super(CrawlerDepthLimit.FILE, Order.PRE_ORDER);
  84. }
  85. @Override
  86. public void visitFile(Component file) {
  87. List<CpdTextBlock> cpdTextBlocks = new ArrayList<>();
  88. try (CloseableIterator<CpdTextBlock> blocksIt = reportReader.readCpdTextBlocks(file.getReportAttributes().getRef())) {
  89. while(blocksIt.hasNext()) {
  90. cpdTextBlocks.add(blocksIt.next());
  91. }
  92. }
  93. LOGGER.trace("Found {} cpd blocks on file {}", cpdTextBlocks.size(), file.getDbKey());
  94. if (cpdTextBlocks.isEmpty()) {
  95. return;
  96. }
  97. Collection<String> hashes = cpdTextBlocks.stream().map(CpdTextBlockToHash.INSTANCE).collect(Collectors.toList());
  98. List<DuplicationUnitDto> dtos = selectDuplicates(file, hashes);
  99. if (dtos.isEmpty()) {
  100. return;
  101. }
  102. Collection<Block> duplicatedBlocks = dtos.stream().map(DtoToBlock.INSTANCE).collect(Collectors.toList());
  103. Collection<Block> originBlocks = cpdTextBlocks.stream().map(new CpdTextBlockToBlock(file.getDbKey())).collect(Collectors.toList());
  104. LOGGER.trace("Found {} duplicated cpd blocks on file {}", duplicatedBlocks.size(), file.getDbKey());
  105. integrateCrossProjectDuplications.computeCpd(file, originBlocks, duplicatedBlocks);
  106. }
  107. private List<DuplicationUnitDto> selectDuplicates(Component file, Collection<String> hashes) {
  108. try (DbSession dbSession = dbClient.openSession(false)) {
  109. Analysis projectAnalysis = analysisMetadataHolder.getBaseAnalysis();
  110. String analysisUuid = projectAnalysis == null ? null : projectAnalysis.getUuid();
  111. return dbClient.duplicationDao().selectCandidates(dbSession, analysisUuid, file.getFileAttributes().getLanguageKey(), hashes);
  112. }
  113. }
  114. }
  115. private enum CpdTextBlockToHash implements Function<CpdTextBlock, String> {
  116. INSTANCE;
  117. @Override
  118. public String apply(@Nonnull CpdTextBlock duplicationBlock) {
  119. return duplicationBlock.getHash();
  120. }
  121. }
  122. private enum DtoToBlock implements Function<DuplicationUnitDto, Block> {
  123. INSTANCE;
  124. @Override
  125. public Block apply(@Nonnull DuplicationUnitDto dto) {
  126. // Note that the dto doesn't contains start/end token indexes
  127. return Block.builder()
  128. .setResourceId(dto.getComponentKey())
  129. .setBlockHash(new ByteArray(dto.getHash()))
  130. .setIndexInFile(dto.getIndexInFile())
  131. .setLines(dto.getStartLine(), dto.getEndLine())
  132. .build();
  133. }
  134. }
  135. private static class CpdTextBlockToBlock implements Function<CpdTextBlock, Block> {
  136. private final String fileKey;
  137. private int indexInFile = 0;
  138. CpdTextBlockToBlock(String fileKey) {
  139. this.fileKey = fileKey;
  140. }
  141. @Override
  142. public Block apply(@Nonnull CpdTextBlock duplicationBlock) {
  143. Block block = Block.builder()
  144. .setResourceId(fileKey)
  145. .setBlockHash(new ByteArray(duplicationBlock.getHash()))
  146. .setIndexInFile(indexInFile)
  147. .setLines(duplicationBlock.getStartLine(), duplicationBlock.getEndLine())
  148. .setUnit(duplicationBlock.getStartTokenIndex(), duplicationBlock.getEndTokenIndex())
  149. .build();
  150. indexInFile++;
  151. return block;
  152. }
  153. }
  154. }