aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java242
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java228
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DeprecatedFileSystemAdapter.java148
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFileFilter.java47
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileFilterContext.java63
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InclusionFileFilter.java58
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageFileFilters.java42
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProvider.java166
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PathResolver.java74
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/WhiteListFileFilter.java42
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/package-info.java27
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java187
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFileFilterTest.java71
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InclusionFileFilterTest.java76
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageFileFiltersTest.java75
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProviderTest.java51
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/PathResolverTest.java88
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/WhiteListFileFilterTest.java54
-rw-r--r--sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/.dirPrefixedWithDot/Excluded.java1
-rw-r--r--sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/.sonar/Excluded2.java1
-rw-r--r--sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/sonar/Included.java3
-rw-r--r--sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/main/java/Foo.java1
-rw-r--r--sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/main/java/Hello.java1
-rw-r--r--sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/test/java/FooTest.java1
-rw-r--r--sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/test/java/HelloTest.java1
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/FileFilter.java8
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultProjectFileSystem.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FailToCreateFileException.java32
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileFilter.java44
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileSystemException.java36
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/IllegalPathException.java37
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/JavaIoFileFilter.java44
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/ModuleFileSystem.java44
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/package-info.java27
35 files changed, 1907 insertions, 117 deletions
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
index 62a8807c33b..44dea680560 100644
--- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
@@ -190,6 +190,22 @@ import java.util.List;
category = CoreProperties.CATEGORY_EXCLUSIONS,
defaultValue = CoreProperties.GLOBAL_TEST_EXCLUSIONS_DEFAULT),
@Property(
+ key = CoreProperties.PROJECT_INCLUSIONS_PROPERTY,
+ name = "Inclusions",
+ description = "Define the file sources to analyze. Changes will be applied during next code analysis.",
+ project = true,
+ global = true,
+ multiValues = true,
+ category = CoreProperties.CATEGORY_EXCLUSIONS),
+ @Property(
+ key = CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY,
+ name = "Test Inclusions",
+ description = "Define the test files to analyze. Changes will be applied during next code analysis.",
+ project = true,
+ global = true,
+ multiValues = true,
+ category = CoreProperties.CATEGORY_EXCLUSIONS),
+ @Property(
key = CoreProperties.PROJECT_EXCLUSIONS_PROPERTY,
name = "Exclusions",
description = "Exclude sources from code analysis. Changes will be applied during next code analysis.",
@@ -395,129 +411,129 @@ public final class CorePlugin extends SonarPlugin {
@SuppressWarnings("unchecked")
public List getExtensions() {
return ImmutableList.of(
- DefaultResourceTypes.class,
- UserManagedMetrics.class,
- ProjectFileSystemLogger.class,
- Periods.class,
+ DefaultResourceTypes.class,
+ UserManagedMetrics.class,
+ ProjectFileSystemLogger.class,
+ Periods.class,
- // maven
- MavenInitializer.class,
+ // maven
+ MavenInitializer.class,
- // languages
- Java.class,
+ // languages
+ Java.class,
- // pages
- Lcom4Viewer.class,
- TestsViewer.class,
+ // pages
+ Lcom4Viewer.class,
+ TestsViewer.class,
- // measure filters
- ProjectFilter.class,
- MyFavouritesFilter.class,
+ // measure filters
+ ProjectFilter.class,
+ MyFavouritesFilter.class,
- // widgets
- AlertsWidget.class,
- CoverageWidget.class,
- ItCoverageWidget.class,
- CommentsDuplicationsWidget.class,
- DescriptionWidget.class,
- ComplexityWidget.class,
- RulesWidget.class,
- SizeWidget.class,
- EventsWidget.class,
- CustomMeasuresWidget.class,
- TimelineWidget.class,
- TimeMachineWidget.class,
- HotspotMetricWidget.class,
- HotspotMostViolatedResourcesWidget.class,
- HotspotMostViolatedRulesWidget.class,
- MyReviewsWidget.class,
- ProjectReviewsWidget.class,
- FalsePositiveReviewsWidget.class,
- ReviewsPerDeveloperWidget.class,
- PlannedReviewsWidget.class,
- UnplannedReviewsWidget.class,
- ActionPlansWidget.class,
- ReviewsMetricsWidget.class,
- TreemapWidget.class,
- MeasureFilterListWidget.class,
- MeasureFilterTreemapWidget.class,
- WelcomeWidget.class,
+ // widgets
+ AlertsWidget.class,
+ CoverageWidget.class,
+ ItCoverageWidget.class,
+ CommentsDuplicationsWidget.class,
+ DescriptionWidget.class,
+ ComplexityWidget.class,
+ RulesWidget.class,
+ SizeWidget.class,
+ EventsWidget.class,
+ CustomMeasuresWidget.class,
+ TimelineWidget.class,
+ TimeMachineWidget.class,
+ HotspotMetricWidget.class,
+ HotspotMostViolatedResourcesWidget.class,
+ HotspotMostViolatedRulesWidget.class,
+ MyReviewsWidget.class,
+ ProjectReviewsWidget.class,
+ FalsePositiveReviewsWidget.class,
+ ReviewsPerDeveloperWidget.class,
+ PlannedReviewsWidget.class,
+ UnplannedReviewsWidget.class,
+ ActionPlansWidget.class,
+ ReviewsMetricsWidget.class,
+ TreemapWidget.class,
+ MeasureFilterListWidget.class,
+ MeasureFilterTreemapWidget.class,
+ WelcomeWidget.class,
- // dashboards
- ProjectDefaultDashboard.class,
- ProjectHotspotDashboard.class,
- ProjectReviewsDashboard.class,
- ProjectTimeMachineDashboard.class,
- GlobalDefaultDashboard.class,
+ // dashboards
+ ProjectDefaultDashboard.class,
+ ProjectHotspotDashboard.class,
+ ProjectReviewsDashboard.class,
+ ProjectTimeMachineDashboard.class,
+ GlobalDefaultDashboard.class,
- // chart
- XradarChart.class,
- DistributionBarChart.class,
- DistributionAreaChart.class,
+ // chart
+ XradarChart.class,
+ DistributionBarChart.class,
+ DistributionAreaChart.class,
- // colorizers
- JavaColorizerFormat.class,
+ // colorizers
+ JavaColorizerFormat.class,
- // batch
- ProfileSensor.class,
- ProfileEventsSensor.class,
- ProjectLinksSensor.class,
- UnitTestDecorator.class,
- VersionEventsSensor.class,
- CheckAlertThresholds.class,
- GenerateAlertEvents.class,
- ViolationsDecorator.class,
- WeightedViolationsDecorator.class,
- ViolationsDensityDecorator.class,
- LineCoverageDecorator.class,
- CoverageDecorator.class,
- BranchCoverageDecorator.class,
- ItLineCoverageDecorator.class,
- ItCoverageDecorator.class,
- ItBranchCoverageDecorator.class,
- OverallLineCoverageDecorator.class,
- OverallCoverageDecorator.class,
- OverallBranchCoverageDecorator.class,
- ApplyProjectRolesDecorator.class,
- ExcludedResourceFilter.class,
- CommentDensityDecorator.class,
- NoSonarFilter.class,
- DirectoriesDecorator.class,
- FilesDecorator.class,
- ReviewNotifications.class,
- ReviewWorkflowDecorator.class,
- ReferenceAnalysis.class,
- ManualMeasureDecorator.class,
- ManualViolationInjector.class,
- ViolationSeverityUpdater.class,
- IndexProjectPostJob.class,
- ReviewsMeasuresDecorator.class,
+ // batch
+ ProfileSensor.class,
+ ProfileEventsSensor.class,
+ ProjectLinksSensor.class,
+ UnitTestDecorator.class,
+ VersionEventsSensor.class,
+ CheckAlertThresholds.class,
+ GenerateAlertEvents.class,
+ ViolationsDecorator.class,
+ WeightedViolationsDecorator.class,
+ ViolationsDensityDecorator.class,
+ LineCoverageDecorator.class,
+ CoverageDecorator.class,
+ BranchCoverageDecorator.class,
+ ItLineCoverageDecorator.class,
+ ItCoverageDecorator.class,
+ ItBranchCoverageDecorator.class,
+ OverallLineCoverageDecorator.class,
+ OverallCoverageDecorator.class,
+ OverallBranchCoverageDecorator.class,
+ ApplyProjectRolesDecorator.class,
+ ExcludedResourceFilter.class,
+ CommentDensityDecorator.class,
+ NoSonarFilter.class,
+ DirectoriesDecorator.class,
+ FilesDecorator.class,
+ ReviewNotifications.class,
+ ReviewWorkflowDecorator.class,
+ ReferenceAnalysis.class,
+ ManualMeasureDecorator.class,
+ ManualViolationInjector.class,
+ ViolationSeverityUpdater.class,
+ IndexProjectPostJob.class,
+ ReviewsMeasuresDecorator.class,
- // time machine
- TendencyDecorator.class,
- VariationDecorator.class,
- ViolationTrackingDecorator.class,
- ViolationPersisterDecorator.class,
- NewViolationsDecorator.class,
- TimeMachineConfigurationPersister.class,
- NewCoverageFileAnalyzer.class,
- NewItCoverageFileAnalyzer.class,
- NewOverallCoverageFileAnalyzer.class,
- NewCoverageAggregator.class,
+ // time machine
+ TendencyDecorator.class,
+ VariationDecorator.class,
+ ViolationTrackingDecorator.class,
+ ViolationPersisterDecorator.class,
+ NewViolationsDecorator.class,
+ TimeMachineConfigurationPersister.class,
+ NewCoverageFileAnalyzer.class,
+ NewItCoverageFileAnalyzer.class,
+ NewOverallCoverageFileAnalyzer.class,
+ NewCoverageAggregator.class,
- // notifications
- // Notify incoming violations on my favourite projects
- NewViolationsOnMyFavouriteProject.class,
- NotificationDispatcherMetadata.create("NewViolationsOnMyFavouriteProject")
- .setProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION, "true"),
- // Notify alerts on my favourite projects
- AlertsOnMyFavouriteProject.class,
- NotificationDispatcherMetadata.create("AlertsOnMyFavouriteProject")
- .setProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION, "true"),
- // Notify reviews changes
- ChangesInReviewAssignedToMeOrCreatedByMe.class,
- NotificationDispatcherMetadata.create("ChangesInReviewAssignedToMeOrCreatedByMe")
- .setProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION, "true")
- .setProperty(NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION, "true"));
+ // notifications
+ // Notify incoming violations on my favourite projects
+ NewViolationsOnMyFavouriteProject.class,
+ NotificationDispatcherMetadata.create("NewViolationsOnMyFavouriteProject")
+ .setProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION, "true"),
+ // Notify alerts on my favourite projects
+ AlertsOnMyFavouriteProject.class,
+ NotificationDispatcherMetadata.create("AlertsOnMyFavouriteProject")
+ .setProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION, "true"),
+ // Notify reviews changes
+ ChangesInReviewAssignedToMeOrCreatedByMe.class,
+ NotificationDispatcherMetadata.create("ChangesInReviewAssignedToMeOrCreatedByMe")
+ .setProperty(NotificationDispatcherMetadata.GLOBAL_NOTIFICATION, "true")
+ .setProperty(NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION, "true"));
}
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java
new file mode 100644
index 00000000000..10733249b6a
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystem.java
@@ -0,0 +1,228 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.apache.commons.io.filefilter.HiddenFileFilter;
+import org.apache.commons.io.filefilter.IOFileFilter;
+import org.apache.commons.io.filefilter.TrueFileFilter;
+import org.sonar.api.scan.filesystem.FileFilter;
+import org.sonar.api.scan.filesystem.ModuleFileSystem;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class can't be immutable because of execution of maven plugins that can change the project structure (see MavenPluginHandler and sonar.phase)
+ *
+ * @since 3.5
+ */
+public class DefaultModuleFileSystem implements ModuleFileSystem {
+
+ private static final IOFileFilter DIR_FILTER = FileFilterUtils.and(HiddenFileFilter.VISIBLE, FileFilterUtils.notFileFilter(FileFilterUtils.prefixFileFilter(".")));
+
+ private final Charset sourceCharset;
+ private File baseDir, workingDir;
+ private List<File> sourceDirs, testDirs, binaryDirs;
+ private final PathResolver pathResolver;
+ private final List<FileFilter> fileFilters;
+ private final LanguageFileFilters languageFileFilters;
+
+ private DefaultModuleFileSystem(Builder builder) {
+ sourceCharset = builder.sourceCharset;
+ baseDir = builder.baseDir;
+ workingDir = builder.workingDir;
+ sourceDirs = ImmutableList.copyOf(builder.sourceDirs);
+ testDirs = ImmutableList.copyOf(builder.testDirs);
+ binaryDirs = ImmutableList.copyOf(builder.binaryDirs);
+ fileFilters = ImmutableList.copyOf(builder.fileFilters);
+ pathResolver = builder.pathResolver;
+ languageFileFilters = builder.languageFileFilters;
+ }
+
+ public File baseDir() {
+ return baseDir;
+ }
+
+ public List<File> sourceDirs() {
+ return sourceDirs;
+ }
+
+ public List<File> sourceFiles() {
+ return files(sourceDirs, FileFilter.FileType.SOURCE, TrueFileFilter.TRUE);
+ }
+
+ public List<File> sourceFilesOfLang(String language) {
+ return files(sourceDirs, FileFilter.FileType.SOURCE, languageFileFilters.forLang(language));
+ }
+
+ public List<File> testDirs() {
+ return testDirs;
+ }
+
+ public List<File> testFiles() {
+ return files(testDirs, FileFilter.FileType.TEST, TrueFileFilter.TRUE);
+ }
+
+ public List<File> testFilesOfLang(String language) {
+ return files(testDirs, FileFilter.FileType.TEST, languageFileFilters.forLang(language));
+ }
+
+ public List<File> binaryDirs() {
+ return binaryDirs;
+ }
+
+ public Charset sourceCharset() {
+ return sourceCharset;
+ }
+
+ public File workingDir() {
+ return workingDir;
+ }
+
+ PathResolver pathResolver() {
+ return pathResolver;
+ }
+
+ List<FileFilter> fileFilters() {
+ return fileFilters;
+ }
+
+ LanguageFileFilters languageFileFilters() {
+ return languageFileFilters;
+ }
+
+ /**
+ * Breaks immutability but it's required to allow Maven Plugins to be executed and to change project structure.
+ */
+ public void resetDirs(File basedir, File workDir, List<File> sourceDirs, List<File> testDirs, List<File> binaryDirs) {
+ this.baseDir = basedir;
+ this.workingDir = workDir;
+ this.sourceDirs = ImmutableList.copyOf(sourceDirs);
+ this.testDirs = ImmutableList.copyOf(testDirs);
+ this.binaryDirs = ImmutableList.copyOf(binaryDirs);
+ }
+
+ private List<File> files(List<File> dirs, FileFilter.FileType fileType, IOFileFilter languageFilter) {
+ List<File> result = Lists.newLinkedList();
+ if (dirs != null && !dirs.isEmpty()) {
+ FileFilterContext context = new FileFilterContext(this, fileType);
+ for (File dir : dirs) {
+ if (dir.exists()) {
+ context.setSourceDir(dir);
+ Collection<File> files = FileUtils.listFiles(dir, FileFilterUtils.and(HiddenFileFilter.VISIBLE, languageFilter), DIR_FILTER);
+ applyFilters(files, context);
+ result.addAll(files);
+ }
+ }
+ }
+ return result;
+ }
+
+ private void applyFilters(Collection<File> files, FileFilterContext context) {
+ if (!fileFilters.isEmpty()) {
+ Iterator<File> it = files.iterator();
+ while (it.hasNext()) {
+ File file = it.next();
+ if (!accept(file, context)) {
+ it.remove();
+ }
+ }
+ }
+ }
+
+ private boolean accept(File file, FileFilterContext context) {
+ context.setFileRelativePath(pathResolver.relativePath(context.sourceDir(), file));
+ for (FileFilter fileFilter : fileFilters) {
+ if (!fileFilter.accept(file, context)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ static final class Builder {
+ private Charset sourceCharset;
+ private File baseDir, workingDir;
+ private List<File> sourceDirs = Lists.newArrayList(), testDirs = Lists.newArrayList(), binaryDirs = Lists.newArrayList();
+ private List<FileFilter> fileFilters = Lists.newArrayList();
+ private PathResolver pathResolver;
+ LanguageFileFilters languageFileFilters;
+
+ Builder sourceCharset(Charset c) {
+ this.sourceCharset = c;
+ return this;
+ }
+
+ Builder baseDir(File f) {
+ this.baseDir = f;
+ return this;
+ }
+
+ Builder workingDir(File f) {
+ this.workingDir = f;
+ return this;
+ }
+
+ Builder addSourceDir(File d) {
+ sourceDirs.add(d);
+ return this;
+ }
+
+ Builder addTestDir(File d) {
+ testDirs.add(d);
+ return this;
+ }
+
+ Builder addBinaryDir(File d) {
+ binaryDirs.add(d);
+ return this;
+ }
+
+ Builder addFileFilter(FileFilter f) {
+ fileFilters.add(f);
+ return this;
+ }
+
+ Builder pathResolver(PathResolver r) {
+ pathResolver = r;
+ return this;
+ }
+
+ Builder languageFileFilters(LanguageFileFilters l) {
+ languageFileFilters = l;
+ return this;
+ }
+
+ DefaultModuleFileSystem build() {
+ Preconditions.checkNotNull(baseDir, "Module base directory is not set");
+ Preconditions.checkNotNull(workingDir, "Module working directory is not set");
+ Preconditions.checkNotNull(sourceCharset, "Module source charset is not set");
+ return new DefaultModuleFileSystem(this);
+ }
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DeprecatedFileSystemAdapter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DeprecatedFileSystemAdapter.java
new file mode 100644
index 00000000000..a0a8120951b
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/DeprecatedFileSystemAdapter.java
@@ -0,0 +1,148 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.CharEncoding;
+import org.sonar.api.resources.InputFile;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Language;
+import org.sonar.api.resources.ProjectFileSystem;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.scan.filesystem.ModuleFileSystem;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.List;
+
+public class DeprecatedFileSystemAdapter implements ProjectFileSystem {
+
+ private final ModuleFileSystem target;
+ private final PathResolver pathResolver;
+
+ public DeprecatedFileSystemAdapter(ModuleFileSystem target, PathResolver pathResolver) {
+ this.target = target;
+ this.pathResolver = pathResolver;
+ }
+
+ public Charset getSourceCharset() {
+ return target.sourceCharset();
+ }
+
+ public File getBasedir() {
+ return target.baseDir();
+ }
+
+ public File getBuildDir() {
+ // TODO
+ return null;
+ }
+
+ public File getBuildOutputDir() {
+ return Iterables.getFirst(target.binaryDirs(), null);
+ }
+
+ public List<File> getSourceDirs() {
+ return target.sourceDirs();
+ }
+
+ public ProjectFileSystem addSourceDir(File dir) {
+ throw new UnsupportedOperationException("File system is immutable");
+ }
+
+ public List<File> getTestDirs() {
+ return target.testDirs();
+ }
+
+ public ProjectFileSystem addTestDir(File dir) {
+ throw new UnsupportedOperationException("File system is immutable");
+ }
+
+ public File getReportOutputDir() {
+ // TODO
+ return null;
+ }
+
+ public File getSonarWorkingDirectory() {
+ return target.workingDir();
+ }
+
+ public File resolvePath(String path) {
+ // TODO
+ return null;
+ }
+
+ public List<File> getSourceFiles(Language... langs) {
+ List<File> result = Lists.newArrayList();
+ for (Language lang : langs) {
+ result.addAll(target.sourceFilesOfLang(lang.getKey()));
+ }
+ return result;
+ }
+
+ public List<File> getJavaSourceFiles() {
+ return getSourceFiles(Java.INSTANCE);
+ }
+
+ public boolean hasJavaSourceFiles() {
+ return !getJavaSourceFiles().isEmpty();
+ }
+
+ public List<File> getTestFiles(Language... langs) {
+ List<File> result = Lists.newArrayList();
+ for (Language lang : langs) {
+ result.addAll(target.testFilesOfLang(lang.getKey()));
+ }
+ return result;
+ }
+
+ public boolean hasTestFiles(Language lang) {
+ return !getTestFiles(lang).isEmpty();
+ }
+
+ public File writeToWorkingDirectory(String content, String fileName) throws IOException {
+ File file = new File(target.workingDir(), fileName);
+ FileUtils.writeStringToFile(file, content, CharEncoding.UTF_8);
+ return file;
+ }
+
+ public File getFileFromBuildDirectory(String filename) {
+ File file = new File(getBuildDir(), filename);
+ return (file.exists() ? file : null);
+ }
+
+ public Resource toResource(File file) {
+ // TODO
+ return null;
+ }
+
+ public List<InputFile> mainFiles(String... langs) {
+ // TODO
+ return null;
+ }
+
+ public List<InputFile> testFiles(String... langs) {
+ // TODO
+ return null;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFileFilter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFileFilter.java
new file mode 100644
index 00000000000..8f1847e7af1
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ExclusionFileFilter.java
@@ -0,0 +1,47 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.scan.filesystem.FileFilter;
+import org.sonar.api.utils.WildcardPattern;
+
+import java.io.File;
+
+/**
+ * @since 3.5
+ */
+class ExclusionFileFilter implements FileFilter {
+ private final FileType fileType;
+ private final WildcardPattern pattern;
+
+ ExclusionFileFilter(FileType fileType, String pattern) {
+ this.fileType = fileType;
+ this.pattern = WildcardPattern.create(StringUtils.trim(pattern));
+ }
+
+ public boolean accept(File file, Context context) {
+ return !fileType.equals(context.fileType()) || !pattern.match(context.fileRelativePath());
+ }
+
+ WildcardPattern pattern() {
+ return pattern;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileFilterContext.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileFilterContext.java
new file mode 100644
index 00000000000..937eb6e0421
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileFilterContext.java
@@ -0,0 +1,63 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.sonar.api.batch.FileFilter;
+import org.sonar.api.scan.filesystem.ModuleFileSystem;
+
+import java.io.File;
+
+class FileFilterContext implements FileFilter.Context {
+ private final ModuleFileSystem fileSystem;
+ private final FileFilter.FileType fileType;
+ private File sourceDir;
+ private String fileRelativePath;
+
+ FileFilterContext(ModuleFileSystem fileSystem, FileFilter.FileType fileType) {
+ this.fileSystem = fileSystem;
+ this.fileType = fileType;
+ }
+
+ public ModuleFileSystem fileSystem() {
+ return fileSystem;
+ }
+
+ public FileFilter.FileType fileType() {
+ return fileType;
+ }
+
+ public File sourceDir() {
+ return sourceDir;
+ }
+
+ public String fileRelativePath() {
+ return fileRelativePath;
+ }
+
+ FileFilterContext setSourceDir(File sourceDir) {
+ this.sourceDir = sourceDir;
+ return this;
+ }
+
+ FileFilterContext setFileRelativePath(String fileRelativePath) {
+ this.fileRelativePath = fileRelativePath;
+ return this;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InclusionFileFilter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InclusionFileFilter.java
new file mode 100644
index 00000000000..6220b6451d8
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/InclusionFileFilter.java
@@ -0,0 +1,58 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.scan.filesystem.FileFilter;
+import org.sonar.api.utils.WildcardPattern;
+
+import javax.annotation.CheckForNull;
+
+import java.io.File;
+
+/**
+ * @since 3.5
+ */
+class InclusionFileFilter implements FileFilter {
+ private final FileType fileType;
+ private final WildcardPattern pattern;
+
+ private InclusionFileFilter(FileType fileType, String pattern) {
+ this.fileType = fileType;
+ this.pattern = WildcardPattern.create(pattern);
+ }
+
+ public boolean accept(File file, Context context) {
+ return !fileType.equals(context.fileType()) || pattern.match(context.fileRelativePath());
+ }
+
+ @CheckForNull
+ static InclusionFileFilter create(FileType fileType, String pattern) {
+ String trimmedPattern = StringUtils.trim(pattern);
+ if (!"".equals(trimmedPattern) && !"**/*".equals(trimmedPattern)) {
+ return new InclusionFileFilter(fileType, trimmedPattern);
+ }
+ return null;
+ }
+
+ WildcardPattern pattern() {
+ return pattern;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageFileFilters.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageFileFilters.java
new file mode 100644
index 00000000000..f5cc8aacfa7
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageFileFilters.java
@@ -0,0 +1,42 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.apache.commons.io.IOCase;
+import org.apache.commons.io.filefilter.IOFileFilter;
+import org.apache.commons.io.filefilter.SuffixFileFilter;
+import org.apache.commons.io.filefilter.TrueFileFilter;
+import org.sonar.api.resources.Languages;
+
+public class LanguageFileFilters {
+ private final Languages languages;
+
+ public LanguageFileFilters(Languages languages) {
+ this.languages = languages;
+ }
+
+ public IOFileFilter forLang(String lang) {
+ String[] suffixes = languages.getSuffixes(lang);
+ if (suffixes != null && suffixes.length>0) {
+ return new SuffixFileFilter(suffixes, IOCase.SENSITIVE);
+ }
+ return TrueFileFilter.TRUE;
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProvider.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProvider.java
new file mode 100644
index 00000000000..4af31fc29be
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProvider.java
@@ -0,0 +1,166 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.picocontainer.injectors.ProviderAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.config.Settings;
+import org.sonar.api.scan.filesystem.FailToCreateFileException;
+import org.sonar.api.scan.filesystem.FileFilter;
+import org.sonar.api.scan.filesystem.ModuleFileSystem;
+import org.sonar.batch.bootstrap.TempDirectories;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * @since 3.5
+ */
+public class ModuleFileSystemProvider extends ProviderAdapter {
+ private static final Logger LOG = LoggerFactory.getLogger(ModuleFileSystemProvider.class);
+
+ private ModuleFileSystem singleton;
+
+ public ModuleFileSystem provide(ProjectDefinition module, PathResolver pathResolver, TempDirectories tempDirectories,
+ LanguageFileFilters languageFileFilters, Settings settings, FileFilter[] pluginFileFilters) {
+ if (singleton == null) {
+ DefaultModuleFileSystem.Builder builder = new DefaultModuleFileSystem.Builder();
+
+ // dependencies
+ builder.pathResolver(pathResolver);
+ builder.languageFileFilters(languageFileFilters);
+
+ // files and directories
+ // TODO should the basedir always exist ? If yes, then we check also check that it's a dir but not a file
+ builder.baseDir(module.getBaseDir());
+ builder.sourceCharset(guessCharset(settings));
+ builder.workingDir(guessWorkingDir(module, tempDirectories));
+ initBinaryDirs(module, pathResolver, builder);
+ initSources(module, pathResolver, builder);
+ initTests(module, pathResolver, builder);
+
+ // file filters
+ initPluginFilters(builder, pluginFileFilters);
+ initSourceInclusions(builder, settings);
+ initTestInclusions(builder, settings);
+
+ singleton = builder.build();
+ }
+ return singleton;
+ }
+
+ private File guessWorkingDir(ProjectDefinition module, TempDirectories tempDirectories) {
+ File workDir = module.getWorkDir();
+ if (workDir == null) {
+ workDir = tempDirectories.getDir("work");
+ LOG.warn("Working dir is not set. Using: " + workDir.getAbsolutePath());
+ } else {
+ LOG.warn("Working dir: " + workDir.getAbsolutePath());
+ try {
+ FileUtils.forceMkdir(workDir);
+ } catch (Exception e) {
+ throw new FailToCreateFileException("Fail to create working dir: " + workDir.getAbsolutePath(), e);
+ }
+ }
+ return workDir;
+ }
+
+ private Charset guessCharset(Settings settings) {
+ final Charset charset;
+ String encoding = settings.getString(CoreProperties.ENCODING_PROPERTY);
+ if (StringUtils.isNotEmpty(encoding)) {
+ charset = Charset.forName(StringUtils.trim(encoding));
+ LOG.info("Source encoding: " + charset.displayName() + ", default locale: " + Locale.getDefault());
+ } else {
+ charset = Charset.defaultCharset();
+ LOG.warn("Source encoding is platform dependent (" + charset.displayName() + "), default locale: " + Locale.getDefault());
+ }
+ return charset;
+ }
+
+ private void initSources(ProjectDefinition module, PathResolver pathResolver, DefaultModuleFileSystem.Builder builder) {
+ for (String sourcePath : module.getSourceDirs()) {
+ builder.addSourceDir(pathResolver.relativeFile(module.getBaseDir(), sourcePath));
+ }
+ List<File> sourceFiles = pathResolver.relativeFiles(module.getBaseDir(), module.getSourceFiles());
+ if (!sourceFiles.isEmpty()) {
+ builder.addFileFilter(new WhiteListFileFilter(FileFilter.FileType.SOURCE, ImmutableSet.copyOf(sourceFiles)));
+ }
+ }
+
+ private void initTests(ProjectDefinition module, PathResolver pathResolver, DefaultModuleFileSystem.Builder builder) {
+ for (String testPath : module.getTestDirs()) {
+ builder.addTestDir(pathResolver.relativeFile(module.getBaseDir(), testPath));
+ }
+ List<File> testFiles = pathResolver.relativeFiles(module.getBaseDir(), module.getTestFiles());
+ if (!testFiles.isEmpty()) {
+ builder.addFileFilter(new WhiteListFileFilter(FileFilter.FileType.TEST, ImmutableSet.copyOf(testFiles)));
+ }
+ }
+
+ private void initPluginFilters(DefaultModuleFileSystem.Builder builder, FileFilter[] pluginFileFilters) {
+ for (FileFilter pluginFileFilter : pluginFileFilters) {
+ builder.addFileFilter(pluginFileFilter);
+ }
+ }
+
+ private void initSourceInclusions(DefaultModuleFileSystem.Builder builder, Settings settings) {
+ initInclusions(builder, settings, FileFilter.FileType.SOURCE,
+ CoreProperties.PROJECT_INCLUSIONS_PROPERTY, CoreProperties.GLOBAL_EXCLUSIONS_PROPERTY, CoreProperties.PROJECT_EXCLUSIONS_PROPERTY);
+ }
+
+ private void initTestInclusions(DefaultModuleFileSystem.Builder builder, Settings settings) {
+ initInclusions(builder, settings, FileFilter.FileType.TEST,
+ CoreProperties.PROJECT_TEST_INCLUSIONS_PROPERTY, CoreProperties.GLOBAL_TEST_EXCLUSIONS_PROPERTY, CoreProperties.PROJECT_TEST_EXCLUSIONS_PROPERTY);
+ }
+
+ private static void initInclusions(DefaultModuleFileSystem.Builder builder, Settings settings, FileFilter.FileType fileType,
+ String inclusionsProperty, String globalExclusionsProperty, String exclusionsProperty) {
+ String[] inclusions = settings.getStringArray(inclusionsProperty);
+ for (String inclusion : inclusions) {
+ InclusionFileFilter filter = InclusionFileFilter.create(fileType, inclusion);
+ if (filter != null) {
+ builder.addFileFilter(filter);
+ }
+ }
+ String[] globalExclusions = settings.getStringArray(globalExclusionsProperty);
+ for (String globalExclusion : globalExclusions) {
+ builder.addFileFilter(new ExclusionFileFilter(fileType, globalExclusion));
+ }
+ String[] exclusions = settings.getStringArray(exclusionsProperty);
+ for (String exclusion : exclusions) {
+ builder.addFileFilter(new ExclusionFileFilter(fileType, exclusion));
+ }
+ }
+
+ private void initBinaryDirs(ProjectDefinition module, PathResolver pathResolver, DefaultModuleFileSystem.Builder builder) {
+ for (String path : module.getBinaries()) {
+ builder.addBinaryDir(pathResolver.relativeFile(module.getBaseDir(), path));
+ }
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PathResolver.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PathResolver.java
new file mode 100644
index 00000000000..357e5845f11
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/PathResolver.java
@@ -0,0 +1,74 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import org.apache.commons.io.FilenameUtils;
+import org.sonar.api.scan.filesystem.IllegalPathException;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @since 3.5
+ */
+public class PathResolver {
+
+ File relativeFile(File dir, String path) {
+ Preconditions.checkArgument(dir.isDirectory(), "Not a directory: " + dir.getAbsolutePath());
+ File file = new File(path);
+ if (!file.isAbsolute()) {
+ try {
+ file = new File(dir, path).getCanonicalFile();
+ } catch (Exception e) {
+ throw new IllegalPathException("Fail to resolve path '" + path + "' relative to: " + dir.getAbsolutePath());
+ }
+ }
+ return file;
+ }
+
+ List<File> relativeFiles(File dir, List<String> paths) {
+ List<File> result = Lists.newArrayList();
+ for (String path : paths) {
+ result.add(relativeFile(dir, path));
+ }
+ return result;
+ }
+
+ String relativePath(File dir, File file) {
+ List<String> stack = Lists.newArrayList();
+ String path = FilenameUtils.normalize(file.getAbsolutePath());
+ File cursor = new File(path);
+ while (cursor != null) {
+ if (containsFile(dir, cursor)) {
+ return Joiner.on("/").join(stack);
+ }
+ stack.add(0, cursor.getName());
+ cursor = cursor.getParentFile();
+ }
+ return null;
+ }
+
+ private boolean containsFile(File dir, File cursor) {
+ return FilenameUtils.equalsNormalizedOnSystem(dir.getAbsolutePath(), cursor.getAbsolutePath());
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/WhiteListFileFilter.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/WhiteListFileFilter.java
new file mode 100644
index 00000000000..1c8ffb3ca25
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/WhiteListFileFilter.java
@@ -0,0 +1,42 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.sonar.api.scan.filesystem.FileFilter;
+
+import java.io.File;
+import java.util.Set;
+
+/**
+ * @since 3.5
+ */
+class WhiteListFileFilter implements FileFilter {
+ private final FileFilter.FileType fileType;
+ private final Set<File> files;
+
+ WhiteListFileFilter(FileType fileType, Set<File> files) {
+ this.fileType = fileType;
+ this.files = files;
+ }
+
+ public boolean accept(File file, Context context) {
+ return !context.fileType().equals(fileType) || files.contains(file);
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/package-info.java
new file mode 100644
index 00000000000..c1e6578d28f
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+
+/**
+ * This package is a part of bootstrap process, so we should take care about backward compatibility.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.batch.scan.filesystem;
+
+import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java
new file mode 100644
index 00000000000..605d6932ff4
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/DefaultModuleFileSystemTest.java
@@ -0,0 +1,187 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.apache.commons.io.filefilter.FileFilterUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.resources.AbstractLanguage;
+import org.sonar.api.resources.Languages;
+import org.sonar.api.scan.filesystem.FileFilter;
+import org.sonar.api.scan.filesystem.JavaIoFileFilter;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class DefaultModuleFileSystemTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void test_builder() throws IOException {
+ File basedir = temp.newFolder("base");
+ File workingDir = temp.newFolder("work");
+ PathResolver pathResolver = mock(PathResolver.class);
+ LanguageFileFilters languageFileFilters = mock(LanguageFileFilters.class);
+ FileFilter fileFilter = mock(FileFilter.class);
+
+ DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem.Builder()
+ .baseDir(basedir)
+ .workingDir(workingDir)
+ .addBinaryDir(new File(basedir, "target/classes"))
+ .addSourceDir(new File(basedir, "src/main/java"))
+ .addSourceDir(new File(basedir, "src/main/groovy"))
+ .addTestDir(new File(basedir, "src/test/java"))
+ .addFileFilter(fileFilter)
+ .sourceCharset(StandardCharsets.UTF_8)
+ .pathResolver(pathResolver)
+ .languageFileFilters(languageFileFilters)
+ .build();
+
+ assertThat(fileSystem).isNotNull();
+ assertThat(fileSystem.baseDir().getCanonicalPath()).isEqualTo(basedir.getCanonicalPath());
+ assertThat(fileSystem.workingDir().getCanonicalPath()).isEqualTo(workingDir.getCanonicalPath());
+ assertThat(fileSystem.sourceDirs()).hasSize(2);
+ assertThat(fileSystem.testDirs()).hasSize(1);
+ assertThat(fileSystem.binaryDirs()).hasSize(1);
+ assertThat(fileSystem.sourceCharset().name()).isEqualTo("UTF-8");
+ assertThat(fileSystem.pathResolver()).isSameAs(pathResolver);
+ assertThat(fileSystem.fileFilters()).containsOnly(fileFilter);
+ assertThat(fileSystem.languageFileFilters()).isSameAs(languageFileFilters);
+ }
+
+ @Test
+ public void should_exclude_dirs_starting_with_dot() throws IOException {
+ File basedir = new File("test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot");
+ DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem.Builder()
+ .baseDir(basedir)
+ .sourceCharset(StandardCharsets.UTF_8)
+ .workingDir(temp.newFolder())
+ .addSourceDir(new File(basedir, "src"))
+ .build();
+
+ assertThat(fileSystem.sourceFiles()).hasSize(1);
+ assertThat(fileSystem.sourceFiles().get(0).getName()).isEqualTo("Included.java");
+ }
+
+ @Test
+ public void should_load_source_files_by_language() throws IOException {
+ File basedir = new File("test-resources/DefaultModuleFileSystemTest/main_and_test_files");
+ DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem.Builder()
+ .baseDir(basedir)
+ .sourceCharset(StandardCharsets.UTF_8)
+ .workingDir(temp.newFolder())
+ .addSourceDir(new File(basedir, "src/main/java"))
+ .addTestDir(new File(basedir, "src/test/java"))
+ .languageFileFilters(new LanguageFileFilters(new Languages(new Java(), new Php())))
+ .build();
+
+ List<File> files = fileSystem.sourceFilesOfLang("java");
+ assertThat(files).hasSize(2);
+ for (File sourceFiles : files) {
+ assertThat(sourceFiles).exists().isFile();
+ assertThat(sourceFiles.getName()).isIn("Hello.java", "Foo.java");
+ }
+ assertThat(fileSystem.sourceFilesOfLang("php")).isEmpty();
+ }
+
+ @Test
+ public void should_load_test_files() throws IOException {
+ File basedir = new File("test-resources/DefaultModuleFileSystemTest/main_and_test_files");
+ DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem.Builder()
+ .baseDir(basedir)
+ .sourceCharset(StandardCharsets.UTF_8)
+ .workingDir(temp.newFolder())
+ .addSourceDir(new File(basedir, "src/main/java"))
+ .addTestDir(new File(basedir, "src/test/java"))
+ .build();
+
+ assertThat(fileSystem.testDirs()).hasSize(1);
+ assertThat(fileSystem.testFiles()).hasSize(2);
+ for (File testFile : fileSystem.testFiles()) {
+ assertThat(testFile).exists().isFile();
+ assertThat(testFile.getName()).endsWith("Test.java");
+ }
+ }
+
+ @Test
+ public void should_load_test_files_by_language() throws IOException {
+ File basedir = new File("test-resources/DefaultModuleFileSystemTest/main_and_test_files");
+ DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem.Builder()
+ .baseDir(basedir)
+ .sourceCharset(StandardCharsets.UTF_8)
+ .workingDir(temp.newFolder())
+ .addSourceDir(new File(basedir, "src/main/java"))
+ .addTestDir(new File(basedir, "src/test/java"))
+ .languageFileFilters(new LanguageFileFilters(new Languages(new Java(), new Php())))
+ .build();
+
+ List<File> testFiles = fileSystem.testFilesOfLang("java");
+ assertThat(testFiles).hasSize(2);
+ for (File testFile : testFiles) {
+ assertThat(testFile).exists().isFile();
+ assertThat(testFile.getName()).endsWith("Test.java");
+ }
+ assertThat(fileSystem.testFilesOfLang("php")).isEmpty();
+ }
+
+ @Test
+ public void should_apply_file_filters() throws IOException {
+ File basedir = new File("test-resources/DefaultModuleFileSystemTest/main_and_test_files");
+ DefaultModuleFileSystem fileSystem = new DefaultModuleFileSystem.Builder()
+ .baseDir(basedir)
+ .sourceCharset(StandardCharsets.UTF_8)
+ .workingDir(temp.newFolder())
+ .addSourceDir(new File(basedir, "src/main/java"))
+ .addFileFilter(JavaIoFileFilter.create(FileFilterUtils.nameFileFilter("Foo.java")))
+ .pathResolver(new PathResolver())
+ .build();
+
+ List<File> files = fileSystem.sourceFiles();
+ assertThat(files).hasSize(1);
+ assertThat(files.get(0).getName()).isEqualTo("Foo.java");
+ }
+
+ static class Php extends AbstractLanguage {
+ public Php() {
+ super("php");
+ }
+
+ public String[] getFileSuffixes() {
+ return new String[]{"php"};
+ }
+ }
+
+ static class Java extends AbstractLanguage {
+ public Java() {
+ super("java");
+ }
+
+ public String[] getFileSuffixes() {
+ return new String[]{"java", "jav"};
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFileFilterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFileFilterTest.java
new file mode 100644
index 00000000000..def69f5182b
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ExclusionFileFilterTest.java
@@ -0,0 +1,71 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.scan.filesystem.FileFilter;
+import org.sonar.api.scan.filesystem.ModuleFileSystem;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class ExclusionFileFilterTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void should_accept() throws IOException {
+ ExclusionFileFilter filter = new ExclusionFileFilter(FileFilter.FileType.SOURCE, "**/*Dao.java");
+ File acceptedFile = temp.newFile("Foo.java");
+
+ FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class), FileFilter.FileType.SOURCE);
+ context.setFileRelativePath("com/mycompany/Foo.java");
+ assertThat(filter.accept(acceptedFile, context)).isTrue();
+
+ context = new FileFilterContext(mock(ModuleFileSystem.class), FileFilter.FileType.TEST);
+ context.setFileRelativePath("com/mycompany/Foo.java");
+ assertThat(filter.accept(acceptedFile, context)).isTrue();
+ }
+
+ @Test
+ public void should_exclude() throws IOException {
+ ExclusionFileFilter filter = new ExclusionFileFilter(FileFilter.FileType.SOURCE, "**/*Dao.java");
+ File excludedFile = temp.newFile("FooDao.java");
+
+ FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class), FileFilter.FileType.SOURCE);
+ context.setFileRelativePath("com/mycompany/FooDao.java");
+ assertThat(filter.accept(excludedFile, context)).isFalse();
+
+ context = new FileFilterContext(mock(ModuleFileSystem.class), FileFilter.FileType.TEST);
+ context.setFileRelativePath("com/mycompany/FooDao.java");
+ assertThat(filter.accept(excludedFile, context)).isTrue();
+ }
+
+ @Test
+ public void should_trim_pattern() throws IOException {
+ ExclusionFileFilter filter = new ExclusionFileFilter(FileFilter.FileType.SOURCE, " **/*Dao.java ");
+ assertThat(filter.pattern().toString()).isEqualTo("**/*Dao.java");
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InclusionFileFilterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InclusionFileFilterTest.java
new file mode 100644
index 00000000000..9bc1e729fbd
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/InclusionFileFilterTest.java
@@ -0,0 +1,76 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.scan.filesystem.FileFilter;
+import org.sonar.api.scan.filesystem.ModuleFileSystem;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class InclusionFileFilterTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void should_accept() throws IOException {
+ InclusionFileFilter filter = InclusionFileFilter.create(FileFilter.FileType.SOURCE, "**/*Dao.java");
+ File acceptedFile = temp.newFile("FooDao.java");
+
+ FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class), FileFilter.FileType.SOURCE);
+ context.setFileRelativePath("com/mycompany/FooDao.java");
+ assertThat(filter.accept(acceptedFile, context)).isTrue();
+
+ context = new FileFilterContext(mock(ModuleFileSystem.class), FileFilter.FileType.TEST);
+ context.setFileRelativePath("com/mycompany/Foo.java");
+ assertThat(filter.accept(acceptedFile, context)).isTrue();
+ }
+
+ @Test
+ public void should_exclude() throws IOException {
+ InclusionFileFilter filter = InclusionFileFilter.create(FileFilter.FileType.SOURCE, "**/*Dao.java");
+ File excludedFile = temp.newFile("Foo.java");
+
+ FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class), FileFilter.FileType.SOURCE);
+ context.setFileRelativePath("com/mycompany/Foo.java");
+ assertThat(filter.accept(excludedFile, context)).isFalse();
+
+ context = new FileFilterContext(mock(ModuleFileSystem.class), FileFilter.FileType.TEST);
+ context.setFileRelativePath("com/mycompany/Foo.java");
+ assertThat(filter.accept(excludedFile, context)).isTrue();
+ }
+
+ @Test
+ public void should_trim_pattern() throws IOException {
+ InclusionFileFilter filter = InclusionFileFilter.create(FileFilter.FileType.SOURCE, " **/*Dao.java ");
+ assertThat(filter.pattern().toString()).isEqualTo("**/*Dao.java");
+ }
+
+ @Test
+ public void ignore_if_include_world() throws IOException {
+ assertThat(InclusionFileFilter.create(FileFilter.FileType.SOURCE, " **/* ")).isNull();
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageFileFiltersTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageFileFiltersTest.java
new file mode 100644
index 00000000000..6cb5185b404
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageFileFiltersTest.java
@@ -0,0 +1,75 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.apache.commons.io.filefilter.IOFileFilter;
+import org.apache.commons.io.filefilter.SuffixFileFilter;
+import org.apache.commons.io.filefilter.TrueFileFilter;
+import org.junit.Test;
+import org.sonar.api.resources.AbstractLanguage;
+import org.sonar.api.resources.Languages;
+
+import java.lang.reflect.Field;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class LanguageFileFiltersTest {
+ @Test
+ public void forLang() throws Exception {
+ LanguageFileFilters filters = new LanguageFileFilters(new Languages(new Java(), new Php()));
+
+ IOFileFilter filter = filters.forLang("php");
+ assertThat(filter).isInstanceOf(SuffixFileFilter.class);
+ assertThat(suffixes((SuffixFileFilter) filter)).containsOnly("php");
+
+ filter = filters.forLang("java");
+ assertThat(filter).isInstanceOf(SuffixFileFilter.class);
+ assertThat(suffixes((SuffixFileFilter) filter)).containsOnly("java", "jav");
+
+ assertThat(filters.forLang("unknown")).isSameAs(TrueFileFilter.TRUE);
+ }
+
+ private String[] suffixes(SuffixFileFilter filter) throws Exception {
+ Field privateField = SuffixFileFilter.class.getDeclaredField("suffixes");
+ privateField.setAccessible(true);
+
+ return (String[]) privateField.get(filter);
+ }
+
+ static class Php extends AbstractLanguage {
+ public Php() {
+ super("php");
+ }
+
+ public String[] getFileSuffixes() {
+ return new String[]{"php"};
+ }
+ }
+
+ static class Java extends AbstractLanguage {
+ public Java() {
+ super("java");
+ }
+
+ public String[] getFileSuffixes() {
+ return new String[]{"java", "jav"};
+ }
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProviderTest.java
new file mode 100644
index 00000000000..de91575bac2
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/ModuleFileSystemProviderTest.java
@@ -0,0 +1,51 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.bootstrap.ProjectDefinition;
+import org.sonar.api.config.Settings;
+import org.sonar.api.scan.filesystem.FileFilter;
+import org.sonar.api.scan.filesystem.ModuleFileSystem;
+import org.sonar.batch.bootstrap.TempDirectories;
+
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class ModuleFileSystemProviderTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void test_provide() throws IOException {
+ ModuleFileSystemProvider provider = new ModuleFileSystemProvider();
+ ProjectDefinition module = ProjectDefinition.create()
+ .setBaseDir(temp.newFolder())
+ .setWorkDir(temp.newFolder());
+ ModuleFileSystem fs = provider.provide(module, new PathResolver(), new TempDirectories(), mock(LanguageFileFilters.class),
+ new Settings(), new FileFilter[0]);
+
+ assertThat(fs).isNotNull();
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/PathResolverTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/PathResolverTest.java
new file mode 100644
index 00000000000..8515aa7a9f7
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/PathResolverTest.java
@@ -0,0 +1,88 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import org.apache.commons.io.FilenameUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class PathResolverTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void get_file_by_relative_path() throws IOException {
+ PathResolver resolver = new PathResolver();
+ File rootDir = temp.newFolder();
+ File file = resolver.relativeFile(rootDir, "org/foo/Bar.java");
+ assertThat(file.getName()).isEqualTo("Bar.java");
+ assertThat(FilenameUtils.separatorsToUnix(file.getCanonicalPath())).endsWith("org/foo/Bar.java");
+ assertThat(file.getParentFile().getParentFile().getParentFile().getCanonicalPath()).isEqualTo(rootDir.getCanonicalPath());
+ }
+
+ @Test
+ public void get_file_by_absolute_path() throws IOException {
+ PathResolver resolver = new PathResolver();
+ File rootDir = temp.newFolder();
+ File file = resolver.relativeFile(rootDir, new File(rootDir, "org/foo/Bar.java").getAbsolutePath());
+ assertThat(file.getName()).isEqualTo("Bar.java");
+ assertThat(FilenameUtils.separatorsToUnix(file.getCanonicalPath())).endsWith("org/foo/Bar.java");
+ assertThat(file.getParentFile().getParentFile().getParentFile().getCanonicalPath()).isEqualTo(rootDir.getCanonicalPath());
+ }
+
+ @Test
+ public void get_files_by_relative_paths() throws IOException {
+ PathResolver resolver = new PathResolver();
+ File rootDir = temp.newFolder();
+ List<File> files = resolver.relativeFiles(rootDir, Arrays.asList("org/foo/Bar.java", "org/hello/World.java"));
+ assertThat(files).hasSize(2);
+ for (File file : files) {
+ assertThat(file.getName()).endsWith(".java");
+ assertThat(file.getParentFile().getParentFile().getParentFile().getCanonicalPath()).isEqualTo(rootDir.getCanonicalPath());
+ }
+ }
+
+ @Test
+ public void get_relative_path() throws IOException {
+ PathResolver resolver = new PathResolver();
+ File rootDir = temp.newFolder();
+ File org = new File(rootDir, "org");
+ File hello = new File(org, "hello");
+ File world = new File(hello, "World.java");
+
+ assertThat(resolver.relativePath(rootDir, world)).isEqualTo("org/hello/World.java");
+ }
+
+ @Test
+ public void null_relative_path_when_file_is_not_in_dir() throws IOException {
+ PathResolver resolver = new PathResolver();
+ File rootDir = temp.newFolder();
+
+ assertThat(resolver.relativePath(rootDir, new File("Elsewhere.java"))).isNull();
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/WhiteListFileFilterTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/WhiteListFileFilterTest.java
new file mode 100644
index 00000000000..38c9e8215ab
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/WhiteListFileFilterTest.java
@@ -0,0 +1,54 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.batch.scan.filesystem;
+
+import com.google.common.collect.Sets;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.scan.filesystem.FileFilter;
+import org.sonar.api.scan.filesystem.ModuleFileSystem;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class WhiteListFileFilterTest {
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ @Test
+ public void should_accept() throws IOException {
+ WhiteListFileFilter filter = new WhiteListFileFilter(FileFilter.FileType.SOURCE, Sets.newHashSet(
+ new File("Foo.java"),
+ new File("Bar.java")
+ ));
+
+ FileFilterContext context = new FileFilterContext(mock(ModuleFileSystem.class), FileFilter.FileType.SOURCE);
+ assertThat(filter.accept(new File("Foo.java"), context)).isTrue();
+ assertThat(filter.accept(new File("Other.java"), context)).isFalse();
+
+ context = new FileFilterContext(mock(ModuleFileSystem.class), FileFilter.FileType.TEST);
+ assertThat(filter.accept(new File("Foo.java"), context)).isTrue();
+ assertThat(filter.accept(new File("Other.java"), context)).isTrue();
+ }
+}
diff --git a/sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/.dirPrefixedWithDot/Excluded.java b/sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/.dirPrefixedWithDot/Excluded.java
new file mode 100644
index 00000000000..9578920820c
--- /dev/null
+++ b/sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/.dirPrefixedWithDot/Excluded.java
@@ -0,0 +1 @@
+public class Excluded2 {} \ No newline at end of file
diff --git a/sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/.sonar/Excluded2.java b/sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/.sonar/Excluded2.java
new file mode 100644
index 00000000000..9578920820c
--- /dev/null
+++ b/sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/.sonar/Excluded2.java
@@ -0,0 +1 @@
+public class Excluded2 {} \ No newline at end of file
diff --git a/sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/sonar/Included.java b/sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/sonar/Included.java
new file mode 100644
index 00000000000..78e1aabbba7
--- /dev/null
+++ b/sonar-batch/test-resources/DefaultModuleFileSystemTest/exclude_dir_starting_with_dot/src/org/sonar/Included.java
@@ -0,0 +1,3 @@
+package org.sonar;
+
+public class Included {} \ No newline at end of file
diff --git a/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/main/java/Foo.java b/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/main/java/Foo.java
new file mode 100644
index 00000000000..f72abf33fa2
--- /dev/null
+++ b/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/main/java/Foo.java
@@ -0,0 +1 @@
+class Foo {} \ No newline at end of file
diff --git a/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/main/java/Hello.java b/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/main/java/Hello.java
new file mode 100644
index 00000000000..449be99af71
--- /dev/null
+++ b/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/main/java/Hello.java
@@ -0,0 +1 @@
+public class Hello {} \ No newline at end of file
diff --git a/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/test/java/FooTest.java b/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/test/java/FooTest.java
new file mode 100644
index 00000000000..31eebff6972
--- /dev/null
+++ b/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/test/java/FooTest.java
@@ -0,0 +1 @@
+class FooTest {} \ No newline at end of file
diff --git a/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/test/java/HelloTest.java b/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/test/java/HelloTest.java
new file mode 100644
index 00000000000..901b93a5cb4
--- /dev/null
+++ b/sonar-batch/test-resources/DefaultModuleFileSystemTest/main_and_test_files/src/test/java/HelloTest.java
@@ -0,0 +1 @@
+public class HelloTest {} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
index 2c785625f71..47027d2108c 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
@@ -108,11 +108,13 @@ public interface CoreProperties {
String DYNAMIC_ANALYSIS_PROPERTY = "sonar.dynamicAnalysis";
/* Exclusions */
+ String PROJECT_INCLUSIONS_PROPERTY = "sonar.inclusions";
String PROJECT_EXCLUSIONS_PROPERTY = "sonar.exclusions";
/**
* @since 3.3
*/
+ String PROJECT_TEST_INCLUSIONS_PROPERTY = "sonar.test.inclusions";
String PROJECT_TEST_EXCLUSIONS_PROPERTY = "sonar.test.exclusions";
String GLOBAL_EXCLUSIONS_PROPERTY = "sonar.global.exclusions";
String GLOBAL_TEST_EXCLUSIONS_PROPERTY = "sonar.global.test.exclusions";
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/FileFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/FileFilter.java
index 4407c33e833..1041bcf5f40 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/FileFilter.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/FileFilter.java
@@ -19,8 +19,10 @@
*/
package org.sonar.api.batch;
-import org.sonar.api.BatchExtension;
-
-public abstract class FileFilter implements java.io.FileFilter, BatchExtension {
+import java.io.File;
+public abstract class FileFilter implements java.io.FileFilter, org.sonar.api.scan.filesystem.FileFilter {
+ public final boolean accept(File file, org.sonar.api.scan.filesystem.FileFilter.Context context) {
+ return accept(file);
+ }
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultProjectFileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultProjectFileSystem.java
index 5bef696d85b..d5553615cd3 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultProjectFileSystem.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultProjectFileSystem.java
@@ -220,7 +220,7 @@ public class DefaultProjectFileSystem implements ProjectFileSystem {
IOFileFilter suffixFilter = getFileSuffixFilter(langs);
WildcardPattern[] exclusionPatterns = WildcardPattern.create(patterns);
- IOFileFilter initialFilesFilter = TrueFileFilter.INSTANCE;
+ IOFileFilter initialFilesFilter = TrueFileFilter.TRUE;
if (initialFiles != null && !initialFiles.isEmpty()) {
initialFilesFilter = new FileSelectionFilter(initialFiles);
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FailToCreateFileException.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FailToCreateFileException.java
new file mode 100644
index 00000000000..7bf68c6c386
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FailToCreateFileException.java
@@ -0,0 +1,32 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.scan.filesystem;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * @since 3.5
+ */
+@Beta
+public class FailToCreateFileException extends FileSystemException {
+ public FailToCreateFileException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileFilter.java
new file mode 100644
index 00000000000..42b98c1fc0d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileFilter.java
@@ -0,0 +1,44 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.scan.filesystem;
+
+import com.google.common.annotations.Beta;
+import org.sonar.api.BatchExtension;
+
+import java.io.File;
+
+/**
+ * @since 3.5
+ */
+@Beta
+public interface FileFilter extends BatchExtension {
+ enum FileType {
+ SOURCE, TEST
+ }
+
+ static interface Context {
+ ModuleFileSystem fileSystem();
+ FileType fileType();
+ File sourceDir();
+ String fileRelativePath();
+ }
+
+ boolean accept(File file, Context context);
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileSystemException.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileSystemException.java
new file mode 100644
index 00000000000..fec731b1a8b
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/FileSystemException.java
@@ -0,0 +1,36 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.scan.filesystem;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * @since 3.5
+ */
+@Beta
+public class FileSystemException extends RuntimeException {
+ public FileSystemException(String message) {
+ super(message);
+ }
+
+ public FileSystemException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/IllegalPathException.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/IllegalPathException.java
new file mode 100644
index 00000000000..04bd23f10bd
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/IllegalPathException.java
@@ -0,0 +1,37 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.scan.filesystem;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * @since 3.5
+ */
+@Beta
+public class IllegalPathException extends FileSystemException {
+
+ public IllegalPathException(String message) {
+ super(message);
+ }
+
+ public IllegalPathException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/JavaIoFileFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/JavaIoFileFilter.java
new file mode 100644
index 00000000000..d2467300e7e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/JavaIoFileFilter.java
@@ -0,0 +1,44 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.scan.filesystem;
+
+import com.google.common.annotations.Beta;
+
+import java.io.File;
+
+/**
+ * @since 3.5
+ */
+@Beta
+public class JavaIoFileFilter implements FileFilter {
+ private java.io.FileFilter ioFilter;
+
+ private JavaIoFileFilter(java.io.FileFilter ioFilter) {
+ this.ioFilter = ioFilter;
+ }
+
+ public static JavaIoFileFilter create(java.io.FileFilter filter) {
+ return new JavaIoFileFilter(filter);
+ }
+
+ public boolean accept(File file, Context context) {
+ return ioFilter.accept(file);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/ModuleFileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/ModuleFileSystem.java
new file mode 100644
index 00000000000..49031608d5e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/ModuleFileSystem.java
@@ -0,0 +1,44 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.api.scan.filesystem;
+
+import com.google.common.annotations.Beta;
+import org.sonar.api.BatchComponent;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.util.List;
+
+/**
+ * @since 3.5
+ */
+@Beta
+public interface ModuleFileSystem extends BatchComponent {
+ File baseDir();
+ List<File> sourceDirs();
+ List<File> sourceFiles();
+ List<File> sourceFilesOfLang(String language);
+ List<File> testDirs();
+ List<File> testFiles();
+ List<File> testFilesOfLang(String language);
+ List<File> binaryDirs();
+ Charset sourceCharset();
+ File workingDir();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/package-info.java b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/package-info.java
new file mode 100644
index 00000000000..891134cca1f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/scan/filesystem/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2008-2012 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+
+/**
+ * This package is a part of bootstrap process, so we should take care about backward compatibility.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.api.scan.filesystem;
+
+import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file