]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7389 Support CPD exclusions in new API 855/head
authorJulien HENRY <julien.henry@sonarsource.com>
Mon, 21 Mar 2016 11:19:23 +0000 (12:19 +0100)
committerJulien HENRY <julien.henry@sonarsource.com>
Tue, 22 Mar 2016 09:42:33 +0000 (10:42 +0100)
26 files changed:
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokens.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/internal/SensorContextTester.java
sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/cpd/internal/DefaultCpdTokensTest.java
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/CpdComponents.java
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/CpdIndexer.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/CpdMappings.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/CpdSensor.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/DefaultCpdBlockIndexer.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/JavaCpdBlockIndexer.java [deleted file]
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/CpdBlockIndexer.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/CpdMappings.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/DefaultCpdBlockIndexer.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/DeprecatedCpdBlockIndexerSensor.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/JavaCpdBlockIndexer.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/package-info.java [new file with mode: 0644]
sonar-scanner-engine/src/main/java/org/sonar/batch/sensor/DefaultSensorContext.java
sonar-scanner-engine/src/main/java/org/sonar/batch/sensor/DefaultSensorStorage.java
sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java [deleted file]
sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java [deleted file]
sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java [deleted file]
sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/deprecated/DefaultCpdBlockIndexerTest.java [new file with mode: 0644]
sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/deprecated/DeprecatedCpdBlockIndexerSensorTest.java [new file with mode: 0644]
sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/deprecated/JavaCpdBlockIndexerTest.java [new file with mode: 0644]
sonar-scanner-engine/src/test/java/org/sonar/batch/mediumtest/cpd/CpdMediumTest.java
sonar-scanner-engine/src/test/resources/org/sonar/batch/cpd/ManyStatements.java [deleted file]
sonar-scanner-engine/src/test/resources/org/sonar/batch/cpd/deprecated/ManyStatements.java [new file with mode: 0644]

