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