diff options
13 files changed, 198 insertions, 151 deletions
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 28604e52bc2..aa4bf2b4da6 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 @@ -30,10 +30,11 @@ import org.sonar.api.batch.SensorContext; import org.sonar.api.config.Settings; import org.sonar.api.resources.Language; import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; import org.sonar.api.scan.filesystem.FileQuery; -import org.sonar.api.scan.filesystem.ModuleFileSystem; +import org.sonar.api.scan.filesystem.internal.DefaultInputFile; +import org.sonar.api.scan.filesystem.internal.InputFile; import org.sonar.api.utils.SonarException; +import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.duplications.DuplicationPredicates; import org.sonar.duplications.block.Block; import org.sonar.duplications.index.CloneGroup; @@ -41,10 +42,13 @@ import org.sonar.duplications.internal.pmd.TokenizerBridge; import org.sonar.plugins.cpd.index.IndexFactory; import org.sonar.plugins.cpd.index.SonarDuplicationsIndex; -import java.io.File; import java.util.Collection; import java.util.List; -import java.util.concurrent.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class SonarBridgeEngine extends CpdEngine { @@ -57,17 +61,17 @@ public class SonarBridgeEngine extends CpdEngine { private final IndexFactory indexFactory; private final CpdMapping[] mappings; - private final ModuleFileSystem fileSystem; + private final DefaultModuleFileSystem fileSystem; private final Settings settings; - public SonarBridgeEngine(IndexFactory indexFactory, CpdMapping[] mappings, ModuleFileSystem moduleFileSystem, Settings settings) { + public SonarBridgeEngine(IndexFactory indexFactory, CpdMapping[] mappings, DefaultModuleFileSystem moduleFileSystem, Settings settings) { this.indexFactory = indexFactory; this.mappings = mappings; this.fileSystem = moduleFileSystem; this.settings = settings; } - public SonarBridgeEngine(IndexFactory indexFactory, ModuleFileSystem moduleFileSystem, Settings settings) { + public SonarBridgeEngine(IndexFactory indexFactory, DefaultModuleFileSystem moduleFileSystem, Settings settings) { this(indexFactory, new CpdMapping[0], moduleFileSystem, settings); } @@ -80,8 +84,9 @@ public class SonarBridgeEngine extends CpdEngine { public void analyse(Project project, SensorContext context) { String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS); logExclusions(cpdExclusions, LOG); - List<File> sourceFiles = fileSystem.files(FileQuery.onSource().onLanguage(project.getLanguageKey()).withExclusions(cpdExclusions)); - if (sourceFiles.isEmpty()) { + Iterable<InputFile> sourceFiles = fileSystem.inputFiles(FileQuery.onSource().onLanguage(project.getLanguageKey()) + .withExclusions(cpdExclusions)); + if (!sourceFiles.iterator().hasNext()) { return; } @@ -91,12 +96,11 @@ public class SonarBridgeEngine extends CpdEngine { SonarDuplicationsIndex index = indexFactory.create(project); TokenizerBridge bridge = new TokenizerBridge(mapping.getTokenizer(), fileSystem.sourceCharset().name(), getBlockSize(project)); - for (File file : sourceFiles) { - LOG.debug("Populating index from {}", file); - Resource resource = mapping.createResource(file, fileSystem.sourceDirs()); - String resourceId = SonarEngine.getFullKey(project, resource); - List<Block> blocks = bridge.chunk(resourceId, file); - index.insert(resource, blocks); + for (InputFile inputFile : sourceFiles) { + LOG.debug("Populating index from {}", inputFile); + String resourceEffectiveKey = inputFile.attribute(DefaultInputFile.ATTRIBUTE_COMPONENT_KEY); + List<Block> blocks = bridge.chunk(resourceEffectiveKey, inputFile.file()); + index.insert(inputFile, blocks); } // Detect @@ -104,12 +108,10 @@ public class SonarBridgeEngine extends CpdEngine { ExecutorService executorService = Executors.newSingleThreadExecutor(); try { - for (File file : sourceFiles) { - LOG.debug("Detection of duplications for {}", file); - Resource resource = mapping.createResource(file, fileSystem.sourceDirs()); - String resourceKey = SonarEngine.getFullKey(project, resource); - - Collection<Block> fileBlocks = index.getByResource(resource, resourceKey); + for (InputFile inputFile : sourceFiles) { + LOG.debug("Detection of duplications for {}", inputFile); + String resourceEffectiveKey = inputFile.attribute(DefaultInputFile.ATTRIBUTE_COMPONENT_KEY); + Collection<Block> fileBlocks = index.getByInputFile(inputFile, resourceEffectiveKey); Iterable<CloneGroup> filtered; try { @@ -117,14 +119,14 @@ public class SonarBridgeEngine extends CpdEngine { filtered = Iterables.filter(duplications, minimumTokensPredicate); } catch (TimeoutException e) { filtered = null; - LOG.warn("Timeout during detection of duplications for " + file, e); + LOG.warn("Timeout during detection of duplications for " + inputFile, e); } catch (InterruptedException e) { - throw new SonarException("Fail during detection of duplication for "+ file, e); + throw new SonarException("Fail during detection of duplication for " + inputFile, e); } catch (ExecutionException e) { - throw new SonarException("Fail during detection of duplication for "+ file, e); + throw new SonarException("Fail during detection of duplication for " + inputFile, e); } - SonarEngine.save(context, resource, filtered); + SonarEngine.save(context, inputFile, filtered); } } finally { executorService.shutdown(); diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java index d92157f3e11..d4d545d34d0 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java @@ -28,19 +28,17 @@ import org.slf4j.LoggerFactory; import org.sonar.api.CoreProperties; import org.sonar.api.batch.SensorContext; import org.sonar.api.config.Settings; -import org.sonar.api.database.model.ResourceModel; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.PersistenceMode; import org.sonar.api.resources.Java; -import org.sonar.api.resources.JavaFile; import org.sonar.api.resources.Language; import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; import org.sonar.api.scan.filesystem.FileQuery; -import org.sonar.api.scan.filesystem.ModuleFileSystem; -import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.api.scan.filesystem.internal.DefaultInputFile; +import org.sonar.api.scan.filesystem.internal.InputFile; import org.sonar.api.utils.SonarException; +import org.sonar.batch.scan.filesystem.DefaultModuleFileSystem; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.BlockChunker; import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm; @@ -57,7 +55,6 @@ import org.sonar.plugins.cpd.index.SonarDuplicationsIndex; import javax.annotation.Nullable; -import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStreamReader; @@ -85,14 +82,12 @@ public class SonarEngine extends CpdEngine { private static final int TIMEOUT = 5 * 60; private final IndexFactory indexFactory; - private final ModuleFileSystem fileSystem; - private final PathResolver pathResolver; + private final DefaultModuleFileSystem fileSystem; private final Settings settings; - public SonarEngine(IndexFactory indexFactory, ModuleFileSystem moduleFileSystem, PathResolver pathResolver, Settings settings) { + public SonarEngine(IndexFactory indexFactory, DefaultModuleFileSystem moduleFileSystem, Settings settings) { this.indexFactory = indexFactory; this.fileSystem = moduleFileSystem; - this.pathResolver = pathResolver; this.settings = settings; } @@ -101,80 +96,70 @@ public class SonarEngine extends CpdEngine { return Java.KEY.equals(language.getKey()); } - static String getFullKey(Project project, Resource resource) { - return new StringBuilder(ResourceModel.KEY_SIZE) - .append(project.getKey()) - .append(':') - .append(resource.getKey()) - .toString(); - } - @Override public void analyse(Project project, SensorContext context) { String[] cpdExclusions = settings.getStringArray(CoreProperties.CPD_EXCLUSIONS); logExclusions(cpdExclusions, LOG); - List<File> sourceFiles = fileSystem.files(FileQuery.onSource().onLanguage(project.getLanguageKey()).withExclusions(cpdExclusions)); - if (sourceFiles.isEmpty()) { + Iterable<InputFile> sourceFiles = fileSystem.inputFiles(FileQuery.onSource().onLanguage(project.getLanguageKey()).withExclusions(cpdExclusions)); + if (!sourceFiles.iterator().hasNext()) { return; } SonarDuplicationsIndex index = createIndex(project, sourceFiles); detect(index, context, project, sourceFiles); } - private SonarDuplicationsIndex createIndex(Project project, List<File> sourceFiles) { + private SonarDuplicationsIndex createIndex(Project project, Iterable<InputFile> sourceFiles) { final SonarDuplicationsIndex index = indexFactory.create(project); TokenChunker tokenChunker = JavaTokenProducer.build(); StatementChunker statementChunker = JavaStatementBuilder.build(); BlockChunker blockChunker = new BlockChunker(BLOCK_SIZE); - for (File file : sourceFiles) { - LOG.debug("Populating index from {}", file); - Resource resource = getResource(file); - String resourceKey = getFullKey(project, resource); + for (InputFile inputFile : sourceFiles) { + LOG.debug("Populating index from {}", inputFile); + String resourceEffectiveKey = inputFile.attribute(DefaultInputFile.ATTRIBUTE_COMPONENT_KEY); List<Statement> statements; Reader reader = null; try { - reader = new InputStreamReader(new FileInputStream(file), fileSystem.sourceCharset()); + reader = new InputStreamReader(new FileInputStream(inputFile.file()), fileSystem.sourceCharset()); statements = statementChunker.chunk(tokenChunker.chunk(reader)); } catch (FileNotFoundException e) { - throw new SonarException("Cannot find file " + file, e); + throw new SonarException("Cannot find file " + inputFile.file(), e); } finally { IOUtils.closeQuietly(reader); } - List<Block> blocks = blockChunker.chunk(resourceKey, statements); - index.insert(resource, blocks); + List<Block> blocks = blockChunker.chunk(resourceEffectiveKey, statements); + index.insert(inputFile, blocks); } return index; } - private void detect(SonarDuplicationsIndex index, SensorContext context, Project project, List<File> sourceFiles) { + private void detect(SonarDuplicationsIndex index, SensorContext context, Project project, Iterable<InputFile> sourceFiles) { ExecutorService executorService = Executors.newSingleThreadExecutor(); try { - for (File file : sourceFiles) { - LOG.debug("Detection of duplications for {}", file); - Resource resource = getResource(file); - String resourceKey = getFullKey(project, resource); + for (InputFile inputFile : sourceFiles) { + LOG.debug("Detection of duplications for {}", inputFile); + String resourceEffectiveKey = inputFile.attribute(DefaultInputFile.ATTRIBUTE_COMPONENT_KEY); - Collection<Block> fileBlocks = index.getByResource(resource, resourceKey); + Collection<Block> fileBlocks = index.getByInputFile(inputFile, resourceEffectiveKey); List<CloneGroup> clones; try { clones = executorService.submit(new Task(index, fileBlocks)).get(TIMEOUT, TimeUnit.SECONDS); } catch (TimeoutException e) { clones = null; - LOG.warn("Timeout during detection of duplications for " + file, e); + LOG.warn("Timeout during detection of duplications for " + inputFile, e); } catch (InterruptedException e) { - throw new SonarException("Fail during detection of duplication for " + file, e); + throw new SonarException("Fail during detection of duplication for " + inputFile, e); } catch (ExecutionException e) { - throw new SonarException("Fail during detection of duplication for " + file, e); + throw new SonarException("Fail during detection of duplication for " + inputFile, e); } - save(context, resource, clones); + save(context, inputFile, clones); } } finally { executorService.shutdown(); @@ -195,12 +180,7 @@ public class SonarEngine extends CpdEngine { } } - protected Resource getResource(File file) { - String relativePathFromBaseDir = pathResolver.relativePath(fileSystem.baseDir(), file); - return JavaFile.create(relativePathFromBaseDir, "unused", false); - } - - static void save(SensorContext context, Resource resource, @Nullable Iterable<CloneGroup> duplications) { + static void save(SensorContext context, InputFile inputFile, @Nullable Iterable<CloneGroup> duplications) { if (duplications == null || Iterables.isEmpty(duplications)) { return; } @@ -219,13 +199,13 @@ public class SonarEngine extends CpdEngine { } } // Save - context.saveMeasure(resource, CoreMetrics.DUPLICATED_FILES, 1.0); - context.saveMeasure(resource, CoreMetrics.DUPLICATED_LINES, (double) duplicatedLines.size()); - context.saveMeasure(resource, CoreMetrics.DUPLICATED_BLOCKS, duplicatedBlocks); + context.saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1.0); + context.saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, (double) duplicatedLines.size()); + context.saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, duplicatedBlocks); Measure data = new Measure(CoreMetrics.DUPLICATIONS_DATA, toXml(duplications)) .setPersistenceMode(PersistenceMode.DATABASE); - context.saveMeasure(resource, data); + context.saveMeasure(inputFile, data); } private static String toXml(Iterable<CloneGroup> duplications) { diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java index 9ea3a65ba38..2ef72e7cae6 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java @@ -23,7 +23,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; +import org.sonar.api.scan.filesystem.internal.InputFile; import org.sonar.batch.index.ResourcePersister; import org.sonar.core.duplication.DuplicationDao; import org.sonar.core.duplication.DuplicationUnitDto; @@ -56,12 +56,12 @@ public class DbDuplicationsIndex { this.languageKey = currentProject.getLanguageKey(); } - int getSnapshotIdFor(Resource resource) { - return resourcePersister.getSnapshotOrFail(resource).getId(); + int getSnapshotIdFor(InputFile inputFile) { + return resourcePersister.getSnapshotOrFail(inputFile).getId(); } - public void prepareCache(Resource resource) { - int resourceSnapshotId = getSnapshotIdFor(resource); + public void prepareCache(InputFile inputFile) { + int resourceSnapshotId = getSnapshotIdFor(inputFile); List<DuplicationUnitDto> units = dao.selectCandidates(resourceSnapshotId, lastSnapshotId, languageKey); cache.clear(); // TODO Godin: maybe remove conversion of units to blocks? @@ -74,11 +74,11 @@ public class DbDuplicationsIndex { // TODO Godin: in fact we could work directly with id instead of key - this will allow to decrease memory consumption Block block = Block.builder() - .setResourceId(resourceKey) - .setBlockHash(new ByteArray(hash)) - .setIndexInFile(indexInFile) - .setLines(startLine, endLine) - .build(); + .setResourceId(resourceKey) + .setBlockHash(new ByteArray(hash)) + .setIndexInFile(indexInFile) + .setLines(startLine, endLine) + .build(); // Group blocks by hash Collection<Block> sameHash = cache.get(block.getBlockHash()); @@ -99,19 +99,19 @@ public class DbDuplicationsIndex { } } - public void insert(Resource resource, Collection<Block> blocks) { - int resourceSnapshotId = getSnapshotIdFor(resource); + public void insert(InputFile inputFile, Collection<Block> blocks) { + int resourceSnapshotId = getSnapshotIdFor(inputFile); // TODO Godin: maybe remove conversion of blocks to units? List<DuplicationUnitDto> units = Lists.newArrayList(); for (Block block : blocks) { DuplicationUnitDto unit = new DuplicationUnitDto( - currentProjectSnapshotId, - resourceSnapshotId, - block.getBlockHash().toString(), - block.getIndexInFile(), - block.getStartLine(), - block.getEndLine()); + currentProjectSnapshotId, + resourceSnapshotId, + block.getBlockHash().toString(), + block.getIndexInFile(), + block.getStartLine(), + block.getEndLine()); units.add(unit); } diff --git a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/SonarDuplicationsIndex.java b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/SonarDuplicationsIndex.java index 9c74235bad6..cd554934c04 100644 --- a/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/SonarDuplicationsIndex.java +++ b/plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/SonarDuplicationsIndex.java @@ -20,7 +20,7 @@ package org.sonar.plugins.cpd.index; import com.google.common.collect.Lists; -import org.sonar.api.resources.Resource; +import org.sonar.api.scan.filesystem.internal.InputFile; import org.sonar.duplications.block.Block; import org.sonar.duplications.block.ByteArray; import org.sonar.duplications.index.AbstractCloneIndex; @@ -43,18 +43,18 @@ public class SonarDuplicationsIndex extends AbstractCloneIndex { this.db = db; } - public void insert(Resource resource, Collection<Block> blocks) { + public void insert(InputFile inputFile, Collection<Block> blocks) { for (Block block : blocks) { mem.insert(block); } if (db != null) { - db.insert(resource, blocks); + db.insert(inputFile, blocks); } } - public Collection<Block> getByResource(Resource resource, String resourceKey) { + public Collection<Block> getByInputFile(InputFile inputFile, String resourceKey) { if (db != null) { - db.prepareCache(resource); + db.prepareCache(inputFile); } return mem.getByResourceId(resourceKey); } 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 26d5637373f..0cf3ade848b 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 @@ -42,7 +42,7 @@ public class CpdSensorTest { @Before public void setUp() { IndexFactory indexFactory = mock(IndexFactory.class); - sonarEngine = new SonarEngine(indexFactory, null, null, null); + sonarEngine = new SonarEngine(indexFactory, null, null); sonarBridgeEngine = new SonarBridgeEngine(indexFactory, null, null); settings = new Settings(new PropertyDefinitions(CpdPlugin.class)); sensor = new CpdSensor(sonarEngine, sonarBridgeEngine, settings); diff --git a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java index c2346fe86fb..f22eabb3a81 100644 --- a/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java +++ b/plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/SonarEngineTest.java @@ -19,15 +19,13 @@ */ package org.sonar.plugins.cpd; +import com.google.common.base.Charsets; import org.junit.Before; import org.junit.Test; import org.sonar.api.batch.SensorContext; import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.resources.File; -import org.sonar.api.resources.JavaFile; -import org.sonar.api.resources.Resource; -import org.sonar.api.scan.filesystem.ModuleFileSystem; -import org.sonar.api.scan.filesystem.PathResolver; +import org.sonar.api.scan.filesystem.internal.InputFile; +import org.sonar.api.scan.filesystem.internal.InputFileBuilder; import org.sonar.api.test.IsMeasure; import org.sonar.duplications.index.CloneGroup; import org.sonar.duplications.index.ClonePart; @@ -36,44 +34,28 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; public class SonarEngineTest { private SensorContext context; - private Resource resource; + private InputFile inputFile; @Before public void setUp() { context = mock(SensorContext.class); - resource = new JavaFile("key1"); - } - - @Test - public void testGetResource() { - PathResolver pathResolver = mock(PathResolver.class); - ModuleFileSystem fileSystem = mock(ModuleFileSystem.class); - when(pathResolver.relativePath(any(java.io.File.class), any(java.io.File.class))).thenReturn("src/main/java/com/foo/Bar.java"); - - SonarEngine engine = new SonarEngine(null, fileSystem, pathResolver, null); - Resource resource = engine.getResource(new java.io.File("")); - - assertThat(resource.getKey()).isEqualTo("/src/main/java/com/foo/Bar.java"); - assertThat(resource).isInstanceOf(JavaFile.class); + inputFile = new InputFileBuilder(new java.io.File(""), Charsets.UTF_8, "src/main/java/Foo.java").build(); } @SuppressWarnings("unchecked") @Test public void testNothingToSave() { - SonarEngine.save(context, resource, null); - SonarEngine.save(context, resource, Collections.EMPTY_LIST); + SonarEngine.save(context, inputFile, null); + SonarEngine.save(context, inputFile, Collections.EMPTY_LIST); verifyZeroInteractions(context); } @@ -81,13 +63,13 @@ public class SonarEngineTest { @Test public void testOneSimpleDuplicationBetweenTwoFiles() { List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214))); - SonarEngine.save(context, resource, groups); + SonarEngine.save(context, inputFile, groups); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_LINES, 200d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 1d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 200d); verify(context).saveMeasure( - eq(resource), + eq(inputFile), argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "<duplications><g>" + "<b s=\"5\" l=\"200\" r=\"key1\"/>" + "<b s=\"15\" l=\"200\" r=\"key2\"/>" @@ -97,13 +79,13 @@ public class SonarEngineTest { @Test public void testDuplicationOnSameFile() throws Exception { List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key1", 0, 215, 414))); - SonarEngine.save(context, resource, groups); + SonarEngine.save(context, inputFile, groups); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_LINES, 400d); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_BLOCKS, 2d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 400d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 2d); verify(context).saveMeasure( - eq(resource), + eq(inputFile), argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "<duplications><g>" + "<b s=\"5\" l=\"200\" r=\"key1\"/>" + "<b s=\"215\" l=\"200\" r=\"key1\"/>" @@ -113,13 +95,13 @@ public class SonarEngineTest { @Test public void testOneDuplicatedGroupInvolvingMoreThanTwoFiles() throws Exception { List<CloneGroup> groups = Arrays.asList(newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214), new ClonePart("key3", 0, 25, 224))); - SonarEngine.save(context, resource, groups); + SonarEngine.save(context, inputFile, groups); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_BLOCKS, 1d); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_LINES, 200d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 1d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 200d); verify(context).saveMeasure( - eq(resource), + eq(inputFile), argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "<duplications><g>" + "<b s=\"5\" l=\"200\" r=\"key1\"/>" + "<b s=\"15\" l=\"200\" r=\"key2\"/>" @@ -132,13 +114,13 @@ public class SonarEngineTest { List<CloneGroup> groups = Arrays.asList( newCloneGroup(new ClonePart("key1", 0, 5, 204), new ClonePart("key2", 0, 15, 214)), newCloneGroup(new ClonePart("key1", 0, 15, 214), new ClonePart("key3", 0, 15, 214))); - SonarEngine.save(context, resource, groups); + SonarEngine.save(context, inputFile, groups); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_FILES, 1d); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_BLOCKS, 2d); - verify(context).saveMeasure(resource, CoreMetrics.DUPLICATED_LINES, 210d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_FILES, 1d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_BLOCKS, 2d); + verify(context).saveMeasure(inputFile, CoreMetrics.DUPLICATED_LINES, 210d); verify(context).saveMeasure( - eq(resource), + eq(inputFile), argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "<duplications>" + "<g>" + "<b s=\"5\" l=\"200\" r=\"key1\"/>" @@ -153,7 +135,7 @@ public class SonarEngineTest { @Test public void shouldEscapeXmlEntities() { - File csharpFile = new File("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs"); + InputFile csharpFile = new InputFileBuilder(new java.io.File(""), Charsets.UTF_8, "Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs").build(); List<CloneGroup> groups = Arrays.asList(newCloneGroup( new ClonePart("Loads/File Loads/Subs & Reds/SubsRedsDelivery.cs", 0, 5, 204), new ClonePart("Loads/File Loads/Subs & Reds/SubsRedsDelivery2.cs", 0, 15, 214))); diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java index d52e6e7d2c2..2d0a1283672 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java @@ -29,11 +29,16 @@ import org.sonar.api.design.Dependency; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; import org.sonar.api.measures.Metric; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.JavaFile; +import org.sonar.api.resources.Languages; import org.sonar.api.resources.Project; import org.sonar.api.resources.ProjectLink; import org.sonar.api.resources.Resource; import org.sonar.api.resources.Scopes; import org.sonar.api.rules.Violation; +import org.sonar.api.scan.filesystem.internal.InputFile; import org.sonar.api.utils.SonarException; import org.sonar.core.measure.MeasurementFilters; @@ -49,11 +54,13 @@ public class DefaultSensorContext implements SensorContext { private SonarIndex index; private Project project; private MeasurementFilters filters; + private Languages languages; - public DefaultSensorContext(SonarIndex index, Project project, MeasurementFilters filters) { + public DefaultSensorContext(SonarIndex index, Project project, MeasurementFilters filters, Languages languages) { this.index = index; this.project = project; this.filters = filters; + this.languages = languages; } public Project getProject() { @@ -74,6 +81,7 @@ public class DefaultSensorContext implements SensorContext { StringUtils.equals(Scopes.FILE, resource.getScope()); } + @Override public boolean index(Resource resource, Resource parentReference) { // SONAR-5006 if (indexedByCore(resource)) { @@ -88,42 +96,52 @@ public class DefaultSensorContext implements SensorContext { "Plugin should not index physical resources")); } + @Override public boolean isExcluded(Resource reference) { return index.isExcluded(reference); } + @Override public boolean isIndexed(Resource reference, boolean acceptExcluded) { return index.isIndexed(reference, acceptExcluded); } + @Override public Resource getParent(Resource reference) { return index.getParent(reference); } + @Override public Collection<Resource> getChildren(Resource reference) { return index.getChildren(reference); } + @Override public Measure getMeasure(Metric metric) { return index.getMeasure(project, metric); } + @Override public <M> M getMeasures(MeasuresFilter<M> filter) { return index.getMeasures(project, filter); } + @Override public Measure saveMeasure(Measure measure) { return index.addMeasure(project, measure); } + @Override public Measure saveMeasure(Metric metric, Double value) { return index.addMeasure(project, new Measure(metric, value)); } + @Override public Measure getMeasure(Resource resource, Metric metric) { return index.getMeasure(resource, metric); } + @Override public String saveResource(Resource resource) { Resource persistedResource = index.addResource(resource); if (persistedResource != null) { @@ -136,18 +154,22 @@ public class DefaultSensorContext implements SensorContext { return index.index(resource, parentReference); } + @Override public Resource getResource(Resource resource) { return index.getResource(resource); } + @Override public <M> M getMeasures(Resource resource, MeasuresFilter<M> filter) { return index.getMeasures(resource, filter); } + @Override public Measure saveMeasure(Resource resource, Metric metric, Double value) { return saveMeasure(resource, new Measure(metric, value)); } + @Override public Measure saveMeasure(Resource resource, Measure measure) { if (filters.accept(resource, measure)) { return index.addMeasure(resourceOrProject(resource), measure); @@ -217,4 +239,24 @@ public class DefaultSensorContext implements SensorContext { private Resource resourceOrProject(Resource resource) { return resource != null ? resource : project; } + + @Override + public Measure saveMeasure(InputFile inputFile, Metric metric, Double value) { + return saveMeasure(fromInputFile(inputFile), metric, value); + } + + @Override + public Measure saveMeasure(InputFile inputFile, Measure measure) { + return saveMeasure(fromInputFile(inputFile), measure); + } + + private Resource fromInputFile(InputFile inputFile) { + String languageKey = inputFile.attribute(InputFile.ATTRIBUTE_LANGUAGE); + boolean unitTest = InputFile.TYPE_TEST.equals(inputFile.attribute(InputFile.ATTRIBUTE_TYPE)); + if (Java.KEY.equals(languageKey)) { + return JavaFile.create(inputFile.path(), inputFile.attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH), unitTest); + } else { + return File.create(inputFile.path(), inputFile.attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH), languages.get(languageKey), unitTest); + } + } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java index fa665e608b9..c76b2498ae1 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java @@ -535,6 +535,11 @@ public class DefaultIndex extends SonarIndex { return bucket; } + if (StringUtils.isBlank(resource.getKey())) { + LOG.warn("Unable to index a resource without key " + resource); + return null; + } + checkLock(resource); Resource parent = null; diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java index cbf24c5e6be..26799593177 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultResourcePersister.java @@ -25,12 +25,16 @@ import org.apache.commons.lang.StringUtils; import org.sonar.api.database.DatabaseSession; import org.sonar.api.database.model.ResourceModel; import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.JavaFile; import org.sonar.api.resources.Library; import org.sonar.api.resources.Project; import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; import org.sonar.api.resources.Scopes; +import org.sonar.api.scan.filesystem.internal.InputFile; import org.sonar.api.security.ResourcePermissions; import org.sonar.api.utils.SonarException; @@ -131,6 +135,21 @@ public final class DefaultResourcePersister implements ResourcePersister { return snapshot; } + @Override + public Snapshot getSnapshotOrFail(InputFile inputFile) { + return getSnapshotOrFail(fromInputFile(inputFile)); + }; + + private Resource fromInputFile(InputFile inputFile) { + String languageKey = inputFile.attribute(InputFile.ATTRIBUTE_LANGUAGE); + boolean unitTest = InputFile.TYPE_TEST.equals(inputFile.attribute(InputFile.ATTRIBUTE_TYPE)); + if (Java.KEY.equals(languageKey)) { + return JavaFile.create(inputFile.path(), inputFile.attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH), unitTest); + } else { + return File.create(inputFile.path(), inputFile.attribute(InputFile.ATTRIBUTE_SOURCE_RELATIVE_PATH), null, unitTest); + } + } + /** * just for unit tests */ diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java index 4310f54c165..8e41528ba36 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/ResourcePersister.java @@ -22,9 +22,10 @@ package org.sonar.batch.index; import org.sonar.api.database.model.Snapshot; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; +import org.sonar.api.scan.filesystem.internal.InputFile; public interface ResourcePersister { - + Snapshot saveProject(Project project, Project parent); /** @@ -39,11 +40,9 @@ public interface ResourcePersister { Snapshot getSnapshot(Resource resource); - /** - * @throws ResourceNotPersistedException if the resource is not persisted. - */ Snapshot getSnapshotOrFail(Resource resource); - + + Snapshot getSnapshotOrFail(InputFile resource); /** * The current snapshot which is flagged as "last", different then the current analysis. diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/CpdMapping.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/CpdMapping.java index 3203740ee48..c000683c4e8 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/CpdMapping.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/CpdMapping.java @@ -36,6 +36,10 @@ public interface CpdMapping extends BatchExtension { Language getLanguage(); + /** + * @deprecated since 4.2 + */ + @Deprecated Resource createResource(File file, List<File> sourceDirs); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java index e4654dd08e3..23a02fd333a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java @@ -26,6 +26,7 @@ import org.sonar.api.measures.Metric; import org.sonar.api.resources.ProjectLink; import org.sonar.api.resources.Resource; import org.sonar.api.rules.Violation; +import org.sonar.api.scan.filesystem.internal.InputFile; import java.util.Collection; import java.util.Date; @@ -143,6 +144,12 @@ public interface SensorContext { /** * Add or update a measure. + * @since 4.2 + */ + Measure saveMeasure(InputFile inputFile, Metric metric, Double value); + + /** + * Add or update a measure. * <p> * The resource is automatically saved, so there is no need to execute the method saveResource(). Does nothing if the resource is set as * excluded. @@ -150,6 +157,12 @@ public interface SensorContext { */ Measure saveMeasure(Resource resource, Measure measure); + /** + * Add or update a measure. + * @since 4.2 + */ + Measure saveMeasure(InputFile inputFile, Measure measure); + // ----------- RULE VIOLATIONS -------------- /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java index 37b4e6a9582..ae901e0bdfe 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java @@ -136,6 +136,7 @@ public final class ResourceUtils { */ public static boolean isPersistable(Resource resource) { return StringUtils.equals(Scopes.PROJECT, resource.getScope()) || StringUtils.equals(Scopes.DIRECTORY, resource.getScope()) || - StringUtils.equals(Scopes.FILE, resource.getScope()); + StringUtils.equals(Scopes.FILE, resource.getScope()); } + } |