/* * SonarQube * Copyright (C) 2009-2017 SonarSource SA * mailto:info 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 com.google.common.base.Preconditions; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.SetMultimap; import com.google.common.collect.Table; import com.google.common.collect.TreeBasedTable; import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import javax.annotation.CheckForNull; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputModule; import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.FileExtensionPredicate; import org.sonar.api.batch.fs.internal.FilenamePredicate; import org.sonar.api.scan.filesystem.PathResolver; /** * Store of all files and dirs. This cache is shared amongst all project modules. Inclusion and * exclusion patterns are already applied. */ @ScannerSide public class InputComponentStore { private final PathResolver pathResolver; private final SortedSet<String> globalLanguagesCache = new TreeSet<>(); private final Map<String, SortedSet<String>> languagesCache = new HashMap<>(); private final Map<String, InputFile> globalInputFileCache = new HashMap<>(); private final Table<String, String, InputFile> inputFileCache = TreeBasedTable.create(); private final Map<String, InputDir> globalInputDirCache = new HashMap<>(); 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 InputComponentStore(PathResolver pathResolver) { this.pathResolver = pathResolver; } public Collection<InputComponent> all() { return inputComponents.values(); } public Iterable<DefaultInputFile> allFilesToPublish() { return inputFileCache.values().stream() .map(f -> (DefaultInputFile) f) .filter(DefaultInputFile::publish)::iterator; } public Iterable<InputFile> allFiles() { return inputFileCache.values(); } public Iterable<InputDir> allDirs() { return inputDirCache.values(); } public InputComponent getByKey(String key) { return inputComponents.get(key); } @CheckForNull public InputModule root() { return root; } public Iterable<InputFile> filesByModule(String moduleKey) { return inputFileCache.row(moduleKey).values(); } public Iterable<InputDir> dirsByModule(String moduleKey) { return inputDirCache.row(moduleKey).values(); } public InputComponentStore removeModule(String moduleKey) { inputFileCache.row(moduleKey).clear(); inputDirCache.row(moduleKey).clear(); return this; } public InputComponentStore remove(InputFile inputFile) { DefaultInputFile file = (DefaultInputFile) inputFile; inputFileCache.remove(file.moduleKey(), inputFile.relativePath()); return this; } public InputComponentStore remove(InputDir inputDir) { DefaultInputDir dir = (DefaultInputDir) inputDir; inputDirCache.remove(dir.moduleKey(), inputDir.relativePath()); return this; } public InputComponentStore put(InputFile inputFile) { DefaultInputFile file = (DefaultInputFile) inputFile; addToLanguageCache(file); inputFileCache.put(file.moduleKey(), inputFile.relativePath(), inputFile); globalInputFileCache.put(getProjectRelativePath(file), inputFile); inputComponents.put(inputFile.key(), inputFile); filesByNameCache.put(FilenamePredicate.getFilename(inputFile), inputFile); filesByExtensionCache.put(FileExtensionPredicate.getExtension(inputFile), inputFile); return this; } private void addToLanguageCache(DefaultInputFile inputFile) { String language = inputFile.language(); if (language != null) { globalLanguagesCache.add(language); languagesCache.computeIfAbsent(inputFile.moduleKey(), k -> new TreeSet<>()).add(language); } } public InputComponentStore put(InputDir inputDir) { DefaultInputDir dir = (DefaultInputDir) inputDir; inputDirCache.put(dir.moduleKey(), inputDir.relativePath(), inputDir); globalInputDirCache.put(getProjectRelativePath(dir), inputDir); inputComponents.put(inputDir.key(), inputDir); return this; } private String getProjectRelativePath(DefaultInputFile file) { return pathResolver.relativePath(getProjectBaseDir(), file.path()); } private String getProjectRelativePath(DefaultInputDir dir) { return pathResolver.relativePath(getProjectBaseDir(), dir.path()); } private Path getProjectBaseDir() { return ((DefaultInputModule) root).definition().getBaseDir().toPath(); } @CheckForNull public InputFile getFile(String moduleKey, String relativePath) { return inputFileCache.get(moduleKey, relativePath); } @CheckForNull public InputFile getFile(String relativePath) { return globalInputFileCache.get(relativePath); } @CheckForNull public InputDir getDir(String moduleKey, String relativePath) { return inputDirCache.get(moduleKey, relativePath); } @CheckForNull public InputDir getDir(String relativePath) { return globalInputDirCache.get(relativePath); } @CheckForNull public InputModule getModule(String moduleKey) { return inputModuleCache.get(moduleKey); } public void put(DefaultInputModule inputModule) { String key = inputModule.key(); Preconditions.checkState(!inputComponents.containsKey(key), "Module '%s' already indexed", key); Preconditions.checkState(!inputModuleCache.containsKey(key), "Module '%s' already indexed", key); inputComponents.put(key, inputModule); inputModuleCache.put(key, inputModule); if (inputModule.definition().getParent() == null) { if (root != null) { throw new IllegalStateException("Root module already indexed: '" + root.key() + "', '" + key + "'"); } root = inputModule; } } public Iterable<InputFile> getFilesByName(String filename) { return filesByNameCache.get(filename); } public Iterable<InputFile> getFilesByExtension(String extension) { return filesByExtensionCache.get(extension); } public SortedSet<String> getLanguages() { return globalLanguagesCache; } public SortedSet<String> getLanguages(String moduleKey) { return languagesCache.getOrDefault(moduleKey, Collections.emptySortedSet()); } }