aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorEvgeny Mandrikov <mandrikov@gmail.com>2012-01-09 11:39:03 +0400
committerEvgeny Mandrikov <mandrikov@gmail.com>2012-01-09 11:39:42 +0400
commit37d3be75d6f52a7d9a831806b3031a23c7c0ef7d (patch)
tree7f000eee1c90b378c57ee0291be1d9ae8d23c17e /plugins
parentbeb8d4cc840962e5581bb8ae3f3ca4717befcd6b (diff)
downloadsonarqube-37d3be75d6f52a7d9a831806b3031a23c7c0ef7d.tar.gz
sonarqube-37d3be75d6f52a7d9a831806b3031a23c7c0ef7d.zip
SONAR-3139 Allow to use Sonar CPD with existing extension point CpdMapping
Diffstat (limited to 'plugins')
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java11
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java26
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java114
-rw-r--r--plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/TokenizerBridge.java77
4 files changed, 216 insertions, 12 deletions
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java
index 429117653ab..bddce84afef 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java
@@ -19,9 +19,6 @@
*/
package org.sonar.plugins.cpd;
-import java.util.Arrays;
-import java.util.List;
-
import org.sonar.api.CoreProperties;
import org.sonar.api.Properties;
import org.sonar.api.Property;
@@ -29,6 +26,9 @@ import org.sonar.api.SonarPlugin;
import org.sonar.plugins.cpd.decorators.DuplicationDensityDecorator;
import org.sonar.plugins.cpd.decorators.SumDuplicationsDecorator;
+import java.util.Arrays;
+import java.util.List;
+
@Properties({
@Property(
key = CoreProperties.CPD_ENGINE,
@@ -88,7 +88,10 @@ import org.sonar.plugins.cpd.decorators.SumDuplicationsDecorator;
public class CpdPlugin extends SonarPlugin {
public List getExtensions() {
- return Arrays.asList(CpdSensor.class, SumDuplicationsDecorator.class, DuplicationDensityDecorator.class, SonarEngine.class, PmdEngine.class);
+ return Arrays.asList(CpdSensor.class, SumDuplicationsDecorator.class, DuplicationDensityDecorator.class,
+ SonarEngine.class,
+ PmdEngine.class,
+ SonarBridgeEngine.class);
}
}
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java
index afc0ec21b71..91e02f34d5b 100644
--- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java
@@ -32,12 +32,19 @@ public class CpdSensor implements Sensor {
private CpdEngine sonarEngine;
private CpdEngine pmdEngine;
+ private CpdEngine sonarBridgeEngine;
public CpdSensor(SonarEngine sonarEngine, PmdEngine pmdEngine) {
this.sonarEngine = sonarEngine;
this.pmdEngine = pmdEngine;
}
+ public CpdSensor(SonarEngine sonarEngine, PmdEngine pmdEngine, SonarBridgeEngine sonarBridgeEngine) {
+ this.sonarEngine = sonarEngine;
+ this.pmdEngine = pmdEngine;
+ this.sonarBridgeEngine = sonarBridgeEngine;
+ }
+
public boolean shouldExecuteOnProject(Project project) {
if (isSkipped(project)) {
LoggerFactory.getLogger(getClass()).info("Detection of duplicated code is skipped");
@@ -53,21 +60,24 @@ public class CpdSensor implements Sensor {
}
private CpdEngine getEngine(Project project) {
- if (isSonarEngineEnabled(project)) {
+ if (isEngineEnabled(project, "sonar")) {
if (sonarEngine.isLanguageSupported(project.getLanguage())) {
return sonarEngine;
- } else {
- // fallback to PMD
- return pmdEngine;
}
- } else {
- return pmdEngine;
+ // falback to PMD
+ } else if (isEngineEnabled(project, "bridge")) {
+ return sonarBridgeEngine;
}
+ return pmdEngine;
}
- boolean isSonarEngineEnabled(Project project) {
+ boolean isEngineEnabled(Project project, String engineName) {
Configuration conf = project.getConfiguration();
- return StringUtils.equalsIgnoreCase(conf.getString(CoreProperties.CPD_ENGINE, CoreProperties.CPD_ENGINE_DEFAULT_VALUE), "sonar");
+ return StringUtils.equalsIgnoreCase(conf.getString(CoreProperties.CPD_ENGINE, CoreProperties.CPD_ENGINE_DEFAULT_VALUE), engineName);
+ }
+
+ boolean isSonarEngineEnabled(Project project) {
+ return isEngineEnabled(project, "sonar");
}
boolean isSkipped(Project project) {
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java
new file mode 100644
index 00000000000..b9465be9d32
--- /dev/null
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarBridgeEngine.java
@@ -0,0 +1,114 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.cpd;
+
+import org.apache.commons.configuration.Configuration;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.CpdMapping;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.resources.*;
+import org.sonar.duplications.block.Block;
+import org.sonar.duplications.block.BlockChunker;
+import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm;
+import org.sonar.duplications.index.CloneGroup;
+import org.sonar.duplications.index.CloneIndex;
+import org.sonar.duplications.index.PackedMemoryCloneIndex;
+
+import java.util.Collection;
+import java.util.List;
+
+public class SonarBridgeEngine extends CpdEngine {
+
+ private final CpdMapping[] mappings;
+
+ public SonarBridgeEngine() {
+ this.mappings = null;
+ }
+
+ public SonarBridgeEngine(CpdMapping[] mappings) {
+ this.mappings = mappings;
+ }
+
+ @Override
+ public boolean isLanguageSupported(Language language) {
+ return getMapping(language) != null;
+ }
+
+ @Override
+ public void analyse(Project project, SensorContext context) {
+ ProjectFileSystem fileSystem = project.getFileSystem();
+ List<InputFile> inputFiles = fileSystem.mainFiles(project.getLanguageKey());
+ if (inputFiles.isEmpty()) {
+ return;
+ }
+
+ CpdMapping mapping = getMapping(project.getLanguage());
+
+ // Create index
+ BlockChunker blockChunker = new BlockChunker(getMinimumTokens(project));
+ CloneIndex index = new PackedMemoryCloneIndex();
+ TokenizerBridge bridge = new TokenizerBridge(mapping.getTokenizer(), fileSystem.getSourceCharset().name());
+ for (InputFile inputFile : inputFiles) {
+ Resource resource = mapping.createResource(inputFile.getFile(), fileSystem.getSourceDirs());
+ String resourceId = getFullKey(project, resource);
+ List<Block> blocks = blockChunker.chunk(resourceId, bridge.tokenize(inputFile.getFile()));
+ for (Block block : blocks) {
+ index.insert(block);
+ }
+ }
+ bridge.clearCache();
+
+ // Detect
+ for (InputFile inputFile : inputFiles) {
+ Resource resource = mapping.createResource(inputFile.getFile(), fileSystem.getSourceDirs());
+ String resourceId = getFullKey(project, resource);
+ Collection<Block> fileBlocks = index.getByResourceId(resourceId);
+ List<CloneGroup> duplications = SuffixTreeCloneDetectionAlgorithm.detect(index, fileBlocks);
+ SonarEngine.save(context, resource, duplications);
+ }
+ }
+
+ private static String getFullKey(Project project, Resource resource) {
+ return new StringBuilder(ResourceModel.KEY_SIZE)
+ .append(project.getKey())
+ .append(':')
+ .append(resource.getKey())
+ .toString();
+ }
+
+ private int getMinimumTokens(Project project) {
+ Configuration conf = project.getConfiguration();
+ return conf.getInt("sonar.cpd." + project.getLanguageKey() + ".minimumTokens",
+ conf.getInt("sonar.cpd.minimumTokens", CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE));
+ }
+
+ private CpdMapping getMapping(Language language) {
+ if (mappings != null) {
+ for (CpdMapping cpdMapping : mappings) {
+ if (cpdMapping.getLanguage().equals(language)) {
+ return cpdMapping;
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/TokenizerBridge.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/TokenizerBridge.java
new file mode 100644
index 00000000000..eab059e5f37
--- /dev/null
+++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/TokenizerBridge.java
@@ -0,0 +1,77 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2011 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar 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.
+ *
+ * Sonar 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 Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.cpd;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import net.sourceforge.pmd.cpd.SourceCode;
+import net.sourceforge.pmd.cpd.TokenEntry;
+import net.sourceforge.pmd.cpd.Tokenizer;
+import net.sourceforge.pmd.cpd.Tokens;
+import org.sonar.duplications.cpd.FileCodeLoaderWithoutCache;
+import org.sonar.duplications.statement.Statement;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+public class TokenizerBridge {
+
+ private final Tokenizer tokenizer;
+ private final String encoding;
+
+ public TokenizerBridge(Tokenizer tokenizer, String encoding) {
+ this.tokenizer = tokenizer;
+ this.encoding = encoding;
+ clearCache();
+ }
+
+ public List<Statement> tokenize(File file) {
+ SourceCode sourceCode = new SourceCode(new FileCodeLoaderWithoutCache(file, encoding));
+ Tokens tokens = new Tokens();
+ try {
+ tokenizer.tokenize(sourceCode, tokens);
+ } catch (IOException e) {
+ throw Throwables.propagate(e);
+ }
+ return convert(tokens.getTokens());
+ }
+
+ /**
+ * We expect that implementation of {@link Tokenizer} is correct:
+ * tokens ordered by occurrence in source code and last token is EOF.
+ */
+ private List<Statement> convert(List<TokenEntry> tokens) {
+ ImmutableList.Builder<Statement> result = ImmutableList.builder();
+ for (TokenEntry token : tokens) {
+ if (token != TokenEntry.EOF) {
+ int line = token.getBeginLine();
+ int id = token.getIdentifier();
+ result.add(new Statement(line, line, Integer.toString(id)));
+ }
+ }
+ return result.build();
+ }
+
+ public void clearCache() {
+ TokenEntry.clearImages();
+ }
+
+}