From a1defc3dd106409930d9323d7bc055f816c2bcd3 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Tue, 22 Jul 2014 09:27:34 +0200 Subject: [PATCH] SONAR-5389 Add InputDir concept in batch API --- .../plugins/core/sensors/FileHashSensor.java | 8 +- .../core/sensors/FileHashSensorTest.java | 10 +- .../batch/mediumtest/BatchMediumTester.java | 4 +- .../batch/scan/ProjectScanContainer.java | 4 +- .../batch/scan/filesystem/FileIndexer.java | 10 +- ...nputFileCache.java => InputPathCache.java} | 43 ++++--- .../scan/filesystem/ModuleInputFileCache.java | 25 ++-- .../sonar/batch/scan/report/JsonReport.java | 49 +++++--- .../scan2/DefaultFileLinesContextFactory.java | 8 +- .../batch/scan2/ProjectScanContainer.java | 4 +- ...CacheTest.java => InputPathCacheTest.java} | 14 +-- .../batch/scan/report/JsonReportTest.java | 4 +- .../org/sonar/api/batch/fs/FileSystem.java | 10 ++ .../java/org/sonar/api/batch/fs/InputDir.java | 61 ++++++++++ .../org/sonar/api/batch/fs/InputFile.java | 6 +- .../org/sonar/api/batch/fs/InputPath.java | 50 ++++++++ .../batch/fs/internal/DefaultFileSystem.java | 18 +++ .../batch/fs/internal/DefaultInputDir.java | 112 ++++++++++++++++++ 18 files changed, 358 insertions(+), 82 deletions(-) rename sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/{InputFileCache.java => InputPathCache.java} (53%) rename sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/{InputFileCacheTest.java => InputPathCacheTest.java} (84%) create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputDir.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java create mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java index b1c5dac5147..d18e146fe53 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FileHashSensor.java @@ -27,7 +27,7 @@ import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.resources.Project; import org.sonar.api.utils.KeyValueFormat; import org.sonar.batch.index.ComponentDataCache; -import org.sonar.batch.scan.filesystem.InputFileCache; +import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.core.DryRunIncompatible; import org.sonar.core.source.SnapshotDataTypes; @@ -43,10 +43,10 @@ import java.util.Map; @DryRunIncompatible public final class FileHashSensor implements Sensor { - private final InputFileCache fileCache; + private final InputPathCache fileCache; private final ComponentDataCache componentDataCache; - public FileHashSensor(InputFileCache fileCache, ComponentDataCache componentDataCache) { + public FileHashSensor(InputPathCache fileCache, ComponentDataCache componentDataCache) { this.fileCache = fileCache; this.componentDataCache = componentDataCache; } @@ -58,7 +58,7 @@ public final class FileHashSensor implements Sensor { @Override public void analyse(Project project, SensorContext context) { Map map = Maps.newHashMap(); - for (InputFile inputFile : fileCache.byModule(project.key())) { + for (InputFile inputFile : fileCache.filesByModule(project.key())) { String hash = ((DeprecatedDefaultInputFile) inputFile).hash(); if (hash != null) { map.put(inputFile.relativePath(), hash); diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java index 29f5e64cce9..4f851f39cae 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/FileHashSensorTest.java @@ -31,7 +31,7 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DeprecatedDefaultInputFile; import org.sonar.api.resources.Project; import org.sonar.batch.index.ComponentDataCache; -import org.sonar.batch.scan.filesystem.InputFileCache; +import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.core.source.SnapshotDataTypes; import java.util.Collections; @@ -51,13 +51,13 @@ public class FileHashSensorTest { public ExpectedException thrown = ExpectedException.none(); Project project = new Project("struts"); - InputFileCache fileCache = mock(InputFileCache.class); + InputPathCache fileCache = mock(InputPathCache.class); ComponentDataCache componentDataCache = mock(ComponentDataCache.class); FileHashSensor sensor = new FileHashSensor(fileCache, componentDataCache); @Test public void store_file_hashes() throws Exception { - when(fileCache.byModule("struts")).thenReturn(Lists.newArrayList( + when(fileCache.filesByModule("struts")).thenReturn(Lists.newArrayList( new DeprecatedDefaultInputFile("src/Foo.java").setFile(temp.newFile()).setHash("ABC"), new DeprecatedDefaultInputFile("src/Bar.java").setFile(temp.newFile()).setHash("DEF"))); @@ -71,7 +71,7 @@ public class FileHashSensorTest { @Test public void store_file_hashes_for_branches() throws Exception { project = new Project("struts", "branch-2.x", "Struts 2.x"); - when(fileCache.byModule("struts:branch-2.x")).thenReturn(Lists.newArrayList( + when(fileCache.filesByModule("struts:branch-2.x")).thenReturn(Lists.newArrayList( new DeprecatedDefaultInputFile("src/Foo.java").setFile(temp.newFile()).setHash("ABC"), new DeprecatedDefaultInputFile("src/Bar.java").setFile(temp.newFile()).setHash("DEF"))); @@ -90,7 +90,7 @@ public class FileHashSensorTest { @Test public void dont_save_hashes_if_no_files() throws Exception { - when(fileCache.byModule("struts")).thenReturn(Collections.emptyList()); + when(fileCache.filesByModule("struts")).thenReturn(Collections.emptyList()); SensorContext sensorContext = mock(SensorContext.class); sensor.analyse(project, sensorContext); diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java index 5eaa1ee13a4..394a2d2688a 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java +++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/BatchMediumTester.java @@ -39,7 +39,7 @@ import org.sonar.batch.protocol.input.GlobalReferentials; import org.sonar.batch.protocol.input.ProjectReferentials; import org.sonar.batch.referential.GlobalReferentialsLoader; import org.sonar.batch.referential.ProjectReferentialsLoader; -import org.sonar.batch.scan.filesystem.InputFileCache; +import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.batch.scan2.AnalyzerIssueCache; import org.sonar.batch.scan2.AnalyzerMeasureCache; import org.sonar.batch.scan2.ProjectScanContainer; @@ -212,7 +212,7 @@ public class BatchMediumTester { measures.add(measure); } - InputFileCache inputFileCache = container.getComponentByType(InputFileCache.class); + InputPathCache inputFileCache = container.getComponentByType(InputPathCache.class); for (InputFile inputFile : inputFileCache.all()) { inputFiles.add(inputFile); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java index 9c2b46bf9b8..e3fde9a8c0f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java @@ -67,7 +67,7 @@ import org.sonar.batch.phases.GraphPersister; import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; import org.sonar.batch.referential.ProjectReferentialsProvider; import org.sonar.batch.rule.RulesProvider; -import org.sonar.batch.scan.filesystem.InputFileCache; +import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.batch.scan.maven.FakeMavenPluginExecutor; import org.sonar.batch.scan.maven.MavenPluginExecutor; import org.sonar.batch.scan.measure.MeasureCache; @@ -153,7 +153,7 @@ public class ProjectScanContainer extends ComponentContainer { DefaultUserFinder.class, // file system - InputFileCache.class, + InputPathCache.class, PathResolver.class, // issues diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java index bdd0da25555..a33bbc50358 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndexer.java @@ -46,7 +46,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; /** - * Index input files into {@link InputFileCache}. + * Index input files into {@link InputPathCache}. */ public class FileIndexer implements BatchComponent { @@ -56,18 +56,18 @@ public class FileIndexer implements BatchComponent { private static final IOFileFilter FILE_FILTER = HiddenFileFilter.VISIBLE; private final List filters; - private final InputFileCache fileCache; + private final InputPathCache fileCache; private final boolean isAggregator; private final ExclusionFilters exclusionFilters; private final InputFileBuilderFactory inputFileBuilderFactory; public FileIndexer(List filters, ExclusionFilters exclusionFilters, InputFileBuilderFactory inputFileBuilderFactory, - InputFileCache cache, ProjectDefinition def) { + InputPathCache cache, ProjectDefinition def) { this(filters, exclusionFilters, inputFileBuilderFactory, cache, !def.getSubProjects().isEmpty()); } private FileIndexer(List filters, ExclusionFilters exclusionFilters, InputFileBuilderFactory inputFileBuilderFactory, - InputFileCache cache, boolean isAggregator) { + InputPathCache cache, boolean isAggregator) { this.filters = filters; this.exclusionFilters = exclusionFilters; this.inputFileBuilderFactory = inputFileBuilderFactory; @@ -83,7 +83,7 @@ public class FileIndexer implements BatchComponent { LOG.info("Index files"); exclusionFilters.prepare(); - Progress progress = new Progress(fileCache.byModule(fileSystem.moduleKey())); + Progress progress = new Progress(fileCache.filesByModule(fileSystem.moduleKey())); InputFileBuilder inputFileBuilder = inputFileBuilderFactory.create(fileSystem); indexFiles(fileSystem, progress, inputFileBuilder, fileSystem.sources(), InputFile.Type.MAIN); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java similarity index 53% rename from sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileCache.java rename to sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java index 3b12d8ba5f6..842ecab6484 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputFileCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InputPathCache.java @@ -20,7 +20,9 @@ package org.sonar.batch.scan.filesystem; import org.sonar.api.BatchComponent; +import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputPath; import org.sonar.batch.index.Cache; import org.sonar.batch.index.Caches; @@ -30,45 +32,50 @@ import javax.annotation.CheckForNull; * Cache of all files. This cache is shared amongst all project modules. Inclusion and * exclusion patterns are already applied. */ -public class InputFileCache implements BatchComponent { +public class InputPathCache implements BatchComponent { - // [path type | module key | path] -> InputFile + private static final String DIR = "DIR"; + private static final String FILE = "FILE"; + // [module key | type | path] -> InputPath // For example: - // [rel | struts-core | src/main/java/Action.java] -> InputFile - // [rel | struts-core | src/main/java/Filter.java] -> InputFile - // [abs | struts-core | /absolute/path/to/src/main/java/Action.java] -> InputFile - // [abs | struts-core | /absolute/path/to/src/main/java/Filter.java] -> InputFile - private final Cache cache; + // [struts-core | FILE | src/main/java/Action.java] -> InputFile + // [struts-core | FILE | src/main/java/Filter.java] -> InputFile + // [struts-core | DIR | src/main/java] -> InputDir + private final Cache cache; - public InputFileCache(Caches caches) { + public InputPathCache(Caches caches) { cache = caches.createCache("inputFiles"); } - public Iterable all() { + public Iterable all() { return cache.values(); } - public Iterable byModule(String moduleKey) { - return cache.values(moduleKey); + public Iterable filesByModule(String moduleKey) { + return (Iterable) cache.values(moduleKey, FILE); } - public InputFileCache removeModule(String moduleKey) { + public InputPathCache removeModule(String moduleKey) { cache.clear(moduleKey); return this; } - public InputFileCache remove(String moduleKey, InputFile inputFile) { - cache.remove(moduleKey, inputFile.relativePath()); + public InputPathCache remove(String moduleKey, InputFile inputFile) { + cache.remove(moduleKey, FILE, inputFile.relativePath()); return this; } - public InputFileCache put(String moduleKey, InputFile inputFile) { - cache.put(moduleKey, inputFile.relativePath(), inputFile); + public InputPathCache put(String moduleKey, InputFile inputFile) { + cache.put(moduleKey, FILE, inputFile.relativePath(), inputFile); return this; } @CheckForNull - public InputFile get(String moduleKey, String relativePath) { - return cache.get(moduleKey, relativePath); + public InputFile getFile(String moduleKey, String relativePath) { + return (InputFile) cache.get(moduleKey, FILE, relativePath); + } + + public InputDir getDir(String moduleKey, String relativePath) { + return (InputDir) cache.get(moduleKey, DIR, relativePath); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java index d555074db79..c11d585a9c5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleInputFileCache.java @@ -21,37 +21,34 @@ package org.sonar.batch.scan.filesystem; import org.sonar.api.BatchComponent; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.RelativePathPredicate; -import org.sonar.api.resources.Project; public class ModuleInputFileCache extends DefaultFileSystem.Cache implements BatchComponent { private final String moduleKey; - private final InputFileCache projectCache; + private final InputPathCache projectCache; - public ModuleInputFileCache(Project module, ProjectDefinition projectDef, InputFileCache projectCache) { - this.moduleKey = module.getKey(); - this.projectCache = projectCache; - } - - /** - * Used by scan2 - */ - public ModuleInputFileCache(ProjectDefinition projectDef, InputFileCache projectCache) { - this.moduleKey = projectDef.getKey(); + public ModuleInputFileCache(ProjectDefinition projectDef, InputPathCache projectCache) { + this.moduleKey = projectDef.getKeyWithBranch(); this.projectCache = projectCache; } @Override protected Iterable inputFiles() { - return projectCache.byModule(moduleKey); + return projectCache.filesByModule(moduleKey); } @Override protected InputFile inputFile(RelativePathPredicate predicate) { - return projectCache.get(moduleKey, predicate.path()); + return projectCache.getFile(moduleKey, predicate.path()); + } + + @Override + protected InputDir inputDir(String relativePath) { + return projectCache.getDir(moduleKey, relativePath); } @Override diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java b/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java index fe8ddc7826d..a5334ace9d6 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/report/JsonReport.java @@ -26,8 +26,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; 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.fs.internal.DeprecatedDefaultInputFile; +import org.sonar.api.batch.fs.InputPath; +import org.sonar.api.batch.fs.internal.DefaultInputDir; +import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.config.Settings; import org.sonar.api.issue.internal.DefaultIssue; import org.sonar.api.platform.Server; @@ -43,9 +46,13 @@ import org.sonar.batch.bootstrap.AnalysisMode; import org.sonar.batch.events.BatchStepEvent; import org.sonar.batch.events.EventBus; import org.sonar.batch.issue.IssueCache; -import org.sonar.batch.scan.filesystem.InputFileCache; +import org.sonar.batch.scan.filesystem.InputPathCache; -import java.io.*; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -67,11 +74,11 @@ public class JsonReport implements BatchComponent { private final EventBus eventBus; private final AnalysisMode analysisMode; private final UserFinder userFinder; - private final InputFileCache fileCache; + private final InputPathCache fileCache; private final Project rootModule; public JsonReport(Settings settings, FileSystem fileSystem, Server server, RuleFinder ruleFinder, IssueCache issueCache, - EventBus eventBus, AnalysisMode analysisMode, UserFinder userFinder, Project rootModule, InputFileCache fileCache) { + EventBus eventBus, AnalysisMode analysisMode, UserFinder userFinder, Project rootModule, InputPathCache fileCache) { this.settings = settings; this.fileSystem = fileSystem; this.server = server; @@ -167,16 +174,28 @@ public class JsonReport implements BatchComponent { json.name("components").beginArray(); // Dump modules writeJsonModuleComponents(json, rootModule); - // TODO we need to dump directories - for (InputFile inputFile : fileCache.all()) { - String key = ((DeprecatedDefaultInputFile) inputFile).key(); - json - .beginObject() - .prop("key", key) - .prop("path", inputFile.relativePath()) - .prop("moduleKey", StringUtils.substringBeforeLast(key, ":")) - .prop("status", inputFile.status().name()) - .endObject(); + for (InputPath inputPath : fileCache.all()) { + if (inputPath instanceof InputFile) { + InputFile inputFile = (InputFile) inputPath; + String key = ((DefaultInputFile) inputFile).key(); + json + .beginObject() + .prop("key", key) + .prop("path", inputFile.relativePath()) + .prop("moduleKey", StringUtils.substringBeforeLast(key, ":")) + .prop("status", inputFile.status().name()) + .endObject(); + } else { + InputDir inputDir = (InputDir) inputPath; + String key = ((DefaultInputDir) inputDir).key(); + json + .beginObject() + .prop("key", key) + .prop("path", inputDir.relativePath()) + .prop("moduleKey", StringUtils.substringBeforeLast(key, ":")) + .endObject(); + } + } json.endArray(); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContextFactory.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContextFactory.java index addc6e18e3c..603cf47bb3f 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContextFactory.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/DefaultFileLinesContextFactory.java @@ -26,16 +26,16 @@ import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.FileLinesContextFactory; import org.sonar.api.resources.Resource; -import org.sonar.batch.scan.filesystem.InputFileCache; +import org.sonar.batch.scan.filesystem.InputPathCache; public class DefaultFileLinesContextFactory implements FileLinesContextFactory { private final AnalyzerMeasureCache measureCache; private final MetricFinder metricFinder; private final ProjectDefinition def; - private InputFileCache fileCache; + private InputPathCache fileCache; - public DefaultFileLinesContextFactory(InputFileCache fileCache, FileSystem fs, MetricFinder metricFinder, AnalyzerMeasureCache measureCache, + public DefaultFileLinesContextFactory(InputPathCache fileCache, FileSystem fs, MetricFinder metricFinder, AnalyzerMeasureCache measureCache, ProjectDefinition def) { this.fileCache = fileCache; this.metricFinder = metricFinder; @@ -50,7 +50,7 @@ public class DefaultFileLinesContextFactory implements FileLinesContextFactory { @Override public FileLinesContext createFor(InputFile inputFile) { - if (fileCache.get(def.getKey(), inputFile.relativePath()) == null) { + if (fileCache.getFile(def.getKey(), inputFile.relativePath()) == null) { throw new IllegalStateException("InputFile is not indexed: " + inputFile); } return new DefaultFileLinesContext(metricFinder, measureCache, def.getKey(), inputFile); diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java index 91b11ce80da..b0738e420e4 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan2/ProjectScanContainer.java @@ -41,7 +41,7 @@ import org.sonar.batch.referential.ProjectReferentialsLoader; import org.sonar.batch.referential.ProjectReferentialsProvider; import org.sonar.batch.scan.ProjectReactorBuilder; import org.sonar.batch.scan.ProjectSettings; -import org.sonar.batch.scan.filesystem.InputFileCache; +import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.batch.scan.maven.FakeMavenPluginExecutor; import org.sonar.batch.scan.maven.MavenPluginExecutor; @@ -98,7 +98,7 @@ public class ProjectScanContainer extends ComponentContainer { AnalyzerMeasureCache.class, // file system - InputFileCache.class, + InputPathCache.class, PathResolver.class, // issues diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java similarity index 84% rename from sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java rename to sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java index 3da4eeba2c0..6a81b667c7c 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputFileCacheTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InputPathCacheTest.java @@ -32,7 +32,7 @@ import org.sonar.batch.index.CachesTest; import static org.fest.assertions.Assertions.assertThat; -public class InputFileCacheTest { +public class InputPathCacheTest { @Rule public TemporaryFolder temp = new TemporaryFolder(); @@ -52,16 +52,16 @@ public class InputFileCacheTest { @Test public void should_add_input_file() throws Exception { - InputFileCache cache = new InputFileCache(caches); + InputPathCache cache = new InputPathCache(caches); DefaultInputFile fooFile = new DefaultInputFile("src/main/java/Foo.java").setFile(temp.newFile("Foo.java")); cache.put("struts", fooFile); cache.put("struts-core", new DeprecatedDefaultInputFile("src/main/java/Bar.java").setFile(temp.newFile("Bar.java"))); - assertThat(cache.get("struts", "src/main/java/Foo.java").relativePath()) + assertThat(cache.getFile("struts", "src/main/java/Foo.java").relativePath()) .isEqualTo("src/main/java/Foo.java"); - assertThat(cache.byModule("struts")).hasSize(1); - assertThat(cache.byModule("struts-core")).hasSize(1); + assertThat(cache.filesByModule("struts")).hasSize(1); + assertThat(cache.filesByModule("struts-core")).hasSize(1); assertThat(cache.all()).hasSize(2); for (InputFile inputFile : cache.all()) { assertThat(inputFile.relativePath()).startsWith("src/main/java/"); @@ -71,8 +71,8 @@ public class InputFileCacheTest { assertThat(cache.all()).hasSize(1); cache.removeModule("struts"); - assertThat(cache.byModule("struts")).hasSize(0); - assertThat(cache.byModule("struts-core")).hasSize(1); + assertThat(cache.filesByModule("struts")).hasSize(0); + assertThat(cache.filesByModule("struts-core")).hasSize(1); assertThat(cache.all()).hasSize(1); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java index 6f4f306a0c5..59092657927 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/report/JsonReportTest.java @@ -44,7 +44,7 @@ import org.sonar.api.user.UserFinder; import org.sonar.batch.bootstrap.AnalysisMode; import org.sonar.batch.events.EventBus; import org.sonar.batch.issue.IssueCache; -import org.sonar.batch.scan.filesystem.InputFileCache; +import org.sonar.batch.scan.filesystem.InputPathCache; import org.sonar.core.user.DefaultUser; import java.io.File; @@ -88,7 +88,7 @@ public class JsonReportTest { DeprecatedDefaultInputFile inputFile = new DeprecatedDefaultInputFile("src/main/java/org/apache/struts/Action.java"); inputFile.setKey("struts:src/main/java/org/apache/struts/Action.java"); inputFile.setStatus(InputFile.Status.CHANGED); - InputFileCache fileCache = mock(InputFileCache.class); + InputPathCache fileCache = mock(InputPathCache.class); when(fileCache.all()).thenReturn(Arrays.asList(inputFile)); Project rootModule = new Project("struts"); Project moduleA = new Project("struts-core"); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FileSystem.java index 56fdc0320f5..0c285f686e2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FileSystem.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/FileSystem.java @@ -95,6 +95,16 @@ public interface FileSystem extends BatchComponent { @CheckForNull InputFile inputFile(FilePredicate predicate); + /** + * Returns {@link InputDir} matching the current {@link File}. + * @return null if directory is not indexed. + * @throw {@link IllegalArgumentException} is File is null or not a directory. + * + * @since 4.5 + */ + @CheckForNull + InputDir inputDir(File dir); + /** * Input files matching the given attributes. Return all the files if the parameter * attributes is empty. diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputDir.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputDir.java new file mode 100644 index 00000000000..d120c609e4e --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputDir.java @@ -0,0 +1,61 @@ +/* + * 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.fs; + +import java.io.File; + +/** + * Layer over {@link java.io.File} for directories. + * + * @since 4.5 + */ +public interface InputDir extends InputPath { + + /** + * Path relative to module base directory. Path is unique and identifies directory + * within given {@link FileSystem}. File separator is the forward + * slash ('/'), even on Microsoft Windows. + *

+ * Returns src/main/java/com if module base dir is + * /path/to/module and if directory is + * /path/to/module/src/main/java/com. + *

+ * Relative path is not null and is normalized ('foo/../foo' is replaced by 'foo'). + */ + @Override + String relativePath(); + + /** + * Normalized absolute path. File separator is forward slash ('/'), even on Microsoft Windows. + *

+ * This is not canonical path. Symbolic links are not resolved. For example if /project/src links + * to /tmp/src and basedir is /project, then this method returns /project/src. Use + * {@code file().getCanonicalPath()} to resolve symbolic link. + */ + @Override + String absolutePath(); + + /** + * The underlying absolute {@link java.io.File} + */ + @Override + File file(); + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java index 83e9697f15f..32ea0e3cf2e 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputFile.java @@ -20,14 +20,13 @@ package org.sonar.api.batch.fs; import java.io.File; -import java.io.Serializable; /** * This layer over {@link java.io.File} adds information for code analyzers. * * @since 4.2 */ -public interface InputFile extends Serializable { +public interface InputFile extends InputPath { enum Type { MAIN, TEST @@ -51,6 +50,7 @@ public interface InputFile extends Serializable { *

* Relative path is not null and is normalized ('foo/../foo' is replaced by 'foo'). */ + @Override String relativePath(); /** @@ -60,11 +60,13 @@ public interface InputFile extends Serializable { * to /tmp/src and basedir is /project, then this method returns /project/src/index.php. Use * {@code file().getCanonicalPath()} to resolve symbolic link. */ + @Override String absolutePath(); /** * The underlying absolute {@link java.io.File} */ + @Override File file(); /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java new file mode 100644 index 00000000000..6a3ac3a0bf2 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java @@ -0,0 +1,50 @@ +/* + * 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.fs; + +import java.io.File; +import java.io.Serializable; + +/** + * Layer over {@link java.io.File} for files or directories. + * + * @since 4.5 + */ +public interface InputPath extends Serializable { + + /** + * @see InputFile#relativePath() + * @see InputDir#relativePath() + */ + String relativePath(); + + /** + * @see InputFile#absolutePath() + * @see InputDir#absolutePath() + */ + String absolutePath(); + + /** + * @see InputFile#file() + * @see InputDir#file() + */ + File file(); + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java index 2002c6feb1f..7b79d948d10 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultFileSystem.java @@ -19,10 +19,13 @@ */ package org.sonar.api.batch.fs.internal; +import org.sonar.api.utils.PathUtils; + import com.google.common.base.Preconditions; import org.sonar.api.batch.fs.FilePredicate; import org.sonar.api.batch.fs.FilePredicates; import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; import javax.annotation.CheckForNull; @@ -155,6 +158,12 @@ public class DefaultFileSystem implements FileSystem { return result; } + @Override + public InputDir inputDir(File dir) { + doPreloadFiles(); + return cache.inputDir(PathUtils.sanitize(new RelativeP)); + } + public static Collection filter(Iterable target, FilePredicate predicate) { Collection result = new ArrayList(); for (InputFile element : target) { @@ -210,6 +219,9 @@ public class DefaultFileSystem implements FileSystem { @CheckForNull protected abstract InputFile inputFile(RelativePathPredicate predicate); + @CheckForNull + protected abstract InputDir inputDir(String relativePath); + protected abstract void doAdd(InputFile inputFile); final void add(InputFile inputFile) { @@ -222,6 +234,7 @@ public class DefaultFileSystem implements FileSystem { */ private static class MapCache extends Cache { private final Map fileMap = new HashMap(); + private final Map dirMap = new HashMap(); @Override public Iterable inputFiles() { @@ -233,6 +246,11 @@ public class DefaultFileSystem implements FileSystem { return fileMap.get(predicate.path()); } + @Override + protected InputDir inputDir(String relativePath) { + return dirMap.get(relativePath); + } + @Override protected void doAdd(InputFile inputFile) { fileMap.put(inputFile.relativePath(), inputFile); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java new file mode 100644 index 00000000000..26fc37a0030 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java @@ -0,0 +1,112 @@ +/* + * 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.fs.internal; + +import org.sonar.api.batch.fs.InputDir; +import org.sonar.api.utils.PathUtils; + +import javax.annotation.CheckForNull; + +import java.io.File; +import java.io.Serializable; + +/** + * @since 4.5 + */ +public class DefaultInputDir implements InputDir, Serializable { + + private final String relativePath; + private String absolutePath; + private String key; + + public DefaultInputDir(String relativePath) { + this.relativePath = PathUtils.sanitize(relativePath); + } + + @Override + public String relativePath() { + return relativePath; + } + + /** + * Marked as nullable just for the unit tests that do not call {@link #setFile(java.io.File)} + * previously. + */ + @Override + @CheckForNull + public String absolutePath() { + return absolutePath; + } + + @Override + public File file() { + if (absolutePath == null) { + throw new IllegalStateException("Can not return the java.io.File because absolute path is not set (see method setFile(java.io.File))"); + } + return new File(absolutePath); + } + + /** + * Component key. It's marked as nullable just for the unit tests that + * do not previously call {@link #setKey(String)}. + */ + @CheckForNull + public String key() { + return key; + } + + public DefaultInputDir setAbsolutePath(String s) { + this.absolutePath = PathUtils.sanitize(s); + return this; + } + + public DefaultInputDir setFile(File file) { + setAbsolutePath(file.getAbsolutePath()); + return this; + } + + public DefaultInputDir setKey(String s) { + this.key = s; + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof DefaultInputDir)) { + return false; + } + + DefaultInputDir that = (DefaultInputDir) o; + return relativePath.equals(that.relativePath); + } + + @Override + public int hashCode() { + return relativePath.hashCode(); + } + + @Override + public String toString() { + return "[relative=" + relativePath + ", abs=" + absolutePath + "]"; + } +} -- 2.39.5