diff options
author | Simon Brandhof <simon.brandhof@gmail.com> | 2013-10-11 12:13:24 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@gmail.com> | 2013-10-11 12:13:32 +0200 |
commit | f71a3cec9d925fb94d06d735c62c51e66109322f (patch) | |
tree | 430cb04495dc4d6a47dbebfb58347edde987f47b /sonar-batch | |
parent | 23506cffec508d01da926795860813e66f36d366 (diff) | |
download | sonarqube-f71a3cec9d925fb94d06d735c62c51e66109322f.tar.gz sonarqube-f71a3cec9d925fb94d06d735c62c51e66109322f.zip |
SONAR-4751 do not fail but warn if a file extension is declared in multiple plugins
Diffstat (limited to 'sonar-batch')
3 files changed, 102 insertions, 71 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndex.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndex.java index c625b4a4ae2..2efaae29c67 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/FileIndex.java @@ -151,9 +151,8 @@ public class FileIndex implements BatchComponent { @CheckForNull private InputFile newInputFile(ModuleFileSystem fileSystem, File sourceDir, String type, File file, String path) { - // File extension must be kept case-sensitive String lang = languageRecognizer.of(file); - if (lang == null) { + if (lang==null) { return null; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageRecognizer.java b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageRecognizer.java index 2214f24df1e..6357ee744ea 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageRecognizer.java +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/filesystem/LanguageRecognizer.java @@ -19,63 +19,82 @@ */ package org.sonar.batch.scan.filesystem; -import com.google.common.collect.Maps; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.picocontainer.Startable; +import org.slf4j.LoggerFactory; import org.sonar.api.BatchComponent; import org.sonar.api.resources.Language; +import org.sonar.api.resources.Project; import javax.annotation.CheckForNull; import java.io.File; -import java.util.Map; +import java.util.Set; /** * Detect language of source files. Simplistic, based on file extensions. */ public class LanguageRecognizer implements BatchComponent, Startable { - /** - * Lower-case extension -> language - */ - private Map<String, String> byExtensions = Maps.newHashMap(); + static final String NO_EXTENSION = "<UNSET>"; + private final Project project; private final Language[] languages; - public LanguageRecognizer(Language[] languages) { + /** + * Lower-case extension -> languages + */ + private SetMultimap<String, String> langsByExtension = HashMultimap.create(); + + public LanguageRecognizer(Project project, Language[] languages) { + this.project = project; this.languages = languages; } @Override public void start() { for (Language language : languages) { - for (String suffix : language.getFileSuffixes()) { - String extension = sanitizeExtension(suffix); + if (language.getFileSuffixes().length == 0) { + langsByExtension.put(NO_EXTENSION, language.getKey()); - String s = byExtensions.get(extension); - if (s != null && !StringUtils.equals(s, language.getKey())) { - throw new IllegalStateException(String.format( - "File extension '%s' is declared by two languages: %s and %s", extension, s, language.getKey() - )); + } else { + for (String suffix : language.getFileSuffixes()) { + String extension = sanitizeExtension(suffix); + langsByExtension.put(extension, language.getKey()); } - byExtensions.put(extension, language.getKey()); + } + } + + for (String extension : langsByExtension.keySet()) { + Set<String> langs = langsByExtension.get(extension); + if (langs.size() > 1) { + warnConflict(extension, langs); } } } + @VisibleForTesting + void warnConflict(String extension, Set<String> langs) { + LoggerFactory.getLogger(LanguageRecognizer.class).warn(String.format( + "File extension '%s' is declared by several plugins: %s", extension, StringUtils.join(langs, ", ") + )); + } + @Override public void stop() { // do nothing } - // TODO what about cobol files without extension ? @CheckForNull String of(File file) { - String extension = FilenameUtils.getExtension(file.getName()); - if (StringUtils.isNotBlank(extension)) { - return byExtensions.get(StringUtils.lowerCase(extension)); - } - return null; + // multi-language is not supported yet. Filter on project language + String extension = sanitizeExtension(FilenameUtils.getExtension(file.getName())); + extension = StringUtils.defaultIfBlank(extension, NO_EXTENSION); + Set<String> langs = langsByExtension.get(extension); + return langs.contains(project.getLanguageKey()) ? project.getLanguageKey() : null; } static String sanitizeExtension(String suffix) { diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageRecognizerTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageRecognizerTest.java index dbba4e17311..b329f3bfbb6 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageRecognizerTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/filesystem/LanguageRecognizerTest.java @@ -22,11 +22,14 @@ package org.sonar.batch.scan.filesystem; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.sonar.api.resources.Java; +import org.mockito.ArgumentMatcher; import org.sonar.api.resources.Language; +import org.sonar.api.resources.Project; + +import java.util.Set; import static org.fest.assertions.Assertions.assertThat; -import static org.fest.assertions.Fail.fail; +import static org.mockito.Mockito.*; public class LanguageRecognizerTest { @@ -43,13 +46,18 @@ public class LanguageRecognizerTest { @Test public void search_by_file_extension() throws Exception { - Language[] languages = new Language[]{Java.INSTANCE, new Cobol()}; - LanguageRecognizer recognizer = new LanguageRecognizer(languages); + Language[] languages = new Language[]{new MockLanguage("java", "java", "jav"), new MockLanguage("cobol", "cbl", "cob")}; + LanguageRecognizer recognizer = new LanguageRecognizer(newProject("java"), languages); recognizer.start(); - assertThat(recognizer.of(temp.newFile("Foo.java"))).isEqualTo(Java.KEY); - assertThat(recognizer.of(temp.newFile("abc.cbl"))).isEqualTo("cobol"); - assertThat(recognizer.of(temp.newFile("abc.CBL"))).isEqualTo("cobol"); + assertThat(recognizer.of(temp.newFile("Foo.java"))).isEqualTo("java"); + assertThat(recognizer.of(temp.newFile("Foo.JAVA"))).isEqualTo("java"); + assertThat(recognizer.of(temp.newFile("Foo.jav"))).isEqualTo("java"); + assertThat(recognizer.of(temp.newFile("Foo.Jav"))).isEqualTo("java"); + + // multi-language is not supported yet -> filter on project language + assertThat(recognizer.of(temp.newFile("abc.cbl"))).isNull(); + assertThat(recognizer.of(temp.newFile("abc.CBL"))).isNull(); assertThat(recognizer.of(temp.newFile("abc.php"))).isNull(); assertThat(recognizer.of(temp.newFile("abc"))).isNull(); recognizer.stop(); @@ -57,72 +65,77 @@ public class LanguageRecognizerTest { @Test public void fail_if_conflict_of_file_extensions() throws Exception { - Language[] languages = new Language[]{Java.INSTANCE, new Language() { - @Override - public String getKey() { - return "java2"; - } + Language[] languages = new Language[]{new MockLanguage("java", "java"), new MockLanguage("java2", "java", "java2")}; - @Override - public String getName() { - return "Java2"; - } + LanguageRecognizer recognizer = spy(new LanguageRecognizer(newProject("java"), languages)); + recognizer.start(); + verify(recognizer).warnConflict(eq("java"), argThat(new ArgumentMatcher<Set<String>>() { @Override - public String[] getFileSuffixes() { - return new String[]{"java2", "java"}; + public boolean matches(Object o) { + Set<String> set = (Set<String>) o; + return set.contains("java") && set.contains("java2") && set.size() == 2; } - }}; - - try { - new LanguageRecognizer(languages).start(); - fail(); - } catch (IllegalStateException e) { - assertThat(e.getMessage()).isEqualTo("File extension 'java' is declared by two languages: java and java2"); - } + })); } + @Test public void plugin_can_declare_a_file_extension_twice_for_case_sensitivity() throws Exception { - Language[] languages = new Language[]{new Language() { - @Override - public String getKey() { - return "abap"; - } + Language[] languages = new Language[]{new MockLanguage("abap", "abap", "ABAP")}; - @Override - public String getName() { - return "ABAP"; - } + LanguageRecognizer recognizer = new LanguageRecognizer(newProject("abap"), languages); + recognizer.start(); + assertThat(recognizer.of(temp.newFile("abc.abap"))).isEqualTo("abap"); + } - @Override - public String[] getFileSuffixes() { - return new String[]{"abap", "ABAP"}; - } - }}; + @Test + public void language_with_no_extension() throws Exception { + // abap here is associated to files without extension + Language[] languages = new Language[]{new MockLanguage("java", "java"), new MockLanguage("abap")}; - LanguageRecognizer recognizer = new LanguageRecognizer(languages); + // files without extension are detected only on abap projects + LanguageRecognizer recognizer = new LanguageRecognizer(newProject("java"), languages); recognizer.start(); - assertThat(recognizer.of(temp.newFile("abc.abap"))).isEqualTo("abap"); + assertThat(recognizer.of(temp.newFile("abc"))).isNull(); + recognizer.stop(); + + recognizer = new LanguageRecognizer(newProject("abap"), languages); + recognizer.start(); + assertThat(recognizer.of(temp.newFile("abc"))).isEqualTo("abap"); + recognizer.stop(); } - static class Cobol implements Language { + private Project newProject(String language) { + Project project = mock(Project.class); + when(project.getLanguageKey()).thenReturn(language); + return project; + } + + + static class MockLanguage implements Language { + private final String key; + private final String[] extensions; + + MockLanguage(String key, String... extensions) { + this.key = key; + this.extensions = extensions; + } + @Override public String getKey() { - return "cobol"; + return key; } @Override public String getName() { - return "Cobol"; + return key; } @Override public String[] getFileSuffixes() { - return new String[]{"cbl", "cob"}; + return extensions; } } - - } |