*/
FilePredicate hasRelativePath(String s);
+ /**
+ * Predicate that matches files by filename, in any directory.
+ * For example, the parameter <code>Foo.java</code> will match both
+ * <code>some/path/Foo.java</code> and <code>other/path/Foo.java</code>.
+ * The parameter must match exactly, no patterns are allowed,
+ * and it must not be <code>null</code>.
+ */
+ FilePredicate hasFilename(String s);
+
+ /**
+ * Predicate that matches files by extension (case insensitive).
+ * For example, the parameter <code>java</code> will match
+ * <code>some/path/Foo.java</code> and <code>other/path/Foo.JAVA</code>
+ * but not <code>some/path/Foo.js</code>.
+ * The parameter must not be <code>null</code>.
+ */
+ FilePredicate hasExtension(String s);
+
/**
* Predicate that gets the files which relative or absolute path matches a wildcard pattern.
* <br>
InputDir inputDir(String relativePath);
InputModule module();
+
+ Iterable<InputFile> getFilesByName(String filename);
+
+ Iterable<InputFile> getFilesByExtension(String extension);
}
}
return new RelativePathPredicate(s);
}
+ @Override
+ public FilePredicate hasFilename(String s) {
+ return new FilenamePredicate(s);
+ }
+
+ @Override public FilePredicate hasExtension(String s) {
+ return new FileExtensionPredicate(s);
+ }
+
@Override
public FilePredicate matchesPathPattern(String inclusionPattern) {
return new PathPatternPredicate(PathPattern.create(inclusionPattern));
package org.sonar.api.batch.fs.internal;
import com.google.common.collect.Iterables;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
private final Map<String, InputFile> fileMap = new HashMap<>();
private final Map<String, InputDir> dirMap = new HashMap<>();
private InputModule module;
+ private final SetMultimap<String, InputFile> filesByNameCache = LinkedHashMultimap.create();
+ private final SetMultimap<String, InputFile> filesByExtensionCache = LinkedHashMultimap.create();
@Override
public Iterable<InputFile> inputFiles() {
return module;
}
+ @Override
+ public Iterable<InputFile> getFilesByName(String filename) {
+ return filesByNameCache.get(filename);
+ }
+
+ @Override public Iterable<InputFile> getFilesByExtension(String extension) {
+ return filesByExtensionCache.get(extension);
+ }
+
@Override
protected void doAdd(InputFile inputFile) {
fileMap.put(inputFile.relativePath(), inputFile);
+ filesByNameCache.put(FilenamePredicate.getFilename(inputFile), inputFile);
+ filesByExtensionCache.put(FileExtensionPredicate.getExtension(inputFile), inputFile);
}
@Override
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import java.util.Locale;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * @since 6.3
+ */
+public class FileExtensionPredicate extends AbstractFilePredicate {
+
+ private final String extension;
+
+ public FileExtensionPredicate(String extension) {
+ this.extension = lowercase(extension);
+ }
+
+ @Override
+ public boolean apply(InputFile inputFile) {
+ return extension.equals(getExtension(inputFile));
+ }
+
+ @Override
+ public Iterable<InputFile> get(FileSystem.Index index) {
+ return index.getFilesByExtension(extension);
+ }
+
+ public static String getExtension(InputFile inputFile) {
+ return getExtension(FilenamePredicate.getFilename(inputFile));
+ }
+
+ static String getExtension(String name) {
+ int index = name.lastIndexOf('.');
+ if (index < 0) {
+ return "";
+ }
+ return lowercase(name.substring(index + 1));
+ }
+
+ private static String lowercase(String extension) {
+ return extension.toLowerCase(Locale.ENGLISH);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+
+/**
+ * @since 6.3
+ */
+public class FilenamePredicate extends AbstractFilePredicate {
+ private final String filename;
+
+ public FilenamePredicate(String filename) {
+ this.filename = filename;
+ }
+
+ @Override
+ public boolean apply(InputFile inputFile) {
+ return filename.equals(getFilename(inputFile));
+ }
+
+ @Override
+ public Iterable<InputFile> get(FileSystem.Index index) {
+ return index.getFilesByName(filename);
+ }
+
+ public static String getFilename(InputFile inputFile) {
+ return inputFile.file().getName();
+ }
+}
assertThat(predicates.or(new FilePredicate[] {predicates.all(), predicates.none()}).apply(javaFile)).isTrue();
assertThat(predicates.or(new FilePredicate[] {predicates.none(), predicates.none()}).apply(javaFile)).isFalse();
}
+
+ @Test
+ public void hasFilename() {
+ assertThat(predicates.hasFilename("Action.java").apply(javaFile)).isTrue();
+ }
+
+ @Test
+ public void hasExtension() {
+ assertThat(predicates.hasExtension("java").apply(javaFile)).isTrue();
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.*;
+import org.sonar.api.batch.fs.InputFile;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+import static org.sonar.api.batch.fs.internal.FileExtensionPredicate.getExtension;
+
+public class FileExtensionPredicateTest {
+
+ @Test
+ public void should_match_correct_extension() throws IOException {
+ FileExtensionPredicate predicate = new FileExtensionPredicate("bat");
+ assertThat(predicate.apply(mockWithName("prog.bat"))).isTrue();
+ assertThat(predicate.apply(mockWithName("prog.bat.bat"))).isTrue();
+ }
+
+ @Test
+ public void should_not_match_incorrect_extension() throws IOException {
+ FileExtensionPredicate predicate = new FileExtensionPredicate("bat");
+ assertThat(predicate.apply(mockWithName("prog.batt"))).isFalse();
+ assertThat(predicate.apply(mockWithName("prog.abat"))).isFalse();
+ assertThat(predicate.apply(mockWithName("prog."))).isFalse();
+ assertThat(predicate.apply(mockWithName("prog.bat."))).isFalse();
+ assertThat(predicate.apply(mockWithName("prog.bat.batt"))).isFalse();
+ assertThat(predicate.apply(mockWithName("prog"))).isFalse();
+ }
+
+ @Test
+ public void should_match_correct_extension_case_insensitively() throws IOException {
+ FileExtensionPredicate predicate = new FileExtensionPredicate("jAVa");
+ assertThat(predicate.apply(mockWithName("Program.java"))).isTrue();
+ assertThat(predicate.apply(mockWithName("Program.JAVA"))).isTrue();
+ assertThat(predicate.apply(mockWithName("Program.Java"))).isTrue();
+ assertThat(predicate.apply(mockWithName("Program.JaVa"))).isTrue();
+ }
+
+ @Test
+ public void test_empty_extension() {
+ assertThat(getExtension("prog")).isEmpty();
+ assertThat(getExtension("prog.")).isEmpty();
+ assertThat(getExtension(".")).isEmpty();
+ }
+
+ private InputFile mockWithName(String filename) throws IOException {
+ InputFile inputFile = mock(InputFile.class);
+ when(inputFile.file()).thenReturn(new File("dummy parent", filename));
+ return inputFile;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.api.batch.fs.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import org.junit.*;
+import org.sonar.api.batch.fs.FileSystem;
+import org.sonar.api.batch.fs.InputFile;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+
+public class FilenamePredicateTest {
+ @Test
+ public void should_match_file_by_filename() throws IOException {
+ String filename = "some name";
+ InputFile inputFile = mock(InputFile.class);
+ when(inputFile.file()).thenReturn(newDummyFile(filename));
+
+ assertThat(new FilenamePredicate(filename).apply(inputFile)).isTrue();
+ }
+
+ @Test
+ public void should_not_match_file_by_different_filename() throws IOException {
+ String filename = "some name";
+ InputFile inputFile = mock(InputFile.class);
+ when(inputFile.file()).thenReturn(newDummyFile(filename + "x"));
+
+ assertThat(new FilenamePredicate(filename).apply(inputFile)).isFalse();
+ }
+
+ @Test
+ public void should_find_matching_file_in_index() throws IOException {
+ String filename = "some name";
+ InputFile inputFile = mock(InputFile.class);
+ when(inputFile.file()).thenReturn(newDummyFile(filename));
+
+ FileSystem.Index index = mock(FileSystem.Index.class);
+ when(index.getFilesByName(filename)).thenReturn(Collections.singleton(inputFile));
+
+ assertThat(new FilenamePredicate(filename).get(index)).containsOnly(inputFile);
+ }
+
+ private File newDummyFile(String filename) {
+ return new File("dummy parent", filename);
+ }
+}
*/
package org.sonar.scanner.scan.filesystem;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.google.common.collect.Table;
import com.google.common.collect.TreeBasedTable;
+import org.sonar.api.batch.fs.internal.FileExtensionPredicate;
+import org.sonar.api.batch.fs.internal.FilenamePredicate;
/**
* Store of all files and dirs. This cache is shared amongst all project modules. Inclusion and
private final Table<String, String, InputDir> inputDirCache = TreeBasedTable.create();
private final Map<String, InputModule> inputModuleCache = new HashMap<>();
private final Map<String, InputComponent> inputComponents = new HashMap<>();
+ private final SetMultimap<String, InputFile> filesByNameCache = LinkedHashMultimap.create();
+ private final SetMultimap<String, InputFile> filesByExtensionCache = LinkedHashMultimap.create();
private InputModule root;
public Collection<InputComponent> all() {
DefaultInputFile file = (DefaultInputFile) inputFile;
inputFileCache.put(file.moduleKey(), inputFile.relativePath(), inputFile);
inputComponents.put(inputFile.key(), inputFile);
+ filesByNameCache.put(FilenamePredicate.getFilename(inputFile), inputFile);
+ filesByExtensionCache.put(FileExtensionPredicate.getExtension(inputFile), inputFile);
return this;
}
inputModuleCache.put(inputModule.key(), inputModule);
}
+ public Iterable<InputFile> getFilesByName(String filename) {
+ return filesByNameCache.get(filename);
+ }
+
+ public Iterable<InputFile> getFilesByExtension(String extension) {
+ return filesByExtensionCache.get(extension);
+ }
}
public InputModule module() {
return inputComponentStore.getModule(moduleKey);
}
+
+ @Override
+ public Iterable<InputFile> getFilesByName(String filename) {
+ return inputComponentStore.getFilesByName(filename);
+ }
+
+ @Override public Iterable<InputFile> getFilesByExtension(String extension) {
+ return inputComponentStore.getFilesByExtension(extension);
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.scanner.scan.filesystem;
+
+import java.io.IOException;
+import org.junit.Test;
+import org.sonar.api.batch.fs.InputFile;
+import org.sonar.api.batch.fs.InputModule;
+import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class ModuleInputComponentStoreTest {
+ @Test
+ public void should_cache_files_by_filename() throws IOException {
+ ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+
+ String filename = "some name";
+ String moduleKey = "dummy key";
+ InputFile inputFile1 = new TestInputFileBuilder(moduleKey, "some/path/" + filename).build();
+ store.doAdd(inputFile1);
+
+ InputFile inputFile2 = new TestInputFileBuilder(moduleKey, "other/path/" + filename).build();
+ store.doAdd(inputFile2);
+
+ InputFile dummyInputFile = new TestInputFileBuilder(moduleKey, "some/path/Dummy.java").build();
+ store.doAdd(dummyInputFile);
+
+ assertThat(store.getFilesByName(filename)).containsOnly(inputFile1, inputFile2);
+ }
+
+ @Test
+ public void should_cache_files_by_extension() throws IOException {
+ ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+
+ String moduleKey = "dummy key";
+ InputFile inputFile1 = new TestInputFileBuilder(moduleKey, "some/path/Program.java").build();
+ store.doAdd(inputFile1);
+
+ InputFile inputFile2 = new TestInputFileBuilder(moduleKey, "other/path/Utils.java").build();
+ store.doAdd(inputFile2);
+
+ InputFile dummyInputFile = new TestInputFileBuilder(moduleKey, "some/path/NotJava.cpp").build();
+ store.doAdd(dummyInputFile);
+
+ assertThat(store.getFilesByExtension("java")).containsOnly(inputFile1, inputFile2);
+ }
+
+ @Test
+ public void should_not_cache_duplicates() throws IOException {
+ ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+
+ String ext = "java";
+ String filename = "Program." + ext;
+ InputFile inputFile = new TestInputFileBuilder("dummy key", "some/path/" + filename).build();
+ store.doAdd(inputFile);
+ store.doAdd(inputFile);
+ store.doAdd(inputFile);
+
+ assertThat(store.getFilesByName(filename)).containsOnly(inputFile);
+ assertThat(store.getFilesByExtension(ext)).containsOnly(inputFile);
+ }
+
+ @Test
+ public void should_get_empty_iterable_on_cache_miss() {
+ ModuleInputComponentStore store = new ModuleInputComponentStore(mock(InputModule.class), new InputComponentStore());
+
+ String ext = "java";
+ String filename = "Program." + ext;
+ InputFile inputFile = new TestInputFileBuilder("dummy key", "some/path/" + filename).build();
+ store.doAdd(inputFile);
+
+ assertThat(store.getFilesByName("nonexistent")).isEmpty();
+ assertThat(store.getFilesByExtension("nonexistent")).isEmpty();
+ }
+}