diff options
author | Evgeny Mandrikov <mandrikov@gmail.com> | 2012-01-09 11:39:03 +0400 |
---|---|---|
committer | Evgeny Mandrikov <mandrikov@gmail.com> | 2012-01-09 11:39:42 +0400 |
commit | 37d3be75d6f52a7d9a831806b3031a23c7c0ef7d (patch) | |
tree | 7f000eee1c90b378c57ee0291be1d9ae8d23c17e /plugins | |
parent | beb8d4cc840962e5581bb8ae3f3ca4717befcd6b (diff) | |
download | sonarqube-37d3be75d6f52a7d9a831806b3031a23c7c0ef7d.tar.gz sonarqube-37d3be75d6f52a7d9a831806b3031a23c7c0ef7d.zip |
SONAR-3139 Allow to use Sonar CPD with existing extension point CpdMapping
Diffstat (limited to 'plugins')
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(); + } + +} |