diff options
author | Evgeny Mandrikov <mandrikov@gmail.com> | 2012-04-30 12:33:15 +0600 |
---|---|---|
committer | Evgeny Mandrikov <mandrikov@gmail.com> | 2012-04-30 16:42:20 +0600 |
commit | 298ece1b781be58ca70d4406a5df84b26869dbe7 (patch) | |
tree | 76c804ba0e699062031bd80bbfd7c0db248f91ea | |
parent | bd01ac1036d4a85c1cc434d720f0897a3f7b5fd3 (diff) | |
download | sonarqube-298ece1b781be58ca70d4406a5df84b26869dbe7.tar.gz sonarqube-298ece1b781be58ca70d4406a5df84b26869dbe7.zip |
SONAR-3182 Do not use PMD CPD
20 files changed, 82 insertions, 1726 deletions
diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdAnalyser.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdAnalyser.java deleted file mode 100644 index 25bf47f6247..00000000000 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdAnalyser.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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 net.sourceforge.pmd.cpd.TokenEntry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.batch.CpdMapping; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.duplications.cpd.Match; - -import java.io.File; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -public class CpdAnalyser { - - private static final Logger LOG = LoggerFactory.getLogger(CpdAnalyser.class); - - private CpdMapping mapping; - private SensorContext context; - private Project project; - - public CpdAnalyser(Project project, SensorContext context, CpdMapping mapping) { - this.mapping = mapping; - this.context = context; - this.project = project; - } - - public void analyse(Iterator<Match> matches) { - Map<Resource, DuplicationsData> duplicationsData = new HashMap<Resource, DuplicationsData>(); - while (matches.hasNext()) { - Match match = matches.next(); - - for (TokenEntry firstMark : match.getMarkSet()) { - String firstAbsolutePath = firstMark.getTokenSrcID(); - int firstLine = firstMark.getBeginLine(); - - Resource firstFile = mapping.createResource(new File(firstAbsolutePath), project.getFileSystem().getSourceDirs()); - if (firstFile == null) { - LOG.warn("CPD - File not found : {}", firstAbsolutePath); - continue; - } - - DuplicationsData firstFileData = getDuplicationsData(duplicationsData, firstFile); - firstFileData.incrementDuplicatedBlock(); - - for (TokenEntry tokenEntry : match.getMarkSet()) { - String secondAbsolutePath = tokenEntry.getTokenSrcID(); - int secondLine = tokenEntry.getBeginLine(); - if (secondAbsolutePath.equals(firstAbsolutePath) && firstLine == secondLine) { - continue; - } - Resource secondFile = mapping.createResource(new File(secondAbsolutePath), project.getFileSystem().getSourceDirs()); - if (secondFile == null) { - LOG.warn("CPD - File not found : {}", secondAbsolutePath); - continue; - } - - String resourceKey = SonarEngine.getFullKey(project, secondFile); - firstFileData.cumulate(resourceKey, secondLine, firstLine, match.getLineCount()); - } - } - } - - for (Map.Entry<Resource, DuplicationsData> entry : duplicationsData.entrySet()) { - entry.getValue().save(context, entry.getKey()); - } - } - - private DuplicationsData getDuplicationsData(Map<Resource, DuplicationsData> fileContainer, Resource file) { - DuplicationsData data = fileContainer.get(file); - if (data == null) { - String resourceKey = SonarEngine.getFullKey(project, file); - data = new DuplicationsData(resourceKey); - fileContainer.put(file, data); - } - return data; - } - -} diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdException.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdException.java deleted file mode 100644 index f4cd45b4d55..00000000000 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdException.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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; - -public class CpdException extends RuntimeException { - public CpdException(Throwable throwable) { - super(throwable); - } -} 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 9b734290990..ccbe4936d82 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 @@ -20,12 +20,7 @@ package org.sonar.plugins.cpd; import com.google.common.collect.ImmutableList; -import org.sonar.api.BatchExtension; -import org.sonar.api.CoreProperties; -import org.sonar.api.Properties; -import org.sonar.api.Property; -import org.sonar.api.PropertyType; -import org.sonar.api.SonarPlugin; +import org.sonar.api.*; import org.sonar.plugins.cpd.decorators.DuplicationDensityDecorator; import org.sonar.plugins.cpd.decorators.SumDuplicationsDecorator; import org.sonar.plugins.cpd.index.IndexFactory; @@ -103,7 +98,6 @@ public final class CpdPlugin extends SonarPlugin { DuplicationDensityDecorator.class, IndexFactory.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 b07ff10dbff..754ef176ee0 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 @@ -19,67 +19,51 @@ */ package org.sonar.plugins.cpd; +import com.google.common.annotations.VisibleForTesting; import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; import org.sonar.api.batch.Sensor; import org.sonar.api.batch.SensorContext; import org.sonar.api.resources.Project; -import org.sonar.api.utils.Logs; public class CpdSensor implements Sensor { + private static final Logger LOG = LoggerFactory.getLogger(CpdSensor.class); + private CpdEngine sonarEngine; - private CpdEngine pmdEngine; private CpdEngine sonarBridgeEngine; - public CpdSensor(SonarEngine sonarEngine, PmdEngine pmdEngine) { + public CpdSensor(SonarEngine sonarEngine, SonarBridgeEngine sonarBridgeEngine) { 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"); + LOG.info("Detection of duplicated code is skipped"); return false; } if (!getEngine(project).isLanguageSupported(project.getLanguage())) { - LoggerFactory.getLogger(getClass()).info("Detection of duplication code is not supported for {}.", project.getLanguage()); + LOG.info("Detection of duplicated code is not supported for {}.", project.getLanguage()); return false; } return true; } - private CpdEngine getEngine(Project project) { - if (isEngineEnabled(project, "sonar")) { - if (sonarEngine.isLanguageSupported(project.getLanguage())) { - return sonarEngine; - } - // falback to bridge - } else if (isEngineEnabled(project, "pmd")) { - return pmdEngine; + @VisibleForTesting + CpdEngine getEngine(Project project) { + if (sonarEngine.isLanguageSupported(project.getLanguage())) { + return sonarEngine; + } else { + return sonarBridgeEngine; } - return sonarBridgeEngine; - } - - boolean isEngineEnabled(Project project, String engineName) { - Configuration conf = project.getConfiguration(); - return StringUtils.equalsIgnoreCase(conf.getString(CoreProperties.CPD_ENGINE, CoreProperties.CPD_ENGINE_DEFAULT_VALUE), engineName); - } - - boolean isSonarEngineEnabled(Project project) { - return isEngineEnabled(project, "sonar"); } + @VisibleForTesting boolean isSkipped(Project project) { Configuration conf = project.getConfiguration(); return conf.getBoolean("sonar.cpd." + project.getLanguageKey() + ".skip", @@ -88,7 +72,7 @@ public class CpdSensor implements Sensor { public void analyse(Project project, SensorContext context) { CpdEngine engine = getEngine(project); - Logs.INFO.info("{} is used", engine); + LOG.info("{} is used", engine); engine.analyse(project, context); } @@ -96,4 +80,5 @@ public class CpdSensor implements Sensor { public String toString() { return getClass().getSimpleName(); } + } diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DuplicationsData.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DuplicationsData.java deleted file mode 100644 index fc800738a86..00000000000 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DuplicationsData.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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.collect.Lists; -import com.google.common.collect.Sets; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.PersistenceMode; -import org.sonar.api.resources.Resource; - -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Set; - -public class DuplicationsData { - - private final String resourceKey; - private final Set<Integer> duplicatedLines = Sets.newHashSet(); - private final List<XmlEntry> duplicationXMLEntries = Lists.newArrayList(); - - private double duplicatedBlocks; - - public DuplicationsData(String resourceKey) { - this.resourceKey = resourceKey; - } - - public void cumulate(String targetResourceKey, int targetDuplicationStartLine, int duplicationStartLine, int duplicatedLines) { - duplicationXMLEntries.add(new XmlEntry(targetResourceKey, targetDuplicationStartLine, duplicationStartLine, duplicatedLines)); - for (int duplicatedLine = duplicationStartLine; duplicatedLine < duplicationStartLine + duplicatedLines; duplicatedLine++) { - this.duplicatedLines.add(duplicatedLine); - } - } - - public void incrementDuplicatedBlock() { - duplicatedBlocks++; - } - - public void save(SensorContext context, Resource resource) { - context.saveMeasure(resource, CoreMetrics.DUPLICATED_FILES, 1d); - context.saveMeasure(resource, CoreMetrics.DUPLICATED_LINES, (double) duplicatedLines.size()); - context.saveMeasure(resource, CoreMetrics.DUPLICATED_BLOCKS, duplicatedBlocks); - - Measure data = new Measure(CoreMetrics.DUPLICATIONS_DATA, getDuplicationXMLData()) - .setPersistenceMode(PersistenceMode.DATABASE); - context.saveMeasure(resource, data); - } - - private String getDuplicationXMLData() { - Collections.sort(duplicationXMLEntries, COMPARATOR); - StringBuilder duplicationXML = new StringBuilder("<duplications>"); - for (XmlEntry xmlEntry : duplicationXMLEntries) { - duplicationXML.append(xmlEntry.toString()); - } - duplicationXML.append("</duplications>"); - return duplicationXML.toString(); - } - - private static final Comparator<XmlEntry> COMPARATOR = new Comparator<XmlEntry>() { - public int compare(XmlEntry o1, XmlEntry o2) { - if (o1.startLine == o2.startLine) { - return o1.lines - o2.lines; - } - return o1.startLine - o2.startLine; - } - }; - - private final class XmlEntry { - private final String target; - private final int targetStartLine; - private final int startLine; - private final int lines; - - private XmlEntry(String target, int targetStartLine, int startLine, int lines) { - this.target = target; - this.targetStartLine = targetStartLine; - this.startLine = startLine; - this.lines = lines; - } - - @Override - public String toString() { - return new StringBuilder() - .append("<g>") - .append("<b s=\"").append(startLine).append("\" l=\"").append(lines).append("\" r=\"").append(resourceKey).append("\" />") - .append("<b s=\"").append(targetStartLine).append("\" l=\"").append(lines).append("\" r=\"").append(target).append("\" />") - .append("</g>") - .toString(); - } - } - -} diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/PmdEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/PmdEngine.java deleted file mode 100644 index e84f22e36de..00000000000 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/PmdEngine.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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.annotations.VisibleForTesting; -import net.sourceforge.pmd.cpd.AbstractLanguage; -import net.sourceforge.pmd.cpd.TokenEntry; -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.resources.Language; -import org.sonar.api.resources.Project; -import org.sonar.duplications.cpd.CPD; - -import java.io.IOException; -import java.nio.charset.Charset; - -public class PmdEngine extends CpdEngine { - - private final CpdMapping[] mappings; - - public PmdEngine() { - this.mappings = null; - } - - public PmdEngine(CpdMapping[] mappings) { - this.mappings = mappings; - } - - @Override - public boolean isLanguageSupported(Language language) { - return getMapping(language) != null; - } - - private CpdMapping getMapping(Language language) { - if (mappings != null) { - for (CpdMapping cpdMapping : mappings) { - if (cpdMapping.getLanguage().equals(language)) { - return cpdMapping; - } - } - } - return null; - } - - @Override - public void analyse(Project project, SensorContext context) { - CpdMapping mapping = getMapping(project.getLanguage()); - CPD cpd = executeCPD(project, mapping, project.getFileSystem().getSourceCharset()); - saveResults(cpd, mapping, project, context); - } - - private void saveResults(CPD cpd, CpdMapping mapping, Project project, SensorContext context) { - CpdAnalyser cpdAnalyser = new CpdAnalyser(project, context, mapping); - cpdAnalyser.analyse(cpd.getMatches()); - } - - private CPD executeCPD(Project project, CpdMapping mapping, Charset encoding) { - try { - CPD cpd = configureCPD(project, mapping, encoding); - cpd.go(); - return cpd; - - } catch (Exception e) { - throw new CpdException(e); - } - } - - private CPD configureCPD(Project project, CpdMapping mapping, Charset encoding) throws IOException { - // To avoid a cpd bug generating error as "java.lang.IndexOutOfBoundsException: Index: 259, Size: 248" - // See http://sourceforge.net/tracker/?func=detail&atid=479921&aid=1947823&group_id=56262 for more details - TokenEntry.clearImages(); - - int minTokens = getMinimumTokens(project); - AbstractLanguage cpdLanguage = new AbstractLanguage(mapping.getTokenizer()) { - }; - - CPD cpd = new CPD(minTokens, cpdLanguage); - cpd.setEncoding(encoding.name()); - cpd.setLoadSourceCodeSlices(false); - cpd.add(project.getFileSystem().getSourceFiles(project.getLanguage())); - return cpd; - } - - @VisibleForTesting - static 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)); - } - -} 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 index 026893d031c..2b3cfe2e8bc 100644 --- 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 @@ -22,8 +22,10 @@ package org.sonar.plugins.cpd; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import org.apache.commons.configuration.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.CoreProperties; import org.sonar.api.batch.CpdMapping; import org.sonar.api.batch.SensorContext; import org.sonar.api.resources.*; @@ -89,7 +91,7 @@ public class SonarBridgeEngine extends CpdEngine { } // Detect - Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(PmdEngine.getMinimumTokens(project)); + Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(getMinimumTokens(project)); ExecutorService executorService = Executors.newSingleThreadExecutor(); try { @@ -137,6 +139,13 @@ public class SonarBridgeEngine extends CpdEngine { } } + @VisibleForTesting + static 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) { diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdAnalyserTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdAnalyserTest.java deleted file mode 100644 index ada79ec7400..00000000000 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdAnalyserTest.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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 net.sourceforge.pmd.cpd.TokenEntry; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.sonar.api.batch.CpdMapping; -import org.sonar.api.batch.SensorContext; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.resources.JavaFile; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.ProjectFileSystem; -import org.sonar.api.resources.Resource; -import org.sonar.duplications.cpd.Match; - -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; - -public class CpdAnalyserTest { - - @Test - public void testOneSimpleDuplicationBetweenTwoFiles() throws Exception { - ProjectFileSystem fileSystem = mock(ProjectFileSystem.class); - when(fileSystem.getSourceDirs()).thenReturn(Collections.<File> emptyList()); - File file1 = new File("target/tmp/file1.ext"); - File file2 = new File("target/tmp/file2.ext"); - - Project project = new Project("key").setFileSystem(fileSystem); - - SensorContext context = mock(SensorContext.class); - - CpdMapping cpdMapping = mock(CpdMapping.class); - Resource resource1 = new JavaFile("foo.Foo"); - Resource resource2 = new JavaFile("foo.Bar"); - when(cpdMapping.createResource((File) anyObject(), anyList())).thenReturn(resource1).thenReturn(resource2).thenReturn(resource2) - .thenReturn(resource1); - - Match match1 = new Match(5, new TokenEntry(null, file1.getAbsolutePath(), 5), new TokenEntry(null, file2.getAbsolutePath(), 15)); - match1.setLineCount(200); - - CpdAnalyser cpdAnalyser = new CpdAnalyser(project, context, cpdMapping); - cpdAnalyser.analyse(Arrays.asList(match1).iterator()); - - ArgumentCaptor<Measure> measureCaptor = ArgumentCaptor.forClass(Measure.class); - - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_LINES, 200d); - verify(context).saveMeasure(eq(resource1), measureCaptor.capture()); - Measure measure = measureCaptor.getValue(); - assertThat(measure.getMetric(), is(CoreMetrics.DUPLICATIONS_DATA)); - assertThat(measure.getData(), is("<duplications><g>" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "<b s=\"15\" l=\"200\" r=\"key:foo.Bar\" />" - + "</g></duplications>")); - - verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_LINES, 200d); - verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(eq(resource2), measureCaptor.capture()); - assertThat(measure.getMetric(), is(CoreMetrics.DUPLICATIONS_DATA)); - assertThat(measure.getData(), is("<duplications><g>" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "<b s=\"15\" l=\"200\" r=\"key:foo.Bar\" />" - + "</g></duplications>")); - } - - @Test - public void testClassicalCaseWithTwoDuplicatedBlocsInvolvingThreeFiles() throws Exception { - ProjectFileSystem fileSystem = mock(ProjectFileSystem.class); - when(fileSystem.getSourceDirs()).thenReturn(Collections.<File> emptyList()); - File file1 = new File("target/tmp/file1.ext"); - File file2 = new File("target/tmp/file2.ext"); - File file3 = new File("target/tmp/file3.ext"); - - Project project = new Project("key").setFileSystem(fileSystem); - - SensorContext context = mock(SensorContext.class); - - CpdMapping cpdMapping = mock(CpdMapping.class); - Resource resource1 = new JavaFile("foo.Foo"); - Resource resource2 = new JavaFile("foo.Bar"); - Resource resource3 = new JavaFile("foo.Hotel"); - when(cpdMapping.createResource((File) anyObject(), anyList())).thenReturn(resource1).thenReturn(resource2).thenReturn(resource2) - .thenReturn(resource1).thenReturn(resource1).thenReturn(resource3).thenReturn(resource3).thenReturn(resource1); - - Match match1 = new Match(5, new TokenEntry(null, file1.getAbsolutePath(), 5), new TokenEntry(null, file2.getAbsolutePath(), 15)); - match1.setLineCount(200); - Match match2 = new Match(5, new TokenEntry(null, file1.getAbsolutePath(), 5), new TokenEntry(null, file3.getAbsolutePath(), 15)); - match2.setLineCount(100); - - CpdAnalyser cpdAnalyser = new CpdAnalyser(project, context, cpdMapping); - cpdAnalyser.analyse(Arrays.asList(match1, match2).iterator()); - - ArgumentCaptor<Measure> measureCaptor = ArgumentCaptor.forClass(Measure.class); - - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_BLOCKS, 2d); - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_LINES, 200d); - verify(context).saveMeasure(eq(resource1), measureCaptor.capture()); - - Measure measure = measureCaptor.getValue(); - assertThat(measure.getMetric(), is(CoreMetrics.DUPLICATIONS_DATA)); - assertThat(measure.getData(), is("<duplications>" - + "<g>" - + "<b s=\"5\" l=\"100\" r=\"key:foo.Foo\" />" - + "<b s=\"15\" l=\"100\" r=\"key:foo.Hotel\" />" - + "</g>" - + "<g>" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "<b s=\"15\" l=\"200\" r=\"key:foo.Bar\" />" - + "</g>" - + "</duplications>")); - - verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_LINES, 200d); - verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(eq(resource2), measureCaptor.capture()); - - measure = measureCaptor.getValue(); - assertThat(measure.getMetric(), is(CoreMetrics.DUPLICATIONS_DATA)); - assertThat(measure.getData(), is("<duplications>" - + "<g>" - + "<b s=\"15\" l=\"200\" r=\"key:foo.Bar\" />" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "</g>" - + "</duplications>")); - - verify(context).saveMeasure(resource3, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource3, CoreMetrics.DUPLICATED_LINES, 100d); - verify(context).saveMeasure(resource3, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(eq(resource3), measureCaptor.capture()); - - measure = measureCaptor.getValue(); - assertThat(measure.getMetric(), is(CoreMetrics.DUPLICATIONS_DATA)); - assertThat(measure.getData(), is("<duplications>" - + "<g>" - + "<b s=\"15\" l=\"100\" r=\"key:foo.Hotel\" />" - + "<b s=\"5\" l=\"100\" r=\"key:foo.Foo\" />" - + "</g>" - + "</duplications>")); - } - - @Test - public void testOneDuplicatedBlocInvolvingMoreThanTwoFiles() throws Exception { - ProjectFileSystem fileSystem = mock(ProjectFileSystem.class); - when(fileSystem.getSourceDirs()).thenReturn(Collections.<File> emptyList()); - File file1 = new File("target/tmp/file1.ext"); - File file2 = new File("target/tmp/file2.ext"); - File file3 = new File("target/tmp/file3.ext"); - File file4 = new File("target/tmp/file4.ext"); - - Project project = new Project("key").setFileSystem(fileSystem); - - SensorContext context = mock(SensorContext.class); - - CpdMapping cpdMapping = mock(CpdMapping.class); - Resource resource1 = new JavaFile("foo.Foo"); - Resource resource2 = new JavaFile("foo.Bar"); - Resource resource3 = new JavaFile("foo.Hotel"); - Resource resource4 = new JavaFile("foo.Coffee"); - when(cpdMapping.createResource((File) anyObject(), anyList())).thenReturn(resource1).thenReturn(resource2).thenReturn(resource3) - .thenReturn(resource4).thenReturn(resource2).thenReturn(resource1).thenReturn(resource3).thenReturn(resource4) - .thenReturn(resource3).thenReturn(resource1).thenReturn(resource2).thenReturn(resource4).thenReturn(resource4) - .thenReturn(resource1).thenReturn(resource2).thenReturn(resource3); - - Match match = new Match(5, createTokenEntry(file1.getAbsolutePath(), 5), createTokenEntry(file2.getAbsolutePath(), 15)); - match.setLineCount(200); - Set<TokenEntry> tokenEntries = new LinkedHashSet<TokenEntry>(); - tokenEntries.add(createTokenEntry(file1.getAbsolutePath(), 5)); - tokenEntries.add(createTokenEntry(file2.getAbsolutePath(), 15)); - tokenEntries.add(createTokenEntry(file3.getAbsolutePath(), 7)); - tokenEntries.add(createTokenEntry(file4.getAbsolutePath(), 10)); - match.setMarkSet(tokenEntries); - - CpdAnalyser cpdAnalyser = new CpdAnalyser(project, context, cpdMapping); - cpdAnalyser.analyse(Arrays.asList(match).iterator()); - - ArgumentCaptor<Measure> measureCaptor = ArgumentCaptor.forClass(Measure.class); - - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_LINES, 200d); - verify(context).saveMeasure(eq(resource1), measureCaptor.capture()); - - Measure measure = measureCaptor.getValue(); - assertThat(measure.getMetric(), is(CoreMetrics.DUPLICATIONS_DATA)); - assertThat(measure.getData(), is("<duplications>" - + "<g>" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "<b s=\"15\" l=\"200\" r=\"key:foo.Bar\" />" - + "</g>" - + "<g>" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "<b s=\"7\" l=\"200\" r=\"key:foo.Hotel\" />" - + "</g>" - + "<g>" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "<b s=\"10\" l=\"200\" r=\"key:foo.Coffee\" />" - + "</g>" - + "</duplications>")); - - verify(context).saveMeasure(resource3, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource3, CoreMetrics.DUPLICATED_LINES, 200d); - verify(context).saveMeasure(resource3, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(eq(resource3), measureCaptor.capture()); - - measure = measureCaptor.getValue(); - assertThat(measure.getMetric(), is(CoreMetrics.DUPLICATIONS_DATA)); - assertThat(measure.getData(), is("<duplications>" - + "<g>" - + "<b s=\"7\" l=\"200\" r=\"key:foo.Hotel\" />" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "</g>" - + "<g>" - + "<b s=\"7\" l=\"200\" r=\"key:foo.Hotel\" />" - + "<b s=\"15\" l=\"200\" r=\"key:foo.Bar\" />" - + "</g>" - + "<g>" - + "<b s=\"7\" l=\"200\" r=\"key:foo.Hotel\" />" - + "<b s=\"10\" l=\"200\" r=\"key:foo.Coffee\" />" - + "</g>" - + "</duplications>")); - - verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_LINES, 200d); - verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(eq(resource2), measureCaptor.capture()); - - measure = measureCaptor.getValue(); - assertThat(measure.getMetric(), is(CoreMetrics.DUPLICATIONS_DATA)); - assertThat(measure.getData(), is("<duplications>" - + "<g>" - + "<b s=\"15\" l=\"200\" r=\"key:foo.Bar\" />" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "</g>" - + "<g>" - + "<b s=\"15\" l=\"200\" r=\"key:foo.Bar\" />" - + "<b s=\"7\" l=\"200\" r=\"key:foo.Hotel\" />" - + "</g>" - + "<g>" - + "<b s=\"15\" l=\"200\" r=\"key:foo.Bar\" />" - + "<b s=\"10\" l=\"200\" r=\"key:foo.Coffee\" />" - + "</g>" - + "</duplications>")); - - verify(context).saveMeasure(resource4, CoreMetrics.DUPLICATED_LINES, 200d); - verify(context).saveMeasure(resource4, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource4, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(eq(resource4), measureCaptor.capture()); - - measure = measureCaptor.getValue(); - assertThat(measure.getMetric(), is(CoreMetrics.DUPLICATIONS_DATA)); - assertThat(measure.getData(), is("<duplications>" - + "<g>" - + "<b s=\"10\" l=\"200\" r=\"key:foo.Coffee\" />" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "</g>" - + "<g>" - + "<b s=\"10\" l=\"200\" r=\"key:foo.Coffee\" />" - + "<b s=\"15\" l=\"200\" r=\"key:foo.Bar\" />" - + "</g>" - + "<g>" - + "<b s=\"10\" l=\"200\" r=\"key:foo.Coffee\" />" - + "<b s=\"7\" l=\"200\" r=\"key:foo.Hotel\" />" - + "</g>" - + "</duplications>")); - } - - @Test - public void testDuplicationOnSameFile() throws Exception { - ProjectFileSystem fileSystem = mock(ProjectFileSystem.class); - when(fileSystem.getSourceDirs()).thenReturn(Collections.<File> emptyList()); - File file1 = new File("target/tmp/file1.ext"); - - Project project = new Project("key").setFileSystem(fileSystem); - - SensorContext context = mock(SensorContext.class); - - CpdMapping cpdMapping = mock(CpdMapping.class); - Resource resource1 = new JavaFile("foo.Foo"); - when(cpdMapping.createResource((File) anyObject(), anyList())).thenReturn(resource1).thenReturn(resource1); - - Match match1 = new Match(304, new TokenEntry(null, file1.getAbsolutePath(), 5), new TokenEntry(null, file1.getAbsolutePath(), 215)); - match1.setLineCount(200); - - CpdAnalyser cpdAnalyser = new CpdAnalyser(project, context, cpdMapping); - cpdAnalyser.analyse(Arrays.asList(match1).iterator()); - - ArgumentCaptor<Measure> measureCaptor = ArgumentCaptor.forClass(Measure.class); - - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_LINES, 400d); - verify(context).saveMeasure(resource1, CoreMetrics.DUPLICATED_BLOCKS, 2d); - verify(context).saveMeasure(eq(resource1), measureCaptor.capture()); - - Measure measure = measureCaptor.getValue(); - assertThat(measure.getMetric(), is(CoreMetrics.DUPLICATIONS_DATA)); - // FIXME in fact should be only one group - see SONAR-3131 - assertThat(measure.getData(), is("<duplications>" - + "<g>" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "<b s=\"215\" l=\"200\" r=\"key:foo.Foo\" />" - + "</g>" - + "<g>" - + "<b s=\"215\" l=\"200\" r=\"key:foo.Foo\" />" - + "<b s=\"5\" l=\"200\" r=\"key:foo.Foo\" />" - + "</g>" - + "</duplications>")); - } - - private static TokenEntry createTokenEntry(String sourceId, int line) { - TokenEntry entry = new TokenEntry(null, sourceId, line); - entry.setHashCode(sourceId.hashCode() + line); - return entry; - } -} diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java index 028b6f707a2..66e946c6d49 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java @@ -20,8 +20,10 @@ package org.sonar.plugins.cpd; import org.apache.commons.configuration.PropertiesConfiguration; +import org.junit.Before; import org.junit.Test; -import org.sonar.api.batch.CpdMapping; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Language; import org.sonar.api.resources.Project; import org.sonar.plugins.cpd.index.IndexFactory; @@ -29,9 +31,22 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; public class CpdSensorTest { + private SonarEngine sonarEngine; + private SonarBridgeEngine sonarBridgeEngine; + private CpdSensor sensor; + + @Before + public void setUp() { + IndexFactory indexFactory = new IndexFactory(null); + sonarEngine = new SonarEngine(indexFactory); + sonarBridgeEngine = new SonarBridgeEngine(indexFactory); + sensor = new CpdSensor(sonarEngine, sonarBridgeEngine); + } + @Test public void generalSkip() { PropertiesConfiguration conf = new PropertiesConfiguration(); @@ -39,7 +54,6 @@ public class CpdSensorTest { Project project = createJavaProject().setConfiguration(conf); - CpdSensor sensor = new CpdSensor(new SonarEngine(new IndexFactory(null)), new PmdEngine(new CpdMapping[0])); assertTrue(sensor.isSkipped(project)); } @@ -47,7 +61,6 @@ public class CpdSensorTest { public void doNotSkipByDefault() { Project project = createJavaProject().setConfiguration(new PropertiesConfiguration()); - CpdSensor sensor = new CpdSensor(new SonarEngine(new IndexFactory(null)), new PmdEngine(new CpdMapping[0])); assertFalse(sensor.isSkipped(project)); } @@ -60,28 +73,26 @@ public class CpdSensorTest { Project phpProject = createPhpProject().setConfiguration(conf); Project javaProject = createJavaProject().setConfiguration(conf); - CpdSensor sensor = new CpdSensor(new SonarEngine(new IndexFactory(null)), new PmdEngine(new CpdMapping[0])); assertTrue(sensor.isSkipped(phpProject)); assertFalse(sensor.isSkipped(javaProject)); } @Test public void engine() { - PropertiesConfiguration conf = new PropertiesConfiguration(); - Project project = createJavaProject().setConfiguration(conf); - CpdSensor sensor = new CpdSensor(new SonarEngine(new IndexFactory(null)), new PmdEngine(new CpdMapping[0])); + Project phpProject = createPhpProject(); + Project javaProject = createJavaProject(); - assertThat(sensor.isSonarEngineEnabled(project), is(true)); - conf.setProperty("sonar.cpd.engine", "pmd"); - assertThat(sensor.isSonarEngineEnabled(project), is(false)); + assertThat(sensor.getEngine(javaProject), is((CpdEngine) sonarEngine)); + assertThat(sensor.getEngine(phpProject), is((CpdEngine) sonarBridgeEngine)); } private Project createJavaProject() { - return new Project("java_project").setLanguageKey("java"); + return new Project("java_project").setLanguageKey("java").setLanguage(Java.INSTANCE); } private Project createPhpProject() { - return new Project("php_project").setLanguageKey("php"); + Language phpLanguage = mock(Language.class); + return new Project("php_project").setLanguageKey("php").setLanguage(phpLanguage); } } diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/PmdEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/PmdEngineTest.java deleted file mode 100644 index 3bbedf7f787..00000000000 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/PmdEngineTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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 static junit.framework.Assert.assertEquals; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.apache.commons.configuration.PropertiesConfiguration; -import org.junit.Test; -import org.sonar.api.CoreProperties; -import org.sonar.api.batch.CpdMapping; -import org.sonar.api.resources.Java; -import org.sonar.api.resources.Language; -import org.sonar.api.resources.Project; - -public class PmdEngineTest { - - @Test - public void shouldNotFailWhenNoMappings() { - PmdEngine engine = new PmdEngine(); - assertThat(engine.isLanguageSupported(Java.INSTANCE), is(false)); - } - - @Test - public void shouldCheckLanguageSupport() { - CpdMapping mapping = mock(CpdMapping.class); - when(mapping.getLanguage()).thenReturn(Java.INSTANCE); - PmdEngine engine = new PmdEngine(new CpdMapping[] { mapping }); - assertThat(engine.isLanguageSupported(Java.INSTANCE), is(true)); - - Language anotherLanguage = mock(Language.class); - assertThat(engine.isLanguageSupported(anotherLanguage), is(false)); - } - - @Test - public void defaultMinimumTokens() { - Project project = createJavaProject().setConfiguration(new PropertiesConfiguration()); - - assertEquals(CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE, PmdEngine.getMinimumTokens(project)); - } - - @Test - public void generalMinimumTokens() { - PropertiesConfiguration conf = new PropertiesConfiguration(); - conf.setProperty("sonar.cpd.minimumTokens", "33"); - Project project = createJavaProject().setConfiguration(conf); - - assertEquals(33, PmdEngine.getMinimumTokens(project)); - } - - @Test - public void minimumTokensByLanguage() { - PropertiesConfiguration conf = new PropertiesConfiguration(); - conf.setProperty("sonar.cpd.minimumTokens", "100"); - conf.setProperty("sonar.cpd.php.minimumTokens", "33"); - - Project phpProject = createPhpProject().setConfiguration(conf); - Project javaProject = createJavaProject().setConfiguration(conf); - - assertEquals(100, PmdEngine.getMinimumTokens(javaProject)); - assertEquals(33, PmdEngine.getMinimumTokens(phpProject)); - } - - private Project createJavaProject() { - return new Project("java_project").setLanguageKey("java"); - } - - private Project createPhpProject() { - return new Project("php_project").setLanguageKey("php"); - } - -} diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarBridgeEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarBridgeEngineTest.java index e9ae76e300f..b036804b74e 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarBridgeEngineTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarBridgeEngineTest.java @@ -19,7 +19,10 @@ */ package org.sonar.plugins.cpd; +import org.apache.commons.configuration.PropertiesConfiguration; import org.junit.Test; +import org.sonar.api.CoreProperties; +import org.sonar.api.resources.Project; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; @@ -34,4 +37,33 @@ public class SonarBridgeEngineTest { assertThat(SonarBridgeEngine.getDefaultBlockSize("other"), is(10)); } + @Test + public void defaultMinimumTokens() { + Project project = new Project("foo").setConfiguration(new PropertiesConfiguration()); + + assertThat(SonarBridgeEngine.getMinimumTokens(project), is(CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE)); + } + + @Test + public void generalMinimumTokens() { + PropertiesConfiguration conf = new PropertiesConfiguration(); + conf.setProperty("sonar.cpd.minimumTokens", "33"); + Project project = new Project("foo").setConfiguration(conf); + + assertThat(SonarBridgeEngine.getMinimumTokens(project), is(33)); + } + + @Test + public void minimumTokensByLanguage() { + PropertiesConfiguration conf = new PropertiesConfiguration(); + conf.setProperty("sonar.cpd.java.minimumTokens", "42"); + conf.setProperty("sonar.cpd.php.minimumTokens", "33"); + + Project javaProject = new Project("foo").setLanguageKey("java").setConfiguration(conf); + Project phpProject = new Project("foo").setLanguageKey("php").setConfiguration(conf); + + assertThat(SonarBridgeEngine.getMinimumTokens(javaProject), is(42)); + assertThat(SonarBridgeEngine.getMinimumTokens(phpProject), is(33)); + } + } diff --git a/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/CPDListener.java b/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/CPDListener.java deleted file mode 100644 index 8832c31ea2f..00000000000 --- a/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/CPDListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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 - */ - -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ -package net.sourceforge.pmd.cpd; - -import java.io.File; - -public interface CPDListener { - - int INIT = 0; - int HASH = 1; - int MATCH = 2; - int GROUPING = 3; - int DONE = 4; - - void addedFile(int fileCount, File file); - - void phaseUpdate(int phase); -} diff --git a/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/CPDNullListener.java b/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/CPDNullListener.java deleted file mode 100644 index 8bf581b1fef..00000000000 --- a/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/CPDNullListener.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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 - */ - -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ -package net.sourceforge.pmd.cpd; - -import java.io.File; - -public class CPDNullListener implements CPDListener { - public void addedFile(int fileCount, File file) { - } - - public void phaseUpdate(int phase) { - } -} diff --git a/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/Tokens.java b/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/Tokens.java index 1f63af26923..a9398dba4d9 100644 --- a/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/Tokens.java +++ b/sonar-duplications/src/main/java/net/sourceforge/pmd/cpd/Tokens.java @@ -23,8 +23,6 @@ */ package net.sourceforge.pmd.cpd; -import org.sonar.duplications.cpd.Match; - import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -46,22 +44,10 @@ public class Tokens { return tokens.iterator(); } - private TokenEntry get(int index) { - return tokens.get(index); - } - public int size() { return tokens.size(); } - public int getLineCount(TokenEntry mark, Match match) { - TokenEntry endTok = get(mark.getIndex() + match.getTokenCount() - 1); - if (endTok == TokenEntry.EOF) { - endTok = get(mark.getIndex() + match.getTokenCount() - 2); - } - return endTok.getBeginLine() - mark.getBeginLine() + 1; - } - public List<TokenEntry> getTokens() { return tokens; } diff --git a/sonar-duplications/src/main/java/net/sourceforge/pmd/util/FileFinder.java b/sonar-duplications/src/main/java/net/sourceforge/pmd/util/FileFinder.java deleted file mode 100644 index fc6b365e5f9..00000000000 --- a/sonar-duplications/src/main/java/net/sourceforge/pmd/util/FileFinder.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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 net.sourceforge.pmd.util; - -import java.io.File; -import java.io.FilenameFilter; -import java.util.ArrayList; -import java.util.List; - -/** - * A utility class for finding files within a directory. - */ -public class FileFinder { - - private FilenameFilter filter; - private static final String FILE_SEP = System.getProperty("file.separator"); - - public List<File> findFilesFrom(String dir, FilenameFilter filter, boolean recurse) { - this.filter = filter; - List<File> files = new ArrayList<File>(); - scanDirectory(new File(dir), files, recurse); - return files; - } - - /** - * Implements a tail recursive file scanner - */ - private void scanDirectory(File dir, List<File> list, boolean recurse) { - String[] candidates = dir.list(filter); - if (candidates == null) { - return; - } - for (int i = 0; i < candidates.length; i++) { - File tmp = new File(dir + FILE_SEP + candidates[i]); - if (tmp.isDirectory()) { - if (recurse) { - scanDirectory(tmp, list, true); - } - } else { - list.add(new File(dir + FILE_SEP + candidates[i])); - } - } - } -} diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/cpd/CPD.java b/sonar-duplications/src/main/java/org/sonar/duplications/cpd/CPD.java deleted file mode 100644 index 61ec8107555..00000000000 --- a/sonar-duplications/src/main/java/org/sonar/duplications/cpd/CPD.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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 - */ - -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ -package org.sonar.duplications.cpd; - -import net.sourceforge.pmd.cpd.*; -import net.sourceforge.pmd.util.FileFinder; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -/** - * @deprecated since 2.14, will be removed soon, and in any case should not be used for unit tests in Sonar plugins: - * instead of using this class for tests, you should test only your implementation of {@link Tokenizer} - */ -@Deprecated -public class CPD { - - private Map<String, SourceCode> source = new HashMap<String, SourceCode>(); - private CPDListener listener = new CPDNullListener(); - private Tokens tokens = new Tokens(); - private int minimumTileSize; - private MatchAlgorithm matchAlgorithm; - private Language language; - private boolean loadSourceCodeSlices = true; - private String encoding = System.getProperty("file.encoding"); - - public CPD(int minimumTileSize, Language language) { - TokenEntry.clearImages(); // workaround for bug 1947823 - this.minimumTileSize = minimumTileSize; - this.language = language; - } - - public void setCpdListener(CPDListener cpdListener) { - this.listener = cpdListener; - } - - public void setEncoding(String encoding) { - this.encoding = encoding; - } - - public void setLoadSourceCodeSlices(boolean loadSourceCodeSlices) { - this.loadSourceCodeSlices = loadSourceCodeSlices; - } - - public void go() { - TokenEntry.clearImages(); - matchAlgorithm = new MatchAlgorithm(source, tokens, minimumTileSize, listener); - matchAlgorithm.setLoadSourceCodeSlices(loadSourceCodeSlices); - matchAlgorithm.findMatches(); - } - - public Iterator<Match> getMatches() { - return matchAlgorithm.matches(); - } - - public void add(File file) throws IOException { - add(1, file); - } - - public void addAllInDirectory(String dir) throws IOException { - addDirectory(dir, false); - } - - public void addRecursively(String dir) throws IOException { - addDirectory(dir, true); - } - - public void add(List<File> files) throws IOException { - for (File f : files) { - add(files.size(), f); - } - } - - private void addDirectory(String dir, boolean recurse) throws IOException { - if (!(new File(dir)).exists()) { - throw new FileNotFoundException("Couldn't find directory " + dir); - } - FileFinder finder = new FileFinder(); - add(finder.findFilesFrom(dir, language.getFileFilter(), recurse)); - } - - private void add(int fileCount, File file) throws IOException { - if (!file.getCanonicalPath().equals(new File(file.getAbsolutePath()).getCanonicalPath())) { - System.out.println("Skipping " + file + " since it appears to be a symlink"); - return; - } - - listener.addedFile(fileCount, file); - SourceCode sourceCode = new SourceCode(new FileCodeLoaderWithoutCache(file, encoding)); - language.getTokenizer().tokenize(sourceCode, tokens); - source.put(sourceCode.getFileName(), sourceCode); - } - -} diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/cpd/Match.java b/sonar-duplications/src/main/java/org/sonar/duplications/cpd/Match.java deleted file mode 100644 index 02012a0d6fe..00000000000 --- a/sonar-duplications/src/main/java/org/sonar/duplications/cpd/Match.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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 - */ - -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ -package org.sonar.duplications.cpd; - -import net.sourceforge.pmd.cpd.TokenEntry; - -import java.util.Comparator; -import java.util.Iterator; -import java.util.Set; -import java.util.TreeSet; - -public class Match implements Comparable<Match> { - - public static final String EOL = System.getProperty("line.separator", "\n"); - - private int tokenCount; - private int lineCount; - private Set<TokenEntry> markSet = new TreeSet<TokenEntry>(); - private TokenEntry[] marks = new TokenEntry[2]; - private String code; - private MatchCode mc; - private String label; - - public static final Comparator<Match> MatchesComparator = new Comparator<Match>() { - - public int compare(Match ma, Match mb) { - return mb.getMarkCount() - ma.getMarkCount(); - } - }; - - public static final Comparator<Match> LinesComparator = new Comparator<Match>() { - - public int compare(Match ma, Match mb) { - return mb.getLineCount() - ma.getLineCount(); - } - }; - - public static final Comparator<Match> LabelComparator = new Comparator<Match>() { - - public int compare(Match ma, Match mb) { - if (ma.getLabel() == null) { - return 1; - } - if (mb.getLabel() == null) { - return -1; - } - return mb.getLabel().compareTo(ma.getLabel()); - } - }; - - public static final Comparator<Match> LengthComparator = new Comparator<Match>() { - - public int compare(Match ma, Match mb) { - return mb.getLineCount() - ma.getLineCount(); - } - }; - - public static class MatchCode { - - private int first; - private int second; - - public MatchCode() { - } - - public MatchCode(TokenEntry m1, TokenEntry m2) { - first = m1.getIndex(); - second = m2.getIndex(); - } - - @Override - public int hashCode() { - return first + 37 * second; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof MatchCode)) { - return false; - } - MatchCode mc = (MatchCode) other; - return mc.first == first && mc.second == second; - } - - public void setFirst(int first) { - this.first = first; - } - - public void setSecond(int second) { - this.second = second; - } - - } - - public Match(int tokenCount, TokenEntry first, TokenEntry second) { - markSet.add(first); - markSet.add(second); - marks[0] = first; - marks[1] = second; - this.tokenCount = tokenCount; - } - - public int getMarkCount() { - return markSet.size(); - } - - public void setLineCount(int lineCount) { - this.lineCount = lineCount; - } - - public int getLineCount() { - return this.lineCount; - } - - public int getTokenCount() { - return this.tokenCount; - } - - public String getSourceCodeSlice() { - return this.code; - } - - public void setSourceCodeSlice(String code) { - this.code = code; - } - - public Iterator<TokenEntry> iterator() { - return markSet.iterator(); - } - - public int compareTo(Match other) { - int diff = other.getTokenCount() - getTokenCount(); // NOSONAR Bad practice - Class defines compareTo(...) and uses Object.equals() - if (diff != 0) { - return diff; - } - return other.getFirstMark().getIndex() - getFirstMark().getIndex(); - } - - public TokenEntry getFirstMark() { - return marks[0]; - } - - public TokenEntry getSecondMark() { - return marks[1]; - } - - @Override - public String toString() { - return "Match: " + EOL + "tokenCount = " + tokenCount + EOL + "marks = " + markSet.size(); - } - - public Set<TokenEntry> getMarkSet() { - return markSet; - } - - public MatchCode getMatchCode() { - if (mc == null) { - mc = new MatchCode(marks[0], marks[1]); - } - return mc; - } - - public int getEndIndex() { - return marks[1].getIndex() + getTokenCount() - 1; - } - - public void setMarkSet(Set<TokenEntry> markSet) { - this.markSet = markSet; - } - - public void setLabel(String aLabel) { - label = aLabel; - } - - public String getLabel() { - return label; - } -} diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/cpd/MatchAlgorithm.java b/sonar-duplications/src/main/java/org/sonar/duplications/cpd/MatchAlgorithm.java deleted file mode 100644 index 0b25bbcfca5..00000000000 --- a/sonar-duplications/src/main/java/org/sonar/duplications/cpd/MatchAlgorithm.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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 - */ - -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ -package org.sonar.duplications.cpd; - -import net.sourceforge.pmd.cpd.*; - -import java.util.*; - -public class MatchAlgorithm { - - private static final int MOD = 37; - private int lastHash; - private int lastMod = 1; - - private List<Match> matches; - private Map<String, SourceCode> source; - private Tokens tokens; - private List<TokenEntry> code; - private CPDListener cpdListener; - private int min; - private boolean loadSourceCodeSlices = true; - - public MatchAlgorithm(Map<String, SourceCode> sourceCode, Tokens tokens, int min) { - this(sourceCode, tokens, min, new CPDNullListener()); - } - - public MatchAlgorithm(Map<String, SourceCode> sourceCode, Tokens tokens, int min, CPDListener listener) { - this.source = sourceCode; - this.tokens = tokens; - this.code = tokens.getTokens(); - this.min = min; - this.cpdListener = listener; - for (int i = 0; i < min; i++) { - lastMod *= MOD; - } - } - - public void setLoadSourceCodeSlices(boolean loadSourceCodeSlices) { - this.loadSourceCodeSlices = loadSourceCodeSlices; - } - - public void setListener(CPDListener listener) { - this.cpdListener = listener; - } - - public Iterator<Match> matches() { - return matches.iterator(); - } - - public TokenEntry tokenAt(int offset, TokenEntry m) { - return code.get(offset + m.getIndex()); - } - - public int getMinimumTileSize() { - return this.min; - } - - public void findMatches() { - cpdListener.phaseUpdate(CPDListener.HASH); - Map<TokenEntry, Object> markGroups = hash(); - - cpdListener.phaseUpdate(CPDListener.MATCH); - MatchCollector matchCollector = new MatchCollector(this); - for (Iterator<Object> i = markGroups.values().iterator(); i.hasNext();) { - Object o = i.next(); - if (o instanceof List) { - List<TokenEntry> l = (List<TokenEntry>) o; - - Collections.reverse(l); - matchCollector.collect(l); - } - i.remove(); - } - cpdListener.phaseUpdate(CPDListener.GROUPING); - matches = matchCollector.getMatches(); - - for (Match match : matches) { - for (Iterator<TokenEntry> occurrences = match.iterator(); occurrences.hasNext();) { - TokenEntry mark = occurrences.next(); - match.setLineCount(tokens.getLineCount(mark, match)); - if (loadSourceCodeSlices && !occurrences.hasNext()) { - int start = mark.getBeginLine(); - int end = start + match.getLineCount() - 1; - SourceCode sourceCode = source.get(mark.getTokenSrcID()); - match.setSourceCodeSlice(sourceCode.getSlice(start, end)); - } - } - } - cpdListener.phaseUpdate(CPDListener.DONE); - } - - private Map<TokenEntry, Object> hash() { - Map<TokenEntry, Object> markGroups = new HashMap<TokenEntry, Object>(tokens.size()); - for (int i = code.size() - 1; i >= 0; i--) { - TokenEntry token = code.get(i); - if (token != TokenEntry.EOF) { - int last = tokenAt(min, token).getIdentifier(); - lastHash = MOD * lastHash + token.getIdentifier() - lastMod * last; - token.setHashCode(lastHash); - Object o = markGroups.get(token); - - // Note that this insertion method is worthwhile since the vast majority - // markGroup keys will have only one value. - if (o == null) { - markGroups.put(token, token); - } else if (o instanceof TokenEntry) { - List<TokenEntry> l = new ArrayList<TokenEntry>(); - l.add((TokenEntry) o); - l.add(token); - markGroups.put(token, l); - } else { - List<TokenEntry> l = (List<TokenEntry>) o; - l.add(token); - } - } else { - lastHash = 0; - for (int end = Math.max(0, i - min + 1); i > end; i--) { - token = code.get(i - 1); - lastHash = MOD * lastHash + token.getIdentifier(); - if (token == TokenEntry.EOF) { - break; - } - } - } - } - return markGroups; - } - -} diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/cpd/MatchCollector.java b/sonar-duplications/src/main/java/org/sonar/duplications/cpd/MatchCollector.java deleted file mode 100644 index f83b183b5b2..00000000000 --- a/sonar-duplications/src/main/java/org/sonar/duplications/cpd/MatchCollector.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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 - */ - -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ -package org.sonar.duplications.cpd; - -import net.sourceforge.pmd.cpd.TokenEntry; - -import java.util.*; - -public class MatchCollector { - - private MatchAlgorithm ma; - private Map<Match.MatchCode, Match> startMap = new HashMap<Match.MatchCode, Match>(); - private Map<String, List<Match>> fileMap = new HashMap<String, List<Match>>(); - - public MatchCollector(MatchAlgorithm ma) { - this.ma = ma; - } - - public void collect(List<TokenEntry> marks) { - // first get a pairwise collection of all maximal matches - for (int i = 0; i < marks.size() - 1; i++) { - TokenEntry mark1 = marks.get(i); - for (int j = i + 1; j < marks.size(); j++) { - TokenEntry mark2 = marks.get(j); - int diff = mark1.getIndex() - mark2.getIndex(); - if (-diff < ma.getMinimumTileSize()) { - continue; - } - if (hasPreviousDupe(mark1, mark2)) { - continue; - } - - // "match too small" check - int dupes = countDuplicateTokens(mark1, mark2); - if (dupes < ma.getMinimumTileSize()) { - continue; - } - // is it still too close together - if (diff + dupes >= 1) { - continue; - } - determineMatch(mark1, mark2, dupes); - } - } - } - - @SuppressWarnings("PMD.CompareObjectsWithEquals") - public List<Match> getMatches() { - List<Match> matchList = new ArrayList<Match>(startMap.values()); - Collections.sort(matchList); - Set<Match.MatchCode> matchSet = new HashSet<Match.MatchCode>(); - Match.MatchCode matchCode = new Match.MatchCode(); - for (int i = matchList.size(); i > 1; i--) { - Match match1 = matchList.get(i - 1); - TokenEntry mark1 = match1.getMarkSet().iterator().next(); - matchSet.clear(); - matchSet.add(match1.getMatchCode()); - for (int j = i - 1; j > 0; j--) { - Match match2 = matchList.get(j - 1); - if (match1.getTokenCount() != match2.getTokenCount()) { - break; - } - TokenEntry mark2 = null; - for (Iterator<TokenEntry> iter = match2.getMarkSet().iterator(); iter.hasNext();) { - mark2 = iter.next(); - if (mark2 != mark1) { - break; - } - } - int dupes = countDuplicateTokens(mark1, mark2); - if (dupes < match1.getTokenCount()) { - break; - } - matchSet.add(match2.getMatchCode()); - match1.getMarkSet().addAll(match2.getMarkSet()); - matchList.remove(i - 2); - i--; - } - if (matchSet.size() == 1) { - continue; - } - // prune the mark set - Set<TokenEntry> pruned = match1.getMarkSet(); - boolean done = false; - ArrayList<TokenEntry> a1 = new ArrayList<TokenEntry>(match1.getMarkSet()); - Collections.sort(a1); - for (int outer = 0; outer < a1.size() - 1 && !done; outer++) { - TokenEntry cmark1 = a1.get(outer); - for (int inner = outer + 1; inner < a1.size() && !done; inner++) { - TokenEntry cmark2 = a1.get(inner); - matchCode.setFirst(cmark1.getIndex()); - matchCode.setSecond(cmark2.getIndex()); - if (!matchSet.contains(matchCode)) { - if (pruned.size() > 2) { - pruned.remove(cmark2); - } - if (pruned.size() == 2) { - done = true; - } - } - } - } - } - return matchList; - } - - /** - * A greedy algorithm for determining non-overlapping matches - */ - private void determineMatch(TokenEntry mark1, TokenEntry mark2, int dupes) { - Match match = new Match(dupes, mark1, mark2); - String fileKey = mark1.getTokenSrcID() + mark2.getTokenSrcID(); - List<Match> pairMatches = fileMap.get(fileKey); - if (pairMatches == null) { - pairMatches = new ArrayList<Match>(); - fileMap.put(fileKey, pairMatches); - } - boolean add = true; - for (int i = 0; i < pairMatches.size(); i++) { - Match other = pairMatches.get(i); - if (other.getFirstMark().getIndex() + other.getTokenCount() - mark1.getIndex() > 0) { - boolean ordered = other.getSecondMark().getIndex() - mark2.getIndex() < 0; - if ((ordered && (other.getEndIndex() - mark2.getIndex() > 0)) - || (!ordered && (match.getEndIndex() - other.getSecondMark().getIndex()) > 0)) { - if (other.getTokenCount() >= match.getTokenCount()) { - add = false; - break; - } else { - pairMatches.remove(i); - startMap.remove(other.getMatchCode()); - } - } - } - } - if (add) { - pairMatches.add(match); - startMap.put(match.getMatchCode(), match); - } - } - - private boolean hasPreviousDupe(TokenEntry mark1, TokenEntry mark2) { - if (mark1.getIndex() == 0) { - return false; - } - return !matchEnded(ma.tokenAt(-1, mark1), ma.tokenAt(-1, mark2)); - } - - private int countDuplicateTokens(TokenEntry mark1, TokenEntry mark2) { - int index = 0; - while (!matchEnded(ma.tokenAt(index, mark1), ma.tokenAt(index, mark2))) { - index++; - } - return index; - } - - private boolean matchEnded(TokenEntry token1, TokenEntry token2) { - return token1.getIdentifier() != token2.getIdentifier() || token1 == TokenEntry.EOF || token2 == TokenEntry.EOF; - } -} diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/cpd/CPDTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/cpd/CPDTest.java deleted file mode 100644 index c7133c4387a..00000000000 --- a/sonar-duplications/src/test/java/org/sonar/duplications/cpd/CPDTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Sonar, open source software quality management tool. - * Copyright (C) 2008-2012 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.duplications.cpd; - -import net.sourceforge.pmd.cpd.AbstractLanguage; -import net.sourceforge.pmd.cpd.JavaTokenizer; -import net.sourceforge.pmd.cpd.TokenEntry; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; - -public class CPDTest { - - @Test - public void testSetLoadSourceCodeSlicesToFalse() throws IOException { - TokenEntry.clearImages(); - AbstractLanguage cpdLanguage = new AbstractLanguage(new JavaTokenizer()) { - }; - CPD cpd = new CPD(20, cpdLanguage); - cpd.setEncoding(Charset.defaultCharset().name()); - cpd.setLoadSourceCodeSlices(false); - cpd.add(new File("test-resources/org/sonar/duplications/cpd/CPDTest/CPDFile1.java")); - cpd.add(new File("test-resources/org/sonar/duplications/cpd/CPDTest/CPDFile2.java")); - cpd.go(); - - List<Match> matches = getMatches(cpd); - assertThat(matches.size(), is(1)); - - org.sonar.duplications.cpd.Match match = matches.get(0); - assertThat(match.getLineCount(), is(26)); - assertThat(match.getFirstMark().getBeginLine(), is(16)); - assertThat(match.getSourceCodeSlice(), is(nullValue())); - assertThat(match.getTokenCount(), is(116)); - } - - @Test - public void testDuplicationOnSameFile() throws IOException { - TokenEntry.clearImages(); - AbstractLanguage cpdLanguage = new AbstractLanguage(new JavaTokenizer()) { - }; - CPD cpd = new CPD(20, cpdLanguage); - cpd.setEncoding(Charset.defaultCharset().name()); - cpd.setLoadSourceCodeSlices(false); - cpd.add(new File("test-resources/org/sonar/duplications/cpd/CPDTest/CPDFile3.java")); - cpd.go(); - - List<Match> matches = getMatches(cpd); - assertThat(matches.size(), is(1)); - - org.sonar.duplications.cpd.Match match = matches.get(0); - assertThat(match.getLineCount(), is(16)); - assertThat(match.getFirstMark().getBeginLine(), is(29)); - assertThat(match.getSourceCodeSlice(), is(nullValue())); - assertThat(match.getTokenCount(), is(160)); - } - - private List<Match> getMatches(CPD cpd) { - List<Match> matches = new ArrayList<org.sonar.duplications.cpd.Match>(); - Iterator<Match> matchesIter = cpd.getMatches(); - while (matchesIter.hasNext()) { - matches.add(matchesIter.next()); - } - return matches; - } - -} |