diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-12-21 13:54:14 +0100 |
---|---|---|
committer | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-12-21 14:37:03 +0100 |
commit | d1e303f8412f61f426a6c746cc2ac10b584940bd (patch) | |
tree | b68693cc8caced2aeee5b06a3590b3e1392e8011 /sonar-batch/src/main/java | |
parent | 6fe8b0cd64c4b4e2386d994016b465b0386ed09c (diff) | |
download | sonarqube-d1e303f8412f61f426a6c746cc2ac10b584940bd.tar.gz sonarqube-d1e303f8412f61f426a6c746cc2ac10b584940bd.zip |
SONAR-2867 Standard copy-paste detection should happen within a project, not only within a module
Diffstat (limited to 'sonar-batch/src/main/java')
9 files changed, 127 insertions, 177 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java index ffac2cba012..b920ef1cefa 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java @@ -27,12 +27,12 @@ public final class CpdComponents { private CpdComponents() { } - public static List all() { + public static List<Class<? extends Object>> all() { return ImmutableList.of( CpdSensor.class, CpdMappings.class, - JavaCpdEngine.class, - DefaultCpdEngine.class); + JavaCpdIndexer.class, + DefaultCpdIndexer.class); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/AbstractCpdEngine.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdExecutor.java index 8905bf5ac07..302035c3973 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cpd/AbstractCpdEngine.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdExecutor.java @@ -17,45 +17,104 @@ * 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.cpd; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; -import java.util.List; +import com.google.common.base.Predicate; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.config.Settings; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; +import org.sonar.batch.cpd.index.SonarDuplicationsIndex; import org.sonar.batch.index.BatchComponent; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.protocol.output.BatchReport; import org.sonar.batch.protocol.output.BatchReport.Duplicate; import org.sonar.batch.protocol.output.BatchReport.Duplication; import org.sonar.batch.report.ReportPublisher; +import org.sonar.duplications.block.Block; +import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm; import org.sonar.duplications.index.CloneGroup; import org.sonar.duplications.index.ClonePart; +import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks; -import static com.google.common.collect.FluentIterable.from; - -public abstract class AbstractCpdEngine extends CpdEngine { +import java.util.Collection; +import java.util.Iterator; +import java.util.List; - private static final Logger LOG = Loggers.get(AbstractCpdEngine.class); +import static com.google.common.collect.FluentIterable.from; +/** + * Runs on the root module, at the end of the project analysis. + * It executes copy paste detection involving all files of all modules, which were indexed during sensors execution for each module + * by {@link CpdSensor). The sensor is responsible for handling exclusions and block sizes. + */ +public class CpdExecutor { + private static final Logger LOG = Loggers.get(CpdExecutor.class); static final int MAX_CLONE_GROUP_PER_FILE = 100; static final int MAX_CLONE_PART_PER_GROUP = 100; + private final SonarDuplicationsIndex index; private final ReportPublisher publisher; private final BatchComponentCache batchComponentCache; + private final Settings settings; - public AbstractCpdEngine(ReportPublisher publisher, BatchComponentCache batchComponentCache) { + public CpdExecutor(Settings settings, SonarDuplicationsIndex index, ReportPublisher publisher, BatchComponentCache batchComponentCache) { + this.settings = settings; + this.index = index; this.publisher = publisher; this.batchComponentCache = batchComponentCache; } - protected final void saveDuplications(final InputFile inputFile, List<CloneGroup> duplications) { + public void execute() { + Iterator<ResourceBlocks> it = index.iterator(); + + while (it.hasNext()) { + ResourceBlocks resourceBlocks = it.next(); + runCpdAnalysis(resourceBlocks.resourceId(), resourceBlocks.blocks()); + } + } + + private void runCpdAnalysis(String resource, Collection<Block> fileBlocks) { + LOG.debug("Detection of duplications for {}", resource); + + BatchComponent component = batchComponentCache.get(resource); + if (component == null) { + LOG.error("Resource not found in component cache: {}. Skipping CPD computation for it", resource); + return; + } + + List<CloneGroup> duplications; + try { + duplications = SuffixTreeCloneDetectionAlgorithm.detect(index, fileBlocks); + } catch (Exception e) { + throw new IllegalStateException("Fail during detection of duplication for " + resource, e); + } + + InputFile inputFile = (InputFile) component.inputComponent(); + Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(getMinimumTokens(inputFile.language())); + List<CloneGroup> filtered = from(duplications).filter(minimumTokensPredicate).toList(); + + saveDuplications(component, filtered); + } + + @VisibleForTesting + int getMinimumTokens(String languageKey) { + int minimumTokens = settings.getInt("sonar.cpd." + languageKey + ".minimumTokens"); + if (minimumTokens == 0) { + minimumTokens = 100; + } + + return minimumTokens; + } + + @VisibleForTesting + final void saveDuplications(final BatchComponent component, List<CloneGroup> duplications) { if (duplications.size() > MAX_CLONE_GROUP_PER_FILE) { - LOG.warn("Too many duplication groups on file " + inputFile.relativePath() + ". Keep only the first " + MAX_CLONE_GROUP_PER_FILE + " groups."); + LOG.warn("Too many duplication groups on file " + component.inputComponent() + ". Keep only the first " + MAX_CLONE_GROUP_PER_FILE + + " groups."); } - final BatchComponent component = batchComponentCache.get(inputFile); Iterable<org.sonar.batch.protocol.output.BatchReport.Duplication> reportDuplications = from(duplications) .limit(MAX_CLONE_GROUP_PER_FILE) .transform( @@ -65,14 +124,14 @@ public abstract class AbstractCpdEngine extends CpdEngine { @Override public BatchReport.Duplication apply(CloneGroup input) { - return toReportDuplication(component, inputFile, dupBuilder, blockBuilder, input); + return toReportDuplication(component, dupBuilder, blockBuilder, input); } }); publisher.getWriter().writeComponentDuplications(component.batchId(), reportDuplications); } - private Duplication toReportDuplication(BatchComponent component, InputFile inputFile, Duplication.Builder dupBuilder, Duplicate.Builder blockBuilder, CloneGroup input) { + private Duplication toReportDuplication(BatchComponent component, Duplication.Builder dupBuilder, Duplicate.Builder blockBuilder, CloneGroup input) { dupBuilder.clear(); ClonePart originBlock = input.getOriginPart(); blockBuilder.clear(); @@ -85,7 +144,8 @@ public abstract class AbstractCpdEngine extends CpdEngine { if (!duplicate.equals(originBlock)) { clonePartCount++; if (clonePartCount > MAX_CLONE_PART_PER_GROUP) { - LOG.warn("Too many duplication references on file " + inputFile.relativePath() + " for block at line " + originBlock.getStartLine() + ". Keep only the first " + LOG.warn("Too many duplication references on file " + component.inputComponent() + " for block at line " + + originBlock.getStartLine() + ". Keep only the first " + MAX_CLONE_PART_PER_GROUP + " references."); break; } @@ -105,5 +165,4 @@ public abstract class AbstractCpdEngine extends CpdEngine { } return dupBuilder.build(); } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdEngine.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdIndexer.java index 3b5cfa3b738..6d53f2a4946 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdEngine.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdIndexer.java @@ -21,14 +21,13 @@ package org.sonar.batch.cpd; import org.slf4j.Logger; import org.sonar.api.batch.BatchSide; -import org.sonar.api.batch.sensor.SensorContext; @BatchSide -public abstract class CpdEngine { +public abstract class CpdIndexer { abstract boolean isLanguageSupported(String language); - abstract void analyse(String language, SensorContext context); + abstract void index(String language); protected void logExclusions(String[] exclusions, Logger logger) { if (exclusions.length > 0) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdSensor.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdSensor.java index ed41921c9eb..63ee56ac819 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdSensor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cpd/CpdSensor.java @@ -35,12 +35,12 @@ public class CpdSensor implements Sensor { private static final Logger LOG = LoggerFactory.getLogger(CpdSensor.class); - private CpdEngine sonarEngine; - private CpdEngine sonarBridgeEngine; + private CpdIndexer sonarEngine; + private CpdIndexer sonarBridgeEngine; private Settings settings; private FileSystem fs; - public CpdSensor(JavaCpdEngine sonarEngine, DefaultCpdEngine sonarBridgeEngine, Settings settings, FileSystem fs) { + public CpdSensor(JavaCpdIndexer sonarEngine, DefaultCpdIndexer sonarBridgeEngine, Settings settings, FileSystem fs) { this.sonarEngine = sonarEngine; this.sonarBridgeEngine = sonarBridgeEngine; this.settings = settings; @@ -54,7 +54,7 @@ public class CpdSensor implements Sensor { } @VisibleForTesting - CpdEngine getEngine(String language) { + CpdIndexer getEngine(String language) { if (sonarEngine.isLanguageSupported(language)) { return sonarEngine; } @@ -87,13 +87,13 @@ public class CpdSensor implements Sensor { continue; } - CpdEngine engine = getEngine(language); + CpdIndexer engine = getEngine(language); if (!engine.isLanguageSupported(language)) { LOG.debug("Detection of duplicated code is not supported for {}", language); continue; } LOG.info("{} is used for {}", engine, language); - engine.analyse(language, context); + engine.index(language); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdEngine.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdIndexer.java index 2eb64fcc73d..bbc07472f95 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdEngine.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdIndexer.java @@ -20,16 +20,8 @@ package org.sonar.batch.cpd; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Predicate; import com.google.common.collect.Lists; -import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; @@ -38,39 +30,25 @@ import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.config.Settings; import org.sonar.batch.cpd.index.SonarDuplicationsIndex; -import org.sonar.batch.index.BatchComponentCache; -import org.sonar.batch.report.ReportPublisher; import org.sonar.duplications.block.Block; -import org.sonar.duplications.index.CloneGroup; import org.sonar.duplications.internal.pmd.TokenizerBridge; -import static com.google.common.collect.FluentIterable.from; +public class DefaultCpdIndexer extends CpdIndexer { -public class DefaultCpdEngine extends AbstractCpdEngine { - - private static final Logger LOG = LoggerFactory.getLogger(DefaultCpdEngine.class); - - /** - * Limit of time to analyse one file (in seconds). - */ - private static final int TIMEOUT = 5 * 60; + private static final Logger LOG = LoggerFactory.getLogger(DefaultCpdIndexer.class); private final CpdMappings mappings; private final FileSystem fs; private final Settings settings; - private final ReportPublisher publisher; - private final BatchComponentCache batchComponentCache; + private final SonarDuplicationsIndex index; - public DefaultCpdEngine(CpdMappings mappings, FileSystem fs, Settings settings, ReportPublisher publisher, BatchComponentCache batchComponentCache) { - super(publisher, batchComponentCache); + public DefaultCpdIndexer(CpdMappings mappings, FileSystem fs, Settings settings, SonarDuplicationsIndex index) { this.mappings = mappings; this.fs = fs; this.settings = settings; - this.publisher = publisher; - this.batchComponentCache = batchComponentCache; + this.index = index; } @Override @@ -79,7 +57,7 @@ public class DefaultCpdEngine extends AbstractCpdEngine { } @Override - public void analyse(String languageKey, SensorContext context) { + public void index(String languageKey) { CpdMapping mapping = mappings.getMapping(languageKey); if (mapping == null) { LOG.debug("No CpdMapping for language " + languageKey); @@ -98,41 +76,7 @@ public class DefaultCpdEngine extends AbstractCpdEngine { } // Create index - SonarDuplicationsIndex index = new SonarDuplicationsIndex(publisher, batchComponentCache, settings); populateIndex(languageKey, sourceFiles, mapping, index); - - // Detect - runCpdAnalysis(languageKey, context, sourceFiles, index); - } - - private void runCpdAnalysis(String languageKey, SensorContext context, List<InputFile> sourceFiles, SonarDuplicationsIndex index) { - Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(getMinimumTokens(languageKey)); - - ExecutorService executorService = Executors.newSingleThreadExecutor(); - try { - for (InputFile inputFile : sourceFiles) { - LOG.debug("Detection of duplications for {}", inputFile); - String resourceEffectiveKey = ((DefaultInputFile) inputFile).key(); - Collection<Block> fileBlocks = index.getByInputFile(inputFile, resourceEffectiveKey); - - List<CloneGroup> filtered; - try { - List<CloneGroup> duplications = executorService.submit(new JavaCpdEngine.Task(index, fileBlocks)).get(TIMEOUT, TimeUnit.SECONDS); - filtered = from(duplications) - .filter(minimumTokensPredicate) - .toList(); - } catch (TimeoutException e) { - filtered = Collections.emptyList(); - LOG.warn("Timeout during detection of duplications for " + inputFile, e); - } catch (InterruptedException | ExecutionException e) { - throw new IllegalStateException("Fail during detection of duplication for " + inputFile, e); - } - - saveDuplications(inputFile, filtered); - } - } finally { - executorService.shutdown(); - } } private void populateIndex(String languageKey, List<InputFile> sourceFiles, CpdMapping mapping, SonarDuplicationsIndex index) { @@ -165,14 +109,4 @@ public class DefaultCpdEngine extends AbstractCpdEngine { } } - @VisibleForTesting - int getMinimumTokens(String languageKey) { - int minimumTokens = settings.getInt("sonar.cpd." + languageKey + ".minimumTokens"); - if (minimumTokens == 0) { - minimumTokens = 100; - } - - return minimumTokens; - } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdEngine.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdIndexer.java index d83e049a45a..42fdf42e000 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdEngine.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdIndexer.java @@ -25,15 +25,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.io.Reader; -import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,44 +34,30 @@ import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.config.Settings; import org.sonar.batch.cpd.index.SonarDuplicationsIndex; -import org.sonar.batch.index.BatchComponentCache; -import org.sonar.batch.report.ReportPublisher; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.BlockChunker; -import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm; -import org.sonar.duplications.index.CloneGroup; -import org.sonar.duplications.index.CloneIndex; import org.sonar.duplications.java.JavaStatementBuilder; import org.sonar.duplications.java.JavaTokenProducer; import org.sonar.duplications.statement.Statement; import org.sonar.duplications.statement.StatementChunker; import org.sonar.duplications.token.TokenChunker; -public class JavaCpdEngine extends AbstractCpdEngine { +public class JavaCpdIndexer extends CpdIndexer { - private static final Logger LOG = LoggerFactory.getLogger(JavaCpdEngine.class); + private static final Logger LOG = LoggerFactory.getLogger(JavaCpdIndexer.class); private static final int BLOCK_SIZE = 10; - /** - * Limit of time to analyse one file (in seconds). - */ - private static final int TIMEOUT = 5 * 60; - private final FileSystem fs; private final Settings settings; - private final ReportPublisher publisher; - private final BatchComponentCache batchComponentCache; + private final SonarDuplicationsIndex index; - public JavaCpdEngine(FileSystem fs, Settings settings, ReportPublisher publisher, BatchComponentCache batchComponentCache) { - super(publisher, batchComponentCache); + public JavaCpdIndexer(FileSystem fs, Settings settings, SonarDuplicationsIndex index) { this.fs = fs; this.settings = settings; - this.publisher = publisher; - this.batchComponentCache = batchComponentCache; + this.index = index; } @Override @@ -88,7 +66,7 @@ public class JavaCpdEngine extends AbstractCpdEngine { } @Override - public void analyse(String languageKey, SensorContext context) { + public void index(String languageKey) { String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS); logExclusions(cpdExclusions, LOG); FilePredicates p = fs.predicates(); @@ -99,13 +77,10 @@ public class JavaCpdEngine extends AbstractCpdEngine { if (sourceFiles.isEmpty()) { return; } - SonarDuplicationsIndex index = createIndex(sourceFiles); - detect(index, context, sourceFiles); + createIndex(sourceFiles); } - private SonarDuplicationsIndex createIndex(Iterable<InputFile> sourceFiles) { - final SonarDuplicationsIndex index = new SonarDuplicationsIndex(publisher, batchComponentCache, settings); - + private void createIndex(Iterable<InputFile> sourceFiles) { TokenChunker tokenChunker = JavaTokenProducer.build(); StatementChunker statementChunker = JavaStatementBuilder.build(); BlockChunker blockChunker = new BlockChunker(BLOCK_SIZE); @@ -129,49 +104,5 @@ public class JavaCpdEngine extends AbstractCpdEngine { List<Block> blocks = blockChunker.chunk(resourceEffectiveKey, statements); index.insert(inputFile, blocks); } - - return index; } - - private void detect(SonarDuplicationsIndex index, org.sonar.api.batch.sensor.SensorContext context, List<InputFile> sourceFiles) { - ExecutorService executorService = Executors.newSingleThreadExecutor(); - try { - for (InputFile inputFile : sourceFiles) { - LOG.debug("Detection of duplications for {}", inputFile); - String resourceEffectiveKey = ((DefaultInputFile) inputFile).key(); - - Collection<Block> fileBlocks = index.getByInputFile(inputFile, resourceEffectiveKey); - - List<CloneGroup> clones; - try { - clones = executorService.submit(new Task(index, fileBlocks)).get(TIMEOUT, TimeUnit.SECONDS); - } catch (TimeoutException e) { - clones = Collections.emptyList(); - LOG.warn("Timeout during detection of duplications for " + inputFile, e); - } catch (InterruptedException | ExecutionException e) { - throw new IllegalStateException("Fail during detection of duplication for " + inputFile, e); - } - - saveDuplications(inputFile, clones); - } - } finally { - executorService.shutdown(); - } - } - - static class Task implements Callable<List<CloneGroup>> { - private final CloneIndex index; - private final Collection<Block> fileBlocks; - - public Task(CloneIndex index, Collection<Block> fileBlocks) { - this.index = index; - this.fileBlocks = fileBlocks; - } - - @Override - public List<CloneGroup> call() { - return SuffixTreeCloneDetectionAlgorithm.detect(index, fileBlocks); - } - } - } diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarDuplicationsIndex.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarDuplicationsIndex.java index f5872efdd50..1d55b45f008 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarDuplicationsIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarDuplicationsIndex.java @@ -22,6 +22,8 @@ package org.sonar.batch.cpd.index; import com.google.common.base.Function; import com.google.common.collect.Iterables; import java.util.Collection; +import java.util.Iterator; + import org.apache.commons.lang.StringUtils; import org.sonar.api.CoreProperties; import org.sonar.api.batch.fs.InputFile; @@ -34,6 +36,7 @@ import org.sonar.duplications.block.ByteArray; import org.sonar.duplications.index.AbstractCloneIndex; import org.sonar.duplications.index.CloneIndex; import org.sonar.duplications.index.PackedMemoryCloneIndex; +import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks; public class SonarDuplicationsIndex extends AbstractCloneIndex { @@ -76,7 +79,7 @@ public class SonarDuplicationsIndex extends AbstractCloneIndex { && StringUtils.isBlank(settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY)); } - public Collection<Block> getByInputFile(InputFile inputFile, String resourceKey) { + public Collection<Block> getByInputFile(String resourceKey) { return mem.getByResourceId(resourceKey); } @@ -95,4 +98,9 @@ public class SonarDuplicationsIndex extends AbstractCloneIndex { throw new UnsupportedOperationException(); } + @Override + public Iterator<ResourceBlocks> iterator() { + return mem.iterator(); + } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java index 3919f4d15af..8e2b7c36693 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java @@ -22,6 +22,7 @@ package org.sonar.batch.phases; import org.sonar.api.batch.SensorContext; import org.sonar.api.resources.Project; import org.sonar.batch.analysis.DefaultAnalysisMode; +import org.sonar.batch.cpd.CpdExecutor; import org.sonar.batch.events.BatchStepEvent; import org.sonar.batch.events.EventBus; import org.sonar.batch.index.DefaultIndex; @@ -52,12 +53,14 @@ public final class PhaseExecutor { private final DefaultAnalysisMode analysisMode; private final IssueTransition localIssueTracking; private final IssueCallback issueCallback; + private final CpdExecutor cpdExecutor; public PhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext, DefaultIndex index, EventBus eventBus, ReportPublisher reportPublisher, ProjectInitializer pi, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier, - IssueExclusionsLoader issueExclusionsLoader, DefaultAnalysisMode analysisMode, IssueTransition localIssueTracking, IssueCallback issueCallback) { + IssueExclusionsLoader issueExclusionsLoader, DefaultAnalysisMode analysisMode, IssueTransition localIssueTracking, IssueCallback issueCallback, + CpdExecutor cpdExecutor) { this.postJobsExecutor = postJobsExecutor; this.initializersExecutor = initializersExecutor; this.sensorsExecutor = sensorsExecutor; @@ -74,6 +77,7 @@ public final class PhaseExecutor { this.analysisMode = analysisMode; this.localIssueTracking = localIssueTracking; this.issueCallback = issueCallback; + this.cpdExecutor = cpdExecutor; } /** @@ -101,6 +105,8 @@ public final class PhaseExecutor { if (analysisMode.isIssues()) { localIssueTracking(); issuesCallback(); + } else { + computeDuplications(); } issuesReport(); publishReportJob(); @@ -110,6 +116,13 @@ public final class PhaseExecutor { eventBus.fireEvent(new ProjectAnalysisEvent(module, false)); } + private void computeDuplications() { + String stepName = "Computing duplications"; + eventBus.fireEvent(new BatchStepEvent(stepName, true)); + cpdExecutor.execute(); + eventBus.fireEvent(new BatchStepEvent(stepName, false)); + } + private void publishReportJob() { String stepName = "Publish report"; eventBus.fireEvent(new BatchStepEvent(stepName, true)); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 427f5c3fb7d..767f419920d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -41,6 +41,8 @@ import org.sonar.batch.bootstrap.ExtensionMatcher; import org.sonar.batch.bootstrap.ExtensionUtils; import org.sonar.batch.bootstrap.MetricProvider; import org.sonar.batch.cache.ProjectPersistentCacheProvider; +import org.sonar.batch.cpd.CpdExecutor; +import org.sonar.batch.cpd.index.SonarDuplicationsIndex; import org.sonar.batch.events.EventBus; import org.sonar.batch.index.BatchComponentCache; import org.sonar.batch.index.Caches; @@ -203,6 +205,10 @@ public class ProjectScanContainer extends ComponentContainer { CoveragePublisher.class, SourcePublisher.class, TestExecutionAndCoveragePublisher.class, + + // Cpd + CpdExecutor.class, + SonarDuplicationsIndex.class, ScanTaskObservers.class, UserRepositoryLoader.class); |