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;
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;
}
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);
@Override
protected void doSave() {
Preconditions.checkState(inputFile != null, "Call onFile() first");
+ if (excluded) {
+ return;
+ }
addNewTokensLine(result, startIndex, currentIndex, startLine, sb);
storage.store(this);
}
@Override
public NewCpdTokens newCpdTokens() {
- return new DefaultCpdTokens(sensorStorage);
+ return new DefaultCpdTokens(settings, sensorStorage);
}
public List<TypeOfText> highlightingTypeAt(String componentKey, int line, int lineOffset) {
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();
@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");
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")
@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");
@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");
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 {
public static List<Class<? extends Object>> all() {
return ImmutableList.of(
- CpdSensor.class,
+ DeprecatedCpdBlockIndexerSensor.class,
CpdMappings.class,
JavaCpdBlockIndexer.class,
DefaultCpdBlockIndexer.class);
+++ /dev/null
-/*
- * 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();
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
-
-}
+++ /dev/null
-/*
- * 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);
- }
- }
-
-}
+++ /dev/null
-/*
- * 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;
- }
- }
-
-}
+++ /dev/null
-/*
- * 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);
- }
- }
-}
--- /dev/null
+/*
+ * 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();
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+
if (analysisMode.isIssues()) {
return NO_OP_NEW_CPD_TOKENS;
}
- return new DefaultCpdTokens(sensorStorage);
+ return new DefaultCpdTokens(settings, sensorStorage);
}
}
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;
+++ /dev/null
-/*
- * 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);
- }
-
-}
+++ /dev/null
-/*
- * 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);
- }
-
-}
+++ /dev/null
-/*
- * 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);
- }
-}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
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");
+++ /dev/null
-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
--- /dev/null
+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