From 6074164392edd3db2dfdfd21d05cd56c19e2b0e6 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Fri, 25 Jul 2014 16:31:45 +0200 Subject: [PATCH] SONAR-5389 New duplication API --- plugins/sonar-cpd-plugin/pom.xml | 5 + .../java/org/sonar/plugins/cpd/CpdEngine.java | 5 +- .../org/sonar/plugins/cpd/CpdMappings.java | 51 ++++++ .../java/org/sonar/plugins/cpd/CpdPlugin.java | 7 +- .../java/org/sonar/plugins/cpd/CpdSensor.java | 26 +-- ...ridgeEngine.java => DefaultCpdEngine.java} | 70 ++++---- .../{SonarEngine.java => JavaCpdEngine.java} | 81 +++++---- .../sonar/plugins/cpd/index/IndexFactory.java | 33 +++- .../org/sonar/plugins/cpd/CpdPluginTest.java | 2 +- .../org/sonar/plugins/cpd/CpdSensorTest.java | 9 +- ...ineTest.java => DefaultCpdEngineTest.java} | 34 ++-- .../sonar/plugins/cpd/JavaCpdEngineTest.java | 155 +++++++++++++++++ .../sonar/plugins/cpd/SonarEngineTest.java | 160 ------------------ .../plugins/cpd/index/IndexFactoryTest.java | 22 ++- .../plugins/cpd/medium/CpdMediumTest.java | 119 +++++++++++++ .../java/org/sonar/xoo}/XooConstants.java | 2 +- .../main/java/org/sonar/xoo/XooPlugin.java | 22 ++- .../org/sonar/xoo}/lang/MeasureSensor.java | 6 +- .../sonar/xoo}/lang/ScmActivitySensor.java | 11 +- .../xoo}/lang/SymbolReferencesSensor.java | 8 +- .../xoo}/lang/SyntaxHighlightingSensor.java | 6 +- .../sonar/xoo/lang/XooTokenizerSensor.java | 84 +++++++++ .../rule/CreateIssueByInternalKeySensor.java | 7 +- .../xoo}/rule/OneIssueOnDirPerFileSensor.java | 7 +- .../xoo}/rule/OneIssuePerLineSensor.java | 7 +- .../java/org/sonar/xoo/XooPluginTest.java | 2 +- pom.xml | 6 + sonar-batch/pom.xml | 5 + .../bootstrap/BatchExtensionDictionnary.java | 10 ++ .../sonar/batch/duplication/BlockCache.java | 56 ++++++ .../DefaultDuplicationBuilder.java | 74 ++++++++ .../duplication/DefaultTokenBuilder.java | 77 +++++++++ .../DuplicationBlockValueCoder.java | 43 +++++ .../batch/duplication/DuplicationCache.java | 58 +++++++ .../batch/duplication/DuplicationGroup.java | 76 +++++++++ .../DuplicationGroupValueCoder.java | 54 ++++++ .../duplication/FileBlocksValueCoder.java | 69 ++++++++ .../batch/duplication/package-info.java} | 23 +-- .../SyntaxHighlightingDataBuilder.java | 3 - .../highlighting/SyntaxHighlightingRule.java | 3 - .../java/org/sonar/batch/index/Cache.java | 13 +- .../java/org/sonar/batch/index/Caches.java | 3 +- .../sonar/batch/index/MeasurePersister.java | 49 +++++- .../batch/mediumtest/BatchMediumTester.java | 14 ++ .../ProjectReferentialsProvider.java | 12 +- .../batch/scan/ProjectScanContainer.java | 6 + .../batch/scan/SensorContextAdaptor.java | 59 ++++++- .../org/sonar/batch/scan/SensorWrapper.java | 10 +- .../DefaultInputFileValueCoder.java | 76 +++++++++ .../batch/scan/filesystem/InputPathCache.java | 2 + .../batch/scan/measure/MeasureValueCoder.java | 24 ++- .../batch/scan2/AnalyzerMeasureCache.java | 7 +- .../batch/scan2/DefaultMeasureValueCoder.java | 64 +++++++ .../batch/scan2/DefaultSensorContext.java | 44 ++++- .../batch/scan2/ProjectScanContainer.java | 6 + .../sonar/batch/scan2/SensorsExecutor.java | 2 +- .../duplication/DuplicationCacheTest.java | 82 +++++++++ .../batch/index/MeasurePersisterTest.java | 9 +- .../mediumtest/fs/FileSystemMediumTest.java | 2 +- .../highlighting/HighlightingMediumTest.java | 2 +- .../mediumtest/issues/IssuesMediumTest.java | 2 +- .../issues/IssuesOnDirMediumTest.java | 2 +- .../measures/MeasuresMediumTest.java | 2 +- .../mediumtest/symbol/SymbolMediumTest.java | 2 +- .../mediumtest/xoo/plugin/XooPlugin.java | 53 ------ .../batch/scan/SensorContextAdapterTest.java | 5 +- .../scan/filesystem/InputPathCacheTest.java | 16 +- .../sonar/core/source/SnapshotDataTypes.java | 1 + .../api/batch/BatchExtensionDictionnary.java | 5 +- .../sonar/duplications/block/ByteArray.java | 8 +- .../sonar/duplications/block/FileBlocks.java | 45 +++++ .../sonar/duplications/index/CloneGroup.java | 13 +- .../internal/pmd/PmdBlockChunker.java | 21 ++- .../internal/pmd/TokenizerBridge.java | 2 +- .../duplications/internal/pmd/TokensLine.java | 3 +- .../java/org/sonar/api/CoreProperties.java | 4 +- .../internal/DeprecatedDefaultInputFile.java | 3 +- .../sonar/api/batch/sensor/SensorContext.java | 17 ++ .../duplication/DuplicationBuilder.java | 38 +++++ .../sensor/duplication/TokenBuilder.java | 40 +++++ .../sensor/duplication/package-info.java | 21 +++ .../java/org/sonar/api/measures/Measure.java | 1 + 82 files changed, 1822 insertions(+), 466 deletions(-) create mode 100644 plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdMappings.java rename plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/{SonarBridgeEngine.java => DefaultCpdEngine.java} (74%) rename plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/{SonarEngine.java => JavaCpdEngine.java} (74%) rename plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/{SonarBridgeEngineTest.java => DefaultCpdEngineTest.java} (70%) create mode 100644 plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java delete mode 100644 plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java create mode 100644 plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java rename {sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base => plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo}/XooConstants.java (95%) rename {sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin => plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo}/lang/MeasureSensor.java (96%) rename {sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin => plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo}/lang/ScmActivitySensor.java (97%) rename {sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin => plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo}/lang/SymbolReferencesSensor.java (94%) rename {sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin => plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo}/lang/SyntaxHighlightingSensor.java (95%) create mode 100644 plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/XooTokenizerSensor.java rename {sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin => plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo}/rule/CreateIssueByInternalKeySensor.java (92%) rename {sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin => plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo}/rule/OneIssueOnDirPerFileSensor.java (92%) rename {sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin => plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo}/rule/OneIssuePerLineSensor.java (94%) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/duplication/BlockCache.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultDuplicationBuilder.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultTokenBuilder.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationBlockValueCoder.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationCache.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroup.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroupValueCoder.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/duplication/FileBlocksValueCoder.java rename sonar-batch/src/{test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java => main/java/org/sonar/batch/duplication/package-info.java} (69%) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultInputFileValueCoder.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/duplication/DuplicationCacheTest.java delete mode 100644 sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java create mode 100644 sonar-duplications/src/main/java/org/sonar/duplications/block/FileBlocks.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/DuplicationBuilder.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/TokenBuilder.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/package-info.java diff --git a/plugins/sonar-cpd-plugin/pom.xml b/plugins/sonar-cpd-plugin/pom.xml index 2903ae5a4be..334122c4b91 100644 --- a/plugins/sonar-cpd-plugin/pom.xml +++ b/plugins/sonar-cpd-plugin/pom.xml @@ -54,6 +54,11 @@ mockito-core test + + org.codehaus.sonar.plugins + sonar-xoo-plugin + test + diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java index 7548282c87b..2f2a7633f40 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java @@ -21,14 +21,13 @@ package org.sonar.plugins.cpd; import org.slf4j.Logger; import org.sonar.api.BatchExtension; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.resources.Project; +import org.sonar.api.batch.sensor.SensorContext; public abstract class CpdEngine implements BatchExtension { abstract boolean isLanguageSupported(String language); - abstract void analyse(Project project, String language, SensorContext context); + abstract void analyse(String language, SensorContext context); protected void logExclusions(String[] exclusions, Logger logger) { if (exclusions.length > 0) { diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdMappings.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdMappings.java new file mode 100644 index 00000000000..3f0f9d83b8c --- /dev/null +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdMappings.java @@ -0,0 +1,51 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.cpd; + +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.CpdMapping; + +import javax.annotation.CheckForNull; + +public class CpdMappings implements BatchComponent { + + private final CpdMapping[] mappings; + + public CpdMappings(CpdMapping[] mappings) { + this.mappings = mappings; + } + + public CpdMappings() { + this(new CpdMapping[0]); + } + + @CheckForNull + public CpdMapping getMapping(String language) { + if (mappings != null) { + for (CpdMapping cpdMapping : mappings) { + if (cpdMapping.getLanguage().getKey().equals(language)) { + return cpdMapping; + } + } + } + return null; + } + +} diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java index 5f9df9621e1..defa9fe260e 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java @@ -35,7 +35,7 @@ public final class CpdPlugin extends SonarPlugin { public List getExtensions() { return ImmutableList.of( - PropertyDefinition.builder(CoreProperties.CPD_CROSS_RPOJECT) + PropertyDefinition.builder(CoreProperties.CPD_CROSS_PROJECT) .defaultValue(CoreProperties.CPD_CROSS_RPOJECT_DEFAULT_VALUE + "") .name("Cross project duplication detection") .description("SonarQube supports the detection of cross project duplications. Activating this property will slightly increase each SonarQube analysis time.") @@ -65,11 +65,12 @@ public final class CpdPlugin extends SonarPlugin { .build(), CpdSensor.class, + CpdMappings.class, SumDuplicationsDecorator.class, DuplicationDensityDecorator.class, IndexFactory.class, - SonarEngine.class, - SonarBridgeEngine.class); + JavaCpdEngine.class, + DefaultCpdEngine.class); } } diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java index 53a45416cb7..dfeabd65635 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java @@ -23,12 +23,14 @@ import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; -import org.sonar.api.batch.Sensor; -import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.Phase; import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.config.Settings; -import org.sonar.api.resources.Project; +@Phase(name = Phase.Name.POST) public class CpdSensor implements Sensor { private static final Logger LOG = LoggerFactory.getLogger(CpdSensor.class); @@ -38,15 +40,17 @@ public class CpdSensor implements Sensor { private Settings settings; private FileSystem fs; - public CpdSensor(SonarEngine sonarEngine, SonarBridgeEngine sonarBridgeEngine, Settings settings, FileSystem fs) { + public CpdSensor(JavaCpdEngine sonarEngine, DefaultCpdEngine sonarBridgeEngine, Settings settings, FileSystem fs) { this.sonarEngine = sonarEngine; this.sonarBridgeEngine = sonarBridgeEngine; this.settings = settings; this.fs = fs; } - public boolean shouldExecuteOnProject(Project project) { - return true; + @Override + public void describe(SensorDescriptor descriptor) { + descriptor.name("CPD Sensor"); + } @VisibleForTesting @@ -66,7 +70,8 @@ public class CpdSensor implements Sensor { return settings.getBoolean(CoreProperties.CPD_SKIP_PROPERTY); } - public void analyse(Project project, SensorContext context) { + @Override + public void execute(SensorContext context) { for (String language : fs.languages()) { if (isSkipped(language)) { LOG.info("Detection of duplicated code is skipped for {}", language); @@ -79,13 +84,8 @@ public class CpdSensor implements Sensor { continue; } LOG.info("{} is used for {}", engine, language); - engine.analyse(project, language, context); + engine.analyse(language, context); } } - @Override - public String toString() { - return getClass().getSimpleName(); - } - } diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DefaultCpdEngine.java similarity index 74% rename from plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java rename to plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DefaultCpdEngine.java index df4bc036115..33558ec898a 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DefaultCpdEngine.java @@ -27,22 +27,25 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; import org.sonar.api.batch.CpdMapping; -import org.sonar.api.batch.SensorContext; 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.DeprecatedDefaultInputFile; +import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; import org.sonar.api.utils.SonarException; +import org.sonar.batch.duplication.BlockCache; import org.sonar.duplications.DuplicationPredicates; import org.sonar.duplications.block.Block; +import org.sonar.duplications.block.FileBlocks; import org.sonar.duplications.index.CloneGroup; import org.sonar.duplications.internal.pmd.TokenizerBridge; import org.sonar.plugins.cpd.index.IndexFactory; import org.sonar.plugins.cpd.index.SonarDuplicationsIndex; -import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; @@ -51,9 +54,9 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -public class SonarBridgeEngine extends CpdEngine { +public class DefaultCpdEngine extends CpdEngine { - private static final Logger LOG = LoggerFactory.getLogger(SonarBridgeEngine.class); + private static final Logger LOG = LoggerFactory.getLogger(DefaultCpdEngine.class); /** * Limit of time to analyse one file (in seconds). @@ -61,28 +64,32 @@ public class SonarBridgeEngine extends CpdEngine { private static final int TIMEOUT = 5 * 60; private final IndexFactory indexFactory; - private final CpdMapping[] mappings; + private final CpdMappings mappings; private final FileSystem fs; private final Settings settings; + private final BlockCache duplicationCache; + private final Project project; - public SonarBridgeEngine(IndexFactory indexFactory, CpdMapping[] mappings, FileSystem fs, Settings settings) { + public DefaultCpdEngine(@Nullable Project project, IndexFactory indexFactory, CpdMappings mappings, FileSystem fs, Settings settings, BlockCache duplicationCache) { + this.project = project; this.indexFactory = indexFactory; this.mappings = mappings; this.fs = fs; this.settings = settings; + this.duplicationCache = duplicationCache; } - public SonarBridgeEngine(IndexFactory indexFactory, FileSystem fs, Settings settings) { - this(indexFactory, new CpdMapping[0], fs, settings); + public DefaultCpdEngine(IndexFactory indexFactory, CpdMappings mappings, FileSystem fs, Settings settings, BlockCache duplicationCache) { + this(null, indexFactory, mappings, fs, settings, duplicationCache); } @Override public boolean isLanguageSupported(String language) { - return getMapping(language) != null; + return true; } @Override - public void analyse(Project project, String languageKey, SensorContext context) { + public void analyse(String languageKey, SensorContext context) { String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS); logExclusions(cpdExclusions, LOG); FilePredicates p = fs.predicates(); @@ -90,29 +97,34 @@ public class SonarBridgeEngine extends CpdEngine { p.hasType(InputFile.Type.MAIN), p.hasLanguage(languageKey), p.doesNotMatchPathPatterns(cpdExclusions) - ))); + ))); if (sourceFiles.isEmpty()) { return; } - CpdMapping mapping = getMapping(languageKey); - if (mapping == null) { - return; - } + CpdMapping mapping = mappings.getMapping(languageKey); // Create index SonarDuplicationsIndex index = indexFactory.create(project, languageKey); - TokenizerBridge bridge = new TokenizerBridge(mapping.getTokenizer(), fs.encoding().name(), getBlockSize(project, languageKey)); + TokenizerBridge bridge = null; + if (mapping != null) { + bridge = new TokenizerBridge(mapping.getTokenizer(), fs.encoding().name(), getBlockSize(languageKey)); + } for (InputFile inputFile : sourceFiles) { LOG.debug("Populating index from {}", inputFile); String resourceEffectiveKey = ((DeprecatedDefaultInputFile) inputFile).key(); - List blocks = bridge.chunk(resourceEffectiveKey, inputFile.file()); - index.insert(inputFile, blocks); + FileBlocks fileBlocks = duplicationCache.byComponent(resourceEffectiveKey); + if (fileBlocks != null) { + index.insert(inputFile, fileBlocks.blocks()); + } else if (bridge != null) { + List blocks2 = bridge.chunk(resourceEffectiveKey, inputFile.file()); + index.insert(inputFile, blocks2); + } } // Detect - Predicate minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(getMinimumTokens(project, languageKey)); + Predicate minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(getMinimumTokens(languageKey)); ExecutorService executorService = Executors.newSingleThreadExecutor(); try { @@ -123,7 +135,7 @@ public class SonarBridgeEngine extends CpdEngine { Iterable filtered; try { - List duplications = executorService.submit(new SonarEngine.Task(index, fileBlocks)).get(TIMEOUT, TimeUnit.SECONDS); + List duplications = executorService.submit(new JavaCpdEngine.Task(index, fileBlocks)).get(TIMEOUT, TimeUnit.SECONDS); filtered = Iterables.filter(duplications, minimumTokensPredicate); } catch (TimeoutException e) { filtered = null; @@ -134,7 +146,7 @@ public class SonarBridgeEngine extends CpdEngine { throw new SonarException("Fail during detection of duplication for " + inputFile, e); } - SonarEngine.save(context, inputFile, filtered); + JavaCpdEngine.save(context, inputFile, filtered); } } finally { executorService.shutdown(); @@ -142,7 +154,7 @@ public class SonarBridgeEngine extends CpdEngine { } @VisibleForTesting - int getBlockSize(Project project, String languageKey) { + int getBlockSize(String languageKey) { int blockSize = settings.getInt("sonar.cpd." + languageKey + ".minimumLines"); if (blockSize == 0) { blockSize = getDefaultBlockSize(languageKey); @@ -162,7 +174,7 @@ public class SonarBridgeEngine extends CpdEngine { } @VisibleForTesting - int getMinimumTokens(Project project, String languageKey) { + int getMinimumTokens(String languageKey) { int minimumTokens = settings.getInt("sonar.cpd." + languageKey + ".minimumTokens"); if (minimumTokens == 0) { minimumTokens = settings.getInt(CoreProperties.CPD_MINIMUM_TOKENS_PROPERTY); @@ -174,16 +186,4 @@ public class SonarBridgeEngine extends CpdEngine { return minimumTokens; } - @CheckForNull - private CpdMapping getMapping(String language) { - if (mappings != null) { - for (CpdMapping cpdMapping : mappings) { - if (cpdMapping.getLanguage().getKey().equals(language)) { - return cpdMapping; - } - } - } - return null; - } - } diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/JavaCpdEngine.java similarity index 74% rename from plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java rename to plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/JavaCpdEngine.java index 130206427fc..2ea908dbb27 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/JavaCpdEngine.java @@ -23,21 +23,20 @@ package org.sonar.plugins.cpd; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; -import org.apache.commons.lang.StringEscapeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; -import org.sonar.api.batch.SensorContext; 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.DeprecatedDefaultInputFile; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.duplication.DuplicationBuilder; import org.sonar.api.config.Settings; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.PersistenceMode; import org.sonar.api.resources.Project; import org.sonar.api.utils.SonarException; +import org.sonar.batch.duplication.DefaultDuplicationBuilder; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.BlockChunker; import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm; @@ -53,6 +52,7 @@ import org.sonar.plugins.cpd.index.IndexFactory; import org.sonar.plugins.cpd.index.SonarDuplicationsIndex; import javax.annotation.Nullable; + import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStreamReader; @@ -61,11 +61,16 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.*; +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; -public class SonarEngine extends CpdEngine { +public class JavaCpdEngine extends CpdEngine { - private static final Logger LOG = LoggerFactory.getLogger(SonarEngine.class); + private static final Logger LOG = LoggerFactory.getLogger(JavaCpdEngine.class); private static final int BLOCK_SIZE = 10; @@ -77,20 +82,26 @@ public class SonarEngine extends CpdEngine { private final IndexFactory indexFactory; private final FileSystem fs; private final Settings settings; + private final Project project; - public SonarEngine(IndexFactory indexFactory, FileSystem fs, Settings settings) { + public JavaCpdEngine(@Nullable Project project, IndexFactory indexFactory, FileSystem fs, Settings settings) { + this.project = project; this.indexFactory = indexFactory; this.fs = fs; this.settings = settings; } + public JavaCpdEngine(IndexFactory indexFactory, FileSystem fs, Settings settings) { + this(null, indexFactory, fs, settings); + } + @Override public boolean isLanguageSupported(String language) { return "java".equals(language); } @Override - public void analyse(Project project, String languageKey, SensorContext context) { + public void analyse(String languageKey, SensorContext context) { String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS); logExclusions(cpdExclusions, LOG); FilePredicates p = fs.predicates(); @@ -98,7 +109,7 @@ public class SonarEngine extends CpdEngine { p.hasType(InputFile.Type.MAIN), p.hasLanguage(languageKey), p.doesNotMatchPathPatterns(cpdExclusions) - ))); + ))); if (sourceFiles.isEmpty()) { return; } @@ -106,7 +117,7 @@ public class SonarEngine extends CpdEngine { detect(index, context, sourceFiles); } - private SonarDuplicationsIndex createIndex(Project project, String language, Iterable sourceFiles) { + private SonarDuplicationsIndex createIndex(@Nullable Project project, String language, Iterable sourceFiles) { final SonarDuplicationsIndex index = indexFactory.create(project, language); TokenChunker tokenChunker = JavaTokenProducer.build(); @@ -136,7 +147,7 @@ public class SonarEngine extends CpdEngine { return index; } - private void detect(SonarDuplicationsIndex index, SensorContext context, List sourceFiles) { + private void detect(SonarDuplicationsIndex index, org.sonar.api.batch.sensor.SensorContext context, List sourceFiles) { ExecutorService executorService = Executors.newSingleThreadExecutor(); try { for (InputFile inputFile : sourceFiles) { @@ -178,13 +189,13 @@ public class SonarEngine extends CpdEngine { } } - static void save(SensorContext context, InputFile inputFile, @Nullable Iterable duplications) { + static void save(org.sonar.api.batch.sensor.SensorContext context, InputFile inputFile, @Nullable Iterable duplications) { if (duplications == null || Iterables.isEmpty(duplications)) { return; } // Calculate number of lines and blocks Set duplicatedLines = new HashSet(); - double duplicatedBlocks = 0; + int duplicatedBlocks = 0; for (CloneGroup clone : duplications) { ClonePart origin = clone.getOriginPart(); for (ClonePart part : clone.getCloneParts()) { @@ -197,30 +208,32 @@ public class SonarEngine extends CpdEngine { } } // Save - context.saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1.0); - context.saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, (double) duplicatedLines.size()); - context.saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, duplicatedBlocks); - - Measure data = new Measure(CoreMetrics.DUPLICATIONS_DATA, toXml(duplications)) - .setPersistenceMode(PersistenceMode.DATABASE); - context.saveMeasure(inputFile, data); - } - - private static String toXml(Iterable duplications) { - StringBuilder xml = new StringBuilder(); - xml.append(""); + context.addMeasure(context.measureBuilder() + .forMetric(CoreMetrics.DUPLICATED_FILES) + .onFile(inputFile) + .withValue(1) + .build()); + context.addMeasure(context.measureBuilder() + .forMetric(CoreMetrics.DUPLICATED_LINES) + .onFile(inputFile) + .withValue(duplicatedLines.size()) + .build()); + context.addMeasure(context.measureBuilder() + .forMetric(CoreMetrics.DUPLICATED_BLOCKS) + .onFile(inputFile) + .withValue(duplicatedBlocks) + .build()); + + DuplicationBuilder builder = context.duplicationBuilder(inputFile); for (CloneGroup duplication : duplications) { - xml.append(""); + builder.originBlock(duplication.getOriginPart().getStartLine(), duplication.getOriginPart().getEndLine()); for (ClonePart part : duplication.getCloneParts()) { - xml.append(""); + if (!part.equals(duplication.getOriginPart())) { + ((DefaultDuplicationBuilder) builder).isDuplicatedBy(part.getResourceId(), part.getStartLine(), part.getEndLine()); + } } - xml.append(""); } - xml.append(""); - return xml.toString(); + builder.done(); } } diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java index f6f8981f6c1..6d5a7f1201d 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/IndexFactory.java @@ -23,29 +23,41 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.sonar.api.BatchExtension; +import org.sonar.api.BatchComponent; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; +import org.sonar.batch.bootstrap.AnalysisMode; import org.sonar.batch.index.ResourcePersister; import org.sonar.core.duplication.DuplicationDao; -public class IndexFactory implements BatchExtension { +import javax.annotation.Nullable; + +public class IndexFactory implements BatchComponent { private static final Logger LOG = LoggerFactory.getLogger(IndexFactory.class); private final Settings settings; private final ResourcePersister resourcePersister; private final DuplicationDao dao; + private final AnalysisMode mode; - public IndexFactory(Settings settings, ResourcePersister resourcePersister, DuplicationDao dao) { + public IndexFactory(AnalysisMode mode, Settings settings, @Nullable ResourcePersister resourcePersister, @Nullable DuplicationDao dao) { + this.mode = mode; this.settings = settings; this.resourcePersister = resourcePersister; this.dao = dao; } - public SonarDuplicationsIndex create(Project project, String languageKey) { - if (verifyCrossProject(project, LOG)) { + /** + * Used by new sensor mode + */ + public IndexFactory(AnalysisMode mode, Settings settings) { + this(mode, settings, null, null); + } + + public SonarDuplicationsIndex create(@Nullable Project project, String languageKey) { + if (verifyCrossProject(project, LOG) && dao != null && resourcePersister != null) { return new SonarDuplicationsIndex(new DbDuplicationsIndex(resourcePersister, project, dao, languageKey)); } return new SonarDuplicationsIndex(); @@ -55,11 +67,14 @@ public class IndexFactory implements BatchExtension { boolean verifyCrossProject(Project project, Logger logger) { boolean crossProject = false; - if (settings.getBoolean(CoreProperties.CPD_CROSS_RPOJECT)) { - if (settings.getBoolean(CoreProperties.DRY_RUN)) { - logger.info("Cross-project analysis disabled. Not supported on dry runs."); - } else if (StringUtils.isNotBlank(project.getBranch())) { + if (settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT)) { + if (mode.isPreview()) { + logger.info("Cross-project analysis disabled. Not supported in preview mode."); + } else if (StringUtils.isNotBlank(settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY))) { logger.info("Cross-project analysis disabled. Not supported on project branches."); + } else if (project == null) { + // New sensor mode + logger.info("Cross-project analysis disabled. Not supported in new sensor mode."); } else { logger.info("Cross-project analysis enabled"); crossProject = true; diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdPluginTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdPluginTest.java index 74cb7f5fa5b..7d1bbeb4a5c 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdPluginTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdPluginTest.java @@ -27,6 +27,6 @@ public class CpdPluginTest { @Test public void getExtensions() { - assertThat(new CpdPlugin().getExtensions()).hasSize(9); + assertThat(new CpdPlugin().getExtensions()).hasSize(10); } } diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java index 89128b77a8a..7724fa6f725 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java @@ -25,6 +25,7 @@ import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.config.PropertyDefinitions; import org.sonar.api.config.Settings; import org.sonar.api.resources.Java; +import org.sonar.batch.duplication.BlockCache; import org.sonar.plugins.cpd.index.IndexFactory; import static org.fest.assertions.Assertions.assertThat; @@ -32,16 +33,16 @@ import static org.mockito.Mockito.mock; public class CpdSensorTest { - SonarEngine sonarEngine; - SonarBridgeEngine sonarBridgeEngine; + JavaCpdEngine sonarEngine; + DefaultCpdEngine sonarBridgeEngine; CpdSensor sensor; Settings settings; @Before public void setUp() { IndexFactory indexFactory = mock(IndexFactory.class); - sonarEngine = new SonarEngine(indexFactory, null, null); - sonarBridgeEngine = new SonarBridgeEngine(indexFactory, null, null); + sonarEngine = new JavaCpdEngine(indexFactory, null, null); + sonarBridgeEngine = new DefaultCpdEngine(indexFactory, new CpdMappings(), null, null, mock(BlockCache.class)); settings = new Settings(new PropertyDefinitions(CpdPlugin.class)); DefaultFileSystem fs = new DefaultFileSystem(); diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarBridgeEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/DefaultCpdEngineTest.java similarity index 70% rename from plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarBridgeEngineTest.java rename to plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/DefaultCpdEngineTest.java index 3923b53fe21..924aa9193c8 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarBridgeEngineTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/DefaultCpdEngineTest.java @@ -25,6 +25,7 @@ import org.slf4j.Logger; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; +import org.sonar.batch.duplication.BlockCache; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.anyString; @@ -33,15 +34,15 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -public class SonarBridgeEngineTest { +public class DefaultCpdEngineTest { - private SonarBridgeEngine engine; + private DefaultCpdEngine engine; private Settings settings; @Before public void init() { settings = new Settings(); - engine = new SonarBridgeEngine(null, null, null, settings); + engine = new DefaultCpdEngine(null, null, null, settings, mock(BlockCache.class)); } @Test @@ -61,53 +62,46 @@ public class SonarBridgeEngineTest { @Test public void shouldReturnDefaultBlockSize() { - assertThat(SonarBridgeEngine.getDefaultBlockSize("cobol")).isEqualTo(30); - assertThat(SonarBridgeEngine.getDefaultBlockSize("natur")).isEqualTo(20); - assertThat(SonarBridgeEngine.getDefaultBlockSize("abap")).isEqualTo(20); - assertThat(SonarBridgeEngine.getDefaultBlockSize("other")).isEqualTo(10); + assertThat(DefaultCpdEngine.getDefaultBlockSize("cobol")).isEqualTo(30); + assertThat(DefaultCpdEngine.getDefaultBlockSize("natur")).isEqualTo(20); + assertThat(DefaultCpdEngine.getDefaultBlockSize("abap")).isEqualTo(20); + assertThat(DefaultCpdEngine.getDefaultBlockSize("other")).isEqualTo(10); } @Test public void defaultBlockSize() { - Project project = newProject("foo"); - assertThat(engine.getBlockSize(project, "java")).isEqualTo(10); + assertThat(engine.getBlockSize("java")).isEqualTo(10); } @Test public void blockSizeForCobol() { - Project project = newProject("foo"); settings.setProperty("sonar.cpd.cobol.minimumLines", "42"); - assertThat(engine.getBlockSize(project, "cobol")).isEqualTo(42); + assertThat(engine.getBlockSize("cobol")).isEqualTo(42); } @Test public void defaultMinimumTokens() { - Project project = newProject("foo"); - - assertThat(engine.getMinimumTokens(project, "java")).isEqualTo(CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE); + assertThat(engine.getMinimumTokens("java")).isEqualTo(CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE); } @Test public void generalMinimumTokens() { - Project project = newProject("foo"); settings.setProperty("sonar.cpd.minimumTokens", 33); - assertThat(engine.getMinimumTokens(project, "java")).isEqualTo(33); + assertThat(engine.getMinimumTokens("java")).isEqualTo(33); } @Test public void minimumTokensByLanguage() { - Project javaProject = newProject("foo"); settings.setProperty("sonar.cpd.java.minimumTokens", "42"); settings.setProperty("sonar.cpd.php.minimumTokens", "33"); - assertThat(engine.getMinimumTokens(javaProject, "java")).isEqualTo(42); + assertThat(engine.getMinimumTokens("java")).isEqualTo(42); - Project phpProject = newProject("foo"); settings.setProperty("sonar.cpd.java.minimumTokens", "42"); settings.setProperty("sonar.cpd.php.minimumTokens", "33"); - assertThat(engine.getMinimumTokens(phpProject, "php")).isEqualTo(33); + assertThat(engine.getMinimumTokens("php")).isEqualTo(33); } private static Project newProject(String key) { diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java new file mode 100644 index 00000000000..d0c50a99b4c --- /dev/null +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/JavaCpdEngineTest.java @@ -0,0 +1,155 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.cpd; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.duplications.index.CloneGroup; +import org.sonar.duplications.index.ClonePart; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +public class JavaCpdEngineTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + SensorContext context = mock(SensorContext.class); + DeprecatedDefaultInputFile inputFile; + + @Before + public void before() throws IOException { + when(context.measureBuilder()).thenReturn(new DefaultMeasureBuilder()); + inputFile = new DeprecatedDefaultInputFile("src/main/java/Foo.java"); + inputFile.setFile(temp.newFile("Foo.java")); + } + + @SuppressWarnings("unchecked") + @Test + public void testNothingToSave() { + JavaCpdEngine.save(context, inputFile, null); + JavaCpdEngine.save(context, inputFile, Collections.EMPTY_LIST); + + verifyZeroInteractions(context); + } + + @Test + public void testOneSimpleDuplicationBetweenTwoFiles() { + List groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214))); + JavaCpdEngine.save(context, inputFile, groups); + + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(1).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(200).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATIONS_DATA).onFile(inputFile).withValue("" + + "" + + "" + + "").build()); + } + + @Test + public void testDuplicationOnSameFile() throws Exception { + List groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key1", 0, 215, 414))); + JavaCpdEngine.save(context, inputFile, groups); + + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(2).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(400).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATIONS_DATA).onFile(inputFile).withValue("" + + "" + + "" + + "").build()); + + } + + @Test + public void testOneDuplicatedGroupInvolvingMoreThanTwoFiles() throws Exception { + List groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214), new ClonePart("key3", 0, 25, 224))); + JavaCpdEngine.save(context, inputFile, groups); + + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(1).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(200).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATIONS_DATA).onFile(inputFile).withValue("" + + "" + + "" + + "" + + "").build()); + + } + + @Test + public void testTwoDuplicatedGroupsInvolvingThreeFiles() throws Exception { + List groups = Arrays.asList( + newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214)), + newCloneGroup(new ClonePart("key1", 0, 15, 214), new ClonePart("key3", 0, 15, 214))); + JavaCpdEngine.save(context, inputFile, groups); + + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_FILES).onFile(inputFile).withValue(1).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_BLOCKS).onFile(inputFile).withValue(2).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATED_LINES).onFile(inputFile).withValue(210).build()); + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATIONS_DATA).onFile(inputFile).withValue("" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "").build()); + + } + + @Test + public void shouldEscapeXmlEntities() throws IOException { + InputFile csharpFile = new DeprecatedDefaultInputFile("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs") + .setFile(temp.newFile("SubsRedsDelivery.cs")); + List groups = Arrays.asList(newCloneGroup( + new ClonePart("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs", 0, 5, 204), + new ClonePart("Loads/File Loads/Subs & Reds/SubsRedsDelivery2.cs", 0, 15, 214))); + JavaCpdEngine.save(context, csharpFile, groups); + + verify(context).addMeasure(new DefaultMeasureBuilder().forMetric(CoreMetrics.DUPLICATIONS_DATA).onFile(csharpFile).withValue("" + + "" + + "" + + "").build()); + } + + private CloneGroup newCloneGroup(ClonePart... parts) { + return CloneGroup.builder().setLength(0).setOrigin(parts[0]).setParts(Arrays.asList(parts)).build(); + } + +} diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java deleted file mode 100644 index c8551a74166..00000000000 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.plugins.cpd; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.test.IsMeasure; -import org.sonar.duplications.index.CloneGroup; -import org.sonar.duplications.index.ClonePart; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.*; - -public class SonarEngineTest { - - @Rule - public TemporaryFolder temp = new TemporaryFolder(); - - SensorContext context = mock(SensorContext.class); - DeprecatedDefaultInputFile inputFile; - - @Before - public void before() throws IOException { - inputFile = new DeprecatedDefaultInputFile("src/main/java/Foo.java"); - inputFile.setFile(temp.newFile("Foo.java")); - } - - @SuppressWarnings("unchecked") - @Test - public void testNothingToSave() { - SonarEngine.save(context, inputFile, null); - SonarEngine.save(context, inputFile, Collections.EMPTY_LIST); - - verifyZeroInteractions(context); - } - - @Test - public void testOneSimpleDuplicationBetweenTwoFiles() { - List groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214))); - SonarEngine.save(context, inputFile, groups); - - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 200d); - verify(context).saveMeasure( - eq(inputFile), - argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "" - + "" - + "" - + ""))); - } - - @Test - public void testDuplicationOnSameFile() throws Exception { - List groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key1", 0, 215, 414))); - SonarEngine.save(context, inputFile, groups); - - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 400d); - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 2d); - verify(context).saveMeasure( - eq(inputFile), - argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "" - + "" - + "" - + ""))); - } - - @Test - public void testOneDuplicatedGroupInvolvingMoreThanTwoFiles() throws Exception { - List groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214), new ClonePart("key3", 0, 25, 224))); - SonarEngine.save(context, inputFile, groups); - - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 200d); - verify(context).saveMeasure( - eq(inputFile), - argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "" - + "" - + "" - + "" - + ""))); - } - - @Test - public void testTwoDuplicatedGroupsInvolvingThreeFiles() throws Exception { - List groups = Arrays.asList( - newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214)), - newCloneGroup(new ClonePart("key1", 0, 15, 214), new ClonePart("key3", 0, 15, 214))); - SonarEngine.save(context, inputFile, groups); - - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 2d); - verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 210d); - verify(context).saveMeasure( - eq(inputFile), - argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + "" - + ""))); - } - - @Test - public void shouldEscapeXmlEntities() throws IOException { - InputFile csharpFile = new DeprecatedDefaultInputFile("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs") - .setFile(temp.newFile("SubsRedsDelivery.cs")); - List groups = Arrays.asList(newCloneGroup( - new ClonePart("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs", 0, 5, 204), - new ClonePart("Loads/File Loads/Subs & Reds/SubsRedsDelivery2.cs", 0, 15, 214))); - SonarEngine.save(context, csharpFile, groups); - - verify(context).saveMeasure( - eq(csharpFile), - argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "" - + "" - + "" - + ""))); - } - - private CloneGroup newCloneGroup(ClonePart... parts) { - return CloneGroup.builder().setLength(0).setOrigin(parts[0]).setParts(Arrays.asList(parts)).build(); - } - -} diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java index 45510dc40ac..7b9fa524fec 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/IndexFactoryTest.java @@ -25,12 +25,14 @@ import org.slf4j.Logger; import org.sonar.api.CoreProperties; import org.sonar.api.config.Settings; import org.sonar.api.resources.Project; +import org.sonar.batch.bootstrap.AnalysisMode; import org.sonar.batch.index.ResourcePersister; import org.sonar.core.duplication.DuplicationDao; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class IndexFactoryTest { @@ -38,41 +40,43 @@ public class IndexFactoryTest { Settings settings; IndexFactory factory; Logger logger; + private AnalysisMode analysisMode; @Before public void setUp() { project = new Project("foo"); settings = new Settings(); - factory = new IndexFactory(settings, mock(ResourcePersister.class), mock(DuplicationDao.class)); + analysisMode = mock(AnalysisMode.class); + factory = new IndexFactory(analysisMode, settings, mock(ResourcePersister.class), mock(DuplicationDao.class)); logger = mock(Logger.class); } @Test public void crossProjectEnabled() { - settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true"); + settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true"); assertThat(factory.verifyCrossProject(project, logger)).isTrue(); verify(logger).info("Cross-project analysis enabled"); } @Test public void noCrossProjectWithBranch() { - settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true"); - project.setBranch("branch"); + settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true"); + settings.setProperty(CoreProperties.PROJECT_BRANCH_PROPERTY, "branch"); assertThat(factory.verifyCrossProject(project, logger)).isFalse(); verify(logger).info("Cross-project analysis disabled. Not supported on project branches."); } @Test - public void cross_project_should_be_disabled_on_dry_run() { - settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "true"); - settings.setProperty(CoreProperties.DRY_RUN, "true"); + public void cross_project_should_be_disabled_on_preview() { + when(analysisMode.isPreview()).thenReturn(true); + settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "true"); assertThat(factory.verifyCrossProject(project, logger)).isFalse(); - verify(logger).info("Cross-project analysis disabled. Not supported on dry runs."); + verify(logger).info("Cross-project analysis disabled. Not supported in preview mode."); } @Test public void crossProjectDisabled() { - settings.setProperty(CoreProperties.CPD_CROSS_RPOJECT, "false"); + settings.setProperty(CoreProperties.CPD_CROSS_PROJECT, "false"); assertThat(factory.verifyCrossProject(project, logger)).isFalse(); verify(logger).info("Cross-project analysis disabled"); } diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java new file mode 100644 index 00000000000..21876b8f3d5 --- /dev/null +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/medium/CpdMediumTest.java @@ -0,0 +1,119 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.plugins.cpd.medium; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.io.FileUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.batch.duplication.DuplicationGroup; +import org.sonar.batch.mediumtest.BatchMediumTester; +import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; +import org.sonar.plugins.cpd.CpdPlugin; +import org.sonar.xoo.XooPlugin; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import static org.fest.assertions.Assertions.assertThat; + +public class CpdMediumTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + public BatchMediumTester tester = BatchMediumTester.builder() + .registerPlugin("xoo", new XooPlugin()) + .registerPlugin("cpd", new CpdPlugin()) + .addDefaultQProfile("xoo", "Sonar Way") + .bootstrapProperties(ImmutableMap.of("sonar.analysis.mode", "sensor")) + .build(); + + private File baseDir; + + private ImmutableMap.Builder builder; + + @Before + public void prepare() throws IOException { + tester.start(); + + baseDir = temp.newFolder(); + + builder = ImmutableMap.builder() + .put("sonar.task", "scan") + .put("sonar.projectBaseDir", baseDir.getAbsolutePath()) + .put("sonar.projectKey", "com.foo.project") + .put("sonar.projectName", "Foo Project") + .put("sonar.projectVersion", "1.0-SNAPSHOT") + .put("sonar.projectDescription", "Description of Foo Project"); + } + + @After + public void stop() { + tester.stop(); + } + + @Test + public void testDuplications() throws IOException { + File srcDir = new File(baseDir, "src"); + srcDir.mkdir(); + + String duplicatedStuff = "Sample xoo\ncontent\nfoo\nbar\ntoto\ntiti\nfoo\nbar\ntoto\ntiti\nbar\ntoto\ntiti\nfoo\nbar\ntoto\ntiti"; + + File xooFile1 = new File(srcDir, "sample1.xoo"); + FileUtils.write(xooFile1, duplicatedStuff); + + File xooFile2 = new File(srcDir, "sample2.xoo"); + FileUtils.write(xooFile2, duplicatedStuff); + + TaskResult result = tester.newTask() + .properties(builder + .put("sonar.sources", "src") + .put("sonar.cpd.xoo.minimumTokens", "10") + .put("sonar.verbose", "true") + .build()) + .start(); + + assertThat(result.inputFiles()).hasSize(2); + + // 4 measures per file + assertThat(result.measures()).hasSize(6); + + InputFile inputFile = result.inputFiles().get(0); + // One clone group + List duplicationGroups = result.duplicationsFor(inputFile); + assertThat(duplicationGroups).hasSize(1); + + DuplicationGroup cloneGroup = duplicationGroups.get(0); + assertThat(cloneGroup.duplicates()).hasSize(1); + assertThat(cloneGroup.originBlock().startLine()).isEqualTo(1); + assertThat(cloneGroup.originBlock().length()).isEqualTo(17); + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/XooConstants.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooConstants.java similarity index 95% rename from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/XooConstants.java rename to plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooConstants.java index c59091fb0f6..62f7d2ba782 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/XooConstants.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooConstants.java @@ -17,7 +17,7 @@ * 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.mediumtest.xoo.plugin.base; +package org.sonar.xoo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java index 95b65dd31a3..3477fab825c 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java @@ -20,6 +20,14 @@ package org.sonar.xoo; import org.sonar.api.SonarPlugin; +import org.sonar.xoo.lang.MeasureSensor; +import org.sonar.xoo.lang.ScmActivitySensor; +import org.sonar.xoo.lang.SymbolReferencesSensor; +import org.sonar.xoo.lang.SyntaxHighlightingSensor; +import org.sonar.xoo.lang.XooTokenizerSensor; +import org.sonar.xoo.rule.CreateIssueByInternalKeySensor; +import org.sonar.xoo.rule.OneIssueOnDirPerFileSensor; +import org.sonar.xoo.rule.OneIssuePerLineSensor; import org.sonar.xoo.rule.XooQualityProfile; import org.sonar.xoo.rule.XooRulesDefinition; @@ -39,7 +47,19 @@ public class XooPlugin extends SonarPlugin { return Arrays.asList( Xoo.class, XooRulesDefinition.class, - XooQualityProfile.class); + XooQualityProfile.class, + + // sensors + MeasureSensor.class, + ScmActivitySensor.class, + SyntaxHighlightingSensor.class, + SymbolReferencesSensor.class, + XooTokenizerSensor.class, + + OneIssuePerLineSensor.class, + OneIssueOnDirPerFileSensor.class, + CreateIssueByInternalKeySensor.class + ); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/MeasureSensor.java similarity index 96% rename from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java rename to plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/MeasureSensor.java index c28ebd5a05c..56e013b3716 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/MeasureSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/MeasureSensor.java @@ -17,7 +17,7 @@ * 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.mediumtest.xoo.plugin.lang; +package org.sonar.xoo.lang; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; @@ -29,8 +29,8 @@ import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.measure.Measure; import org.sonar.api.batch.sensor.measure.MeasureBuilder; import org.sonar.api.measures.CoreMetrics; -import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; -import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants; +import org.sonar.xoo.Xoo; +import org.sonar.xoo.XooConstants; import java.io.File; import java.io.IOException; diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/ScmActivitySensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/ScmActivitySensor.java similarity index 97% rename from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/ScmActivitySensor.java rename to plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/ScmActivitySensor.java index b3352dba62a..12663b0b697 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/ScmActivitySensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/ScmActivitySensor.java @@ -17,11 +17,7 @@ * 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.mediumtest.xoo.plugin.lang; - -import org.sonar.api.batch.sensor.Sensor; -import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.batch.sensor.SensorDescriptor; +package org.sonar.xoo.lang; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; @@ -31,11 +27,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.FileLinesContextFactory; import org.sonar.api.utils.DateUtils; -import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; +import org.sonar.xoo.Xoo; import java.io.File; import java.io.IOException; diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SymbolReferencesSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SymbolReferencesSensor.java similarity index 94% rename from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SymbolReferencesSensor.java rename to plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SymbolReferencesSensor.java index 91fa61e5c78..c8cafc2d705 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SymbolReferencesSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SymbolReferencesSensor.java @@ -17,7 +17,7 @@ * 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.mediumtest.xoo.plugin.lang; +package org.sonar.xoo.lang; import com.google.common.base.Splitter; import org.apache.commons.io.FileUtils; @@ -29,8 +29,8 @@ import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.symbol.Symbol; import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder; import org.sonar.api.measures.CoreMetrics; -import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; -import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants; +import org.sonar.xoo.Xoo; +import org.sonar.xoo.XooConstants; import java.io.File; import java.io.IOException; @@ -83,7 +83,7 @@ public class SymbolReferencesSensor implements Sensor { @Override public void describe(SensorDescriptor descriptor) { descriptor - .name("Xoo Highlighting Sensor") + .name("Xoo Symbol Reference Sensor") .provides(CoreMetrics.LINES) .workOnLanguages(Xoo.KEY) .workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST); diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SyntaxHighlightingSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SyntaxHighlightingSensor.java similarity index 95% rename from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SyntaxHighlightingSensor.java rename to plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SyntaxHighlightingSensor.java index 5b78759dbb9..0ae23954442 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/lang/SyntaxHighlightingSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/SyntaxHighlightingSensor.java @@ -17,7 +17,7 @@ * 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.mediumtest.xoo.plugin.lang; +package org.sonar.xoo.lang; import com.google.common.base.Splitter; import org.apache.commons.io.FileUtils; @@ -28,8 +28,8 @@ import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder; import org.sonar.api.measures.CoreMetrics; -import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; -import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants; +import org.sonar.xoo.Xoo; +import org.sonar.xoo.XooConstants; import java.io.File; import java.io.IOException; diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/XooTokenizerSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/XooTokenizerSensor.java new file mode 100644 index 00000000000..1ee33e560ef --- /dev/null +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/XooTokenizerSensor.java @@ -0,0 +1,84 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.xoo.lang; + +import com.google.common.base.Splitter; +import com.google.common.collect.Lists; +import org.apache.commons.io.FileUtils; +import org.sonar.api.CoreProperties; +import org.sonar.api.batch.fs.FilePredicates; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.batch.sensor.duplication.TokenBuilder; +import org.sonar.xoo.Xoo; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +/** + * Parse files *.xoo.highlighting + */ +public class XooTokenizerSensor implements Sensor { + + private void computeTokens(InputFile inputFile, SensorContext context) { + TokenBuilder tokenBuilder = context.tokenBuilder(inputFile); + File ioFile = inputFile.file(); + int lineId = 0; + try { + for (String line : FileUtils.readLines(ioFile)) { + lineId++; + for (String token : Splitter.on(" ").split(line)) { + tokenBuilder.addToken(lineId, token); + } + } + tokenBuilder.done(); + } catch (IOException e) { + throw new IllegalStateException("Unable to read file " + ioFile, e); + } + } + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor + .name("Xoo Tokenizer Sensor") + .workOnLanguages(Xoo.KEY) + .workOnFileTypes(InputFile.Type.MAIN); + } + + @Override + public void execute(SensorContext context) { + String[] cpdExclusions = context.settings().getStringArray(CoreProperties.CPD_EXCLUSIONS); + FilePredicates p = context.fileSystem().predicates(); + List sourceFiles = Lists.newArrayList(context.fileSystem().inputFiles(p.and( + p.hasType(InputFile.Type.MAIN), + p.hasLanguage(Xoo.KEY), + p.doesNotMatchPathPatterns(cpdExclusions) + ))); + if (sourceFiles.isEmpty()) { + return; + } + for (InputFile file : sourceFiles) { + computeTokens(file, context); + } + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/CreateIssueByInternalKeySensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java similarity index 92% rename from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/CreateIssueByInternalKeySensor.java rename to plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java index 1ee150a5c01..c3e7d2641e6 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/CreateIssueByInternalKeySensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/CreateIssueByInternalKeySensor.java @@ -17,15 +17,15 @@ * 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.mediumtest.xoo.plugin.rule; +package org.sonar.xoo.rule; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.rule.ActiveRule; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; -import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; -import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants; +import org.sonar.xoo.Xoo; +import org.sonar.xoo.XooConstants; public class CreateIssueByInternalKeySensor implements Sensor { @@ -36,6 +36,7 @@ public class CreateIssueByInternalKeySensor implements Sensor { descriptor .name("CreateIssueByInternalKeySensor") .workOnLanguages(Xoo.KEY) + .createIssuesForRuleRepositories(XooConstants.REPOSITORY_KEY) .workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssueOnDirPerFileSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java similarity index 92% rename from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssueOnDirPerFileSensor.java rename to plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java index 2a02ebef9dd..c2478830975 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssueOnDirPerFileSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssueOnDirPerFileSensor.java @@ -17,7 +17,7 @@ * 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.mediumtest.xoo.plugin.rule; +package org.sonar.xoo.rule; import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; @@ -25,8 +25,8 @@ import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; import org.sonar.api.rule.RuleKey; -import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; -import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants; +import org.sonar.xoo.Xoo; +import org.sonar.xoo.XooConstants; public class OneIssueOnDirPerFileSensor implements Sensor { @@ -37,6 +37,7 @@ public class OneIssueOnDirPerFileSensor implements Sensor { descriptor .name("One Issue On Dir Per File") .workOnLanguages(Xoo.KEY) + .createIssuesForRuleRepositories(XooConstants.REPOSITORY_KEY) .workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java similarity index 94% rename from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java rename to plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java index 2a811068e54..bc0697b64b4 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/rule/OneIssuePerLineSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java @@ -17,7 +17,7 @@ * 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.mediumtest.xoo.plugin.rule; +package org.sonar.xoo.rule; import org.slf4j.LoggerFactory; import org.sonar.api.batch.fs.InputFile; @@ -28,8 +28,8 @@ import org.sonar.api.batch.sensor.issue.IssueBuilder; import org.sonar.api.batch.sensor.measure.Measure; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.rule.RuleKey; -import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; -import org.sonar.batch.mediumtest.xoo.plugin.base.XooConstants; +import org.sonar.xoo.Xoo; +import org.sonar.xoo.XooConstants; public class OneIssuePerLineSensor implements Sensor { @@ -43,6 +43,7 @@ public class OneIssuePerLineSensor implements Sensor { .name("One Issue Per Line") .dependsOn(CoreMetrics.LINES) .workOnLanguages(Xoo.KEY) + .createIssuesForRuleRepositories(XooConstants.REPOSITORY_KEY) .workOnFileTypes(InputFile.Type.MAIN, InputFile.Type.TEST); } diff --git a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java index b65ad573daa..bfe1fd99afa 100644 --- a/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java +++ b/plugins/sonar-xoo-plugin/src/test/java/org/sonar/xoo/XooPluginTest.java @@ -27,6 +27,6 @@ public class XooPluginTest { @Test public void provide_extensions() { - assertThat(new XooPlugin().getExtensions()).hasSize(3); + assertThat(new XooPlugin().getExtensions()).hasSize(11); } } diff --git a/pom.xml b/pom.xml index 20de056d4d3..02da68aa17a 100644 --- a/pom.xml +++ b/pom.xml @@ -574,6 +574,12 @@ sonar-server ${project.version} + + org.codehaus.sonar.plugins + sonar-xoo-plugin + ${project.version} + test + org.codehaus.sonar sonar-squid diff --git a/sonar-batch/pom.xml b/sonar-batch/pom.xml index 1ba3a32d61f..89f7c9252a4 100644 --- a/sonar-batch/pom.xml +++ b/sonar-batch/pom.xml @@ -149,5 +149,10 @@ jsonassert test + + org.codehaus.sonar.plugins + sonar-xoo-plugin + test + diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java index 7317c9afd08..32bebf6182e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionDictionnary.java @@ -22,6 +22,7 @@ package org.sonar.batch.bootstrap; import com.google.common.collect.Lists; import org.apache.commons.lang.ClassUtils; import org.sonar.api.batch.CheckProject; +import org.sonar.api.batch.Phase; import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.platform.ComponentContainer; @@ -56,6 +57,15 @@ public class BatchExtensionDictionnary extends org.sonar.api.batch.BatchExtensio return result; } + @Override + protected Phase.Name evaluatePhase(Object extension) { + if (extension instanceof SensorWrapper) { + return super.evaluatePhase(((SensorWrapper) extension).wrappedSensor()); + } else { + return super.evaluatePhase(extension); + } + } + private List getFilteredExtensions(Class type, @Nullable Project project, @Nullable ExtensionMatcher matcher) { List result = Lists.newArrayList(); for (Object extension : getExtensions(type)) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/BlockCache.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/BlockCache.java new file mode 100644 index 00000000000..f6a4e3d18bf --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/BlockCache.java @@ -0,0 +1,56 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.duplication; + +import org.sonar.api.BatchComponent; +import org.sonar.batch.index.Cache; +import org.sonar.batch.index.Cache.Entry; +import org.sonar.batch.index.Caches; +import org.sonar.duplications.block.FileBlocks; + +import javax.annotation.CheckForNull; + +/** + * Cache of duplication blocks. This cache is shared amongst all project modules. + */ +public class BlockCache implements BatchComponent { + + private final Cache cache; + + public BlockCache(Caches caches) { + caches.registerValueCoder(FileBlocks.class, new FileBlocksValueCoder()); + cache = caches.createCache("blocks"); + } + + public Iterable> entries() { + return cache.entries(); + } + + @CheckForNull + public FileBlocks byComponent(String effectiveKey) { + return cache.get(effectiveKey); + } + + public BlockCache put(String effectiveKey, FileBlocks blocks) { + cache.put(effectiveKey, blocks); + return this; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultDuplicationBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultDuplicationBuilder.java new file mode 100644 index 00000000000..d6dd18026a2 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultDuplicationBuilder.java @@ -0,0 +1,74 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.duplication; + +import com.google.common.base.Preconditions; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.duplication.DuplicationBuilder; + +import java.util.ArrayList; + +public class DefaultDuplicationBuilder implements DuplicationBuilder { + + private final InputFile inputFile; + private final DuplicationCache duplicationCache; + private boolean done = false; + private DuplicationGroup current = null; + private ArrayList duplications; + + public DefaultDuplicationBuilder(InputFile inputFile, DuplicationCache duplicationCache) { + this.inputFile = inputFile; + this.duplicationCache = duplicationCache; + duplications = new ArrayList(); + } + + @Override + public DuplicationBuilder originBlock(int startLine, int endLine) { + if (current != null) { + duplications.add(current); + } + current = new DuplicationGroup(new DuplicationGroup.Block(((DefaultInputFile) inputFile).key(), startLine, endLine - startLine + 1)); + return this; + } + + @Override + public DuplicationBuilder isDuplicatedBy(InputFile sameOrOtherFile, int startLine, int endLine) { + return isDuplicatedBy(((DefaultInputFile) sameOrOtherFile).key(), startLine, endLine); + } + + /** + * For internal use. Global duplications are referencing files outside of current project so + * no way to manipulate an InputFile. + */ + public DuplicationBuilder isDuplicatedBy(String fileKey, int startLine, int endLine) { + Preconditions.checkNotNull(current, "Call originBlock() first"); + current.addDuplicate(new DuplicationGroup.Block(fileKey, startLine, endLine - startLine + 1)); + return this; + } + + @Override + public void done() { + Preconditions.checkState(!done, "done() already called"); + Preconditions.checkNotNull(current, "Call originBlock() first"); + duplications.add(current); + duplicationCache.put(((DefaultInputFile) inputFile).key(), duplications); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultTokenBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultTokenBuilder.java new file mode 100644 index 00000000000..6c7836d9e39 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DefaultTokenBuilder.java @@ -0,0 +1,77 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.duplication; + +import com.google.common.base.Preconditions; +import net.sourceforge.pmd.cpd.TokenEntry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.duplication.TokenBuilder; +import org.sonar.duplications.block.Block; +import org.sonar.duplications.block.FileBlocks; +import org.sonar.duplications.internal.pmd.PmdBlockChunker; +import org.sonar.duplications.internal.pmd.TokenizerBridge; +import org.sonar.duplications.internal.pmd.TokensLine; + +import java.util.ArrayList; +import java.util.List; + +public class DefaultTokenBuilder implements TokenBuilder { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultTokenBuilder.class); + + private final BlockCache cache; + private final InputFile inputFile; + private final List tokens = new ArrayList(); + private final PmdBlockChunker blockChunker; + private boolean done = false; + private int previousLine = 0; + + public DefaultTokenBuilder(InputFile inputFile, BlockCache cache, PmdBlockChunker blockChunker) { + this.inputFile = inputFile; + this.cache = cache; + this.blockChunker = blockChunker; + TokenEntry.clearImages(); + } + + @Override + public DefaultTokenBuilder addToken(int line, String image) { + Preconditions.checkState(!done, "done() already called"); + Preconditions.checkState(line >= previousLine, "Token should be created in order. Previous line was " + previousLine + " and you tried to create a token at line " + line); + TokenEntry cpdToken = new TokenEntry(image, inputFile.absolutePath(), line); + tokens.add(cpdToken); + previousLine = line; + return this; + } + + @Override + public void done() { + Preconditions.checkState(!done, "done() already called"); + tokens.add(TokenEntry.getEOF()); + TokenEntry.clearImages(); + List tokensLines = TokenizerBridge.convert(tokens); + ArrayList blocks = blockChunker.chunk(((DefaultInputFile) inputFile).key(), tokensLines); + + cache.put(((DefaultInputFile) inputFile).key(), new FileBlocks(((DefaultInputFile) inputFile).key(), blocks)); + tokens.clear(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationBlockValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationBlockValueCoder.java new file mode 100644 index 00000000000..ab1dad47009 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationBlockValueCoder.java @@ -0,0 +1,43 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.duplication; + +import com.persistit.Value; +import com.persistit.encoding.CoderContext; +import com.persistit.encoding.ValueCoder; + +class DuplicationBlockValueCoder implements ValueCoder { + + @Override + public void put(Value value, Object object, CoderContext context) { + DuplicationGroup.Block b = (DuplicationGroup.Block) object; + value.putUTF(b.resourceKey()); + value.put(b.startLine()); + value.put(b.length()); + } + + @Override + public Object get(Value value, Class clazz, CoderContext context) { + String resourceKey = value.getString(); + int startLine = value.getInt(); + int length = value.getInt(); + return new DuplicationGroup.Block(resourceKey, startLine, length); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationCache.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationCache.java new file mode 100644 index 00000000000..e0265215efc --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationCache.java @@ -0,0 +1,58 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.duplication; + +import org.sonar.api.BatchComponent; +import org.sonar.batch.index.Cache; +import org.sonar.batch.index.Cache.Entry; +import org.sonar.batch.index.Caches; + +import javax.annotation.CheckForNull; + +import java.util.ArrayList; + +/** + * Cache of duplication blocks. This cache is shared amongst all project modules. + */ +public class DuplicationCache implements BatchComponent { + + private final Cache> cache; + + public DuplicationCache(Caches caches) { + caches.registerValueCoder(DuplicationGroup.class, new DuplicationGroupValueCoder()); + caches.registerValueCoder(DuplicationGroup.Block.class, new DuplicationBlockValueCoder()); + cache = caches.createCache("duplications"); + } + + public Iterable>> entries() { + return cache.entries(); + } + + @CheckForNull + public ArrayList byComponent(String effectiveKey) { + return cache.get(effectiveKey); + } + + public DuplicationCache put(String effectiveKey, ArrayList blocks) { + cache.put(effectiveKey, blocks); + return this; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroup.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroup.java new file mode 100644 index 00000000000..dc9a7aa2602 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroup.java @@ -0,0 +1,76 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.duplication; + +import java.util.ArrayList; +import java.util.List; + +public class DuplicationGroup { + + public static class Block { + private final String resourceKey; + private final int startLine; + private final int length; + + public Block(String resourceKey, int startLine, int length) { + this.resourceKey = resourceKey; + this.startLine = startLine; + this.length = length; + } + + public String resourceKey() { + return resourceKey; + } + + public int startLine() { + return startLine; + } + + public int length() { + return length; + } + } + + private final Block originBlock; + + private List duplicates = new ArrayList(); + + public DuplicationGroup(Block originBlock) { + this.originBlock = originBlock; + } + + public void setDuplicates(List duplicates) { + this.duplicates = duplicates; + } + + public DuplicationGroup addDuplicate(Block anotherBlock) { + this.duplicates.add(anotherBlock); + return this; + } + + public Block originBlock() { + return originBlock; + } + + public List duplicates() { + return duplicates; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroupValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroupValueCoder.java new file mode 100644 index 00000000000..244cffccd5f --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/DuplicationGroupValueCoder.java @@ -0,0 +1,54 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.duplication; + +import com.persistit.Value; +import com.persistit.encoding.CoderContext; +import com.persistit.encoding.ValueCoder; +import org.sonar.batch.duplication.DuplicationGroup.Block; + +import java.util.ArrayList; + +class DuplicationGroupValueCoder implements ValueCoder { + + private DuplicationBlockValueCoder blockCoder = new DuplicationBlockValueCoder(); + + @Override + public void put(Value value, Object object, CoderContext context) { + DuplicationGroup c = (DuplicationGroup) object; + value.put(c.originBlock()); + value.put(c.duplicates().size()); + for (DuplicationGroup.Block block : c.duplicates()) { + blockCoder.put(value, block, context); + } + } + + @Override + public Object get(Value value, Class clazz, CoderContext context) { + DuplicationGroup g = new DuplicationGroup((DuplicationGroup.Block) value.get()); + int count = value.getInt(); + ArrayList blocks = new ArrayList(count); + for (int i = 0; i < count; i++) { + blocks.add((Block) blockCoder.get(value, DuplicationGroup.Block.class, context)); + } + g.setDuplicates(blocks); + return g; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/duplication/FileBlocksValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/FileBlocksValueCoder.java new file mode 100644 index 00000000000..99b3467c921 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/FileBlocksValueCoder.java @@ -0,0 +1,69 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.duplication; + +import com.persistit.Value; +import com.persistit.encoding.CoderContext; +import com.persistit.encoding.ValueCoder; +import org.sonar.duplications.block.Block; +import org.sonar.duplications.block.ByteArray; +import org.sonar.duplications.block.FileBlocks; + +import java.util.ArrayList; +import java.util.List; + +class FileBlocksValueCoder implements ValueCoder { + + @Override + public void put(Value value, Object object, CoderContext context) { + FileBlocks blocks = (FileBlocks) object; + value.putUTF(blocks.resourceId()); + value.put(blocks.blocks().size()); + for (Block b : blocks.blocks()) { + value.putByteArray(b.getBlockHash().getBytes()); + value.put(b.getIndexInFile()); + value.put(b.getStartLine()); + value.put(b.getEndLine()); + value.put(b.getStartUnit()); + value.put(b.getEndUnit()); + } + } + + @Override + public Object get(Value value, Class clazz, CoderContext context) { + String resourceId = value.getString(); + int count = value.getInt(); + List blocks = new ArrayList(count); + for (int i = 0; i < count; i++) { + Block.Builder b = Block.builder(); + b.setResourceId(resourceId); + b.setBlockHash(new ByteArray(value.getByteArray())); + b.setIndexInFile(value.getInt()); + int startLine = value.getInt(); + int endLine = value.getInt(); + b.setLines(startLine, endLine); + int startUnit = value.getInt(); + int endUnit = value.getInt(); + b.setUnit(startUnit, endUnit); + blocks.add(b.build()); + } + return new FileBlocks(resourceId, blocks); + } +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java b/sonar-batch/src/main/java/org/sonar/batch/duplication/package-info.java similarity index 69% rename from sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java rename to sonar-batch/src/main/java/org/sonar/batch/duplication/package-info.java index 1922fd7aa35..103e90ab281 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/base/Xoo.java +++ b/sonar-batch/src/main/java/org/sonar/batch/duplication/package-info.java @@ -17,24 +17,7 @@ * 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.mediumtest.xoo.plugin.base; +@ParametersAreNonnullByDefault +package org.sonar.batch.duplication; -import org.sonar.api.resources.AbstractLanguage; - -public class Xoo extends AbstractLanguage { - - public static final String KEY = "xoo"; - public static final String NAME = "Xoo"; - - public Xoo() { - super(KEY, NAME); - } - - /** - * ${@inheritDoc} - */ - public String[] getFileSuffixes() { - return XooConstants.FILE_SUFFIXES; - } - -} +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java index 38ae875af98..3a74dbf4884 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingDataBuilder.java @@ -32,9 +32,6 @@ import java.util.List; import static com.google.common.collect.Lists.newArrayList; -/** - * @since 3.6 - */ public class SyntaxHighlightingDataBuilder { private List syntaxHighlightingRuleSet; diff --git a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java index 08985752d60..e6faa7651b4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java +++ b/sonar-batch/src/main/java/org/sonar/batch/highlighting/SyntaxHighlightingRule.java @@ -21,9 +21,6 @@ package org.sonar.batch.highlighting; import java.io.Serializable; -/** - * @since 3.6 - */ public class SyntaxHighlightingRule implements Serializable { private final int startPosition; diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java b/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java index ff57e4c065d..c98b88773e6 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java @@ -28,7 +28,6 @@ import org.apache.commons.lang.builder.ToStringBuilder; import javax.annotation.CheckForNull; -import java.io.Serializable; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; @@ -38,7 +37,7 @@ import java.util.Set; * This cache is not thread-safe, due to direct usage of {@link com.persistit.Exchange} *

*/ -public class Cache { +public class Cache { private final String name; private final Exchange exchange; @@ -383,7 +382,7 @@ public class Cache { // LAZY ITERATORS AND ITERABLES // - private static class ValueIterable implements Iterable { + private static class ValueIterable implements Iterable { private final Iterator iterator; private ValueIterable(Exchange exchange, KeyFilter keyFilter) { @@ -396,7 +395,7 @@ public class Cache { } } - private static class ValueIterator implements Iterator { + private static class ValueIterator implements Iterator { private final Exchange exchange; private final KeyFilter keyFilter; @@ -434,7 +433,7 @@ public class Cache { } } - private static class EntryIterable implements Iterable> { + private static class EntryIterable implements Iterable> { private final EntryIterator it; private EntryIterable(Exchange exchange, KeyFilter keyFilter) { @@ -447,7 +446,7 @@ public class Cache { } } - private static class EntryIterator implements Iterator> { + private static class EntryIterator implements Iterator> { private final Exchange exchange; private final KeyFilter keyFilter; @@ -491,7 +490,7 @@ public class Cache { } } - public static class Entry { + public static class Entry { private final Object[] key; private final V value; diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java b/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java index 1e8c53d8853..ca2a33a5014 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/Caches.java @@ -36,7 +36,6 @@ import org.sonar.api.BatchComponent; import org.sonar.api.utils.TempFolder; import java.io.File; -import java.io.Serializable; import java.util.Properties; import java.util.Set; @@ -85,7 +84,7 @@ public class Caches implements BatchComponent, Startable { cm.registerValueCoder(clazz, coder); } - public Cache createCache(String cacheName) { + public Cache createCache(String cacheName) { Preconditions.checkState(volume != null && volume.isOpened(), "Caches are not initialized"); Preconditions.checkState(!cacheNames.contains(cacheName), "Cache is already created: " + cacheName); try { diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java index 2a63d4b8b9e..0f7691728a1 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java @@ -20,10 +20,13 @@ package org.sonar.batch.index; import com.google.common.annotations.VisibleForTesting; +import org.apache.commons.lang.StringEscapeUtils; import org.sonar.api.database.model.MeasureMapper; import org.sonar.api.database.model.MeasureModel; import org.sonar.api.database.model.Snapshot; +import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; +import org.sonar.api.measures.PersistenceMode; import org.sonar.api.measures.RuleMeasure; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; @@ -31,6 +34,8 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.technicaldebt.batch.Characteristic; +import org.sonar.batch.duplication.DuplicationCache; +import org.sonar.batch.duplication.DuplicationGroup; import org.sonar.batch.index.Cache.Entry; import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.core.persistence.DbSession; @@ -38,20 +43,27 @@ import org.sonar.core.persistence.MyBatis; import javax.annotation.Nullable; +import java.util.ArrayList; + public final class MeasurePersister implements ScanPersister { private final MyBatis mybatis; private final RuleFinder ruleFinder; private final MeasureCache measureCache; private final SnapshotCache snapshotCache; private final ResourceCache resourceCache; + private final DuplicationCache duplicationCache; + private final org.sonar.api.measures.MetricFinder metricFinder; public MeasurePersister(MyBatis mybatis, RuleFinder ruleFinder, - MeasureCache measureCache, SnapshotCache snapshotCache, ResourceCache resourceCache) { + MeasureCache measureCache, SnapshotCache snapshotCache, ResourceCache resourceCache, + DuplicationCache duplicationCache, org.sonar.api.measures.MetricFinder metricFinder) { this.mybatis = mybatis; this.ruleFinder = ruleFinder; this.measureCache = measureCache; this.snapshotCache = snapshotCache; this.resourceCache = resourceCache; + this.duplicationCache = duplicationCache; + this.metricFinder = metricFinder; } @Override @@ -72,6 +84,19 @@ public final class MeasurePersister implements ScanPersister { } } + org.sonar.api.measures.Metric duplicationMetricWithId = metricFinder.findByKey(CoreMetrics.DUPLICATIONS_DATA_KEY); + for (Entry> entry : duplicationCache.entries()) { + String effectiveKey = entry.key()[0].toString(); + Measure measure = new Measure(duplicationMetricWithId, toXml(entry.value())).setPersistenceMode(PersistenceMode.DATABASE); + Resource resource = resourceCache.get(effectiveKey); + + if (shouldPersistMeasure(resource, measure)) { + Snapshot snapshot = snapshotCache.get(effectiveKey); + MeasureModel measureModel = model(measure).setSnapshotId(snapshot.getId()); + mapper.insert(measureModel); + } + } + session.commit(); } catch (Exception e) { throw new IllegalStateException("Unable to save some measures", e); @@ -80,6 +105,28 @@ public final class MeasurePersister implements ScanPersister { } } + private static String toXml(Iterable duplications) { + StringBuilder xml = new StringBuilder(); + xml.append(""); + for (DuplicationGroup duplication : duplications) { + xml.append(""); + toXml(xml, duplication.originBlock()); + for (DuplicationGroup.Block part : duplication.duplicates()) { + toXml(xml, part); + } + xml.append(""); + } + xml.append(""); + return xml.toString(); + } + + private static void toXml(StringBuilder xml, DuplicationGroup.Block part) { + xml.append(""); + } + @VisibleForTesting static boolean shouldPersistMeasure(@Nullable Resource resource, @Nullable Measure measure) { if (resource == null || measure == null) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java index fc7a89ebad8..58cc4f461af 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java +++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java @@ -41,8 +41,11 @@ import org.sonar.api.resources.Languages; import org.sonar.batch.bootstrap.PluginsReferential; import org.sonar.batch.bootstrapper.Batch; import org.sonar.batch.bootstrapper.EnvironmentInformation; +import org.sonar.batch.duplication.DuplicationCache; +import org.sonar.batch.duplication.DuplicationGroup; import org.sonar.batch.highlighting.SyntaxHighlightingData; import org.sonar.batch.highlighting.SyntaxHighlightingRule; +import org.sonar.batch.index.Cache.Entry; import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.protocol.input.ActiveRule; import org.sonar.batch.protocol.input.GlobalReferentials; @@ -222,6 +225,7 @@ public class BatchMediumTester { private List issues = new ArrayList(); private List measures = new ArrayList(); + private Map> duplications = new HashMap>(); private List inputFiles = new ArrayList(); private List inputDirs = new ArrayList(); private Map highlightingPerFile = new HashMap(); @@ -259,6 +263,12 @@ public class BatchMediumTester { } } + DuplicationCache duplicationCache = container.getComponentByType(DuplicationCache.class); + for (Entry> entry : duplicationCache.entries()) { + String effectiveKey = entry.key()[0].toString(); + duplications.put(effectiveKey, entry.value()); + } + } public List issues() { @@ -277,6 +287,10 @@ public class BatchMediumTester { return inputDirs; } + public List duplicationsFor(InputFile inputFile) { + return duplications.get(((DefaultInputFile) inputFile).key()); + } + /** * Get highlighting type at a given position in an inputfile * @param charIndex 0-based offset in file diff --git a/sonar-batch/src/main/java/org/sonar/batch/referential/ProjectReferentialsProvider.java b/sonar-batch/src/main/java/org/sonar/batch/referential/ProjectReferentialsProvider.java index 75a063eb119..ac08e40f5fa 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/referential/ProjectReferentialsProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/referential/ProjectReferentialsProvider.java @@ -20,14 +20,24 @@ package org.sonar.batch.referential; import org.picocontainer.injectors.ProviderAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.sonar.api.batch.bootstrap.ProjectReactor; import org.sonar.api.config.Settings; import org.sonar.api.resources.Languages; +import org.sonar.api.utils.TimeProfiler; import org.sonar.batch.protocol.input.ProjectReferentials; public class ProjectReferentialsProvider extends ProviderAdapter { + private static final Logger LOG = LoggerFactory.getLogger(ProjectReferentialsProvider.class); + public ProjectReferentials provide(ProjectReferentialsLoader loader, ProjectReactor reactor, Settings settings, Languages languages) { - return loader.load(reactor, settings, languages); + TimeProfiler profiler = new TimeProfiler(LOG).start("Load project referentials"); + try { + return loader.load(reactor, settings, languages); + } finally { + profiler.stop(); + } } } 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 c5e21b89a66..3a209b91961 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 @@ -42,6 +42,8 @@ import org.sonar.batch.bootstrap.MetricProvider; import org.sonar.batch.components.PeriodsDefinition; import org.sonar.batch.debt.DebtModelProvider; import org.sonar.batch.debt.IssueChangelogDebtCalculator; +import org.sonar.batch.duplication.BlockCache; +import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.index.Caches; import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.index.ComponentDataPersister; @@ -194,6 +196,10 @@ public class ProjectScanContainer extends ComponentContainer { new RulesProvider(), new DebtModelProvider(), + // Duplications + BlockCache.class, + DuplicationCache.class, + ProjectSettings.class); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java index 1de8fd7201c..d285a6c8646 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorContextAdaptor.java @@ -27,6 +27,8 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.duplication.DuplicationBuilder; +import org.sonar.api.batch.sensor.duplication.TokenBuilder; import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder; import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.api.batch.sensor.issue.IssueBuilder; @@ -49,9 +51,14 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.Scopes; import org.sonar.api.rule.RuleKey; +import org.sonar.batch.duplication.BlockCache; +import org.sonar.batch.duplication.DefaultDuplicationBuilder; +import org.sonar.batch.duplication.DefaultTokenBuilder; +import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.highlighting.DefaultHighlightingBuilder; import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.symbol.DefaultSymbolTableBuilder; +import org.sonar.duplications.internal.pmd.PmdBlockChunker; import java.io.Serializable; @@ -61,17 +68,20 @@ import java.io.Serializable; */ public class SensorContextAdaptor implements SensorContext { - private org.sonar.api.batch.SensorContext sensorContext; - private MetricFinder metricFinder; - private Project project; - private ResourcePerspectives perspectives; - private Settings settings; - private FileSystem fs; - private ActiveRules activeRules; - private ComponentDataCache componentDataCache; + private final org.sonar.api.batch.SensorContext sensorContext; + private final MetricFinder metricFinder; + private final Project project; + private final ResourcePerspectives perspectives; + private final Settings settings; + private final FileSystem fs; + private final ActiveRules activeRules; + private final ComponentDataCache componentDataCache; + private final BlockCache blockCache; + private final DuplicationCache duplicationCache; public SensorContextAdaptor(org.sonar.api.batch.SensorContext sensorContext, MetricFinder metricFinder, Project project, ResourcePerspectives perspectives, - Settings settings, FileSystem fs, ActiveRules activeRules, ComponentDataCache componentDataCache) { + Settings settings, FileSystem fs, ActiveRules activeRules, ComponentDataCache componentDataCache, BlockCache blockCache, + DuplicationCache duplicationCache) { this.sensorContext = sensorContext; this.metricFinder = metricFinder; this.project = project; @@ -80,6 +90,8 @@ public class SensorContextAdaptor implements SensorContext { this.fs = fs; this.activeRules = activeRules; this.componentDataCache = componentDataCache; + this.blockCache = blockCache; + this.duplicationCache = duplicationCache; } @Override @@ -254,4 +266,33 @@ public class SensorContextAdaptor implements SensorContext { return new DefaultSymbolTableBuilder(((DefaultInputFile) inputFile).key(), componentDataCache); } + @Override + public TokenBuilder tokenBuilder(InputFile inputFile) { + PmdBlockChunker blockChunker = new PmdBlockChunker(getBlockSize(inputFile.language())); + return new DefaultTokenBuilder(inputFile, blockCache, blockChunker); + } + + @Override + public DuplicationBuilder duplicationBuilder(InputFile inputFile) { + return new DefaultDuplicationBuilder(inputFile, duplicationCache); + } + + private int getBlockSize(String languageKey) { + int blockSize = settings.getInt("sonar.cpd." + languageKey + ".minimumLines"); + if (blockSize == 0) { + blockSize = getDefaultBlockSize(languageKey); + } + return blockSize; + } + + private static int getDefaultBlockSize(String languageKey) { + if ("cobol".equals(languageKey)) { + return 30; + } else if ("abap".equals(languageKey) || "natur".equals(languageKey)) { + return 20; + } else { + return 10; + } + } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java index b5929008269..32a49a16492 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/SensorWrapper.java @@ -33,19 +33,23 @@ import java.util.List; public class SensorWrapper implements org.sonar.api.batch.Sensor { - private Sensor analyzer; + private Sensor wrappedSensor; private SensorContext adaptor; private DefaultSensorDescriptor descriptor; private AnalyzerOptimizer optimizer; public SensorWrapper(Sensor newSensor, SensorContext adaptor, AnalyzerOptimizer optimizer) { - this.analyzer = newSensor; + this.wrappedSensor = newSensor; this.optimizer = optimizer; descriptor = new DefaultSensorDescriptor(); newSensor.describe(descriptor); this.adaptor = adaptor; } + public Sensor wrappedSensor() { + return wrappedSensor; + } + @DependedUpon public List provides() { return Arrays.asList(descriptor.provides()); @@ -63,7 +67,7 @@ public class SensorWrapper implements org.sonar.api.batch.Sensor { @Override public void analyse(Project module, org.sonar.api.batch.SensorContext context) { - analyzer.execute(adaptor); + wrappedSensor.execute(adaptor); } @Override diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultInputFileValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultInputFileValueCoder.java new file mode 100644 index 00000000000..d60cbac0088 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultInputFileValueCoder.java @@ -0,0 +1,76 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.scan.filesystem; + +import com.persistit.Value; +import com.persistit.encoding.CoderContext; +import com.persistit.encoding.ValueCoder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; + +import javax.annotation.Nullable; + +import java.io.File; + +class DefaultInputFileValueCoder implements ValueCoder { + + @Override + public void put(Value value, Object object, CoderContext context) { + DeprecatedDefaultInputFile f = (DeprecatedDefaultInputFile) object; + putUTFOrNull(value, f.relativePath()); + putUTFOrNull(value, f.getFileBaseDir().toString()); + putUTFOrNull(value, f.deprecatedKey()); + putUTFOrNull(value, f.sourceDirAbsolutePath()); + putUTFOrNull(value, f.pathRelativeToSourceDir()); + putUTFOrNull(value, f.absolutePath()); + putUTFOrNull(value, f.language()); + putUTFOrNull(value, f.type().name()); + putUTFOrNull(value, f.status().name()); + putUTFOrNull(value, f.hash()); + value.put(f.lines()); + putUTFOrNull(value, f.key()); + } + + private void putUTFOrNull(Value value, @Nullable String utfOrNull) { + if (utfOrNull != null) { + value.putUTF(utfOrNull); + } else { + value.putNull(); + } + } + + @Override + public Object get(Value value, Class clazz, CoderContext context) { + DeprecatedDefaultInputFile file = new DeprecatedDefaultInputFile(value.getString()); + file.setBasedir(new File(value.getString())); + file.setDeprecatedKey(value.getString()); + file.setSourceDirAbsolutePath(value.getString()); + file.setPathRelativeToSourceDir(value.getString()); + file.setAbsolutePath(value.getString()); + file.setLanguage(value.getString()); + file.setType(InputFile.Type.valueOf(value.getString())); + file.setStatus(InputFile.Status.valueOf(value.getString())); + file.setHash(value.getString()); + file.setLines(value.getInt()); + file.setKey(value.getString()); + return file; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java index 5d901a517ae..39672d7f7c5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java @@ -23,6 +23,7 @@ import org.sonar.api.BatchComponent; import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputPath; +import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.batch.index.Cache; import org.sonar.batch.index.Caches; @@ -44,6 +45,7 @@ public class InputPathCache implements BatchComponent { private final Cache cache; public InputPathCache(Caches caches) { + caches.registerValueCoder(DeprecatedDefaultInputFile.class, new DefaultInputFileValueCoder()); cache = caches.createCache("inputFiles"); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java index 7f010d97d0a..c0906b51d6d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureValueCoder.java @@ -30,6 +30,8 @@ import org.sonar.api.technicaldebt.batch.Characteristic; import org.sonar.api.technicaldebt.batch.Requirement; import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; +import javax.annotation.Nullable; + class MeasureValueCoder implements ValueCoder { private final MetricFinder metricFinder; @@ -42,12 +44,12 @@ class MeasureValueCoder implements ValueCoder { public void put(Value value, Object object, CoderContext context) { Measure m = (Measure) object; - value.putString(m.getMetricKey()); + value.putUTF(m.getMetricKey()); value.put(m.getValue()); - value.putString(m.getData()); - value.putString(m.getDescription()); - value.putString(m.getAlertStatus() != null ? m.getAlertStatus().name() : null); - value.putString(m.getAlertText()); + putUTFOrNull(value, m.getData()); + putUTFOrNull(value, m.getDescription()); + putUTFOrNull(value, m.getAlertStatus() != null ? m.getAlertStatus().name() : null); + putUTFOrNull(value, m.getAlertText()); value.put(m.getTendency()); value.putDate(m.getDate()); value.put(m.getVariation1()); @@ -55,7 +57,7 @@ class MeasureValueCoder implements ValueCoder { value.put(m.getVariation3()); value.put(m.getVariation4()); value.put(m.getVariation5()); - value.putString(m.getUrl()); + putUTFOrNull(value, m.getUrl()); Characteristic characteristic = m.getCharacteristic(); value.put(characteristic != null ? characteristic.id() : null); Requirement requirement = m.getRequirement(); @@ -63,7 +65,15 @@ class MeasureValueCoder implements ValueCoder { Integer personId = m.getPersonId(); value.put(personId != null ? personId.intValue() : null); PersistenceMode persistenceMode = m.getPersistenceMode(); - value.putString(persistenceMode != null ? persistenceMode.name() : null); + putUTFOrNull(value, persistenceMode != null ? persistenceMode.name() : null); + } + + private void putUTFOrNull(Value value, @Nullable String utfOrNull) { + if (utfOrNull != null) { + value.putUTF(utfOrNull); + } else { + value.putNull(); + } } public Object get(Value value, Class clazz, CoderContext context) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerMeasureCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerMeasureCache.java index 75d0e31932c..801744de2c7 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerMeasureCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/AnalyzerMeasureCache.java @@ -19,10 +19,10 @@ */ package org.sonar.batch.scan2; -import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; - import com.google.common.base.Preconditions; import org.sonar.api.BatchComponent; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.batch.index.Cache; import org.sonar.batch.index.Cache.Entry; import org.sonar.batch.index.Caches; @@ -35,7 +35,8 @@ public class AnalyzerMeasureCache implements BatchComponent { // project key -> component key -> metric key -> measure private final Cache cache; - public AnalyzerMeasureCache(Caches caches) { + public AnalyzerMeasureCache(Caches caches, MetricFinder metricFinder) { + caches.registerValueCoder(DefaultMeasure.class, new DefaultMeasureValueCoder(metricFinder)); cache = caches.createCache("measures"); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java new file mode 100644 index 00000000000..8b82a684dd0 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultMeasureValueCoder.java @@ -0,0 +1,64 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.scan2; + +import com.persistit.Value; +import com.persistit.encoding.CoderContext; +import com.persistit.encoding.ValueCoder; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.measure.Metric; +import org.sonar.api.batch.measure.MetricFinder; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder; + +import java.io.Serializable; + +class DefaultMeasureValueCoder implements ValueCoder { + + private MetricFinder metricFinder; + + public DefaultMeasureValueCoder(MetricFinder metricFinder) { + this.metricFinder = metricFinder; + } + + @Override + public void put(Value value, Object object, CoderContext context) { + DefaultMeasure m = (DefaultMeasure) object; + value.put(m.inputFile()); + value.putUTF(m.metric().key()); + value.put(m.value()); + } + + @Override + public Object get(Value value, Class clazz, CoderContext context) { + DefaultMeasureBuilder builder = new DefaultMeasureBuilder(); + InputFile f = (InputFile) value.get(); + if (f != null) { + builder.onFile(f); + } else { + builder.onProject(); + } + Metric m = metricFinder.findByKey(value.getString()); + builder.forMetric(m); + builder.withValue((Serializable) value.get()); + return builder.build(); + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java index dcd6efd1ca4..73d6429bfcd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultSensorContext.java @@ -28,6 +28,8 @@ import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.rule.ActiveRules; import org.sonar.api.batch.rule.internal.DefaultActiveRule; import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.duplication.DuplicationBuilder; +import org.sonar.api.batch.sensor.duplication.TokenBuilder; import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder; import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.api.batch.sensor.issue.IssueBuilder; @@ -41,12 +43,17 @@ import org.sonar.api.batch.sensor.symbol.SymbolTableBuilder; import org.sonar.api.config.Settings; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.MessageException; +import org.sonar.batch.duplication.BlockCache; +import org.sonar.batch.duplication.DefaultDuplicationBuilder; +import org.sonar.batch.duplication.DefaultTokenBuilder; +import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.highlighting.DefaultHighlightingBuilder; import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.issue.IssueFilters; import org.sonar.batch.scan.SensorContextAdaptor; import org.sonar.batch.symbol.DefaultSymbolTableBuilder; import org.sonar.core.component.ComponentKeys; +import org.sonar.duplications.internal.pmd.PmdBlockChunker; import java.io.Serializable; @@ -60,9 +67,12 @@ public class DefaultSensorContext implements SensorContext { private final ActiveRules activeRules; private final IssueFilters issueFilters; private final ComponentDataCache componentDataCache; + private final BlockCache blockCache; + private final DuplicationCache duplicationCache; public DefaultSensorContext(ProjectDefinition def, AnalyzerMeasureCache measureCache, AnalyzerIssueCache issueCache, - Settings settings, FileSystem fs, ActiveRules activeRules, IssueFilters issueFilters, ComponentDataCache componentDataCache) { + Settings settings, FileSystem fs, ActiveRules activeRules, IssueFilters issueFilters, ComponentDataCache componentDataCache, + BlockCache blockCache, DuplicationCache duplicationCache) { this.def = def; this.measureCache = measureCache; this.issueCache = issueCache; @@ -71,6 +81,8 @@ public class DefaultSensorContext implements SensorContext { this.activeRules = activeRules; this.issueFilters = issueFilters; this.componentDataCache = componentDataCache; + this.blockCache = blockCache; + this.duplicationCache = duplicationCache; } @Override @@ -175,4 +187,34 @@ public class DefaultSensorContext implements SensorContext { return new DefaultSymbolTableBuilder(((DefaultInputFile) inputFile).key(), componentDataCache); } + @Override + public TokenBuilder tokenBuilder(InputFile inputFile) { + PmdBlockChunker blockChunker = new PmdBlockChunker(getBlockSize(inputFile.language())); + + return new DefaultTokenBuilder(inputFile, blockCache, blockChunker); + } + + @Override + public DuplicationBuilder duplicationBuilder(InputFile inputFile) { + return new DefaultDuplicationBuilder(inputFile, duplicationCache); + } + + private int getBlockSize(String languageKey) { + int blockSize = settings.getInt("sonar.cpd." + languageKey + ".minimumLines"); + if (blockSize == 0) { + blockSize = getDefaultBlockSize(languageKey); + } + return blockSize; + } + + private static int getDefaultBlockSize(String languageKey) { + if ("cobol".equals(languageKey)) { + return 30; + } else if ("abap".equals(languageKey) || "natur".equals(languageKey)) { + return 20; + } else { + return 10; + } + } + } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java index e13fca37d28..b0a2b3e0af5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java @@ -33,6 +33,8 @@ import org.sonar.api.scan.filesystem.PathResolver; import org.sonar.batch.bootstrap.ExtensionInstaller; import org.sonar.batch.bootstrap.ExtensionMatcher; import org.sonar.batch.bootstrap.ExtensionUtils; +import org.sonar.batch.duplication.BlockCache; +import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.index.Caches; import org.sonar.batch.index.ComponentDataCache; import org.sonar.batch.languages.DefaultLanguagesReferential; @@ -107,6 +109,10 @@ public class ProjectScanContainer extends ComponentContainer { ComponentDataCache.class, + // Duplications + BlockCache.class, + DuplicationCache.class, + ScanTaskObservers.class); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java index d437eadb6fd..ea872c4c1bf 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/SensorsExecutor.java @@ -53,7 +53,7 @@ public class SensorsExecutor implements BatchComponent { continue; } - LOG.info("Execute analyzer: " + descriptor.name()); + LOG.info("Execute sensor: " + descriptor.name()); executeSensor(context, analyzer); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/duplication/DuplicationCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/duplication/DuplicationCacheTest.java new file mode 100644 index 00000000000..ade701fce45 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/duplication/DuplicationCacheTest.java @@ -0,0 +1,82 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.batch.duplication; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.sonar.batch.index.Caches; +import org.sonar.batch.index.CachesTest; + +import java.util.ArrayList; +import java.util.Arrays; + +import static org.fest.assertions.Assertions.assertThat; + +public class DuplicationCacheTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + Caches caches; + + @Before + public void start() throws Exception { + caches = CachesTest.createCacheOnTemp(temp); + caches.start(); + } + + @After + public void stop() { + caches.stop(); + } + + @Test + public void should_add_clone_groups() throws Exception { + DuplicationCache cache = new DuplicationCache(caches); + + DuplicationGroup group1 = new DuplicationGroup(new DuplicationGroup.Block("foo", 1, 2)) + .addDuplicate(new DuplicationGroup.Block("foo", 1, 2)) + .addDuplicate(new DuplicationGroup.Block("foo2", 12, 22)) + .addDuplicate(new DuplicationGroup.Block("foo3", 13, 23)); + + DuplicationGroup group2 = new DuplicationGroup(new DuplicationGroup.Block("2foo", 1, 2)) + .addDuplicate(new DuplicationGroup.Block("2foo", 1, 2)) + .addDuplicate(new DuplicationGroup.Block("2foo2", 12, 22)) + .addDuplicate(new DuplicationGroup.Block("2foo3", 13, 23)); + + assertThat(cache.entries()).hasSize(0); + + cache.put("foo", new ArrayList(Arrays.asList(group1, group2))); + + assertThat(cache.entries()).hasSize(1); + + ArrayList entry = cache.byComponent("foo"); + assertThat(entry.get(0).originBlock().resourceKey()).isEqualTo("foo"); + + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java index fe8a1a45a65..dfed349e696 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java @@ -27,6 +27,7 @@ import org.sonar.api.database.model.Snapshot; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; +import org.sonar.api.measures.MetricFinder; import org.sonar.api.measures.PersistenceMode; import org.sonar.api.measures.RuleMeasure; import org.sonar.api.resources.Directory; @@ -35,10 +36,14 @@ import org.sonar.api.resources.Project; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; +import org.sonar.batch.duplication.DuplicationCache; +import org.sonar.batch.duplication.DuplicationGroup; import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.core.persistence.AbstractDaoTestCase; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -70,6 +75,8 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { public void mockResourcePersister() { snapshotCache = mock(SnapshotCache.class); measureCache = mock(MeasureCache.class); + DuplicationCache duplicationCache = mock(DuplicationCache.class); + when(duplicationCache.entries()).thenReturn(Collections.>>emptyList()); ResourceCache resourceCache = mock(ResourceCache.class); when(snapshotCache.get("foo")).thenReturn(projectSnapshot); when(snapshotCache.get("foo:org/foo")).thenReturn(packageSnapshot); @@ -77,7 +84,7 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { when(resourceCache.get("foo:org/foo/Bar.java")).thenReturn(aFile); when(resourceCache.get("foo:org/foo")).thenReturn(aDirectory); - measurePersister = new MeasurePersister(getMyBatis(), ruleFinder, measureCache, snapshotCache, resourceCache); + measurePersister = new MeasurePersister(getMyBatis(), ruleFinder, measureCache, snapshotCache, resourceCache, duplicationCache, mock(MetricFinder.class)); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java index 674f472d0b5..19d7aea020a 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/fs/FileSystemMediumTest.java @@ -31,8 +31,8 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.utils.MessageException; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; -import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin; import org.sonar.batch.protocol.input.ActiveRule; +import org.sonar.xoo.XooPlugin; import java.io.File; import java.io.IOException; diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java index 259b2ffcec0..b9fab7ba450 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/highlighting/HighlightingMediumTest.java @@ -29,7 +29,7 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; -import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin; +import org.sonar.xoo.XooPlugin; import java.io.File; import java.io.IOException; diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java index 875e7e6d510..f3eebf971e3 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesMediumTest.java @@ -29,8 +29,8 @@ import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; -import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin; import org.sonar.batch.protocol.input.ActiveRule; +import org.sonar.xoo.XooPlugin; import java.io.File; import java.io.IOException; diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java index fdf6b15e857..fb3e8d16edd 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/IssuesOnDirMediumTest.java @@ -28,8 +28,8 @@ import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; -import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin; import org.sonar.batch.protocol.input.ActiveRule; +import org.sonar.xoo.XooPlugin; import java.io.File; import java.io.IOException; diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java index bb93ae4b7ba..146b89eb316 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/measures/MeasuresMediumTest.java @@ -30,7 +30,7 @@ import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder; import org.sonar.api.measures.CoreMetrics; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; -import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin; +import org.sonar.xoo.XooPlugin; import java.io.File; import java.io.IOException; diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java index c3875c625f3..8772d371ba2 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/symbol/SymbolMediumTest.java @@ -28,7 +28,7 @@ import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.fs.InputFile; import org.sonar.batch.mediumtest.BatchMediumTester; import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult; -import org.sonar.batch.mediumtest.xoo.plugin.XooPlugin; +import org.sonar.xoo.XooPlugin; import java.io.File; import java.io.IOException; diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java deleted file mode 100644 index 78718da3682..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/xoo/plugin/XooPlugin.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.batch.mediumtest.xoo.plugin; - -import org.sonar.api.SonarPlugin; -import org.sonar.batch.mediumtest.xoo.plugin.base.Xoo; -import org.sonar.batch.mediumtest.xoo.plugin.lang.MeasureSensor; -import org.sonar.batch.mediumtest.xoo.plugin.lang.ScmActivitySensor; -import org.sonar.batch.mediumtest.xoo.plugin.lang.SymbolReferencesSensor; -import org.sonar.batch.mediumtest.xoo.plugin.lang.SyntaxHighlightingSensor; -import org.sonar.batch.mediumtest.xoo.plugin.rule.CreateIssueByInternalKeySensor; -import org.sonar.batch.mediumtest.xoo.plugin.rule.OneIssueOnDirPerFileSensor; -import org.sonar.batch.mediumtest.xoo.plugin.rule.OneIssuePerLineSensor; - -import java.util.Arrays; -import java.util.List; - -public final class XooPlugin extends SonarPlugin { - - @Override - public List getExtensions() { - return Arrays.asList( - // language - MeasureSensor.class, - ScmActivitySensor.class, - SyntaxHighlightingSensor.class, - SymbolReferencesSensor.class, - Xoo.class, - - // sensors - OneIssuePerLineSensor.class, - OneIssueOnDirPerFileSensor.class, - CreateIssueByInternalKeySensor.class - ); - } -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java index 7b7eb21ee58..a93bf9bae83 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/SensorContextAdapterTest.java @@ -42,6 +42,8 @@ import org.sonar.api.measures.MetricFinder; import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.rule.RuleKey; +import org.sonar.batch.duplication.BlockCache; +import org.sonar.batch.duplication.DuplicationCache; import org.sonar.batch.index.ComponentDataCache; import static org.fest.assertions.Assertions.assertThat; @@ -71,8 +73,9 @@ public class SensorContextAdapterTest { settings = new Settings(); resourcePerspectives = mock(ResourcePerspectives.class); ComponentDataCache componentDataCache = mock(ComponentDataCache.class); + BlockCache blockCache = mock(BlockCache.class); adaptor = new SensorContextAdaptor(sensorContext, metricFinder, new Project("myProject"), - resourcePerspectives, settings, fs, activeRules, componentDataCache); + resourcePerspectives, settings, fs, activeRules, componentDataCache, blockCache, mock(DuplicationCache.class)); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java index f1bf43f37e3..03dbec79af8 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java @@ -24,6 +24,8 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.sonar.api.batch.fs.InputFile.Status; +import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.InputPath; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; @@ -55,7 +57,18 @@ public class InputPathCacheTest { InputPathCache cache = new InputPathCache(caches); DefaultInputFile fooFile = new DefaultInputFile("src/main/java/Foo.java").setFile(temp.newFile("Foo.java")); cache.put("struts", fooFile); - cache.put("struts-core", new DeprecatedDefaultInputFile("src/main/java/Bar.java").setFile(temp.newFile("Bar.java"))); + cache.put("struts-core", new DeprecatedDefaultInputFile("src/main/java/Bar.java") + .setBasedir(temp.newFolder()) + .setDeprecatedKey("foo") + .setSourceDirAbsolutePath("foo") + .setPathRelativeToSourceDir("foo") + .setLanguage("bla") + .setType(Type.MAIN) + .setStatus(Status.ADDED) + .setHash("xyz") + .setLines(1) + .setKey("foo") + .setFile(temp.newFile("Bar.java"))); assertThat(cache.getFile("struts", "src/main/java/Foo.java").relativePath()) .isEqualTo("src/main/java/Foo.java"); @@ -75,5 +88,4 @@ public class InputPathCacheTest { assertThat(cache.filesByModule("struts-core")).hasSize(1); assertThat(cache.all()).hasSize(1); } - } diff --git a/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataTypes.java b/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataTypes.java index 745b21b8c97..cb7ff46ff60 100644 --- a/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataTypes.java +++ b/sonar-core/src/main/java/org/sonar/core/source/SnapshotDataTypes.java @@ -24,6 +24,7 @@ public interface SnapshotDataTypes { String SYNTAX_HIGHLIGHTING = "highlight_syntax"; String SYMBOL_HIGHLIGHTING = "symbol"; + String TOKEN = "token"; /** * Key-values [relative path, hash] of all files. Stored on modules. diff --git a/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java b/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java index ffd90b34de2..34c5d6bbdbd 100644 --- a/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java +++ b/sonar-deprecated/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java @@ -19,9 +19,6 @@ */ package org.sonar.api.batch; -import org.sonar.api.batch.sensor.Sensor; -import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; - import com.google.common.base.Predicates; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; @@ -29,6 +26,8 @@ import org.apache.commons.lang.ClassUtils; import org.sonar.api.BatchExtension; import org.sonar.api.batch.maven.DependsUponMavenPlugin; import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.platform.ComponentContainer; import org.sonar.api.resources.Project; import org.sonar.api.utils.AnnotationUtils; diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/block/ByteArray.java b/sonar-duplications/src/main/java/org/sonar/duplications/block/ByteArray.java index aab2802c27b..a3deacff154 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/block/ByteArray.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/block/ByteArray.java @@ -60,7 +60,7 @@ public final class ByteArray { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), - (byte) value }; + (byte) value}; } public ByteArray(int value) { @@ -68,7 +68,7 @@ public final class ByteArray { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), - (byte) value }; + (byte) value}; } public ByteArray(int[] intArray) { @@ -79,6 +79,10 @@ public final class ByteArray { this.bytes = bb.array(); } + public byte[] getBytes() { + return bytes; + } + public int[] toIntArray() { // Pad the size to multiple of 4 int size = (bytes.length / 4) + (bytes.length % 4 == 0 ? 0 : 1); diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/block/FileBlocks.java b/sonar-duplications/src/main/java/org/sonar/duplications/block/FileBlocks.java new file mode 100644 index 00000000000..7b43988efc2 --- /dev/null +++ b/sonar-duplications/src/main/java/org/sonar/duplications/block/FileBlocks.java @@ -0,0 +1,45 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.duplications.block; + +import java.util.List; + +/** + * Represents all blocks in a file. + */ +public final class FileBlocks { + + private final String resourceId; + private final List blocks; + + public FileBlocks(String resourceId, List blocks) { + this.resourceId = resourceId; + this.blocks = blocks; + } + + public String resourceId() { + return resourceId; + } + + public List blocks() { + return blocks; + } + +} diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneGroup.java b/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneGroup.java index 7c9ad2a8278..01b7273da1c 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneGroup.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneGroup.java @@ -20,8 +20,10 @@ package org.sonar.duplications.index; import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import java.util.ArrayList; import java.util.List; /** @@ -32,6 +34,7 @@ public class CloneGroup { private final ClonePart originPart; private final int cloneLength; private final List parts; + private int length; /** * Cache for hash code. @@ -52,7 +55,7 @@ public class CloneGroup { private ClonePart origin; private int length; private int lengthInUnits; - private List parts; + private List parts = new ArrayList(); public Builder setLength(int length) { this.length = length; @@ -69,6 +72,12 @@ public class CloneGroup { return this; } + public Builder addPart(ClonePart part) { + Preconditions.checkNotNull(part); + this.parts.add(part); + return this; + } + public Builder setLengthInUnits(int length) { this.lengthInUnits = length; return this; @@ -90,8 +99,6 @@ public class CloneGroup { return originPart; } - private int length; - /** * Length of duplication measured in original units, e.g. for token-based detection - in tokens. * diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java index 1c7c0ee4437..b0ae419332a 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/PmdBlockChunker.java @@ -23,7 +23,7 @@ import com.google.common.collect.Lists; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.ByteArray; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; /** @@ -48,7 +48,10 @@ public class PmdBlockChunker { this.power = pow; } - public List chunk(String resourceId, List fragments) { + /** + * @return ArrayList as we need a serializable object + */ + public ArrayList chunk(String resourceId, List fragments) { List filtered = Lists.newArrayList(); int i = 0; while (i < fragments.size()) { @@ -66,10 +69,10 @@ public class PmdBlockChunker { fragments = filtered; if (fragments.size() < blockSize) { - return Collections.emptyList(); + return Lists.newArrayList(); } TokensLine[] fragmentsArr = fragments.toArray(new TokensLine[fragments.size()]); - List blocks = Lists.newArrayListWithCapacity(fragmentsArr.length - blockSize + 1); + ArrayList blocks = Lists.newArrayListWithCapacity(fragmentsArr.length - blockSize + 1); long hash = 0; int first = 0; int last = 0; @@ -84,11 +87,11 @@ public class PmdBlockChunker { hash = hash * PRIME_BASE + lastFragment.getHashCode(); // create block Block block = blockBuilder - .setBlockHash(new ByteArray(hash)) - .setIndexInFile(first) - .setLines(firstFragment.getStartLine(), lastFragment.getEndLine()) - .setUnit(firstFragment.getStartUnit(), lastFragment.getEndUnit()) - .build(); + .setBlockHash(new ByteArray(hash)) + .setIndexInFile(first) + .setLines(firstFragment.getStartLine(), lastFragment.getEndLine()) + .setUnit(firstFragment.getStartUnit(), lastFragment.getEndUnit()) + .build(); blocks.add(block); // remove first statement from hash hash -= power * firstFragment.getHashCode(); diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java index e8dcc4bad76..80fc8691717 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokenizerBridge.java @@ -69,7 +69,7 @@ public class TokenizerBridge { * We expect that implementation of {@link Tokenizer} is correct: * tokens ordered by occurrence in source code and last token is EOF. */ - private static List convert(List tokens) { + public static List convert(List tokens) { ImmutableList.Builder result = ImmutableList.builder(); StringBuilder sb = new StringBuilder(); int startLine = Integer.MIN_VALUE; diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java index 9c07b381240..d8f5a304af9 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/internal/pmd/TokensLine.java @@ -25,7 +25,7 @@ import org.sonar.duplications.CodeFragment; /** * Immutable code fragment, which formed from tokens of one line. */ -class TokensLine implements CodeFragment { +public class TokensLine implements CodeFragment { private final String value; @@ -35,7 +35,6 @@ class TokensLine implements CodeFragment { private final int startUnit; private final int endUnit; - public TokensLine(int startUnit, int endUnit, int startLine, String value) { Preconditions.checkArgument(startLine > 0); // TODO do we have requirements for length and hashcode ? diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java index aad1de5ea03..b73e0a0ecee 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java @@ -344,10 +344,10 @@ public interface CoreProperties { /** * @since 2.11 */ - String CPD_CROSS_RPOJECT = "sonar.cpd.cross_project"; + String CPD_CROSS_PROJECT = "sonar.cpd.cross_project"; /** - * @see #CPD_CROSS_RPOJECT + * @see #CPD_CROSS_PROJECT * @since 2.11 */ boolean CPD_CROSS_RPOJECT_DEFAULT_VALUE = false; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java index 156e140fa57..7c82aa501d0 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DeprecatedDefaultInputFile.java @@ -50,8 +50,9 @@ public class DeprecatedDefaultInputFile extends DefaultInputFile implements org. return new File(basedir); } - public void setBasedir(File basedir) { + public DeprecatedDefaultInputFile setBasedir(File basedir) { this.basedir = PathUtils.sanitize(basedir.getAbsolutePath()); + return this; } /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java index 787d2fddb3d..647cab3f3f9 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java @@ -24,6 +24,8 @@ import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.measure.Metric; import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.batch.sensor.duplication.DuplicationBuilder; +import org.sonar.api.batch.sensor.duplication.TokenBuilder; import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder; import org.sonar.api.batch.sensor.issue.Issue; import org.sonar.api.batch.sensor.issue.IssueBuilder; @@ -126,4 +128,19 @@ public interface SensorContext { */ SymbolTableBuilder symbolTableBuilder(InputFile inputFile); + // ------------ DUPLICATIONS ------------ + + /** + * Builder to define tokens in a file. Tokens are used to compute duplication by the core. + * @since 4.5 + */ + TokenBuilder tokenBuilder(InputFile inputFile); + + /** + * Builder to manually define duplications in a file. When duplication are manually computed then + * no need to use {@link #tokenBuilder(InputFile)}. + * @since 4.5 + */ + DuplicationBuilder duplicationBuilder(InputFile inputFile); + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/DuplicationBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/DuplicationBuilder.java new file mode 100644 index 00000000000..2f37220b1a3 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/DuplicationBuilder.java @@ -0,0 +1,38 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.api.batch.sensor.duplication; + +import org.sonar.api.batch.fs.InputFile; + +/** + * This builder is used to declare duplications on files of the project. + * @since 4.5 + */ +public interface DuplicationBuilder { + + DuplicationBuilder originBlock(int startLine, int endLine); + + DuplicationBuilder isDuplicatedBy(InputFile sameOrOtherFile, int startLine, int endLine); + + /** + * Call this method only once when your are done with defining all duplicates of origin block. + */ + void done(); +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/TokenBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/TokenBuilder.java new file mode 100644 index 00000000000..f4de9fffc8c --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/TokenBuilder.java @@ -0,0 +1,40 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.api.batch.sensor.duplication; + +/** + * This builder is used to define token on files. Tokens are later used to compute duplication. + * Tokens should be declared in sequential order. + * @since 4.5 + */ +public interface TokenBuilder { + + /** + * Call this method to register a new token. + * @param line Line number of the token. Line starts at 1. + * @param image Text of the token. + */ + TokenBuilder addToken(int line, String image); + + /** + * Call this method only once when your are done with defining tokens of the file. + */ + void done(); +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/package-info.java new file mode 100644 index 00000000000..4973316830a --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/duplication/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonar.api.batch.sensor.duplication; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java index 7a86f7b145c..870a8519271 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java @@ -343,6 +343,7 @@ public class Measure implements Serializable { /** * @return the data field of the measure */ + @CheckForNull public String getData() { return data; } -- 2.39.5