diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-12-21 13:54:14 +0100 |
---|---|---|
committer | Duarte Meneses <duarte.meneses@sonarsource.com> | 2015-12-21 14:37:03 +0100 |
commit | d1e303f8412f61f426a6c746cc2ac10b584940bd (patch) | |
tree | b68693cc8caced2aeee5b06a3590b3e1392e8011 /sonar-duplications/src | |
parent | 6fe8b0cd64c4b4e2386d994016b465b0386ed09c (diff) | |
download | sonarqube-d1e303f8412f61f426a6c746cc2ac10b584940bd.tar.gz sonarqube-d1e303f8412f61f426a6c746cc2ac10b584940bd.zip |
SONAR-2867 Standard copy-paste detection should happen within a project, not only within a module
Diffstat (limited to 'sonar-duplications/src')
4 files changed, 145 insertions, 34 deletions
diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneIndex.java b/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneIndex.java index 357fca44aa8..7bcf94b914a 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneIndex.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/index/CloneIndex.java @@ -20,9 +20,11 @@ package org.sonar.duplications.index; import java.util.Collection; +import java.util.Iterator; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.ByteArray; +import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks; public interface CloneIndex { @@ -45,4 +47,9 @@ public interface CloneIndex { */ void insert(Block block); + /** + * Iterators through the resources, providing the list of blocks for each resource. + */ + Iterator<ResourceBlocks> iterator(); + } diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/index/MemoryCloneIndex.java b/sonar-duplications/src/main/java/org/sonar/duplications/index/MemoryCloneIndex.java index 393a8e7d1dd..1b454bf6eff 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/index/MemoryCloneIndex.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/index/MemoryCloneIndex.java @@ -23,8 +23,10 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.ByteArray; +import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks; import java.util.Collection; +import java.util.Iterator; public class MemoryCloneIndex implements CloneIndex { @@ -47,4 +49,9 @@ public class MemoryCloneIndex implements CloneIndex { byHash.put(block.getBlockHash(), block); } + @Override + public Iterator<ResourceBlocks> iterator() { + throw new UnsupportedOperationException(); + } + } diff --git a/sonar-duplications/src/main/java/org/sonar/duplications/index/PackedMemoryCloneIndex.java b/sonar-duplications/src/main/java/org/sonar/duplications/index/PackedMemoryCloneIndex.java index f0257cef0e2..5681acf1f55 100644 --- a/sonar-duplications/src/main/java/org/sonar/duplications/index/PackedMemoryCloneIndex.java +++ b/sonar-duplications/src/main/java/org/sonar/duplications/index/PackedMemoryCloneIndex.java @@ -21,11 +21,16 @@ package org.sonar.duplications.index; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; + import org.sonar.duplications.block.Block; import org.sonar.duplications.block.ByteArray; import org.sonar.duplications.utils.FastStringComparator; +import javax.annotation.Nullable; + /** * Provides an index optimized by memory. * <p> @@ -104,31 +109,105 @@ public class PackedMemoryCloneIndex extends AbstractCloneIndex { List<Block> result = new ArrayList<>(); int realIndex = resourceIdsIndex[index]; while (index < size && FastStringComparator.INSTANCE.compare(resourceIds[realIndex], resourceId) == 0) { - // extract block (note that there is no need to extract resourceId) - int offset = realIndex * blockInts; + result.add(getBlock(realIndex, resourceId)); + + index++; + realIndex = resourceIdsIndex[index]; + } + return result; + } + + private Block createBlock(int index, String resourceId, @Nullable ByteArray byteHash) { + int offset = index * blockInts; + ByteArray blockHash; + + if (byteHash == null) { int[] hash = new int[hashInts]; for (int j = 0; j < hashInts; j++) { hash[j] = blockData[offset++]; } - int indexInFile = blockData[offset++]; - int firstLineNumber = blockData[offset++]; - int lastLineNumber = blockData[offset++]; - int startUnit = blockData[offset++]; - int endUnit = blockData[offset]; - - Block block = blockBuilder - .setResourceId(resourceId) - .setBlockHash(new ByteArray(hash)) - .setIndexInFile(indexInFile) - .setLines(firstLineNumber, lastLineNumber) - .setUnit(startUnit, endUnit) - .build(); - result.add(block); + blockHash = new ByteArray(hash); + } else { + blockHash = byteHash; + offset += hashInts; + } - index++; - realIndex = resourceIdsIndex[index]; + int indexInFile = blockData[offset++]; + int firstLineNumber = blockData[offset++]; + int lastLineNumber = blockData[offset++]; + int startUnit = blockData[offset++]; + int endUnit = blockData[offset]; + + return blockBuilder + .setResourceId(resourceId) + .setBlockHash(blockHash) + .setIndexInFile(indexInFile) + .setLines(firstLineNumber, lastLineNumber) + .setUnit(startUnit, endUnit) + .build(); + } + + private Block getBlock(int index, String resourceId) { + return createBlock(index, resourceId, null); + } + + private class ResourceIterator implements Iterator<ResourceBlocks> { + private int index = 0; + + @Override + public boolean hasNext() { + return index < size; } - return result; + + @Override + public ResourceBlocks next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + String resourceId = resourceIds[resourceIdsIndex[index]]; + List<Block> blocks = new ArrayList<>(); + + // while we are at the same resource, keep going + do { + blocks.add(getBlock(resourceIdsIndex[index], resourceId)); + index++; + } while (hasNext() && FastStringComparator.INSTANCE.compare(resourceIds[resourceIdsIndex[index]], resourceId) == 0); + + return new ResourceBlocks(resourceId, blocks); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + public static class ResourceBlocks { + private Collection<Block> blocks; + private String resourceId; + + public ResourceBlocks(String resourceId, Collection<Block> blocks) { + this.resourceId = resourceId; + this.blocks = blocks; + } + + public Collection<Block> blocks() { + return blocks; + } + + public String resourceId() { + return resourceId; + } + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator<ResourceBlocks> iterator() { + ensureSorted(); + return new ResourceIterator(); } /** @@ -154,21 +233,7 @@ public class PackedMemoryCloneIndex extends AbstractCloneIndex { while (index < size && !isLessByHash(size, index)) { // extract block (note that there is no need to extract hash) String resourceId = resourceIds[index]; - offset = index * blockInts + hashInts; - int indexInFile = blockData[offset++]; - int firstLineNumber = blockData[offset++]; - int lastLineNumber = blockData[offset++]; - int startUnit = blockData[offset++]; - int endUnit = blockData[offset]; - - Block block = blockBuilder - .setResourceId(resourceId) - .setBlockHash(sequenceHash) - .setIndexInFile(indexInFile) - .setLines(firstLineNumber, lastLineNumber) - .setUnit(startUnit, endUnit) - .build(); - result.add(block); + result.add(createBlock(index, resourceId, sequenceHash)); index++; } return result; diff --git a/sonar-duplications/src/test/java/org/sonar/duplications/index/PackedMemoryCloneIndexTest.java b/sonar-duplications/src/test/java/org/sonar/duplications/index/PackedMemoryCloneIndexTest.java index cb91fe37b6f..0812e1eb767 100644 --- a/sonar-duplications/src/test/java/org/sonar/duplications/index/PackedMemoryCloneIndexTest.java +++ b/sonar-duplications/src/test/java/org/sonar/duplications/index/PackedMemoryCloneIndexTest.java @@ -23,9 +23,13 @@ import org.junit.Before; import org.junit.Test; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.ByteArray; +import org.sonar.duplications.index.PackedMemoryCloneIndex.ResourceBlocks; +import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertThat; @@ -76,6 +80,34 @@ public class PackedMemoryCloneIndexTest { assertThat(block.getBlockHash(), sameInstance(requestedHash)); } } + + @Test + public void iterate() { + index.insert(newBlock("a", 1)); + index.insert(newBlock("c", 1)); + index.insert(newBlock("b", 1)); + index.insert(newBlock("c", 2)); + index.insert(newBlock("a", 2)); + + Iterator<ResourceBlocks> it = index.iterator(); + + ArrayList<ResourceBlocks> resourcesBlocks = new ArrayList<>(); + + while(it.hasNext()) { + resourcesBlocks.add(it.next()); + } + + assertThat(resourcesBlocks).hasSize(3); + + assertThat(resourcesBlocks.get(0).resourceId()).isEqualTo("a"); + assertThat(resourcesBlocks.get(1).resourceId()).isEqualTo("b"); + assertThat(resourcesBlocks.get(2).resourceId()).isEqualTo("c"); + + assertThat(resourcesBlocks.get(0).blocks()).hasSize(2); + assertThat(resourcesBlocks.get(1).blocks()).hasSize(1); + assertThat(resourcesBlocks.get(2).blocks()).hasSize(2); + + } /** * Given: index with initial capacity 1. |