index a177a554fa12f7dbff242e4cfe353c5e7460fc22..954f2869671b220072351338a7dc32bd9fa1b12b 100644 (file)
@@ -22,16 +22,20 @@ 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.CoreProperties;
 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.fs.internal.PathPattern;
 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.api.config.Settings;
 import org.sonar.duplications.internal.pmd.TokensLine;
 
 public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens {
 
+  private final Settings settings;
   private final ImmutableList.Builder<TokensLine> result = ImmutableList.builder();
   private DefaultInputFile inputFile;
   private int startLine = Integer.MIN_VALUE;
@@ -39,18 +43,39 @@ public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens {
   private int currentIndex = 0;
   private StringBuilder sb = new StringBuilder();
   private TextRange lastRange;
+  private boolean excluded;
 
-  public DefaultCpdTokens(SensorStorage storage) {
+  public DefaultCpdTokens(Settings settings, SensorStorage storage) {
     super(storage);
+    this.settings = settings;
   }
 
   @Override
   public DefaultCpdTokens onFile(InputFile inputFile) {
     Preconditions.checkNotNull(inputFile, "file can't be null");
     this.inputFile = (DefaultInputFile) inputFile;
+    String language = inputFile.language();
+    if (language != null && isSkipped(language)) {
+      this.excluded = true;
+    } else {
+      String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS);
+      for (PathPattern cpdExclusion : PathPattern.create(cpdExclusions)) {
+        if (cpdExclusion.match(inputFile)) {
+          this.excluded = true;
+        }
+      }
+    }
     return this;
   }
 
+  boolean isSkipped(String language) {
+    String key = "sonar.cpd." + language + ".skip";
+    if (settings.hasKey(key)) {
+      return settings.getBoolean(key);
+    }
+    return settings.getBoolean(CoreProperties.CPD_SKIP_PROPERTY);
+  }
+
   public InputFile inputFile() {
     return inputFile;
   }
@@ -60,6 +85,9 @@ public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens {
     Preconditions.checkNotNull(range, "Range should not be null");
     Preconditions.checkNotNull(image, "Image should not be null");
     Preconditions.checkState(inputFile != null, "Call onFile() first");
+    if (excluded) {
+      return this;
+    }
     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);
 
@@ -92,6 +120,9 @@ public class DefaultCpdTokens extends DefaultStorable implements NewCpdTokens {
   @Override
   protected void doSave() {
     Preconditions.checkState(inputFile != null, "Call onFile() first");
+    if (excluded) {
+      return;
+    }
     addNewTokensLine(result, startIndex, currentIndex, startLine, sb);
     storage.store(this);
   }
index 82d425841411b682984a9d9ce8517e8ecb789e42..24a06a241bde368552cd760257d397f7586fdf7c 100644 (file)
@@ -201,7 +201,7 @@ public class SensorContextTester implements SensorContext {
 
   @Override
   public NewCpdTokens newCpdTokens() {
-    return new DefaultCpdTokens(sensorStorage);
+    return new DefaultCpdTokens(settings, sensorStorage);
   }
 
   public List<TypeOfText> highlightingTypeAt(String componentKey, int line, int lineOffset) {
index 5f09cf064d932b49d3d400c22333703a75f2d92f..94494b959237c4a796fdf2dab19fb9c8289dbcf0 100644 (file)
@@ -22,24 +22,27 @@ package org.sonar.api.batch.sensor.cpd.internal;
 import org.junit.Test;
 import org.sonar.api.batch.fs.internal.DefaultInputFile;
 import org.sonar.api.batch.sensor.internal.SensorStorage;
+import org.sonar.api.config.Settings;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.fail;
 import static org.assertj.core.api.Assertions.tuple;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 
 public class DefaultCpdTokensTest {
 
   private static final DefaultInputFile INPUT_FILE = new DefaultInputFile("foo", "src/Foo.java")
     .setLines(2)
+    .setLanguage("java")
     .setOriginalLineOffsets(new int[] {0, 50})
     .setLastValidOffset(100);
 
   @Test
   public void save_no_tokens() {
     SensorStorage sensorStorage = mock(SensorStorage.class);
-    DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage)
+    DefaultCpdTokens tokens = new DefaultCpdTokens(new Settings(), sensorStorage)
       .onFile(INPUT_FILE);
 
     tokens.save();
@@ -52,7 +55,7 @@ public class DefaultCpdTokensTest {
   @Test
   public void save_one_token() {
     SensorStorage sensorStorage = mock(SensorStorage.class);
-    DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage)
+    DefaultCpdTokens tokens = new DefaultCpdTokens(new Settings(), sensorStorage)
       .onFile(INPUT_FILE)
       .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo");
 
@@ -63,10 +66,58 @@ public class DefaultCpdTokensTest {
     assertThat(tokens.getTokenLines()).extracting("value", "startLine", "hashCode", "startUnit", "endUnit").containsExactly(tuple("foo", 1, "foo".hashCode(), 1, 1));
   }
 
+  @Test
+  public void handle_exclusions_by_pattern() {
+    SensorStorage sensorStorage = mock(SensorStorage.class);
+    Settings settings = new Settings();
+    settings.setProperty("sonar.cpd.exclusions", "src/Foo.java,another");
+    DefaultCpdTokens tokens = new DefaultCpdTokens(settings, sensorStorage)
+      .onFile(INPUT_FILE)
+      .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo");
+
+    tokens.save();
+
+    verifyZeroInteractions(sensorStorage);
+
+    assertThat(tokens.getTokenLines()).isEmpty();
+  }
+
+  @Test
+  public void handle_exclusions_by_language() {
+    SensorStorage sensorStorage = mock(SensorStorage.class);
+    Settings settings = new Settings();
+    settings.setProperty("sonar.cpd.java.skip", "true");
+    DefaultCpdTokens tokens = new DefaultCpdTokens(settings, sensorStorage)
+      .onFile(INPUT_FILE)
+      .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo");
+
+    tokens.save();
+
+    verifyZeroInteractions(sensorStorage);
+
+    assertThat(tokens.getTokenLines()).isEmpty();
+  }
+
+  @Test
+  public void handle_exclusions() {
+    SensorStorage sensorStorage = mock(SensorStorage.class);
+    Settings settings = new Settings();
+    settings.setProperty("sonar.cpd.skip", "true");
+    DefaultCpdTokens tokens = new DefaultCpdTokens(settings, sensorStorage)
+      .onFile(INPUT_FILE)
+      .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo");
+
+    tokens.save();
+
+    verifyZeroInteractions(sensorStorage);
+
+    assertThat(tokens.getTokenLines()).isEmpty();
+  }
+
   @Test
   public void save_many_tokens() {
     SensorStorage sensorStorage = mock(SensorStorage.class);
-    DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage)
+    DefaultCpdTokens tokens = new DefaultCpdTokens(new Settings(), sensorStorage)
       .onFile(INPUT_FILE)
       .addToken(INPUT_FILE.newRange(1, 2, 1, 5), "foo")
       .addToken(INPUT_FILE.newRange(1, 6, 1, 10), "bar")
@@ -87,7 +138,7 @@ public class DefaultCpdTokensTest {
   @Test
   public void basic_validation() {
     SensorStorage sensorStorage = mock(SensorStorage.class);
-    DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage);
+    DefaultCpdTokens tokens = new DefaultCpdTokens(new Settings(), sensorStorage);
     try {
       tokens.save();
       fail("Expected exception");
@@ -117,7 +168,7 @@ public class DefaultCpdTokensTest {
   @Test
   public void validate_tokens_order() {
     SensorStorage sensorStorage = mock(SensorStorage.class);
-    DefaultCpdTokens tokens = new DefaultCpdTokens(sensorStorage)
+    DefaultCpdTokens tokens = new DefaultCpdTokens(new Settings(), sensorStorage)
       .onFile(INPUT_FILE)
       .addToken(INPUT_FILE.newRange(1, 6, 1, 10), "bar");
 
index 30f4863783db650df67b327fcf8abf5d4828b83e..a204f14d9fe8f8fbd5cb4578ee0d853157f796eb 100644 (file)
@@ -21,6 +21,10 @@ package org.sonar.batch.cpd;
 
 import com.google.common.collect.ImmutableList;
 import java.util.List;
+import org.sonar.batch.cpd.deprecated.CpdMappings;
+import org.sonar.batch.cpd.deprecated.DefaultCpdBlockIndexer;
+import org.sonar.batch.cpd.deprecated.DeprecatedCpdBlockIndexerSensor;
+import org.sonar.batch.cpd.deprecated.JavaCpdBlockIndexer;
 
 public final class CpdComponents {
 
@@ -29,7 +33,7 @@ public final class CpdComponents {
 
   public static List<Class<? extends Object>> all() {
     return ImmutableList.of(
-      CpdSensor.class,
+      DeprecatedCpdBlockIndexerSensor.class,
       CpdMappings.class,
       JavaCpdBlockIndexer.class,
       DefaultCpdBlockIndexer.class);
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/CpdIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/CpdIndexer.java
deleted file mode 100644 (file)
index 9ebf80c..0000000
+++ /dev/null
@@ -1,49 +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.slf4j.Logger;
-import org.sonar.api.batch.BatchSide;
-
-@BatchSide
-public abstract class CpdIndexer {
-
-  abstract boolean isLanguageSupported(String language);
-
-  abstract void index(String language);
-
-  protected void logExclusions(String[] exclusions, Logger logger) {
-    if (exclusions.length > 0) {
-      StringBuilder message = new StringBuilder("Copy-paste detection exclusions:");
-      for (String exclusion : exclusions) {
-        message.append("\n  ");
-        message.append(exclusion);
-      }
-
-      logger.info(message.toString());
-    }
-  }
-
-  @Override
-  public String toString() {
-    return getClass().getSimpleName();
-  }
-
-}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/CpdMappings.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/CpdMappings.java
deleted file mode 100644 (file)
index 9a930d6..0000000
+++ /dev/null
@@ -1,52 +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.sonar.api.batch.BatchSide;
-import org.sonar.api.batch.CpdMapping;
-
-import javax.annotation.CheckForNull;
-
-@BatchSide
-public class CpdMappings {
-
-  private final CpdMapping[] mappings;
-
-  public CpdMappings(CpdMapping[] mappings) {
-    this.mappings = mappings;
-  }
-
-  public CpdMappings() {
-    this(new CpdMapping[0]);
-  }
-
-  @CheckForNull
-  public CpdMapping getMapping(String language) {
-    if (mappings != null) {
-      for (CpdMapping cpdMapping : mappings) {
-        if (cpdMapping.getLanguage().getKey().equals(language)) {
-          return cpdMapping;
-        }
-      }
-    }
-    return null;
-  }
-
-}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/CpdSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/CpdSensor.java
deleted file mode 100644 (file)
index 9ea1bac..0000000
+++ /dev/null
@@ -1,99 +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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.CoreProperties;
-import org.sonar.api.batch.Phase;
-import org.sonar.api.batch.fs.FileSystem;
-import org.sonar.api.batch.sensor.Sensor;
-import org.sonar.api.batch.sensor.SensorContext;
-import org.sonar.api.batch.sensor.SensorDescriptor;
-import org.sonar.api.config.Settings;
-
-@Phase(name = Phase.Name.POST)
-public class CpdSensor implements Sensor {
-
-  private static final Logger LOG = LoggerFactory.getLogger(CpdSensor.class);
-
-  private CpdIndexer sonarEngine;
-  private CpdIndexer sonarBridgeEngine;
-  private Settings settings;
-  private FileSystem fs;
-
-  public CpdSensor(JavaCpdBlockIndexer sonarEngine, DefaultCpdBlockIndexer sonarBridgeEngine, Settings settings, FileSystem fs) {
-    this.sonarEngine = sonarEngine;
-    this.sonarBridgeEngine = sonarBridgeEngine;
-    this.settings = settings;
-    this.fs = fs;
-  }
-
-  @Override
-  public void describe(SensorDescriptor descriptor) {
-    descriptor.name("CPD Sensor");
-  }
-
-  @VisibleForTesting
-  CpdIndexer getEngine(String language) {
-    if (sonarEngine.isLanguageSupported(language)) {
-      return sonarEngine;
-    }
-    return sonarBridgeEngine;
-  }
-
-  @VisibleForTesting
-  boolean isSkipped(String language) {
-    String key = "sonar.cpd." + language + ".skip";
-    if (settings.hasKey(key)) {
-      return settings.getBoolean(key);
-    }
-    return settings.getBoolean(CoreProperties.CPD_SKIP_PROPERTY);
-  }
-
-  @Override
-  public void execute(SensorContext context) {
-    if (settings.hasKey(CoreProperties.CPD_SKIP_PROPERTY)) {
-      LOG.warn("\"sonar.cpd.skip\" property is deprecated and will be removed. Please set \"sonar.cpd.exclusions=**\" instead to disable duplication mechanism.");
-    }
-
-    for (String language : fs.languages()) {
-      if (settings.hasKey("sonar.cpd." + language + ".skip")) {
-        LOG
-          .warn("\"sonar.cpd." + language + ".skip\" property is deprecated and will be removed. Please set \"sonar.cpd.exclusions=**\" instead to disable duplication mechanism.");
-      }
-
-      if (isSkipped(language)) {
-        LOG.info("Detection of duplicated code is skipped for {}", language);
-        continue;
-      }
-
-      CpdIndexer engine = getEngine(language);
-      if (!engine.isLanguageSupported(language)) {
-        LOG.debug("Detection of duplicated code is not supported for {}", language);
-        continue;
-      }
-      LOG.info("{} is used for {}", engine, language);
-      engine.index(language);
-    }
-  }
-
-}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/DefaultCpdBlockIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/DefaultCpdBlockIndexer.java
deleted file mode 100644 (file)
index 33b2f26..0000000
+++ /dev/null
@@ -1,114 +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.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-scanner-engine/src/main/java/org/sonar/batch/cpd/JavaCpdBlockIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/JavaCpdBlockIndexer.java
deleted file mode 100644 (file)
index a2dde81..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.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-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/CpdBlockIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/CpdBlockIndexer.java
new file mode 100644 (file)
index 0000000..fe58cb6
--- /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.batch.cpd.deprecated;
+
+import org.slf4j.Logger;
+import org.sonar.api.batch.BatchSide;
+
+@BatchSide
+public abstract class CpdBlockIndexer {
+
+  abstract boolean isLanguageSupported(String language);
+
+  abstract void index(String language);
+
+  protected void logExclusions(String[] exclusions, Logger logger) {
+    if (exclusions.length > 0) {
+      StringBuilder message = new StringBuilder("Copy-paste detection exclusions:");
+      for (String exclusion : exclusions) {
+        message.append("\n  ");
+        message.append(exclusion);
+      }
+
+      logger.info(message.toString());
+    }
+  }
+
+  @Override
+  public String toString() {
+    return getClass().getSimpleName();
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/CpdMappings.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/CpdMappings.java
new file mode 100644 (file)
index 0000000..ad2275e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.deprecated;
+
+import org.sonar.api.batch.BatchSide;
+import org.sonar.api.batch.CpdMapping;
+
+import javax.annotation.CheckForNull;
+
+@BatchSide
+public class CpdMappings {
+
+  private final CpdMapping[] mappings;
+
+  public CpdMappings(CpdMapping[] mappings) {
+    this.mappings = mappings;
+  }
+
+  public CpdMappings() {
+    this(new CpdMapping[0]);
+  }
+
+  @CheckForNull
+  public CpdMapping getMapping(String language) {
+    if (mappings != null) {
+      for (CpdMapping cpdMapping : mappings) {
+        if (cpdMapping.getLanguage().getKey().equals(language)) {
+          return cpdMapping;
+        }
+      }
+    }
+    return null;
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/DefaultCpdBlockIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/DefaultCpdBlockIndexer.java
new file mode 100644 (file)
index 0000000..db51246
--- /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.deprecated;
+
+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 CpdBlockIndexer {
+
+  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.absolutePath());
+        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-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/DeprecatedCpdBlockIndexerSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/DeprecatedCpdBlockIndexerSensor.java
new file mode 100644 (file)
index 0000000..ad72d67
--- /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.deprecated;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.CpdMapping;
+import org.sonar.api.batch.Phase;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.sensor.Sensor;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.config.Settings;
+
+/**
+ * Feed block index using deprecated {@link CpdMapping} extension point if not already
+ * fed by another Sensor using {@link SensorContext#newCpdTokens()}. Special case for Java
+ * that use a dedicated block indexer.
+ * Can be removed when {@link CpdMapping} extension is removed and Java specific part moved to Java plugin.
+ */
+@Phase(name = Phase.Name.POST)
+public class DeprecatedCpdBlockIndexerSensor implements Sensor {
+
+  private static final Logger LOG = LoggerFactory.getLogger(DeprecatedCpdBlockIndexerSensor.class);
+
+  private CpdBlockIndexer javaCpdBlockIndexer;
+  private CpdBlockIndexer defaultCpdBlockIndexer;
+  private Settings settings;
+  private FileSystem fs;
+
+  public DeprecatedCpdBlockIndexerSensor(JavaCpdBlockIndexer javaCpdBlockIndexer, DefaultCpdBlockIndexer defaultCpdBlockIndexer, Settings settings, FileSystem fs) {
+    this.javaCpdBlockIndexer = javaCpdBlockIndexer;
+    this.defaultCpdBlockIndexer = defaultCpdBlockIndexer;
+    this.settings = settings;
+    this.fs = fs;
+  }
+
+  @Override
+  public void describe(SensorDescriptor descriptor) {
+    descriptor.name("CPD Block Indexer");
+  }
+
+  @VisibleForTesting
+  CpdBlockIndexer getBlockIndexer(String language) {
+    if (javaCpdBlockIndexer.isLanguageSupported(language)) {
+      return javaCpdBlockIndexer;
+    }
+    return defaultCpdBlockIndexer;
+  }
+
+  @VisibleForTesting
+  boolean isSkipped(String language) {
+    String key = "sonar.cpd." + language + ".skip";
+    if (settings.hasKey(key)) {
+      return settings.getBoolean(key);
+    }
+    return settings.getBoolean(CoreProperties.CPD_SKIP_PROPERTY);
+  }
+
+  @Override
+  public void execute(SensorContext context) {
+    if (settings.hasKey(CoreProperties.CPD_SKIP_PROPERTY)) {
+      LOG.warn("\"sonar.cpd.skip\" property is deprecated and will be removed. Please set \"sonar.cpd.exclusions=**\" instead to disable duplication mechanism.");
+    }
+
+    for (String language : fs.languages()) {
+      if (settings.hasKey("sonar.cpd." + language + ".skip")) {
+        LOG
+          .warn("\"sonar.cpd." + language + ".skip\" property is deprecated and will be removed. Please set \"sonar.cpd.exclusions=**\" instead to disable duplication mechanism.");
+      }
+
+      if (isSkipped(language)) {
+        LOG.info("Detection of duplicated code is skipped for {}", language);
+        continue;
+      }
+
+      CpdBlockIndexer blockIndexer = getBlockIndexer(language);
+      if (!blockIndexer.isLanguageSupported(language)) {
+        LOG.debug("Detection of duplicated code is not supported for {}", language);
+        continue;
+      }
+      LOG.info("{} is used for {}", blockIndexer, language);
+      blockIndexer.index(language);
+    }
+  }
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/JavaCpdBlockIndexer.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/JavaCpdBlockIndexer.java
new file mode 100644 (file)
index 0000000..7e07724
--- /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.deprecated;
+
+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 CpdBlockIndexer {
+
+  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-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/batch/cpd/deprecated/package-info.java
new file mode 100644 (file)
index 0000000..8a03575
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.batch.cpd.deprecated;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
index 1080dab084a89ca0f9577c9f64ea695e56e43d90..e0a61eb91177e111518fa37b75e2b8409ae251d6 100644 (file)
@@ -109,7 +109,7 @@ public class DefaultSensorContext implements SensorContext {
     if (analysisMode.isIssues()) {
       return NO_OP_NEW_CPD_TOKENS;
     }
-    return new DefaultCpdTokens(sensorStorage);
+    return new DefaultCpdTokens(settings, sensorStorage);
   }
 
 }
index 62e7d982d3430fefbc9abcf51dc89e061d915a30..c8de752737bfe83f8ff44a64f452f934ea38eada 100644 (file)
@@ -53,7 +53,7 @@ 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.deprecated.DefaultCpdBlockIndexer;
 import org.sonar.batch.cpd.index.SonarCpdBlockIndex;
 import org.sonar.batch.index.BatchComponent;
 import org.sonar.batch.index.BatchComponentCache;
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/CpdSensorTest.java
deleted file mode 100644 (file)
index ce27f07..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 java.io.IOException;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.sonar.api.batch.fs.internal.DefaultFileSystem;
-import org.sonar.api.config.PropertyDefinitions;
-import org.sonar.api.config.Settings;
-import org.sonar.api.resources.Java;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class CpdSensorTest {
-
-  @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
-
-  JavaCpdBlockIndexer sonarEngine;
-  DefaultCpdBlockIndexer sonarBridgeEngine;
-  CpdSensor sensor;
-  Settings settings;
-
-  @Before
-  public void setUp() throws IOException {
-    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());
-    sensor = new CpdSensor(sonarEngine, sonarBridgeEngine, settings, fs);
-  }
-
-  @Test
-  public void test_global_skip() {
-    settings.setProperty("sonar.cpd.skip", true);
-    assertThat(sensor.isSkipped(Java.KEY)).isTrue();
-  }
-
-  @Test
-  public void should_not_skip_by_default() {
-    assertThat(sensor.isSkipped(Java.KEY)).isFalse();
-  }
-
-  @Test
-  public void should_skip_by_language() {
-    settings.setProperty("sonar.cpd.skip", false);
-    settings.setProperty("sonar.cpd.php.skip", true);
-
-    assertThat(sensor.isSkipped("php")).isTrue();
-    assertThat(sensor.isSkipped(Java.KEY)).isFalse();
-  }
-
-  @Test
-  public void test_engine() {
-    assertThat(sensor.getEngine(Java.KEY)).isSameAs(sonarEngine);
-    assertThat(sensor.getEngine("PHP")).isSameAs(sonarBridgeEngine);
-  }
-
-}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/DefaultCpdBlockIndexerTest.java
deleted file mode 100644 (file)
index 9fcd03a..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 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-scanner-engine/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/JavaCpdBlockIndexerTest.java
deleted file mode 100644 (file)
index 851fb52..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.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-scanner-engine/src/test/java/org/sonar/batch/cpd/deprecated/DefaultCpdBlockIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/deprecated/DefaultCpdBlockIndexerTest.java
new file mode 100644 (file)
index 0000000..5dc28fd
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.deprecated;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.sonar.api.config.Settings;
+import org.sonar.batch.cpd.deprecated.DefaultCpdBlockIndexer;
+
+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-scanner-engine/src/test/java/org/sonar/batch/cpd/deprecated/DeprecatedCpdBlockIndexerSensorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/deprecated/DeprecatedCpdBlockIndexerSensorTest.java
new file mode 100644 (file)
index 0000000..4adc630
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.deprecated;
+
+import java.io.IOException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.internal.DefaultFileSystem;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Java;
+import org.sonar.batch.cpd.CpdComponents;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DeprecatedCpdBlockIndexerSensorTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  JavaCpdBlockIndexer sonarEngine;
+  DefaultCpdBlockIndexer sonarBridgeEngine;
+  DeprecatedCpdBlockIndexerSensor sensor;
+  Settings settings;
+
+  @Before
+  public void setUp() throws IOException {
+    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());
+    sensor = new DeprecatedCpdBlockIndexerSensor(sonarEngine, sonarBridgeEngine, settings, fs);
+  }
+
+  @Test
+  public void test_global_skip() {
+    settings.setProperty("sonar.cpd.skip", true);
+    assertThat(sensor.isSkipped(Java.KEY)).isTrue();
+  }
+
+  @Test
+  public void should_not_skip_by_default() {
+    assertThat(sensor.isSkipped(Java.KEY)).isFalse();
+  }
+
+  @Test
+  public void should_skip_by_language() {
+    settings.setProperty("sonar.cpd.skip", false);
+    settings.setProperty("sonar.cpd.php.skip", true);
+
+    assertThat(sensor.isSkipped("php")).isTrue();
+    assertThat(sensor.isSkipped(Java.KEY)).isFalse();
+  }
+
+  @Test
+  public void test_engine() {
+    assertThat(sensor.getBlockIndexer(Java.KEY)).isSameAs(sonarEngine);
+    assertThat(sensor.getBlockIndexer("PHP")).isSameAs(sonarBridgeEngine);
+  }
+
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/deprecated/JavaCpdBlockIndexerTest.java b/sonar-scanner-engine/src/test/java/org/sonar/batch/cpd/deprecated/JavaCpdBlockIndexerTest.java
new file mode 100644 (file)
index 0000000..b42843d
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * 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.deprecated;
+
+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.deprecated.JavaCpdBlockIndexer;
+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);
+  }
+}
index 662ed7dbcfdce1a3391a3c4dc5c3af2bce1b2338..6613421a4da772ab8ecf7c8ee6fe4e2fd60e08c7 100644 (file)
@@ -219,6 +219,43 @@ public class CpdMediumTest {
     assertThat(result.duplicationBlocksFor(inputFile1)).isEmpty();
   }
 
+  @Test
+  public void testExclusions() throws IOException {
+    File srcDir = new File(baseDir, "src");
+    srcDir.mkdir();
+
+    String duplicatedStuff = "Sample xoo\ncontent\n"
+      + "foo\nbar\ntoto\ntiti\n"
+      + "foo\nbar\ntoto\ntiti\n"
+      + "bar\ntoto\ntiti\n"
+      + "foo\nbar\ntoto\ntiti";
+
+    File xooFile1 = new File(srcDir, "sample1.xoo");
+    FileUtils.write(xooFile1, duplicatedStuff);
+
+    File xooFile2 = new File(srcDir, "sample2.xoo");
+    FileUtils.write(xooFile2, duplicatedStuff);
+
+    TaskResult result = tester.newTask()
+      .properties(builder
+        .put("sonar.sources", "src")
+        .put("sonar.cpd.xoo.minimumTokens", "10")
+        .put("sonar.cpd.exclusions", "src/sample1.xoo")
+        .build())
+      .start();
+
+    assertThat(result.inputFiles()).hasSize(2);
+
+    InputFile inputFile1 = result.inputFile("src/sample1.xoo");
+    InputFile inputFile2 = result.inputFile("src/sample2.xoo");
+
+    List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile1 = result.duplicationsFor(inputFile1);
+    assertThat(duplicationGroupsFile1).isEmpty();
+
+    List<org.sonar.scanner.protocol.output.ScannerReport.Duplication> duplicationGroupsFile2 = result.duplicationsFor(inputFile2);
+    assertThat(duplicationGroupsFile2).isEmpty();
+  }
+
   @Test
   public void enableCrossProjectDuplication() throws IOException {
     File srcDir = new File(baseDir, "src");
diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/batch/cpd/ManyStatements.java b/sonar-scanner-engine/src/test/resources/org/sonar/batch/cpd/ManyStatements.java
deleted file mode 100644 (file)
index ed22970..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.foo;
-
-public class ManyStatements {
-  
-  void foo() {
-    int A1 = 0; int B = 0; int C = 0; int D = 0; int E = 0; int F = 0; int G = 0; int H = 0; int I = 0; int J = 0; int K = 0;
-    int A2 = 0; int B = 0; int C = 0; int D = 0; int E = 0; int F = 0; int G = 0; int H = 0; int I = 0; int J = 0; int K = 0; 
-    int A1 = 0; int B = 0; int C = 0; int D = 0; int E = 0; int F = 0; int G = 0; int H = 0; int I = 0; int J = 0; int K = 0; 
-  }
-
-}
\ No newline at end of file
diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/batch/cpd/deprecated/ManyStatements.java b/sonar-scanner-engine/src/test/resources/org/sonar/batch/cpd/deprecated/ManyStatements.java
new file mode 100644 (file)
index 0000000..ed22970
--- /dev/null
@@ -0,0 +1,11 @@
+package org.foo;
+
+public class ManyStatements {
+  
+  void foo() {
+    int A1 = 0; int B = 0; int C = 0; int D = 0; int E = 0; int F = 0; int G = 0; int H = 0; int I = 0; int J = 0; int K = 0;
+    int A2 = 0; int B = 0; int C = 0; int D = 0; int E = 0; int F = 0; int G = 0; int H = 0; int I = 0; int J = 0; int K = 0; 
+    int A1 = 0; int B = 0; int C = 0; int D = 0; int E = 0; int F = 0; int G = 0; int H = 0; int I = 0; int J = 0; int K = 0; 
+  }
+
+}
\ No newline at end of file