]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7389 Add new Sensor API to provide duplication tokens 840/head
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 14 Mar 2016 16:57:48 +0000 (17:57 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 15 Mar 2016 14:42:51 +0000 (15:42 +0100)
52 files changed:
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/XooPlugin.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/CpdTokenizerSensor.java [new file with mode: 0644]
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/XooTokenizer.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchComponents.java
sonar-batch/src/main/java/org/sonar/batch/bootstrap/ExtensionInstaller.java
sonar-batch/src/main/java/org/sonar/batch/cpd/CpdComponents.java
sonar-batch/src/main/java/org/sonar/batch/cpd/CpdExecutor.java
sonar-batch/src/main/java/org/sonar/batch/cpd/CpdSensor.java
sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdBlockIndexer.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdIndexer.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdBlockIndexer.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdIndexer.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarCpdBlockIndex.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarDuplicationsIndex.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/phases/AbstractPhaseExecutor.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/phases/IssuesPhaseExecutor.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/phases/PublishPhaseExecutor.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/report/MetadataPublisher.java
sonar-batch/src/main/java/org/sonar/batch/scan/ModuleScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/report/IssuesReports.java
sonar-batch/src/main/java/org/sonar/batch/scm/ScmSensor.java
sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorContext.java
sonar-batch/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
sonar-batch/src/main/java/org/sonar/batch/sensor/SensorOptimizer.java
sonar-batch/src/main/java/org/sonar/batch/sensor/noop/NoOpNewCpdTokens.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/sensor/noop/NoOpNewHighlighting.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/sensor/noop/package-info.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/source/CodeColorizerSensor.java
sonar-batch/src/test/java/org/sonar/batch/bootstrap/ExtensionInstallerTest.java
sonar-batch/src/test/java/org/sonar/batch/cpd/CpdExecutorTest.java
sonar-batch/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java
sonar-batch/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/cpd/DefaultCpdIndexerTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/cpd/JavaCpdIndexerTest.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorContextTest.java
sonar-batch/src/test/java/org/sonar/batch/sensor/DefaultSensorStorageTest.java
sonar-batch/src/test/java/org/sonar/batch/sensor/SensorOptimizerTest.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorContext.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/SensorDescriptor.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/NewCpdTokens.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/package-info.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/package-info.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptor.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorStorage.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/DefaultSensorDescriptorTest.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java

index 3da7721c4486c886f13ff3ff8ac9b1950f33ea43..679d3c108974032374ac2eedb040c7329bb63f63 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.xoo.coverage.OverallCoverageSensor;
 import org.sonar.xoo.coverage.UtCoverageSensor;
 import org.sonar.xoo.extensions.XooPostJob;
 import org.sonar.xoo.extensions.XooProjectBuilder;
+import org.sonar.xoo.lang.CpdTokenizerSensor;
 import org.sonar.xoo.lang.MeasureSensor;
 import org.sonar.xoo.lang.SymbolReferencesSensor;
 import org.sonar.xoo.lang.SyntaxHighlightingSensor;
@@ -97,6 +98,7 @@ public class XooPlugin extends SonarPlugin {
       ChecksSensor.class,
       RandomAccessSensor.class,
       DeprecatedResourceApiSensor.class,
+      CpdTokenizerSensor.class,
 
       OneBlockerIssuePerFileSensor.class,
       OneIssuePerLineSensor.class,
diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/CpdTokenizerSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/lang/CpdTokenizerSensor.java
new file mode 100644 (file)
index 0000000..aa2c69f
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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 java.io.IOException;
+import org.apache.commons.io.FileUtils;
+import org.sonar.api.batch.fs.FilePredicates;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputFile.Type;
+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.cpd.NewCpdTokens;
+import org.sonar.xoo.Xoo;
+
+/**
+ * Tokenize files for CPD
+ */
+public class CpdTokenizerSensor implements Sensor {
+
+  public static final String ENABLE_PROP = "sonar.xoo.useNewCpdTokenizerApi";
+
+  private void tokenize(InputFile inputFile, SensorContext context) {
+    int lineIdx = 1;
+    NewCpdTokens newCpdTokens = context.newCpdTokens().onFile(inputFile);
+    try {
+      StringBuilder sb = new StringBuilder();
+      for (String line : FileUtils.readLines(inputFile.file(), context.fileSystem().encoding())) {
+        int startOffset = 0;
+        int endOffset = 0;
+        for (int i = 0; i < line.length(); i++) {
+          char c = line.charAt(i);
+          if (Character.isWhitespace(c)) {
+            if (sb.length() > 0) {
+              newCpdTokens.addToken(inputFile.newRange(lineIdx, startOffset, lineIdx, endOffset), sb.toString());
+              sb.setLength(0);
+            }
+            startOffset = endOffset;
+          } else {
+            sb.append(c);
+          }
+          endOffset++;
+        }
+        if (sb.length() > 0) {
+          newCpdTokens.addToken(inputFile.newRange(lineIdx, startOffset, lineIdx, endOffset), sb.toString());
+          sb.setLength(0);
+        }
+        lineIdx++;
+      }
+    } catch (IOException e) {
+      throw new IllegalStateException("Unable to tokenize", e);
+    }
+    newCpdTokens.save();
+  }
+
+  @Override
+  public void describe(SensorDescriptor descriptor) {
+    descriptor
+      .name("Xoo Cpd Tokenizer Sensor")
+      .requireProperty(ENABLE_PROP)
+      .onlyOnLanguages(Xoo.KEY);
+  }
+
+  @Override
+  public void execute(SensorContext context) {
+    FilePredicates p = context.fileSystem().predicates();
+    for (InputFile file : context.fileSystem().inputFiles(p.and(p.hasLanguages(Xoo.KEY), p.hasType(Type.MAIN)))) {
+      tokenize(file, context);
+    }
+  }
+}
index 74babada4e065ceb3a82869dc227b4d7b0aa032a..7773d36df39b919acce20620f36967ec976dd229 100644 (file)
@@ -20,6 +20,8 @@
 package org.sonar.xoo.lang;
 
 import com.google.common.base.Splitter;
+import java.io.File;
+import java.io.IOException;
 import net.sourceforge.pmd.cpd.SourceCode;
 import net.sourceforge.pmd.cpd.TokenEntry;
 import net.sourceforge.pmd.cpd.Tokenizer;
@@ -27,13 +29,14 @@ import net.sourceforge.pmd.cpd.Tokens;
 import org.apache.commons.io.FileUtils;
 import org.sonar.api.batch.BatchSide;
 import org.sonar.api.batch.fs.FileSystem;
-
-import java.io.File;
-import java.io.IOException;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
 
 @BatchSide
 public class XooTokenizer implements Tokenizer {
 
+  private static final Logger LOG = Loggers.get(XooTokenizer.class);
+
   private FileSystem fs;
 
   public XooTokenizer(FileSystem fs) {
@@ -43,6 +46,7 @@ public class XooTokenizer implements Tokenizer {
   @Override
   public final void tokenize(SourceCode source, Tokens cpdTokens) {
     String fileName = source.getFileName();
+    LOG.info("Using deprecated tokenizer extension point to tokenize {}", fileName);
     int lineIdx = 1;
     try {
       for (String line : FileUtils.readLines(new File(fileName), fs.encoding())) {
index 4b7c4b61f572ad9d1adf4fd7a5aa5f4c7b742432..aa107887ea44aff67c55f79e92b105b12b23a22d 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.batch.bootstrap;
 import com.google.common.collect.Lists;
 import java.util.Collection;
 import java.util.List;
+import org.sonar.api.batch.AnalysisMode;
 import org.sonar.batch.cpd.CpdComponents;
 import org.sonar.batch.issue.tracking.ServerIssueFromWs;
 import org.sonar.batch.issue.tracking.TrackedIssue;
@@ -48,27 +49,9 @@ public class BatchComponents {
     // only static stuff
   }
 
-  public static Collection<Object> all() {
+  public static Collection<Object> all(AnalysisMode analysisMode) {
     List<Object> components = Lists.newArrayList(
       DefaultResourceTypes.get(),
-      // SCM
-      ScmConfiguration.class,
-      ScmSensor.class,
-
-      LinesSensor.class,
-      ZeroCoverageSensor.class,
-      CodeColorizerSensor.class,
-
-      // Issues tracking
-      new Tracker<TrackedIssue, ServerIssueFromWs>(),
-
-      // Reports
-      ConsoleReport.class,
-      JSONReport.class,
-      HtmlReport.class,
-      IssuesReportBuilder.class,
-      SourceProvider.class,
-      RuleNameProvider.class,
 
       // Tasks
       Tasks.class,
@@ -77,8 +60,29 @@ public class BatchComponents {
       ScanTask.DEFINITION,
       ScanTask.class);
     components.addAll(CorePropertyDefinitions.all());
-    // CPD
-    components.addAll(CpdComponents.all());
+    if (!analysisMode.isIssues()) {
+      // SCM
+      components.add(ScmConfiguration.class);
+      components.add(ScmSensor.class);
+
+      components.add(LinesSensor.class);
+      components.add(ZeroCoverageSensor.class);
+      components.add(CodeColorizerSensor.class);
+
+      // CPD
+      components.addAll(CpdComponents.all());
+    } else {
+      // Issues tracking
+      components.add(new Tracker<TrackedIssue, ServerIssueFromWs>());
+
+      // Issues report
+      components.add(ConsoleReport.class);
+      components.add(JSONReport.class);
+      components.add(HtmlReport.class);
+      components.add(IssuesReportBuilder.class);
+      components.add(SourceProvider.class);
+      components.add(RuleNameProvider.class);
+    }
     return components;
   }
 }
index 02517f6d6967b4e46a904c5048d5f77a2e2dd2e6..ff610139032b3e027b94b0c2fb561a1005cfd0b1 100644 (file)
@@ -23,6 +23,7 @@ import java.util.List;
 import javax.annotation.Nullable;
 import org.sonar.api.ExtensionProvider;
 import org.sonar.api.SonarPlugin;
+import org.sonar.api.batch.AnalysisMode;
 import org.sonar.core.platform.ComponentContainer;
 import org.sonar.core.platform.PluginInfo;
 import org.sonar.core.platform.PluginRepository;
@@ -30,15 +31,17 @@ import org.sonar.core.platform.PluginRepository;
 public class ExtensionInstaller {
 
   private final PluginRepository pluginRepository;
+  private final AnalysisMode analysisMode;
 
-  public ExtensionInstaller(PluginRepository pluginRepository) {
+  public ExtensionInstaller(PluginRepository pluginRepository, AnalysisMode analysisMode) {
     this.pluginRepository = pluginRepository;
+    this.analysisMode = analysisMode;
   }
 
   public ExtensionInstaller install(ComponentContainer container, ExtensionMatcher matcher) {
 
     // core components
-    for (Object o : BatchComponents.all()) {
+    for (Object o : BatchComponents.all(analysisMode)) {
       doInstall(container, matcher, null, o);
     }
 
index 9414d409d6ed876be4aa55a6141fe8cf133f1819..30f4863783db650df67b327fcf8abf5d4828b83e 100644 (file)
@@ -31,8 +31,8 @@ public final class CpdComponents {
     return ImmutableList.of(
       CpdSensor.class,
       CpdMappings.class,
-      JavaCpdIndexer.class,
-      DefaultCpdIndexer.class);
+      JavaCpdBlockIndexer.class,
+      DefaultCpdBlockIndexer.class);
   }
 
 }
index 59a48f83fcff1b5ffcff32c1861a431dae9dcd78..38cee8c127c361759dffe690238c99892c7ff815 100644 (file)
@@ -26,7 +26,7 @@ import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.config.Settings;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
-import org.sonar.batch.cpd.index.SonarDuplicationsIndex;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.protocol.output.BatchReport;
@@ -64,7 +64,7 @@ public class CpdExecutor {
   static final int MAX_CLONE_GROUP_PER_FILE = 100;
   static final int MAX_CLONE_PART_PER_GROUP = 100;
 
-  private final SonarDuplicationsIndex index;
+  private final SonarCpdBlockIndex index;
   private final ReportPublisher publisher;
   private final BatchComponentCache batchComponentCache;
   private final Settings settings;
@@ -73,7 +73,7 @@ public class CpdExecutor {
   private int count;
   private int total;
 
-  public CpdExecutor(Settings settings, SonarDuplicationsIndex index, ReportPublisher publisher, BatchComponentCache batchComponentCache) {
+  public CpdExecutor(Settings settings, SonarCpdBlockIndex index, ReportPublisher publisher, BatchComponentCache batchComponentCache) {
     this.settings = settings;
     this.index = index;
     this.publisher = publisher;
index 7abede1b42ed955b45712966c22c810acc04bc33..9ea1bac92540832f1ee6d4131b0247e8dccf6216 100644 (file)
@@ -40,7 +40,7 @@ public class CpdSensor implements Sensor {
   private Settings settings;
   private FileSystem fs;
 
-  public CpdSensor(JavaCpdIndexer sonarEngine, DefaultCpdIndexer sonarBridgeEngine, Settings settings, FileSystem fs) {
+  public CpdSensor(JavaCpdBlockIndexer sonarEngine, DefaultCpdBlockIndexer sonarBridgeEngine, Settings settings, FileSystem fs) {
     this.sonarEngine = sonarEngine;
     this.sonarBridgeEngine = sonarBridgeEngine;
     this.settings = settings;
@@ -49,8 +49,7 @@ public class CpdSensor implements Sensor {
 
   @Override
   public void describe(SensorDescriptor descriptor) {
-    descriptor.name("CPD Sensor")
-      .disabledInIssues();
+    descriptor.name("CPD Sensor");
   }
 
   @VisibleForTesting
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdBlockIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdBlockIndexer.java
new file mode 100644 (file)
index 0000000..33b2f26
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.cpd;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.CpdMapping;
+import org.sonar.api.batch.fs.FilePredicates;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.internal.pmd.TokenizerBridge;
+
+public class DefaultCpdBlockIndexer extends CpdIndexer {
+
+  private static final Logger LOG = LoggerFactory.getLogger(DefaultCpdBlockIndexer.class);
+
+  private final CpdMappings mappings;
+  private final FileSystem fs;
+  private final Settings settings;
+  private final SonarCpdBlockIndex index;
+
+  public DefaultCpdBlockIndexer(CpdMappings mappings, FileSystem fs, Settings settings, SonarCpdBlockIndex index) {
+    this.mappings = mappings;
+    this.fs = fs;
+    this.settings = settings;
+    this.index = index;
+  }
+
+  @Override
+  public boolean isLanguageSupported(String language) {
+    return true;
+  }
+
+  @Override
+  public void index(String languageKey) {
+    CpdMapping mapping = mappings.getMapping(languageKey);
+    if (mapping == null) {
+      LOG.debug("No CpdMapping for language " + languageKey);
+      return;
+    }
+
+    String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS);
+    logExclusions(cpdExclusions, LOG);
+    FilePredicates p = fs.predicates();
+    List<InputFile> sourceFiles = Lists.newArrayList(fs.inputFiles(p.and(
+      p.hasType(InputFile.Type.MAIN),
+      p.hasLanguage(languageKey),
+      p.doesNotMatchPathPatterns(cpdExclusions))));
+    if (sourceFiles.isEmpty()) {
+      return;
+    }
+
+    // Create index
+    populateIndex(languageKey, sourceFiles, mapping);
+  }
+
+  private void populateIndex(String languageKey, List<InputFile> sourceFiles, CpdMapping mapping) {
+    TokenizerBridge bridge = new TokenizerBridge(mapping.getTokenizer(), fs.encoding().name(), getBlockSize(languageKey));
+    for (InputFile inputFile : sourceFiles) {
+      if (!index.isIndexed(inputFile)) {
+        LOG.debug("Populating index from {}", inputFile);
+        String resourceEffectiveKey = ((DefaultInputFile) inputFile).key();
+        List<Block> blocks = bridge.chunk(resourceEffectiveKey, inputFile.file());
+        index.insert(inputFile, blocks);
+      }
+    }
+  }
+
+  @VisibleForTesting
+  int getBlockSize(String languageKey) {
+    int blockSize = settings.getInt("sonar.cpd." + languageKey + ".minimumLines");
+    if (blockSize == 0) {
+      blockSize = getDefaultBlockSize(languageKey);
+    }
+    return blockSize;
+  }
+
+  @VisibleForTesting
+  public static int getDefaultBlockSize(String languageKey) {
+    if ("cobol".equals(languageKey)) {
+      return 30;
+    } else if ("abap".equals(languageKey)) {
+      return 20;
+    } else {
+      return 10;
+    }
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/DefaultCpdIndexer.java
deleted file mode 100644 (file)
index ffa2a2b..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.cpd;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Lists;
-import java.util.List;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.CpdMapping;
-import org.sonar.api.batch.fs.FilePredicates;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.config.Settings;
-import org.sonar.batch.cpd.index.SonarDuplicationsIndex;
-import org.sonar.duplications.block.Block;
-import org.sonar.duplications.internal.pmd.TokenizerBridge;
-
-public class DefaultCpdIndexer extends CpdIndexer {
-
-  private static final Logger LOG = LoggerFactory.getLogger(DefaultCpdIndexer.class);
-
-  private final CpdMappings mappings;
-  private final FileSystem fs;
-  private final Settings settings;
-  private final SonarDuplicationsIndex index;
-
-  public DefaultCpdIndexer(CpdMappings mappings, FileSystem fs, Settings settings, SonarDuplicationsIndex index) {
-    this.mappings = mappings;
-    this.fs = fs;
-    this.settings = settings;
-    this.index = index;
-  }
-
-  @Override
-  public boolean isLanguageSupported(String language) {
-    return true;
-  }
-
-  @Override
-  public void index(String languageKey) {
-    CpdMapping mapping = mappings.getMapping(languageKey);
-    if (mapping == null) {
-      LOG.debug("No CpdMapping for language " + languageKey);
-      return;
-    }
-
-    String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS);
-    logExclusions(cpdExclusions, LOG);
-    FilePredicates p = fs.predicates();
-    List<InputFile> sourceFiles = Lists.newArrayList(fs.inputFiles(p.and(
-      p.hasType(InputFile.Type.MAIN),
-      p.hasLanguage(languageKey),
-      p.doesNotMatchPathPatterns(cpdExclusions))));
-    if (sourceFiles.isEmpty()) {
-      return;
-    }
-
-    // Create index
-    populateIndex(languageKey, sourceFiles, mapping, index);
-  }
-
-  private void populateIndex(String languageKey, List<InputFile> sourceFiles, CpdMapping mapping, SonarDuplicationsIndex index) {
-    TokenizerBridge bridge = new TokenizerBridge(mapping.getTokenizer(), fs.encoding().name(), getBlockSize(languageKey));
-    for (InputFile inputFile : sourceFiles) {
-      LOG.debug("Populating index from {}", inputFile);
-      String resourceEffectiveKey = ((DefaultInputFile) inputFile).key();
-      List<Block> blocks = bridge.chunk(resourceEffectiveKey, inputFile.file());
-      index.insert(inputFile, blocks);
-    }
-  }
-
-  @VisibleForTesting
-  int getBlockSize(String languageKey) {
-    int blockSize = settings.getInt("sonar.cpd." + languageKey + ".minimumLines");
-    if (blockSize == 0) {
-      blockSize = getDefaultBlockSize(languageKey);
-    }
-    return blockSize;
-  }
-
-  @VisibleForTesting
-  static int getDefaultBlockSize(String languageKey) {
-    if ("cobol".equals(languageKey)) {
-      return 30;
-    } else if ("abap".equals(languageKey)) {
-      return 20;
-    } else {
-      return 10;
-    }
-  }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdBlockIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdBlockIndexer.java
new file mode 100644 (file)
index 0000000..a2dde81
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.cpd;
+
+import com.google.common.collect.Lists;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.fs.FilePredicates;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.block.BlockChunker;
+import org.sonar.duplications.java.JavaStatementBuilder;
+import org.sonar.duplications.java.JavaTokenProducer;
+import org.sonar.duplications.statement.Statement;
+import org.sonar.duplications.statement.StatementChunker;
+import org.sonar.duplications.token.TokenChunker;
+
+public class JavaCpdBlockIndexer extends CpdIndexer {
+
+  private static final Logger LOG = LoggerFactory.getLogger(JavaCpdBlockIndexer.class);
+
+  private static final int BLOCK_SIZE = 10;
+
+  private final FileSystem fs;
+  private final Settings settings;
+  private final SonarCpdBlockIndex index;
+
+  public JavaCpdBlockIndexer(FileSystem fs, Settings settings, SonarCpdBlockIndex index) {
+    this.fs = fs;
+    this.settings = settings;
+    this.index = index;
+  }
+
+  @Override
+  public boolean isLanguageSupported(String language) {
+    return "java".equals(language);
+  }
+
+  @Override
+  public void index(String languageKey) {
+    String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS);
+    logExclusions(cpdExclusions, LOG);
+    FilePredicates p = fs.predicates();
+    List<InputFile> sourceFiles = Lists.newArrayList(fs.inputFiles(p.and(
+      p.hasType(InputFile.Type.MAIN),
+      p.hasLanguage(languageKey),
+      p.doesNotMatchPathPatterns(cpdExclusions))));
+    if (sourceFiles.isEmpty()) {
+      return;
+    }
+    createIndex(sourceFiles);
+  }
+
+  private void createIndex(Iterable<InputFile> sourceFiles) {
+    TokenChunker tokenChunker = JavaTokenProducer.build();
+    StatementChunker statementChunker = JavaStatementBuilder.build();
+    BlockChunker blockChunker = new BlockChunker(BLOCK_SIZE);
+
+    for (InputFile inputFile : sourceFiles) {
+      LOG.debug("Populating index from {}", inputFile);
+      String resourceEffectiveKey = ((DefaultInputFile) inputFile).key();
+
+      List<Statement> statements;
+
+      try(Reader reader = new InputStreamReader(new FileInputStream(inputFile.file()), fs.encoding())) {
+        statements = statementChunker.chunk(tokenChunker.chunk(reader));
+      } catch (FileNotFoundException e) {
+        throw new IllegalStateException("Cannot find file " + inputFile.file(), e);
+      } catch (IOException e ) {
+        throw new IllegalStateException("Exception hnadling file: " + inputFile.file(), e);
+      }
+
+      List<Block> blocks = blockChunker.chunk(resourceEffectiveKey, statements);
+      index.insert(inputFile, blocks);
+    }
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/JavaCpdIndexer.java
deleted file mode 100644 (file)
index fe8fb50..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.cpd;
-
-import com.google.common.collect.Lists;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.List;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.fs.FilePredicates;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.config.Settings;
-import org.sonar.batch.cpd.index.SonarDuplicationsIndex;
-import org.sonar.duplications.block.Block;
-import org.sonar.duplications.block.BlockChunker;
-import org.sonar.duplications.java.JavaStatementBuilder;
-import org.sonar.duplications.java.JavaTokenProducer;
-import org.sonar.duplications.statement.Statement;
-import org.sonar.duplications.statement.StatementChunker;
-import org.sonar.duplications.token.TokenChunker;
-
-public class JavaCpdIndexer extends CpdIndexer {
-
-  private static final Logger LOG = LoggerFactory.getLogger(JavaCpdIndexer.class);
-
-  private static final int BLOCK_SIZE = 10;
-
-  private final FileSystem fs;
-  private final Settings settings;
-  private final SonarDuplicationsIndex index;
-
-  public JavaCpdIndexer(FileSystem fs, Settings settings, SonarDuplicationsIndex index) {
-    this.fs = fs;
-    this.settings = settings;
-    this.index = index;
-  }
-
-  @Override
-  public boolean isLanguageSupported(String language) {
-    return "java".equals(language);
-  }
-
-  @Override
-  public void index(String languageKey) {
-    String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS);
-    logExclusions(cpdExclusions, LOG);
-    FilePredicates p = fs.predicates();
-    List<InputFile> sourceFiles = Lists.newArrayList(fs.inputFiles(p.and(
-      p.hasType(InputFile.Type.MAIN),
-      p.hasLanguage(languageKey),
-      p.doesNotMatchPathPatterns(cpdExclusions))));
-    if (sourceFiles.isEmpty()) {
-      return;
-    }
-    createIndex(sourceFiles);
-  }
-
-  private void createIndex(Iterable<InputFile> sourceFiles) {
-    TokenChunker tokenChunker = JavaTokenProducer.build();
-    StatementChunker statementChunker = JavaStatementBuilder.build();
-    BlockChunker blockChunker = new BlockChunker(BLOCK_SIZE);
-
-    for (InputFile inputFile : sourceFiles) {
-      LOG.debug("Populating index from {}", inputFile);
-      String resourceEffectiveKey = ((DefaultInputFile) inputFile).key();
-
-      List<Statement> statements;
-
-      try(Reader reader = new InputStreamReader(new FileInputStream(inputFile.file()), fs.encoding())) {
-        statements = statementChunker.chunk(tokenChunker.chunk(reader));
-      } catch (FileNotFoundException e) {
-        throw new IllegalStateException("Cannot find file " + inputFile.file(), e);
-      } catch (IOException e ) {
-        throw new IllegalStateException("Exception hnadling file: " + inputFile.file(), e);
-      }
-
-      List<Block> blocks = blockChunker.chunk(resourceEffectiveKey, statements);
-      index.insert(inputFile, blocks);
-    }
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarCpdBlockIndex.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarCpdBlockIndex.java
new file mode 100644 (file)
index 0000000..48ee611
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.cpd.index;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.batch.protocol.output.BatchReport;
+import org.sonar.batch.report.ReportPublisher;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.block.ByteArray;
+import org.sonar.duplications.index.AbstractCloneIndex;
+import org.sonar.duplications.index.CloneIndex;
+import org.sonar.duplications.index.PackedMemoryCloneIndex;
+import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks;
+
+public class SonarCpdBlockIndex extends AbstractCloneIndex {
+
+  private final CloneIndex mem = new PackedMemoryCloneIndex();
+  private final ReportPublisher publisher;
+  private final BatchComponentCache batchComponentCache;
+  private final Settings settings;
+  // Files already tokenized
+  private final Set<InputFile> indexedFiles = new HashSet<>();
+
+  public SonarCpdBlockIndex(ReportPublisher publisher, BatchComponentCache batchComponentCache, Settings settings) {
+    this.publisher = publisher;
+    this.batchComponentCache = batchComponentCache;
+    this.settings = settings;
+  }
+
+  public void insert(InputFile inputFile, Collection<Block> blocks) {
+    if (isCrossProjectDuplicationEnabled(settings)) {
+      int id = batchComponentCache.get(inputFile).batchId();
+      final BatchReport.CpdTextBlock.Builder builder = BatchReport.CpdTextBlock.newBuilder();
+      publisher.getWriter().writeCpdTextBlocks(id, Iterables.transform(blocks, new Function<Block, BatchReport.CpdTextBlock>() {
+        @Override
+        public BatchReport.CpdTextBlock apply(Block input) {
+          builder.clear();
+          builder.setStartLine(input.getStartLine());
+          builder.setEndLine(input.getEndLine());
+          builder.setStartTokenIndex(input.getStartUnit());
+          builder.setEndTokenIndex(input.getEndUnit());
+          builder.setHash(input.getBlockHash().toHexString());
+          return builder.build();
+        }
+      }));
+    }
+    for (Block block : blocks) {
+      mem.insert(block);
+    }
+    indexedFiles.add(inputFile);
+  }
+
+  public boolean isIndexed(InputFile inputFile) {
+    return indexedFiles.contains(inputFile);
+  }
+
+  public static boolean isCrossProjectDuplicationEnabled(Settings settings) {
+    return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT)
+      // No cross project duplication for branches
+      && StringUtils.isBlank(settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY));
+  }
+
+  public Collection<Block> getByInputFile(String resourceKey) {
+    return mem.getByResourceId(resourceKey);
+  }
+
+  @Override
+  public Collection<Block> getBySequenceHash(ByteArray hash) {
+    return mem.getBySequenceHash(hash);
+  }
+
+  @Override
+  public Collection<Block> getByResourceId(String resourceId) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public void insert(Block block) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public Iterator<ResourceBlocks> iterator() {
+    return mem.iterator();
+  }
+  
+  @Override
+  public int noResources() {
+    return mem.noResources();
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarDuplicationsIndex.java b/sonar-batch/src/main/java/org/sonar/batch/cpd/index/SonarDuplicationsIndex.java
deleted file mode 100644 (file)
index b9c904b..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.cpd.index;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
-import java.util.Collection;
-import java.util.Iterator;
-
-import org.apache.commons.lang.StringUtils;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.config.Settings;
-import org.sonar.batch.index.BatchComponentCache;
-import org.sonar.batch.protocol.output.BatchReport;
-import org.sonar.batch.report.ReportPublisher;
-import org.sonar.duplications.block.Block;
-import org.sonar.duplications.block.ByteArray;
-import org.sonar.duplications.index.AbstractCloneIndex;
-import org.sonar.duplications.index.CloneIndex;
-import org.sonar.duplications.index.PackedMemoryCloneIndex;
-import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks;
-
-public class SonarDuplicationsIndex extends AbstractCloneIndex {
-
-  private final CloneIndex mem = new PackedMemoryCloneIndex();
-  private final ReportPublisher publisher;
-  private final BatchComponentCache batchComponentCache;
-  private final Settings settings;
-
-  public SonarDuplicationsIndex(ReportPublisher publisher, BatchComponentCache batchComponentCache, Settings settings) {
-    this.publisher = publisher;
-    this.batchComponentCache = batchComponentCache;
-    this.settings = settings;
-  }
-
-  public void insert(InputFile inputFile, Collection<Block> blocks) {
-    if (isCrossProjectDuplicationEnabled(settings)) {
-      int id = batchComponentCache.get(inputFile).batchId();
-      final BatchReport.CpdTextBlock.Builder builder = BatchReport.CpdTextBlock.newBuilder();
-      publisher.getWriter().writeCpdTextBlocks(id, Iterables.transform(blocks, new Function<Block, BatchReport.CpdTextBlock>() {
-        @Override
-        public BatchReport.CpdTextBlock apply(Block input) {
-          builder.clear();
-          builder.setStartLine(input.getStartLine());
-          builder.setEndLine(input.getEndLine());
-          builder.setStartTokenIndex(input.getStartUnit());
-          builder.setEndTokenIndex(input.getEndUnit());
-          builder.setHash(input.getBlockHash().toHexString());
-          return builder.build();
-        }
-      }));
-    }
-    for (Block block : blocks) {
-      mem.insert(block);
-    }
-  }
-
-  public static boolean isCrossProjectDuplicationEnabled(Settings settings) {
-    return settings.getBoolean(CoreProperties.CPD_CROSS_PROJECT)
-      // No cross project duplication for branches
-      && StringUtils.isBlank(settings.getString(CoreProperties.PROJECT_BRANCH_PROPERTY));
-  }
-
-  public Collection<Block> getByInputFile(String resourceKey) {
-    return mem.getByResourceId(resourceKey);
-  }
-
-  @Override
-  public Collection<Block> getBySequenceHash(ByteArray hash) {
-    return mem.getBySequenceHash(hash);
-  }
-
-  @Override
-  public Collection<Block> getByResourceId(String resourceId) {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
-  public void insert(Block block) {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
-  public Iterator<ResourceBlocks> iterator() {
-    return mem.iterator();
-  }
-  
-  @Override
-  public int noResources() {
-    return mem.noResources();
-  }
-
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/AbstractPhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/AbstractPhaseExecutor.java
new file mode 100644 (file)
index 0000000..c388b75
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.phases;
+
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.resources.Project;
+import org.sonar.batch.events.BatchStepEvent;
+import org.sonar.batch.events.EventBus;
+import org.sonar.batch.index.DefaultIndex;
+import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader;
+import org.sonar.batch.rule.QProfileVerifier;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.batch.scan.filesystem.FileSystemLogger;
+
+public abstract class AbstractPhaseExecutor {
+
+  private final EventBus eventBus;
+  private final PostJobsExecutor postJobsExecutor;
+  private final InitializersExecutor initializersExecutor;
+  private final SensorsExecutor sensorsExecutor;
+  private final SensorContext sensorContext;
+  private final DefaultIndex index;
+  private final ProjectInitializer pi;
+  private final FileSystemLogger fsLogger;
+  private final DefaultModuleFileSystem fs;
+  private final QProfileVerifier profileVerifier;
+  private final IssueExclusionsLoader issueExclusionsLoader;
+
+  public AbstractPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
+    SensorContext sensorContext, DefaultIndex index,
+    EventBus eventBus, ProjectInitializer pi,
+    FileSystemLogger fsLogger, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
+    IssueExclusionsLoader issueExclusionsLoader) {
+    this.postJobsExecutor = postJobsExecutor;
+    this.initializersExecutor = initializersExecutor;
+    this.sensorsExecutor = sensorsExecutor;
+    this.sensorContext = sensorContext;
+    this.index = index;
+    this.eventBus = eventBus;
+    this.pi = pi;
+    this.fsLogger = fsLogger;
+    this.fs = fs;
+    this.profileVerifier = profileVerifier;
+    this.issueExclusionsLoader = issueExclusionsLoader;
+  }
+
+  /**
+   * Executed on each module
+   */
+  public final void execute(Project module) {
+    pi.execute(module);
+
+    eventBus.fireEvent(new ProjectAnalysisEvent(module, true));
+
+    executeInitializersPhase();
+
+    // Index and lock the filesystem
+    indexFs();
+
+    // Log detected languages and their profiles after FS is indexed and languages detected
+    profileVerifier.execute();
+
+    // Initialize issue exclusions
+    initIssueExclusions();
+
+    sensorsExecutor.execute(sensorContext);
+
+    if (module.isRoot()) {
+      executeOnRoot();
+      postJobsExecutor.execute(sensorContext);
+    }
+    cleanMemory();
+    eventBus.fireEvent(new ProjectAnalysisEvent(module, false));
+  }
+
+  protected abstract void executeOnRoot();
+
+  private void initIssueExclusions() {
+    String stepName = "Init issue exclusions";
+    eventBus.fireEvent(new BatchStepEvent(stepName, true));
+    issueExclusionsLoader.execute();
+    eventBus.fireEvent(new BatchStepEvent(stepName, false));
+  }
+
+  private void indexFs() {
+    String stepName = "Index filesystem";
+    eventBus.fireEvent(new BatchStepEvent(stepName, true));
+    fs.index();
+    eventBus.fireEvent(new BatchStepEvent(stepName, false));
+  }
+
+  private void executeInitializersPhase() {
+    initializersExecutor.execute();
+    fsLogger.log();
+  }
+
+  private void cleanMemory() {
+    String cleanMemory = "Clean memory";
+    eventBus.fireEvent(new BatchStepEvent(cleanMemory, true));
+    index.clear();
+    eventBus.fireEvent(new BatchStepEvent(cleanMemory, false));
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/IssuesPhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/IssuesPhaseExecutor.java
new file mode 100644 (file)
index 0000000..ba9a155
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.phases;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.batch.events.BatchStepEvent;
+import org.sonar.batch.events.EventBus;
+import org.sonar.batch.index.DefaultIndex;
+import org.sonar.batch.issue.IssueCallback;
+import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader;
+import org.sonar.batch.issue.tracking.IssueTransition;
+import org.sonar.batch.rule.QProfileVerifier;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.batch.scan.filesystem.FileSystemLogger;
+import org.sonar.batch.scan.report.IssuesReports;
+
+public final class IssuesPhaseExecutor extends AbstractPhaseExecutor {
+
+  private static final Logger LOG = LoggerFactory.getLogger(IssuesPhaseExecutor.class);
+
+  private final EventBus eventBus;
+  private final IssuesReports issuesReport;
+  private final IssueTransition localIssueTracking;
+  private final IssueCallback issueCallback;
+
+  public IssuesPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext,
+    DefaultIndex index, EventBus eventBus, ProjectInitializer pi, FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
+    IssueExclusionsLoader issueExclusionsLoader, IssueTransition localIssueTracking, IssueCallback issueCallback) {
+    super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, index, eventBus, pi, fsLogger, fs, profileVerifier, issueExclusionsLoader);
+    this.eventBus = eventBus;
+    this.issuesReport = jsonReport;
+    this.localIssueTracking = localIssueTracking;
+    this.issueCallback = issueCallback;
+  }
+
+  @Override
+  protected void executeOnRoot() {
+    localIssueTracking();
+    issuesCallback();
+    issuesReport();
+    LOG.info("ANALYSIS SUCCESSFUL");
+  }
+
+  private void localIssueTracking() {
+    String stepName = "Local Issue Tracking";
+    eventBus.fireEvent(new BatchStepEvent(stepName, true));
+    localIssueTracking.execute();
+    eventBus.fireEvent(new BatchStepEvent(stepName, false));
+  }
+
+  private void issuesCallback() {
+    String stepName = "Issues Callback";
+    eventBus.fireEvent(new BatchStepEvent(stepName, true));
+    issueCallback.execute();
+    eventBus.fireEvent(new BatchStepEvent(stepName, false));
+  }
+
+  private void issuesReport() {
+    String stepName = "Issues Reports";
+    eventBus.fireEvent(new BatchStepEvent(stepName, true));
+    issuesReport.execute();
+    eventBus.fireEvent(new BatchStepEvent(stepName, false));
+  }
+
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java
deleted file mode 100644 (file)
index e8cbd7d..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.phases;
-
-import org.sonar.api.batch.SensorContext;
-import org.sonar.api.resources.Project;
-import org.sonar.batch.analysis.DefaultAnalysisMode;
-import org.sonar.batch.cpd.CpdExecutor;
-import org.sonar.batch.events.BatchStepEvent;
-import org.sonar.batch.events.EventBus;
-import org.sonar.batch.index.DefaultIndex;
-import org.sonar.batch.issue.IssueCallback;
-import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader;
-import org.sonar.batch.issue.tracking.IssueTransition;
-import org.sonar.batch.report.ReportPublisher;
-import org.sonar.batch.rule.QProfileVerifier;
-import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
-import org.sonar.batch.scan.filesystem.FileSystemLogger;
-import org.sonar.batch.scan.report.IssuesReports;
-
-public final class PhaseExecutor {
-
-  private final EventBus eventBus;
-  private final PostJobsExecutor postJobsExecutor;
-  private final InitializersExecutor initializersExecutor;
-  private final SensorsExecutor sensorsExecutor;
-  private final ReportPublisher reportPublisher;
-  private final SensorContext sensorContext;
-  private final DefaultIndex index;
-  private final ProjectInitializer pi;
-  private final FileSystemLogger fsLogger;
-  private final DefaultModuleFileSystem fs;
-  private final QProfileVerifier profileVerifier;
-  private final IssueExclusionsLoader issueExclusionsLoader;
-  private final IssuesReports issuesReport;
-  private final DefaultAnalysisMode analysisMode;
-  private final IssueTransition localIssueTracking;
-  private final IssueCallback issueCallback;
-  private final CpdExecutor cpdExecutor;
-
-  public PhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor,
-    SensorContext sensorContext, DefaultIndex index,
-    EventBus eventBus, ReportPublisher reportPublisher, ProjectInitializer pi,
-    FileSystemLogger fsLogger, IssuesReports jsonReport, DefaultModuleFileSystem fs, QProfileVerifier profileVerifier,
-    IssueExclusionsLoader issueExclusionsLoader, DefaultAnalysisMode analysisMode, IssueTransition localIssueTracking, IssueCallback issueCallback,
-    CpdExecutor cpdExecutor) {
-    this.postJobsExecutor = postJobsExecutor;
-    this.initializersExecutor = initializersExecutor;
-    this.sensorsExecutor = sensorsExecutor;
-    this.sensorContext = sensorContext;
-    this.index = index;
-    this.eventBus = eventBus;
-    this.reportPublisher = reportPublisher;
-    this.pi = pi;
-    this.fsLogger = fsLogger;
-    this.issuesReport = jsonReport;
-    this.fs = fs;
-    this.profileVerifier = profileVerifier;
-    this.issueExclusionsLoader = issueExclusionsLoader;
-    this.analysisMode = analysisMode;
-    this.localIssueTracking = localIssueTracking;
-    this.issueCallback = issueCallback;
-    this.cpdExecutor = cpdExecutor;
-  }
-
-  /**
-   * Executed on each module
-   */
-  public void execute(Project module) {
-    pi.execute(module);
-
-    eventBus.fireEvent(new ProjectAnalysisEvent(module, true));
-
-    executeInitializersPhase();
-
-    // Index and lock the filesystem
-    indexFs();
-
-    // Log detected languages and their profiles after FS is indexed and languages detected
-    profileVerifier.execute();
-
-    // Initialize issue exclusions
-    initIssueExclusions();
-
-    sensorsExecutor.execute(sensorContext);
-
-    if (module.isRoot()) {
-      if (analysisMode.isIssues()) {
-        localIssueTracking();
-        issuesCallback();
-      } else {
-        computeDuplications();
-      }
-      issuesReport();
-      publishReportJob();
-      postJobsExecutor.execute(sensorContext);
-    }
-    cleanMemory();
-    eventBus.fireEvent(new ProjectAnalysisEvent(module, false));
-  }
-
-  private void computeDuplications() {
-    String stepName = "Computing duplications";
-    eventBus.fireEvent(new BatchStepEvent(stepName, true));
-    cpdExecutor.execute();
-    eventBus.fireEvent(new BatchStepEvent(stepName, false));
-  }
-
-  private void publishReportJob() {
-    String stepName = "Publish report";
-    eventBus.fireEvent(new BatchStepEvent(stepName, true));
-    this.reportPublisher.execute();
-    eventBus.fireEvent(new BatchStepEvent(stepName, false));
-  }
-
-  private void localIssueTracking() {
-    String stepName = "Local Issue Tracking";
-    eventBus.fireEvent(new BatchStepEvent(stepName, true));
-    localIssueTracking.execute();
-    eventBus.fireEvent(new BatchStepEvent(stepName, false));
-  }
-
-  private void issuesCallback() {
-    String stepName = "Issues Callback";
-    eventBus.fireEvent(new BatchStepEvent(stepName, true));
-    issueCallback.execute();
-    eventBus.fireEvent(new BatchStepEvent(stepName, false));
-  }
-
-  private void issuesReport() {
-    String stepName = "Issues Reports";
-    eventBus.fireEvent(new BatchStepEvent(stepName, true));
-    issuesReport.execute();
-    eventBus.fireEvent(new BatchStepEvent(stepName, false));
-  }
-
-  private void initIssueExclusions() {
-    String stepName = "Init issue exclusions";
-    eventBus.fireEvent(new BatchStepEvent(stepName, true));
-    issueExclusionsLoader.execute();
-    eventBus.fireEvent(new BatchStepEvent(stepName, false));
-  }
-
-  private void indexFs() {
-    String stepName = "Index filesystem";
-    eventBus.fireEvent(new BatchStepEvent(stepName, true));
-    fs.index();
-    eventBus.fireEvent(new BatchStepEvent(stepName, false));
-  }
-
-  private void executeInitializersPhase() {
-    initializersExecutor.execute();
-    fsLogger.log();
-  }
-
-  private void cleanMemory() {
-    String cleanMemory = "Clean memory";
-    eventBus.fireEvent(new BatchStepEvent(cleanMemory, true));
-    index.clear();
-    eventBus.fireEvent(new BatchStepEvent(cleanMemory, false));
-  }
-}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/PublishPhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/PublishPhaseExecutor.java
new file mode 100644 (file)
index 0000000..33073a1
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.phases;
+
+import org.sonar.api.batch.SensorContext;
+import org.sonar.batch.cpd.CpdExecutor;
+import org.sonar.batch.events.BatchStepEvent;
+import org.sonar.batch.events.EventBus;
+import org.sonar.batch.index.DefaultIndex;
+import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader;
+import org.sonar.batch.report.ReportPublisher;
+import org.sonar.batch.rule.QProfileVerifier;
+import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem;
+import org.sonar.batch.scan.filesystem.FileSystemLogger;
+
+public final class PublishPhaseExecutor extends AbstractPhaseExecutor {
+
+  private final EventBus eventBus;
+  private final ReportPublisher reportPublisher;
+  private final CpdExecutor cpdExecutor;
+
+  public PublishPhaseExecutor(InitializersExecutor initializersExecutor, PostJobsExecutor postJobsExecutor, SensorsExecutor sensorsExecutor, SensorContext sensorContext,
+    DefaultIndex index, EventBus eventBus, ReportPublisher reportPublisher, ProjectInitializer pi, FileSystemLogger fsLogger, DefaultModuleFileSystem fs,
+    QProfileVerifier profileVerifier, IssueExclusionsLoader issueExclusionsLoader, CpdExecutor cpdExecutor) {
+    super(initializersExecutor, postJobsExecutor, sensorsExecutor, sensorContext, index, eventBus, pi, fsLogger, fs, profileVerifier, issueExclusionsLoader);
+    this.eventBus = eventBus;
+    this.reportPublisher = reportPublisher;
+    this.cpdExecutor = cpdExecutor;
+  }
+
+  @Override
+  protected void executeOnRoot() {
+    computeDuplications();
+    publishReportJob();
+  }
+
+  private void computeDuplications() {
+    String stepName = "Computing duplications";
+    eventBus.fireEvent(new BatchStepEvent(stepName, true));
+    cpdExecutor.execute();
+    eventBus.fireEvent(new BatchStepEvent(stepName, false));
+  }
+
+  private void publishReportJob() {
+    String stepName = "Publish report";
+    eventBus.fireEvent(new BatchStepEvent(stepName, true));
+    this.reportPublisher.execute();
+    eventBus.fireEvent(new BatchStepEvent(stepName, false));
+  }
+}
index f6df892913c1b279374475a4923e6a1090f371a5..eac42e0ad7dfc86c361294cf206a952f450e100c 100644 (file)
@@ -23,7 +23,7 @@ import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Project;
-import org.sonar.batch.cpd.index.SonarDuplicationsIndex;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.protocol.output.BatchReport;
@@ -50,7 +50,7 @@ public class MetadataPublisher implements ReportPublisherStep {
       .setAnalysisDate(((Project) rootProject.resource()).getAnalysisDate().getTime())
       // Here we want key without branch
       .setProjectKey(root.getKey())
-      .setCrossProjectDuplicationActivated(SonarDuplicationsIndex.isCrossProjectDuplicationEnabled(settings))
+      .setCrossProjectDuplicationActivated(SonarCpdBlockIndex.isCrossProjectDuplicationEnabled(settings))
       .setRootComponentRef(rootProject.batchId());
     String branch = root.properties().get(CoreProperties.PROJECT_BRANCH_PROPERTY);
     if (branch != null) {
index 9f16173f65e3835afb2d841e1db7f214a33c689e..56da7fd624d98c50470dc1a57a50e6098ed7fbe3 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.batch.scan;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.InstantiationStrategy;
 import org.sonar.api.batch.bootstrap.ProjectDefinition;
 import org.sonar.api.batch.fs.internal.FileMetadata;
@@ -46,10 +47,12 @@ import org.sonar.batch.issue.ignore.pattern.IssueExclusionPatternInitializer;
 import org.sonar.batch.issue.ignore.pattern.IssueInclusionPatternInitializer;
 import org.sonar.batch.issue.ignore.scanner.IssueExclusionsLoader;
 import org.sonar.batch.issue.ignore.scanner.IssueExclusionsRegexpScanner;
+import org.sonar.batch.phases.AbstractPhaseExecutor;
 import org.sonar.batch.phases.InitializersExecutor;
-import org.sonar.batch.phases.PhaseExecutor;
+import org.sonar.batch.phases.IssuesPhaseExecutor;
 import org.sonar.batch.phases.PostJobsExecutor;
 import org.sonar.batch.phases.ProjectInitializer;
+import org.sonar.batch.phases.PublishPhaseExecutor;
 import org.sonar.batch.phases.SensorsExecutor;
 import org.sonar.batch.postjob.DefaultPostJobContext;
 import org.sonar.batch.postjob.PostJobOptimizer;
@@ -104,9 +107,15 @@ public class ModuleScanContainer extends ComponentContainer {
     ModuleSettings moduleSettings = getComponentByType(ModuleSettings.class);
     module.setSettings(moduleSettings);
 
+    if (getComponentByType(AnalysisMode.class).isIssues()) {
+      add(IssuesPhaseExecutor.class,
+        IssuesReports.class);
+    } else {
+      add(PublishPhaseExecutor.class);
+    }
+
     add(
       EventBus.class,
-      PhaseExecutor.class,
       RuleFinderCompatibility.class,
       PostJobsExecutor.class,
       SensorsExecutor.class,
@@ -145,9 +154,6 @@ public class ModuleScanContainer extends ComponentContainer {
       QProfileSensor.class,
       CheckFactory.class,
 
-      // report
-      IssuesReports.class,
-
       // issues
       IssuableFactory.class,
       ModuleIssues.class,
@@ -182,7 +188,7 @@ public class ModuleScanContainer extends ComponentContainer {
     DefaultIndex index = getComponentByType(DefaultIndex.class);
     index.setCurrentProject(module, getComponentByType(DefaultSensorStorage.class));
 
-    getComponentByType(PhaseExecutor.class).execute(module);
+    getComponentByType(AbstractPhaseExecutor.class).execute(module);
 
     // Free memory since module settings are no more used
     module.setSettings(null);
index 1c7d13a0df5a41bff371a663f7b3d576a71be639..f92f07acce879068890255319ef60352a461d269 100644 (file)
@@ -42,7 +42,7 @@ import org.sonar.batch.bootstrap.ExtensionUtils;
 import org.sonar.batch.bootstrap.MetricProvider;
 import org.sonar.batch.cache.ProjectPersistentCacheProvider;
 import org.sonar.batch.cpd.CpdExecutor;
-import org.sonar.batch.cpd.index.SonarDuplicationsIndex;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
 import org.sonar.batch.events.EventBus;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.index.Caches;
@@ -209,7 +209,7 @@ public class ProjectScanContainer extends ComponentContainer {
 
       // Cpd
       CpdExecutor.class,
-      SonarDuplicationsIndex.class,
+      SonarCpdBlockIndex.class,
 
       ScanTaskObservers.class,
       UserRepositoryLoader.class);
index 09007f830354b080baf2037bbd06180febf9523d..ac4a5abb3756012a188d9c2f475521b764d4fc9b 100644 (file)
  */
 package org.sonar.batch.scan.report;
 
-import org.sonar.batch.analysis.DefaultAnalysisMode;
-
 import org.sonar.api.batch.BatchSide;
 
 @BatchSide
 public class IssuesReports {
 
-  private final DefaultAnalysisMode analysisMode;
   private final Reporter[] reporters;
 
-  public IssuesReports(DefaultAnalysisMode analysisMode, Reporter... reporters) {
+  public IssuesReports(Reporter... reporters) {
     this.reporters = reporters;
-    this.analysisMode = analysisMode;
   }
 
   public void execute() {
-    if (analysisMode.isIssues() || analysisMode.isMediumTest()) {
-      for (Reporter reporter : reporters) {
-        reporter.execute();
-      }
+    for (Reporter reporter : reporters) {
+      reporter.execute();
     }
   }
 }
index 34547bfa932fdfad9a8f1039530e2939d643f478..078224bb6c6e3d4a5b5d5aa01334272de8139430 100644 (file)
@@ -60,8 +60,7 @@ public final class ScmSensor implements Sensor {
 
   @Override
   public void describe(SensorDescriptor descriptor) {
-    descriptor.name("SCM Sensor")
-      .disabledInIssues();
+    descriptor.name("SCM Sensor");
   }
 
   @Override
index 22251b3de9c63cd3dbeed8190f8c6bc1b41f5bbc..1080dab084a89ca0f9577c9f64ea695e56e43d90 100644 (file)
@@ -22,14 +22,14 @@ package org.sonar.batch.sensor;
 import java.io.Serializable;
 import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.InputModule;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.batch.sensor.coverage.NewCoverage;
 import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
+import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
+import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
 import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
-import org.sonar.api.batch.sensor.highlighting.TypeOfText;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
 import org.sonar.api.batch.sensor.internal.SensorStorage;
 import org.sonar.api.batch.sensor.issue.NewIssue;
@@ -37,29 +37,13 @@ import org.sonar.api.batch.sensor.issue.internal.DefaultIssue;
 import org.sonar.api.batch.sensor.measure.NewMeasure;
 import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure;
 import org.sonar.api.config.Settings;
+import org.sonar.batch.sensor.noop.NoOpNewCpdTokens;
+import org.sonar.batch.sensor.noop.NoOpNewHighlighting;
 
 public class DefaultSensorContext implements SensorContext {
 
   private static final NoOpNewHighlighting NO_OP_NEW_HIGHLIGHTING = new NoOpNewHighlighting();
-
-  private static final class NoOpNewHighlighting implements NewHighlighting {
-    @Override
-    public void save() {
-      // Do nothing
-    }
-
-    @Override
-    public NewHighlighting onFile(InputFile inputFile) {
-      // Do nothing
-      return this;
-    }
-
-    @Override
-    public NewHighlighting highlight(int startOffset, int endOffset, TypeOfText typeOfText) {
-      // Do nothing
-      return this;
-    }
-  }
+  private static final NoOpNewCpdTokens NO_OP_NEW_CPD_TOKENS = new NoOpNewCpdTokens();
 
   private final Settings settings;
   private final FileSystem fs;
@@ -92,11 +76,6 @@ public class DefaultSensorContext implements SensorContext {
     return activeRules;
   }
 
-  @Override
-  public AnalysisMode analysisMode() {
-    return analysisMode;
-  }
-
   @Override
   public InputModule module() {
     return module;
@@ -125,4 +104,12 @@ public class DefaultSensorContext implements SensorContext {
     return new DefaultCoverage(sensorStorage);
   }
 
+  @Override
+  public NewCpdTokens newCpdTokens() {
+    if (analysisMode.isIssues()) {
+      return NO_OP_NEW_CPD_TOKENS;
+    }
+    return new DefaultCpdTokens(sensorStorage);
+  }
+
 }
index 09604877532798a8b008fdd0c1fdde116c2aec9a..096a115d425d61071b62a57cfd1ae8f6897ec2c9 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.batch.sensor;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.collect.Iterables;
 import java.util.Arrays;
@@ -37,6 +38,7 @@ import org.sonar.api.batch.measure.MetricFinder;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.sensor.coverage.CoverageType;
 import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
+import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
 import org.sonar.api.batch.sensor.highlighting.internal.SyntaxHighlightingRule;
 import org.sonar.api.batch.sensor.internal.SensorStorage;
@@ -51,6 +53,8 @@ import org.sonar.api.resources.Resource;
 import org.sonar.api.source.Symbol;
 import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.api.utils.SonarException;
+import org.sonar.batch.cpd.DefaultCpdBlockIndexer;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.issue.ModuleIssues;
@@ -61,6 +65,8 @@ import org.sonar.batch.report.ReportPublisher;
 import org.sonar.batch.scan.measure.MeasureCache;
 import org.sonar.batch.sensor.coverage.CoverageExclusions;
 import org.sonar.batch.source.DefaultSymbol;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.internal.pmd.PmdBlockChunker;
 
 public class DefaultSensorStorage implements SensorStorage {
 
@@ -90,16 +96,20 @@ public class DefaultSensorStorage implements SensorStorage {
   private final BatchComponentCache componentCache;
   private final ReportPublisher reportPublisher;
   private final MeasureCache measureCache;
+  private final SonarCpdBlockIndex index;
+  private final Settings settings;
 
   public DefaultSensorStorage(MetricFinder metricFinder, ModuleIssues moduleIssues,
     Settings settings, FileSystem fs, ActiveRules activeRules,
-    CoverageExclusions coverageExclusions, BatchComponentCache componentCache, ReportPublisher reportPublisher, MeasureCache measureCache) {
+    CoverageExclusions coverageExclusions, BatchComponentCache componentCache, ReportPublisher reportPublisher, MeasureCache measureCache, SonarCpdBlockIndex index) {
     this.metricFinder = metricFinder;
     this.moduleIssues = moduleIssues;
+    this.settings = settings;
     this.coverageExclusions = coverageExclusions;
     this.componentCache = componentCache;
     this.reportPublisher = reportPublisher;
     this.measureCache = measureCache;
+    this.index = index;
   }
 
   private Metric findMetricOrFail(String metricKey) {
@@ -254,4 +264,22 @@ public class DefaultSensorStorage implements SensorStorage {
       return builder.build();
     }
   }
+
+  @Override
+  public void store(DefaultCpdTokens defaultCpdTokens) {
+    InputFile inputFile = defaultCpdTokens.inputFile();
+    PmdBlockChunker blockChunker = new PmdBlockChunker(getBlockSize(inputFile.language()));
+    List<Block> blocks = blockChunker.chunk(inputFile.key(), defaultCpdTokens.getTokenLines());
+    index.insert(inputFile, blocks);
+  }
+
+  @VisibleForTesting
+  int getBlockSize(String languageKey) {
+    int blockSize = settings.getInt("sonar.cpd." + languageKey + ".minimumLines");
+    if (blockSize == 0) {
+      blockSize = DefaultCpdBlockIndexer.getDefaultBlockSize(languageKey);
+    }
+    return blockSize;
+  }
+
 }
index 9b64ba381d3b69a317754a51dd2e7e16b1999dfc..7e8dd63e93a1f120c20805c773f8988073b15e9b 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.batch.sensor;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.BatchSide;
 import org.sonar.api.batch.fs.FilePredicate;
 import org.sonar.api.batch.fs.FileSystem;
@@ -37,13 +36,11 @@ public class SensorOptimizer {
   private final FileSystem fs;
   private final ActiveRules activeRules;
   private final Settings settings;
-  private final AnalysisMode analysisMode;
 
-  public SensorOptimizer(FileSystem fs, ActiveRules activeRules, Settings settings, AnalysisMode analysisMode) {
+  public SensorOptimizer(FileSystem fs, ActiveRules activeRules, Settings settings) {
     this.fs = fs;
     this.activeRules = activeRules;
     this.settings = settings;
-    this.analysisMode = analysisMode;
   }
 
   /**
@@ -62,10 +59,6 @@ public class SensorOptimizer {
       LOG.debug("'{}' skipped because one of the required properties is missing", descriptor.name());
       return false;
     }
-    if (descriptor.isDisabledInIssues() && analysisMode.isIssues()) {
-      LOG.debug("'{}' skipped in issues mode", descriptor.name());
-      return false;
-    }
     return true;
   }
 
diff --git a/sonar-batch/src/main/java/org/sonar/batch/sensor/noop/NoOpNewCpdTokens.java b/sonar-batch/src/main/java/org/sonar/batch/sensor/noop/NoOpNewCpdTokens.java
new file mode 100644 (file)
index 0000000..afd0047
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.sensor.noop;
+
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
+
+public class NoOpNewCpdTokens implements NewCpdTokens {
+  @Override
+  public void save() {
+    // Do nothing
+  }
+
+  @Override
+  public NoOpNewCpdTokens onFile(InputFile inputFile) {
+    // Do nothing
+    return this;
+  }
+
+  @Override
+  public NewCpdTokens addToken(TextRange range, String image) {
+    // Do nothing
+    return this;
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/sensor/noop/NoOpNewHighlighting.java b/sonar-batch/src/main/java/org/sonar/batch/sensor/noop/NoOpNewHighlighting.java
new file mode 100644 (file)
index 0000000..7ead32f
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.sensor.noop;
+
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
+import org.sonar.api.batch.sensor.highlighting.TypeOfText;
+
+public class NoOpNewHighlighting implements NewHighlighting {
+  @Override
+  public void save() {
+    // Do nothing
+  }
+
+  @Override
+  public NewHighlighting onFile(InputFile inputFile) {
+    // Do nothing
+    return this;
+  }
+
+  @Override
+  public NewHighlighting highlight(int startOffset, int endOffset, TypeOfText typeOfText) {
+    // Do nothing
+    return this;
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/sensor/noop/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/sensor/noop/package-info.java
new file mode 100644 (file)
index 0000000..0a654a4
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.batch.sensor.noop;
index f2d9f40bfb90bcd426e2c22484d3c51c8552282c..158e02a97775b1ec57fce2bbd1431172ad81889c 100644 (file)
@@ -44,8 +44,7 @@ public final class CodeColorizerSensor implements Sensor {
 
   @Override
   public void describe(SensorDescriptor descriptor) {
-    descriptor.name("Code Colorizer Sensor")
-      .disabledInIssues();
+    descriptor.name("Code Colorizer Sensor");
   }
 
   @Override
index 948f54dde46c7cec859b9e4d366708f0ad263f47..c368fb146b6e0c177103cb794140b9730c1a0f10 100644 (file)
@@ -27,6 +27,7 @@ import org.junit.Test;
 import org.sonar.api.BatchExtension;
 import org.sonar.api.ExtensionProvider;
 import org.sonar.api.SonarPlugin;
+import org.sonar.api.batch.AnalysisMode;
 import org.sonar.core.platform.ComponentContainer;
 import org.sonar.core.platform.PluginInfo;
 
@@ -58,7 +59,7 @@ public class ExtensionInstallerTest {
     when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(Foo.class, Bar.class));
 
     ComponentContainer container = new ComponentContainer();
-    ExtensionInstaller installer = new ExtensionInstaller(pluginRepository);
+    ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class));
     installer.install(container, new FooMatcher());
 
     assertThat(container.getComponentByType(Foo.class)).isNotNull();
@@ -70,7 +71,7 @@ public class ExtensionInstallerTest {
     when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("foo")));
     when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(new FooProvider(), new BarProvider()));
     ComponentContainer container = new ComponentContainer();
-    ExtensionInstaller installer = new ExtensionInstaller(pluginRepository);
+    ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class));
 
     installer.install(container, new FooMatcher());
 
@@ -83,7 +84,7 @@ public class ExtensionInstallerTest {
     when(pluginRepository.getPluginInfos()).thenReturn(Arrays.asList(new PluginInfo("foo")));
     when(pluginRepository.getPluginInstance("foo")).thenReturn(newPluginInstance(new FooBarProvider()));
     ComponentContainer container = new ComponentContainer();
-    ExtensionInstaller installer = new ExtensionInstaller(pluginRepository);
+    ExtensionInstaller installer = new ExtensionInstaller(pluginRepository, mock(AnalysisMode.class));
 
     installer.install(container, new TrueMatcher());
 
index 614078e78608e537eb01db669c9820ff40c44902..1da83a0e73021d160150820444effd3f44df9322 100644 (file)
@@ -29,7 +29,7 @@ import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Project;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
-import org.sonar.batch.cpd.index.SonarDuplicationsIndex;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.protocol.output.BatchReportReader;
@@ -55,7 +55,7 @@ import static org.mockito.Mockito.when;
 public class CpdExecutorTest {
   private CpdExecutor executor;
   private Settings settings;
-  private SonarDuplicationsIndex index;
+  private SonarCpdBlockIndex index;
   private ReportPublisher publisher;
   private BatchComponentCache componentCache;
 
@@ -77,7 +77,7 @@ public class CpdExecutorTest {
     File outputDir = temp.newFolder();
 
     settings = new Settings();
-    index = mock(SonarDuplicationsIndex.class);
+    index = mock(SonarCpdBlockIndex.class);
     publisher = mock(ReportPublisher.class);
     when(publisher.getWriter()).thenReturn(new BatchReportWriter(outputDir));
     componentCache = new BatchComponentCache();
index 44e1b900c3621f0a2ec932631b0b37d67a8cc404..ce27f0776c4a4742cf0e097cce084cbf0648b869 100644 (file)
@@ -36,15 +36,15 @@ public class CpdSensorTest {
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
 
-  JavaCpdIndexer sonarEngine;
-  DefaultCpdIndexer sonarBridgeEngine;
+  JavaCpdBlockIndexer sonarEngine;
+  DefaultCpdBlockIndexer sonarBridgeEngine;
   CpdSensor sensor;
   Settings settings;
 
   @Before
   public void setUp() throws IOException {
-    sonarEngine = new JavaCpdIndexer(null, null, null);
-    sonarBridgeEngine = new DefaultCpdIndexer(new CpdMappings(), null, null, null);
+    sonarEngine = new JavaCpdBlockIndexer(null, null, null);
+    sonarBridgeEngine = new DefaultCpdBlockIndexer(new CpdMappings(), null, null, null);
     settings = new Settings(new PropertyDefinitions(CpdComponents.class));
 
     DefaultFileSystem fs = new DefaultFileSystem(temp.newFolder().toPath());
diff --git a/sonar-batch/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java b/sonar-batch/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java
new file mode 100644 (file)
index 0000000..9fcd03a
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.cpd;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.sonar.api.config.Settings;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class DefaultCpdBlockIndexerTest {
+
+  private DefaultCpdBlockIndexer engine;
+  private Settings settings;
+
+  @Before
+  public void init() {
+    settings = new Settings();
+    engine = new DefaultCpdBlockIndexer(null, null, settings, null);
+  }
+
+  @Test
+  public void shouldLogExclusions() {
+    Logger logger = mock(Logger.class);
+    engine.logExclusions(new String[0], logger);
+    verify(logger, never()).info(anyString());
+
+    logger = mock(Logger.class);
+    engine.logExclusions(new String[] {"Foo*", "**/Bar*"}, logger);
+
+    String message = "Copy-paste detection exclusions:"
+      + "\n  Foo*"
+      + "\n  **/Bar*";
+    verify(logger, times(1)).info(message);
+  }
+
+  @Test
+  public void shouldReturnDefaultBlockSize() {
+    assertThat(DefaultCpdBlockIndexer.getDefaultBlockSize("cobol")).isEqualTo(30);
+    assertThat(DefaultCpdBlockIndexer.getDefaultBlockSize("abap")).isEqualTo(20);
+    assertThat(DefaultCpdBlockIndexer.getDefaultBlockSize("other")).isEqualTo(10);
+  }
+
+  @Test
+  public void defaultBlockSize() {
+
+    assertThat(engine.getBlockSize("java")).isEqualTo(10);
+  }
+
+  @Test
+  public void blockSizeForCobol() {
+    settings.setProperty("sonar.cpd.cobol.minimumLines", "42");
+
+    assertThat(engine.getBlockSize("cobol")).isEqualTo(42);
+  }
+
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/cpd/DefaultCpdIndexerTest.java b/sonar-batch/src/test/java/org/sonar/batch/cpd/DefaultCpdIndexerTest.java
deleted file mode 100644 (file)
index 65a9cc7..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.cpd;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.sonar.api.config.Settings;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-public class DefaultCpdIndexerTest {
-
-  private DefaultCpdIndexer engine;
-  private Settings settings;
-
-  @Before
-  public void init() {
-    settings = new Settings();
-    engine = new DefaultCpdIndexer(null, null, settings, null);
-  }
-
-  @Test
-  public void shouldLogExclusions() {
-    Logger logger = mock(Logger.class);
-    engine.logExclusions(new String[0], logger);
-    verify(logger, never()).info(anyString());
-
-    logger = mock(Logger.class);
-    engine.logExclusions(new String[] {"Foo*", "**/Bar*"}, logger);
-
-    String message = "Copy-paste detection exclusions:"
-      + "\n  Foo*"
-      + "\n  **/Bar*";
-    verify(logger, times(1)).info(message);
-  }
-
-  @Test
-  public void shouldReturnDefaultBlockSize() {
-    assertThat(DefaultCpdIndexer.getDefaultBlockSize("cobol")).isEqualTo(30);
-    assertThat(DefaultCpdIndexer.getDefaultBlockSize("abap")).isEqualTo(20);
-    assertThat(DefaultCpdIndexer.getDefaultBlockSize("other")).isEqualTo(10);
-  }
-
-  @Test
-  public void defaultBlockSize() {
-
-    assertThat(engine.getBlockSize("java")).isEqualTo(10);
-  }
-
-  @Test
-  public void blockSizeForCobol() {
-    settings.setProperty("sonar.cpd.cobol.minimumLines", "42");
-
-    assertThat(engine.getBlockSize("cobol")).isEqualTo(42);
-  }
-
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java b/sonar-batch/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java
new file mode 100644 (file)
index 0000000..851fb52
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.cpd;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
+import org.sonar.batch.index.BatchComponentCache;
+import org.sonar.duplications.block.Block;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+public class JavaCpdBlockIndexerTest {
+  private static final String JAVA = "java";
+
+  @Mock
+  private SonarCpdBlockIndex index;
+
+  @Captor
+  private ArgumentCaptor<List<Block>> blockCaptor;
+
+  private Settings settings;
+  private JavaCpdBlockIndexer engine;
+  private DefaultInputFile file;
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  @Before
+  public void setUp() throws IOException {
+    MockitoAnnotations.initMocks(this);
+
+    File baseDir = temp.newFolder();
+    DefaultFileSystem fs = new DefaultFileSystem(baseDir);
+    file = new DefaultInputFile("foo", "src/ManyStatements.java").setLanguage(JAVA);
+    fs.add(file);
+    BatchComponentCache batchComponentCache = new BatchComponentCache();
+    batchComponentCache.add(org.sonar.api.resources.File.create("src/Foo.java").setEffectiveKey("foo:src/ManyStatements.java"), null).setInputComponent(file);
+    File ioFile = file.file();
+    FileUtils.copyURLToFile(this.getClass().getResource("ManyStatements.java"), ioFile);
+
+    settings = new Settings();
+    engine = new JavaCpdBlockIndexer(fs, settings, index);
+  }
+
+  @Test
+  public void languageSupported() {
+    JavaCpdBlockIndexer engine = new JavaCpdBlockIndexer(mock(FileSystem.class), new Settings(), index);
+    assertThat(engine.isLanguageSupported(JAVA)).isTrue();
+    assertThat(engine.isLanguageSupported("php")).isFalse();
+  }
+
+  @Test
+  public void testExclusions() {
+    settings.setProperty(CoreProperties.CPD_EXCLUSIONS, "**");
+    engine.index(JAVA);
+    verifyZeroInteractions(index);
+  }
+
+  @Test
+  public void testJavaIndexing() throws Exception {
+    engine.index(JAVA);
+
+    verify(index).insert(eq(file), blockCaptor.capture());
+    List<Block> blockList = blockCaptor.getValue();
+
+    assertThat(blockList).hasSize(26);
+  }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/cpd/JavaCpdIndexerTest.java b/sonar-batch/src/test/java/org/sonar/batch/cpd/JavaCpdIndexerTest.java
deleted file mode 100644 (file)
index afbedc2..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2016 SonarSource SA
- * mailto:contact AT sonarsource DOT com
- *
- * This program 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.
- *
- * This program 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.cpd;
-
-import org.apache.commons.io.FileUtils;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.config.Settings;
-import org.sonar.batch.cpd.index.SonarDuplicationsIndex;
-import org.sonar.batch.index.BatchComponentCache;
-import org.sonar.duplications.block.Block;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.List;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-public class JavaCpdIndexerTest {
-  private static final String JAVA = "java";
-
-  @Mock
-  private SonarDuplicationsIndex index;
-
-  @Captor
-  private ArgumentCaptor<List<Block>> blockCaptor;
-
-  private Settings settings;
-  private JavaCpdIndexer engine;
-  private DefaultInputFile file;
-
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  @Before
-  public void setUp() throws IOException {
-    MockitoAnnotations.initMocks(this);
-
-    File baseDir = temp.newFolder();
-    DefaultFileSystem fs = new DefaultFileSystem(baseDir);
-    file = new DefaultInputFile("foo", "src/ManyStatements.java").setLanguage(JAVA);
-    fs.add(file);
-    BatchComponentCache batchComponentCache = new BatchComponentCache();
-    batchComponentCache.add(org.sonar.api.resources.File.create("src/Foo.java").setEffectiveKey("foo:src/ManyStatements.java"), null).setInputComponent(file);
-    File ioFile = file.file();
-    FileUtils.copyURLToFile(this.getClass().getResource("ManyStatements.java"), ioFile);
-
-    settings = new Settings();
-    engine = new JavaCpdIndexer(fs, settings, index);
-  }
-
-  @Test
-  public void languageSupported() {
-    JavaCpdIndexer engine = new JavaCpdIndexer(mock(FileSystem.class), new Settings(), index);
-    assertThat(engine.isLanguageSupported(JAVA)).isTrue();
-    assertThat(engine.isLanguageSupported("php")).isFalse();
-  }
-
-  @Test
-  public void testExclusions() {
-    settings.setProperty(CoreProperties.CPD_EXCLUSIONS, "**");
-    engine.index(JAVA);
-    verifyZeroInteractions(index);
-  }
-
-  @Test
-  public void testJavaIndexing() throws Exception {
-    engine.index(JAVA);
-
-    verify(index).insert(eq(file), blockCaptor.capture());
-    List<Block> blockList = blockCaptor.getValue();
-
-    assertThat(blockList).hasSize(26);
-  }
-}
index 1fc1a64ff32cd7816591297826b5dd3bb487ffe5..ac30e0466f5c3b3779192e8f5a5c93eb900d21b4 100644 (file)
@@ -22,6 +22,8 @@ package org.sonar.batch.mediumtest.cpd;
 import com.google.common.collect.ImmutableMap;
 import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import org.apache.commons.io.FileUtils;
@@ -32,6 +34,9 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.measures.CoreMetrics;
@@ -40,11 +45,20 @@ import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.output.BatchReport;
 import org.sonar.batch.protocol.output.BatchReport.Measure;
 import org.sonar.xoo.XooPlugin;
+import org.sonar.xoo.lang.CpdTokenizerSensor;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+@RunWith(Parameterized.class)
 public class CpdMediumTest {
 
+  @Parameters(name = "new api: {0}")
+  public static Collection<Object[]> data() {
+    return Arrays.asList(new Object[][] {
+      {true}, {false}
+    });
+  }
+
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
 
@@ -60,6 +74,12 @@ public class CpdMediumTest {
 
   private ImmutableMap.Builder<String, String> builder;
 
+  private boolean useNewSensorApi;
+
+  public CpdMediumTest(boolean useNewSensorApi) {
+    this.useNewSensorApi = useNewSensorApi;
+  }
+
   @Before
   public void prepare() throws IOException {
     tester.start();
@@ -73,6 +93,9 @@ public class CpdMediumTest {
       .put("sonar.projectName", "Foo Project")
       .put("sonar.projectVersion", "1.0-SNAPSHOT")
       .put("sonar.projectDescription", "Description of Foo Project");
+    if (useNewSensorApi) {
+      builder.put(CpdTokenizerSensor.ENABLE_PROP, "true");
+    }
   }
 
   @After
index d2b6163fbeb2990733bd0f20de02218b64265127..ba40858840df291a18030bbfa456c60c9662b908 100644 (file)
@@ -71,7 +71,6 @@ public class DefaultSensorContextTest {
     assertThat(adaptor.activeRules()).isEqualTo(activeRules);
     assertThat(adaptor.fileSystem()).isEqualTo(fs);
     assertThat(adaptor.settings()).isEqualTo(settings);
-    assertThat(adaptor.analysisMode()).isEqualTo(analysisMode);
 
     assertThat(adaptor.newIssue()).isNotNull();
     assertThat(adaptor.newMeasure()).isNotNull();
index 3ecfbdfa5022631c784f80bb91a3eec458e9d5b2..caa16b11a19b4d627c0030d8053e54d1271e033e 100644 (file)
@@ -39,6 +39,7 @@ import org.sonar.api.measures.Measure;
 import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
+import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
 import org.sonar.batch.index.BatchComponentCache;
 import org.sonar.batch.issue.ModuleIssues;
 import org.sonar.batch.report.ReportPublisher;
@@ -84,7 +85,7 @@ public class DefaultSensorStorageTest {
     when(coverageExclusions.accept(any(Resource.class), any(Measure.class))).thenReturn(true);
     resourceCache = new BatchComponentCache();
     sensorStorage = new DefaultSensorStorage(metricFinder,
-      moduleIssues, settings, fs, activeRules, coverageExclusions, resourceCache, mock(ReportPublisher.class), measureCache);
+      moduleIssues, settings, fs, activeRules, coverageExclusions, resourceCache, mock(ReportPublisher.class), measureCache, mock(SonarCpdBlockIndex.class));
   }
 
   @Test
index 8db339e2f8f2379d26b40a1f1185ce09c45677dc..0fac9a7213927b9f0837876f748e766cc7c84bba 100644 (file)
@@ -24,7 +24,6 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
@@ -35,8 +34,6 @@ import org.sonar.api.config.Settings;
 import org.sonar.api.rule.RuleKey;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
 public class SensorOptimizerTest {
 
@@ -49,14 +46,12 @@ public class SensorOptimizerTest {
   private DefaultFileSystem fs;
   private SensorOptimizer optimizer;
   private Settings settings;
-  private AnalysisMode analysisMode;
 
   @Before
   public void prepare() throws Exception {
     fs = new DefaultFileSystem(temp.newFolder().toPath());
     settings = new Settings();
-    analysisMode = mock(AnalysisMode.class);
-    optimizer = new SensorOptimizer(fs, new ActiveRulesBuilder().build(), settings, analysisMode);
+    optimizer = new SensorOptimizer(fs, new ActiveRulesBuilder().build(), settings);
   }
 
   @Test
@@ -114,7 +109,7 @@ public class SensorOptimizerTest {
       .create(RuleKey.of("repo1", "foo"))
       .activate()
       .build();
-    optimizer = new SensorOptimizer(fs, activeRules, settings, analysisMode);
+    optimizer = new SensorOptimizer(fs, activeRules, settings);
 
     assertThat(optimizer.shouldExecute(descriptor)).isFalse();
 
@@ -124,7 +119,7 @@ public class SensorOptimizerTest {
       .create(RuleKey.of("squid", "rule"))
       .activate()
       .build();
-    optimizer = new SensorOptimizer(fs, activeRules, settings, analysisMode);
+    optimizer = new SensorOptimizer(fs, activeRules, settings);
     assertThat(optimizer.shouldExecute(descriptor)).isTrue();
   }
 
@@ -138,15 +133,4 @@ public class SensorOptimizerTest {
     assertThat(optimizer.shouldExecute(descriptor)).isTrue();
   }
 
-  @Test
-  public void should_disabled_in_issues_mode() {
-    DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor()
-      .disabledInIssues();
-    assertThat(optimizer.shouldExecute(descriptor)).isTrue();
-
-    when(analysisMode.isIssues()).thenReturn(true);
-
-    assertThat(optimizer.shouldExecute(descriptor)).isFalse();
-  }
-
 }
index 228dab0a51ce15f4d3f9010a170038c5cc2f908d..3d49859470e9273e53c8bd6ef2c5b8a1c432c372 100644 (file)
@@ -21,11 +21,11 @@ package org.sonar.api.batch.sensor;
 
 import com.google.common.annotations.Beta;
 import java.io.Serializable;
-import org.sonar.api.batch.AnalysisMode;
 import org.sonar.api.batch.fs.FileSystem;
 import org.sonar.api.batch.fs.InputModule;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.sensor.coverage.NewCoverage;
+import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
 import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
 import org.sonar.api.batch.sensor.internal.SensorContextTester;
 import org.sonar.api.batch.sensor.issue.Issue;
@@ -57,11 +57,6 @@ public interface SensorContext {
    */
   ActiveRules activeRules();
 
-  /**
-   * Get analysis mode.
-   */
-  AnalysisMode analysisMode();
-
   /**
    * @since 5.5
    */
@@ -100,4 +95,13 @@ public interface SensorContext {
    */
   NewCoverage newCoverage();
 
+  // ------------ CPD ------------
+
+  /**
+   * Builder to define CPD tokens in a file.
+   * Don't forget to call {@link NewCpdTokens#save()}.
+   * @since 5.5
+   */
+  NewCpdTokens newCpdTokens();
+
 }
index b1eb1294661275fcf4373cbf0ff0ed1c7769cb81..9000a81432bfe2d4d9b0a3335ada1b2f41ea7e6b 100644 (file)
@@ -81,9 +81,4 @@ public interface SensorDescriptor {
    */
   SensorDescriptor requireProperties(String... propertyKeys);
 
-  /**
-   * Should this sensor be disabled in issues mode. Default is to run all sensors in issues mode.
-   */
-  SensorDescriptor disabledInIssues();
-
 }
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/NewCpdTokens.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/NewCpdTokens.java
new file mode 100644 (file)
index 0000000..e2a48d1
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.cpd;
+
+import com.google.common.annotations.Beta;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextRange;
+
+/**
+ * This builder is used to define tokens used by CPD algorithm on files.
+ * @since 5.5
+ */
+@Beta
+public interface NewCpdTokens {
+
+  /**
+   * The tokenized file.
+   */
+  NewCpdTokens onFile(InputFile inputFile);
+
+  /**
+   * Call this method to register a token in a range. Tokens should be registered in order.
+   * @param range Token position. Use {@link InputFile#newRange(int, int, int, int)} to get a valid range.
+   * @param image Text content of the token. Can be replaced by a constant placeholder for some tokens (like litterals).
+   */
+  NewCpdTokens addToken(TextRange range, String image);
+
+  /**
+   * Call this method only once when your are done with defining tokens of the file.
+   */
+  void save();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java
new file mode 100644 (file)
index 0000000..47a62b8
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.cpd.internal;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.TextRange;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
+import org.sonar.api.batch.sensor.internal.DefaultStorable;
+import org.sonar.api.batch.sensor.internal.SensorStorage;
+import org.sonar.duplications.internal.pmd.TokensLine;
+
+public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens {
+
+  private final ImmutableList.Builder<TokensLine> result = ImmutableList.builder();
+  private DefaultInputFile inputFile;
+  private int startLine = Integer.MIN_VALUE;
+  private int startIndex = 0;
+  private int currentIndex = 0;
+  private StringBuilder sb = new StringBuilder();
+  private TextRange lastRange;
+
+  public DefaultCpdTokens(SensorStorage storage) {
+    super(storage);
+  }
+
+  @Override
+  public DefaultCpdTokens onFile(InputFile inputFile) {
+    Preconditions.checkNotNull(inputFile, "file can't be null");
+    this.inputFile = (DefaultInputFile) inputFile;
+    return this;
+  }
+
+  public InputFile inputFile() {
+    return inputFile;
+  }
+
+  @Override
+  public NewCpdTokens addToken(TextRange range, String image) {
+    Preconditions.checkNotNull(range, "Range should not be null");
+    Preconditions.checkState(inputFile != null, "Call onFile() first");
+    Preconditions.checkState(lastRange == null || lastRange.end().compareTo(range.start()) >= 0,
+      "Tokens of file %s should be provided in order. \nPrevious token: %s\nLast token: %s", inputFile, lastRange, range);
+
+    String value = image;
+
+    int line = range.start().line();
+    if (line != startLine) {
+      addNewTokensLine(result, startIndex, currentIndex, startLine, sb);
+      startIndex = currentIndex + 1;
+      startLine = line;
+    }
+    currentIndex++;
+    sb.append(value);
+
+    return this;
+  }
+
+  public List<TokensLine> getTokenLines() {
+    return result.build();
+  }
+
+  private static void addNewTokensLine(ImmutableList.Builder<TokensLine> result, int startUnit, int endUnit, int startLine, StringBuilder sb) {
+    if (sb.length() != 0) {
+      result.add(new TokensLine(startUnit, endUnit, startLine, sb.toString()));
+      sb.setLength(0);
+    }
+  }
+
+  @Override
+  protected void doSave() {
+    Preconditions.checkState(inputFile != null, "Call onFile() first");
+    addNewTokensLine(result, startIndex, currentIndex, startLine, sb);
+    storage.store(this);
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/package-info.java
new file mode 100644 (file)
index 0000000..0e670a3
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.cpd.internal;
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/package-info.java
new file mode 100644 (file)
index 0000000..5e53aa2
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.cpd;
index ddf836c0f33c9709e08fc5d946fd50eb08317073..985ca6832c6e3e1ff77adff536864d37514a9421 100644 (file)
@@ -32,7 +32,6 @@ public class DefaultSensorDescriptor implements SensorDescriptor {
   private InputFile.Type type = null;
   private String[] ruleRepositories = new String[0];
   private String[] properties = new String[0];
-  private boolean disabledInIssues = false;
 
   public String name() {
     return name;
@@ -55,10 +54,6 @@ public class DefaultSensorDescriptor implements SensorDescriptor {
     return Arrays.asList(properties);
   }
 
-  public boolean isDisabledInIssues() {
-    return disabledInIssues;
-  }
-
   @Override
   public DefaultSensorDescriptor name(String name) {
     this.name = name;
@@ -104,10 +99,4 @@ public class DefaultSensorDescriptor implements SensorDescriptor {
     return this;
   }
 
-  @Override
-  public DefaultSensorDescriptor disabledInIssues() {
-    this.disabledInIssues = true;
-    return this;
-  }
-
 }
index d5bb9d21ea15f848b95329add696455be3312950..46301c089ec9fbf1d7c66c562eb3fc25e5bb780d 100644 (file)
@@ -44,6 +44,8 @@ import org.sonar.api.batch.sensor.SensorContext;
 import org.sonar.api.batch.sensor.coverage.CoverageType;
 import org.sonar.api.batch.sensor.coverage.NewCoverage;
 import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
+import org.sonar.api.batch.sensor.cpd.NewCpdTokens;
+import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
 import org.sonar.api.batch.sensor.highlighting.NewHighlighting;
 import org.sonar.api.batch.sensor.highlighting.TypeOfText;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
@@ -71,7 +73,6 @@ public class SensorContextTester implements SensorContext {
   private Settings settings;
   private DefaultFileSystem fs;
   private ActiveRules activeRules;
-  private MockAnalysisMode analysisMode;
   private InMemorySensorStorage sensorStorage;
   private InputModule module;
 
@@ -79,7 +80,6 @@ public class SensorContextTester implements SensorContext {
     this.settings = new Settings();
     this.fs = new DefaultFileSystem(moduleBaseDir);
     this.activeRules = new ActiveRulesBuilder().build();
-    this.analysisMode = new MockAnalysisMode();
     this.sensorStorage = new InMemorySensorStorage();
     this.module = new DefaultInputModule("projectKey");
   }
@@ -115,11 +115,6 @@ public class SensorContextTester implements SensorContext {
     this.activeRules = activeRules;
   }
 
-  @Override
-  public MockAnalysisMode analysisMode() {
-    return analysisMode;
-  }
-
   @Override
   public InputModule module() {
     return module;
@@ -197,6 +192,11 @@ public class SensorContextTester implements SensorContext {
     return new DefaultCoverage(sensorStorage);
   }
 
+  @Override
+  public NewCpdTokens newCpdTokens() {
+    return new DefaultCpdTokens(sensorStorage);
+  }
+
   public List<TypeOfText> highlightingTypeAt(String componentKey, int line, int lineOffset) {
     DefaultHighlighting syntaxHighlightingData = sensorStorage.highlightingByComponent.get(componentKey);
     if (syntaxHighlightingData == null) {
@@ -247,6 +247,7 @@ public class SensorContextTester implements SensorContext {
     private Collection<Issue> allIssues = new ArrayList<>();
 
     private Map<String, DefaultHighlighting> highlightingByComponent = new HashMap<>();
+    private Map<String, DefaultCpdTokens> cpdTokensByComponent = new HashMap<>();
     private Map<String, Map<CoverageType, DefaultCoverage>> coverageByComponent = new HashMap<>();
 
     @Override
@@ -273,6 +274,11 @@ public class SensorContextTester implements SensorContext {
       coverageByComponent.get(key).put(defaultCoverage.type(), defaultCoverage);
     }
 
+    @Override
+    public void store(DefaultCpdTokens defaultCpdTokens) {
+      cpdTokensByComponent.put(defaultCpdTokens.inputFile().key(), defaultCpdTokens);
+    }
+
   }
 
 }
index 2e1bd88c825449d93fba246a7f252e1de9cd681f..031ee7257288a5bd6d82f53a6ca234d738c6525b 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.api.batch.sensor.internal;
 
 import org.sonar.api.batch.BatchSide;
 import org.sonar.api.batch.sensor.coverage.internal.DefaultCoverage;
+import org.sonar.api.batch.sensor.cpd.internal.DefaultCpdTokens;
 import org.sonar.api.batch.sensor.highlighting.internal.DefaultHighlighting;
 import org.sonar.api.batch.sensor.issue.Issue;
 import org.sonar.api.batch.sensor.measure.Measure;
@@ -43,4 +44,9 @@ public interface SensorStorage {
    */
   void store(DefaultCoverage defaultCoverage);
 
+  /**
+   * @since 5.5 
+   */
+  void store(DefaultCpdTokens defaultCpdTokens);
+
 }
index 05481b0aae518d5005289cb740ff7db93af5078f..7ade972164b2d48ba4976c9723e984f6fffeca37 100644 (file)
@@ -43,13 +43,4 @@ public class DefaultSensorDescriptorTest {
     assertThat(descriptor.ruleRepositories()).containsOnly("squid-java");
   }
 
-  @Test
-  public void disabledAnalysisModes() {
-    DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor();
-    descriptor
-      .disabledInIssues();
-
-    assertThat(descriptor.isDisabledInIssues()).isTrue();
-  }
-
 }
index c8a70a0630bfeacb311c7a9a97c066e974b73564..4e70a3e293bf2664802a73f968ad2a98e2f3b9dc 100644 (file)
@@ -81,13 +81,6 @@ public class SensorContextTesterTest {
     assertThat(tester.fileSystem().baseDir()).isNotEqualTo(baseDir);
   }
 
-  @Test
-  public void testAnalysisMode() {
-    assertThat(tester.analysisMode().isPreview()).isFalse();
-    tester.analysisMode().setPreview(true);
-    assertThat(tester.analysisMode().isPreview()).isTrue();
-  }
-
   @Test
   public void testIssues() {
     assertThat(tester.allIssues()).isEmpty();