import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
+import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplicationBuilder;
import org.sonar.api.config.Settings;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.FileLinesContext;
import org.sonar.api.measures.FileLinesContextFactory;
import org.sonar.api.resources.Project;
import org.sonar.api.utils.SonarException;
-import org.sonar.batch.duplication.DefaultDuplicationBuilder;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.BlockChunker;
import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm;
}
}
}
- builder.done();
+ context.saveDuplications(inputFile, builder.build());
}
}
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile;
import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
+import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplicationBuilder;
import org.sonar.api.batch.sensor.measure.internal.DefaultMeasureBuilder;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.FileLinesContext;
import org.sonar.api.measures.FileLinesContextFactory;
-import org.sonar.batch.duplication.DefaultDuplicationBuilder;
-import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.duplications.index.CloneGroup;
import org.sonar.duplications.index.ClonePart;
public void before() throws IOException {
when(context.measureBuilder()).thenReturn(new DefaultMeasureBuilder());
inputFile = new DeprecatedDefaultInputFile("src/main/java/Foo.java");
- DuplicationCache duplicationCache = mock(DuplicationCache.class);
- duplicationBuilder = spy(new DefaultDuplicationBuilder(inputFile, duplicationCache));
+ duplicationBuilder = spy(new DefaultDuplicationBuilder(inputFile));
when(context.duplicationBuilder(any(InputFile.class))).thenReturn(duplicationBuilder);
inputFile.setFile(temp.newFile("Foo.java"));
+ inputFile.setKey("key1");
contextFactory = mock(FileLinesContextFactory.class);
linesContext = mock(FileLinesContext.class);
when(contextFactory.createFor(inputFile)).thenReturn(linesContext);
InOrder inOrder = Mockito.inOrder(duplicationBuilder);
inOrder.verify(duplicationBuilder).originBlock(5, 204);
inOrder.verify(duplicationBuilder).isDuplicatedBy("key2", 15, 214);
- inOrder.verify(duplicationBuilder).done();
+ inOrder.verify(duplicationBuilder).build();
verify(linesContext).setIntValue(CoreMetrics.DUPLICATION_LINES_DATA_KEY, 1, 0);
verify(linesContext).setIntValue(CoreMetrics.DUPLICATION_LINES_DATA_KEY, 4, 0);
InOrder inOrder = Mockito.inOrder(duplicationBuilder);
inOrder.verify(duplicationBuilder).originBlock(5, 204);
inOrder.verify(duplicationBuilder).isDuplicatedBy("key1", 215, 414);
- inOrder.verify(duplicationBuilder).done();
+ inOrder.verify(duplicationBuilder).build();
}
@Test
inOrder.verify(duplicationBuilder).originBlock(5, 204);
inOrder.verify(duplicationBuilder).isDuplicatedBy("key2", 15, 214);
inOrder.verify(duplicationBuilder).isDuplicatedBy("key3", 25, 224);
- inOrder.verify(duplicationBuilder).done();
+ inOrder.verify(duplicationBuilder).build();
+
+ verify(context).saveDuplications(inputFile, Arrays.asList(
+ new DuplicationGroup(new DuplicationGroup.Block("key1", 5, 200))
+ .addDuplicate(new DuplicationGroup.Block("key2", 15, 200))
+ .addDuplicate(new DuplicationGroup.Block("key3", 25, 200))
+ ));
}
@Test
inOrder.verify(duplicationBuilder).isDuplicatedBy("key2", 15, 214);
inOrder.verify(duplicationBuilder).originBlock(15, 214);
inOrder.verify(duplicationBuilder).isDuplicatedBy("key3", 15, 214);
- inOrder.verify(duplicationBuilder).done();
+ inOrder.verify(duplicationBuilder).build();
}
private CloneGroup newCloneGroup(ClonePart... parts) {
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sonar.api.batch.fs.InputFile;
-import org.sonar.batch.duplication.DuplicationGroup;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
import org.sonar.batch.mediumtest.BatchMediumTester;
import org.sonar.batch.mediumtest.BatchMediumTester.TaskResult;
import org.sonar.plugins.cpd.CpdPlugin;
import org.sonar.api.batch.sensor.Sensor;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.SensorDescriptor;
-import org.sonar.api.batch.sensor.duplication.TokenBuilder;
+import org.sonar.api.batch.sensor.duplication.DuplicationTokenBuilder;
import org.sonar.xoo.Xoo;
import java.io.File;
public class XooTokenizerSensor implements Sensor {
private void computeTokens(InputFile inputFile, SensorContext context) {
- TokenBuilder tokenBuilder = context.tokenBuilder(inputFile);
+ DuplicationTokenBuilder tokenBuilder = context.duplicationTokenBuilder(inputFile);
File ioFile = inputFile.file();
int lineId = 0;
try {
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.batch.duplication;
-
-import com.google.common.base.Preconditions;
-import org.sonar.api.batch.fs.InputFile;
-import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
-
-import java.util.ArrayList;
-
-public class DefaultDuplicationBuilder implements DuplicationBuilder {
-
- private final InputFile inputFile;
- private final DuplicationCache duplicationCache;
- private boolean done = false;
- private DuplicationGroup current = null;
- private ArrayList<DuplicationGroup> duplications;
-
- public DefaultDuplicationBuilder(InputFile inputFile, DuplicationCache duplicationCache) {
- this.inputFile = inputFile;
- this.duplicationCache = duplicationCache;
- duplications = new ArrayList<DuplicationGroup>();
- }
-
- @Override
- public DuplicationBuilder originBlock(int startLine, int endLine) {
- if (current != null) {
- duplications.add(current);
- }
- current = new DuplicationGroup(new DuplicationGroup.Block(((DefaultInputFile) inputFile).key(), startLine, endLine - startLine + 1));
- return this;
- }
-
- @Override
- public DuplicationBuilder isDuplicatedBy(InputFile sameOrOtherFile, int startLine, int endLine) {
- return isDuplicatedBy(((DefaultInputFile) sameOrOtherFile).key(), startLine, endLine);
- }
-
- /**
- * For internal use. Global duplications are referencing files outside of current project so
- * no way to manipulate an InputFile.
- */
- public DuplicationBuilder isDuplicatedBy(String fileKey, int startLine, int endLine) {
- Preconditions.checkNotNull(current, "Call originBlock() first");
- current.addDuplicate(new DuplicationGroup.Block(fileKey, startLine, endLine - startLine + 1));
- return this;
- }
-
- @Override
- public void done() {
- Preconditions.checkState(!done, "done() already called");
- Preconditions.checkNotNull(current, "Call originBlock() first");
- duplications.add(current);
- duplicationCache.put(((DefaultInputFile) inputFile).key(), duplications);
- }
-}
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
-import org.sonar.api.batch.sensor.duplication.TokenBuilder;
+import org.sonar.api.batch.sensor.duplication.DuplicationTokenBuilder;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.FileBlocks;
import org.sonar.duplications.internal.pmd.PmdBlockChunker;
import java.util.ArrayList;
import java.util.List;
-public class DefaultTokenBuilder implements TokenBuilder {
+public class DefaultTokenBuilder implements DuplicationTokenBuilder {
private static final Logger LOG = LoggerFactory.getLogger(DefaultTokenBuilder.class);
import com.persistit.Value;
import com.persistit.encoding.CoderContext;
import com.persistit.encoding.ValueCoder;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
class DuplicationBlockValueCoder implements ValueCoder {
package org.sonar.batch.duplication;
import org.sonar.api.BatchComponent;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
import org.sonar.batch.index.Cache;
import org.sonar.batch.index.Cache.Entry;
import org.sonar.batch.index.Caches;
import javax.annotation.CheckForNull;
-import java.util.ArrayList;
+import java.util.List;
/**
* Cache of duplication blocks. This cache is shared amongst all project modules.
*/
public class DuplicationCache implements BatchComponent {
- private final Cache<ArrayList<DuplicationGroup>> cache;
+ private final Cache<List<DuplicationGroup>> cache;
public DuplicationCache(Caches caches) {
caches.registerValueCoder(DuplicationGroup.class, new DuplicationGroupValueCoder());
cache = caches.createCache("duplications");
}
- public Iterable<Entry<ArrayList<DuplicationGroup>>> entries() {
+ public Iterable<Entry<List<DuplicationGroup>>> entries() {
return cache.entries();
}
@CheckForNull
- public ArrayList<DuplicationGroup> byComponent(String effectiveKey) {
+ public List<DuplicationGroup> byComponent(String effectiveKey) {
return cache.get(effectiveKey);
}
- public DuplicationCache put(String effectiveKey, ArrayList<DuplicationGroup> blocks) {
+ public DuplicationCache put(String effectiveKey, List<DuplicationGroup> blocks) {
cache.put(effectiveKey, blocks);
return this;
}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.batch.duplication;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class DuplicationGroup {
-
- public static class Block {
- private final String resourceKey;
- private final int startLine;
- private final int length;
-
- public Block(String resourceKey, int startLine, int length) {
- this.resourceKey = resourceKey;
- this.startLine = startLine;
- this.length = length;
- }
-
- public String resourceKey() {
- return resourceKey;
- }
-
- public int startLine() {
- return startLine;
- }
-
- public int length() {
- return length;
- }
- }
-
- private final Block originBlock;
-
- private List<Block> duplicates = new ArrayList<DuplicationGroup.Block>();
-
- public DuplicationGroup(Block originBlock) {
- this.originBlock = originBlock;
- }
-
- public void setDuplicates(List<Block> duplicates) {
- this.duplicates = duplicates;
- }
-
- public DuplicationGroup addDuplicate(Block anotherBlock) {
- this.duplicates.add(anotherBlock);
- return this;
- }
-
- public Block originBlock() {
- return originBlock;
- }
-
- public List<Block> duplicates() {
- return duplicates;
- }
-
-}
import com.persistit.Value;
import com.persistit.encoding.CoderContext;
import com.persistit.encoding.ValueCoder;
-import org.sonar.batch.duplication.DuplicationGroup.Block;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup.Block;
import java.util.ArrayList;
package org.sonar.batch.index;
import org.apache.commons.lang.StringEscapeUtils;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
import org.sonar.api.database.model.MeasureMapper;
import org.sonar.api.database.model.MeasureModel;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.resources.Resource;
import org.sonar.api.rules.RuleFinder;
import org.sonar.batch.duplication.DuplicationCache;
-import org.sonar.batch.duplication.DuplicationGroup;
import org.sonar.batch.index.Cache.Entry;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
-import java.util.ArrayList;
+import java.util.List;
public final class DuplicationPersister implements ScanPersister {
private final MyBatis mybatis;
try {
MeasureMapper mapper = session.getMapper(MeasureMapper.class);
org.sonar.api.measures.Metric duplicationMetricWithId = metricFinder.findByKey(CoreMetrics.DUPLICATIONS_DATA_KEY);
- for (Entry<ArrayList<DuplicationGroup>> entry : duplicationCache.entries()) {
+ for (Entry<List<DuplicationGroup>> entry : duplicationCache.entries()) {
String effectiveKey = entry.key()[0].toString();
Measure measure = new Measure(duplicationMetricWithId, toXml(entry.value())).setPersistenceMode(PersistenceMode.DATABASE);
Resource resource = resourceCache.get(effectiveKey);
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputPath;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.measure.Measure;
import org.sonar.batch.bootstrapper.Batch;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonar.batch.duplication.DuplicationCache;
-import org.sonar.batch.duplication.DuplicationGroup;
import org.sonar.batch.highlighting.SyntaxHighlightingData;
import org.sonar.batch.highlighting.SyntaxHighlightingRule;
import org.sonar.batch.index.Cache.Entry;
}
DuplicationCache duplicationCache = container.getComponentByType(DuplicationCache.class);
- for (Entry<ArrayList<DuplicationGroup>> entry : duplicationCache.entries()) {
+ for (Entry<List<DuplicationGroup>> entry : duplicationCache.entries()) {
String effectiveKey = entry.key()[0].toString();
duplications.put(effectiveKey, entry.value());
}
*/
package org.sonar.batch.scan;
+import com.google.common.base.Preconditions;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputDir;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
-import org.sonar.api.batch.sensor.duplication.TokenBuilder;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
+import org.sonar.api.batch.sensor.duplication.DuplicationTokenBuilder;
+import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplicationBuilder;
import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueBuilder;
import org.sonar.api.resources.Scopes;
import org.sonar.api.rule.RuleKey;
import org.sonar.batch.duplication.BlockCache;
-import org.sonar.batch.duplication.DefaultDuplicationBuilder;
import org.sonar.batch.duplication.DefaultTokenBuilder;
import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.highlighting.DefaultHighlightingBuilder;
import org.sonar.duplications.internal.pmd.PmdBlockChunker;
import java.io.Serializable;
+import java.util.List;
/**
* Implements {@link SensorContext} but forward everything to {@link org.sonar.api.batch.SensorContext} for backward compatibility.
}
@Override
- public TokenBuilder tokenBuilder(InputFile inputFile) {
+ public DuplicationTokenBuilder duplicationTokenBuilder(InputFile inputFile) {
PmdBlockChunker blockChunker = new PmdBlockChunker(getBlockSize(inputFile.language()));
return new DefaultTokenBuilder(inputFile, blockCache, blockChunker);
}
@Override
public DuplicationBuilder duplicationBuilder(InputFile inputFile) {
- return new DefaultDuplicationBuilder(inputFile, duplicationCache);
+ return new DefaultDuplicationBuilder(inputFile);
+ }
+
+ @Override
+ public void saveDuplications(InputFile inputFile, List<DuplicationGroup> duplications) {
+ Preconditions.checkState(duplications.size() > 0, "Empty duplications");
+ String effectiveKey = ((DefaultInputFile) inputFile).key();
+ for (DuplicationGroup duplicationGroup : duplications) {
+ Preconditions.checkState(effectiveKey.equals(duplicationGroup.originBlock().resourceKey()), "Invalid duplication group");
+ }
+ duplicationCache.put(effectiveKey, duplications);
}
private int getBlockSize(String languageKey) {
*/
package org.sonar.batch.scan2;
+import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.rule.internal.DefaultActiveRule;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
-import org.sonar.api.batch.sensor.duplication.TokenBuilder;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
+import org.sonar.api.batch.sensor.duplication.DuplicationTokenBuilder;
+import org.sonar.api.batch.sensor.duplication.internal.DefaultDuplicationBuilder;
import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueBuilder;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.MessageException;
import org.sonar.batch.duplication.BlockCache;
-import org.sonar.batch.duplication.DefaultDuplicationBuilder;
import org.sonar.batch.duplication.DefaultTokenBuilder;
import org.sonar.batch.duplication.DuplicationCache;
import org.sonar.batch.highlighting.DefaultHighlightingBuilder;
import org.sonar.duplications.internal.pmd.PmdBlockChunker;
import java.io.Serializable;
+import java.util.List;
public class DefaultSensorContext implements SensorContext {
}
@Override
- public TokenBuilder tokenBuilder(InputFile inputFile) {
+ public DuplicationTokenBuilder duplicationTokenBuilder(InputFile inputFile) {
PmdBlockChunker blockChunker = new PmdBlockChunker(getBlockSize(inputFile.language()));
return new DefaultTokenBuilder(inputFile, blockCache, blockChunker);
@Override
public DuplicationBuilder duplicationBuilder(InputFile inputFile) {
- return new DefaultDuplicationBuilder(inputFile, duplicationCache);
+ return new DefaultDuplicationBuilder(inputFile);
+ }
+
+ @Override
+ public void saveDuplications(InputFile inputFile, List<DuplicationGroup> duplications) {
+ Preconditions.checkState(duplications.size() > 0, "Empty duplications");
+ String effectiveKey = ((DefaultInputFile) inputFile).key();
+ for (DuplicationGroup duplicationGroup : duplications) {
+ Preconditions.checkState(effectiveKey.equals(duplicationGroup.originBlock().resourceKey()), "Invalid duplication group");
+ }
+ duplicationCache.put(effectiveKey, duplications);
}
private int getBlockSize(String languageKey) {
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
import org.sonar.batch.index.Caches;
import org.sonar.batch.index.CachesTest;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import static org.fest.assertions.Assertions.assertThat;
assertThat(cache.entries()).hasSize(1);
- ArrayList<DuplicationGroup> entry = cache.byComponent("foo");
+ List<DuplicationGroup> entry = cache.byComponent("foo");
assertThat(entry.get(0).originBlock().resourceKey()).isEqualTo("foo");
}
import org.junit.Before;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.MetricFinder;
import org.sonar.api.resources.File;
import org.sonar.api.rules.RuleFinder;
import org.sonar.batch.duplication.DuplicationCache;
-import org.sonar.batch.duplication.DuplicationGroup;
import org.sonar.core.persistence.AbstractDaoTestCase;
-import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
.addDuplicate(new DuplicationGroup.Block("foo:org/foo/Foo.java", 5, 9));
when(duplicationCache.entries()).thenReturn(
- Arrays.<Cache.Entry<ArrayList<DuplicationGroup>>>asList(new Cache.Entry(new String[] {"foo:org/foo/Bar.java"}, Arrays.asList(group))));
+ Arrays.<Cache.Entry<List<DuplicationGroup>>>asList(new Cache.Entry(new String[] {"foo:org/foo/Bar.java"}, Arrays.asList(group))));
duplicationPersister.persist();
}
/**
- * Component key. It's marked as nullable just for the unit tests that
- * do not previously call {@link #setKey(String)}.
+ * Component key.
*/
- @CheckForNull
public String key() {
return key;
}
import org.sonar.api.batch.measure.Metric;
import org.sonar.api.batch.rule.ActiveRules;
import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
-import org.sonar.api.batch.sensor.duplication.TokenBuilder;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
+import org.sonar.api.batch.sensor.duplication.DuplicationTokenBuilder;
import org.sonar.api.batch.sensor.highlighting.HighlightingBuilder;
import org.sonar.api.batch.sensor.issue.Issue;
import org.sonar.api.batch.sensor.issue.IssueBuilder;
import javax.annotation.CheckForNull;
import java.io.Serializable;
+import java.util.List;
/**
* @since 4.4
/**
* Add a measure. Use {@link #measureBuilder()} to create the new measure.
+ * A measure for a given metric can only be saved once for the same resource.
*/
void addMeasure(Measure<?> measure);
* Builder to define tokens in a file. Tokens are used to compute duplication by the core.
* @since 4.5
*/
- TokenBuilder tokenBuilder(InputFile inputFile);
+ DuplicationTokenBuilder duplicationTokenBuilder(InputFile inputFile);
/**
* Builder to manually define duplications in a file. When duplication are manually computed then
- * no need to use {@link #tokenBuilder(InputFile)}.
+ * no need to use {@link #duplicationTokenBuilder(InputFile)}.
* @since 4.5
*/
DuplicationBuilder duplicationBuilder(InputFile inputFile);
+ /**
+ * Register all duplications of an {@link InputFile}. Use {@link #duplicationBuilder(InputFile)} to create
+ * list of duplications.
+ */
+ void saveDuplications(InputFile inputFile, List<DuplicationGroup> duplications);
+
}
import org.sonar.api.batch.fs.InputFile;
+import java.util.List;
+
/**
* This builder is used to declare duplications on files of the project.
+ * Usage:
+ * <code><pre>
+ * DuplicationBuilder builder = context.duplicationBuilder(inputFile);
+ * .originBlock(2, 10)
+ * .isDuplicatedBy(inputFile, 14, 22)
+ * .isDuplicatedBy(anotherInputFile, 3, 11)
+ * // Start another duplication
+ * .originBlock(45, 50)
+ * .isDuplicatedBy(yetAnotherInputFile, 10, 15);
+ * context.saveDuplications(inputFile, builder.build());
+ * </pre></code>
* @since 4.5
*/
public interface DuplicationBuilder {
+ /**
+ * Declare duplication origin block. Then call {@link #isDuplicatedBy(InputFile, int, int)} to reference all duplicates of this block.
+ * Then call again {@link #originBlock(int, int)} to declare another duplication.
+ */
DuplicationBuilder originBlock(int startLine, int endLine);
+ /**
+ * Declare duplicate block of the previously declared {@link #originBlock(int, int)}.
+ * @param sameOrOtherFile duplicate can be in the same file or in another file.
+ */
DuplicationBuilder isDuplicatedBy(InputFile sameOrOtherFile, int startLine, int endLine);
/**
- * Call this method only once when your are done with defining all duplicates of origin block.
+ * Call this method when you have declared all duplications of the file.
*/
- void done();
+ List<DuplicationGroup> build();
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.batch.sensor.duplication;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.batch.sensor.SensorContext;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A {@link DuplicationGroup} is a list of duplicated {@link Block}s. One block
+ * is considered as the original code and all others are duplicates.
+ * Use {@link SensorContext#duplicationBuilder(org.sonar.api.batch.fs.InputFile)} and
+ * {@link SensorContext#saveDuplications(org.sonar.api.batch.fs.InputFile, List)}.
+ * @since 4.5
+ */
+public class DuplicationGroup {
+
+ public static class Block {
+ private final String resourceKey;
+ private final int startLine;
+ private final int length;
+
+ public Block(String resourceKey, int startLine, int length) {
+ this.resourceKey = resourceKey;
+ this.startLine = startLine;
+ this.length = length;
+ }
+
+ public String resourceKey() {
+ return resourceKey;
+ }
+
+ public int startLine() {
+ return startLine;
+ }
+
+ public int length() {
+ return length;
+ }
+
+ // Just for unit tests
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj == this) {
+ return true;
+ }
+ if (obj.getClass() != getClass()) {
+ return false;
+ }
+ Block rhs = (Block) obj;
+ return new EqualsBuilder()
+ .append(resourceKey, rhs.resourceKey)
+ .append(startLine, rhs.startLine)
+ .append(length, rhs.length).isEquals();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).
+ append("resourceKey", resourceKey).
+ append("startLine", startLine).
+ append("length", length).
+ toString();
+ }
+ }
+
+ private final Block originBlock;
+ private List<Block> duplicates = new ArrayList<DuplicationGroup.Block>();
+
+ /**
+ * For unit test and internal use only.
+ */
+ public DuplicationGroup(Block originBlock) {
+ this.originBlock = originBlock;
+ }
+
+ public void setDuplicates(List<Block> duplicates) {
+ this.duplicates = duplicates;
+ }
+
+ public DuplicationGroup addDuplicate(Block anotherBlock) {
+ this.duplicates.add(anotherBlock);
+ return this;
+ }
+
+ public Block originBlock() {
+ return originBlock;
+ }
+
+ public List<Block> duplicates() {
+ return duplicates;
+ }
+
+ // Just for unit tests
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (obj == this) {
+ return true;
+ }
+ if (obj.getClass() != getClass()) {
+ return false;
+ }
+ DuplicationGroup rhs = (DuplicationGroup) obj;
+ EqualsBuilder equalsBuilder = new EqualsBuilder()
+ .append(originBlock, rhs.originBlock)
+ .append(duplicates.size(), rhs.duplicates.size());
+ for (int i = 0; i < duplicates.size(); i++) {
+ equalsBuilder.append(duplicates.get(i), rhs.duplicates.get(i));
+ }
+ return equalsBuilder.isEquals();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).
+ append("origin", originBlock).
+ append("duplicates", duplicates, true).
+ toString();
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.batch.sensor.duplication;
+
+
+/**
+ * This builder is used to define token on files. Tokens are later used to compute duplication.
+ * Tokens should be declared in sequential order.
+ * Example:
+ * <code><pre>
+ * DuplicationTokenBuilder tokenBuilder = context.duplicationTokenBuilder(inputFile)
+ * .addToken(1, "public")
+ * .addToken(1, "class")
+ * .addToken(1, "Foo")
+ * .addToken(1, "{")
+ * .addToken(2, "}")
+ * .done();
+ * </pre></code>
+ * @since 4.5
+ */
+public interface DuplicationTokenBuilder {
+
+ /**
+ * Call this method to register a new token.
+ * @param line Line number of the token. Line starts at 1.
+ * @param image Text of the token.
+ */
+ DuplicationTokenBuilder addToken(int line, String image);
+
+ /**
+ * Call this method only once when your are done with defining tokens of the file.
+ */
+ void done();
+}
+++ /dev/null
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube 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.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.api.batch.sensor.duplication;
-
-/**
- * This builder is used to define token on files. Tokens are later used to compute duplication.
- * Tokens should be declared in sequential order.
- * @since 4.5
- */
-public interface TokenBuilder {
-
- /**
- * Call this method to register a new token.
- * @param line Line number of the token. Line starts at 1.
- * @param image Text of the token.
- */
- TokenBuilder addToken(int line, String image);
-
- /**
- * Call this method only once when your are done with defining tokens of the file.
- */
- void done();
-}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.batch.sensor.duplication.internal;
+
+import com.google.common.base.Preconditions;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.sensor.duplication.DuplicationBuilder;
+import org.sonar.api.batch.sensor.duplication.DuplicationGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DefaultDuplicationBuilder implements DuplicationBuilder {
+
+ private final InputFile inputFile;
+ private DuplicationGroup current = null;
+ private List<DuplicationGroup> duplications;
+
+ public DefaultDuplicationBuilder(InputFile inputFile) {
+ this.inputFile = inputFile;
+ duplications = new ArrayList<DuplicationGroup>();
+ }
+
+ @Override
+ public DuplicationBuilder originBlock(int startLine, int endLine) {
+ if (current != null) {
+ duplications.add(current);
+ }
+ current = new DuplicationGroup(new DuplicationGroup.Block(((DefaultInputFile) inputFile).key(), startLine, endLine - startLine + 1));
+ return this;
+ }
+
+ @Override
+ public DuplicationBuilder isDuplicatedBy(InputFile sameOrOtherFile, int startLine, int endLine) {
+ return isDuplicatedBy(((DefaultInputFile) sameOrOtherFile).key(), startLine, endLine);
+ }
+
+ /**
+ * For internal use. Global duplications are referencing files outside of current project so
+ * no way to manipulate an InputFile.
+ */
+ public DuplicationBuilder isDuplicatedBy(String fileKey, int startLine, int endLine) {
+ Preconditions.checkNotNull(current, "Call originBlock() first");
+ current.addDuplicate(new DuplicationGroup.Block(fileKey, startLine, endLine - startLine + 1));
+ return this;
+ }
+
+ @Override
+ public List<DuplicationGroup> build() {
+ Preconditions.checkNotNull(current, "Call originBlock() first");
+ duplications.add(current);
+ List<DuplicationGroup> result = duplications;
+ reset();
+ return result;
+ }
+
+ private void reset() {
+ duplications = new ArrayList<DuplicationGroup>();
+ current = null;
+ }
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@javax.annotation.ParametersAreNonnullByDefault
+package org.sonar.api.batch.sensor.duplication.internal;