001 /*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
019 */
020 package org.sonar.batch.scan.filesystem;
021
022 import com.google.common.base.Function;
023 import com.google.common.collect.Collections2;
024 import com.google.common.collect.ImmutableList;
025 import com.google.common.collect.Lists;
026 import org.apache.commons.lang.StringUtils;
027 import org.slf4j.Logger;
028 import org.slf4j.LoggerFactory;
029 import org.sonar.api.CoreProperties;
030 import org.sonar.api.batch.bootstrap.ProjectDefinition;
031 import org.sonar.api.batch.fs.FilePredicate;
032 import org.sonar.api.batch.fs.internal.DefaultFileSystem;
033 import org.sonar.api.config.Settings;
034 import org.sonar.api.resources.Project;
035 import org.sonar.api.scan.filesystem.FileQuery;
036 import org.sonar.api.scan.filesystem.ModuleFileSystem;
037 import org.sonar.api.utils.MessageException;
038
039 import javax.annotation.CheckForNull;
040 import javax.annotation.Nullable;
041
042 import java.io.File;
043 import java.nio.charset.Charset;
044 import java.util.ArrayList;
045 import java.util.Collection;
046 import java.util.List;
047 import java.util.Map;
048
049 /**
050 * This class can't be immutable because of execution of maven plugins that can change the project structure (see MavenPluginHandler and sonar.phase)
051 *
052 * @since 3.5
053 */
054 public class DefaultModuleFileSystem extends DefaultFileSystem implements ModuleFileSystem {
055
056 private static final Logger LOG = LoggerFactory.getLogger(DefaultModuleFileSystem.class);
057
058 private final String moduleKey;
059 private final FileIndexer indexer;
060 private final Settings settings;
061
062 private File buildDir;
063 private List<File> sourceDirsOrFiles = Lists.newArrayList();
064 private List<File> testDirsOrFiles = Lists.newArrayList();
065 private List<File> binaryDirs = Lists.newArrayList();
066 private ComponentIndexer componentIndexer;
067 private boolean initialized;
068
069 /**
070 * Used by scan2
071 */
072 public DefaultModuleFileSystem(ModuleInputFileCache moduleInputFileCache, ProjectDefinition def, Settings settings,
073 FileIndexer indexer, ModuleFileSystemInitializer initializer) {
074 this(moduleInputFileCache, def.getKey(), settings, indexer, initializer, null);
075 }
076
077 public DefaultModuleFileSystem(ModuleInputFileCache moduleInputFileCache, ProjectDefinition def, Project project,
078 Settings settings, FileIndexer indexer,
079 ModuleFileSystemInitializer initializer,
080 ComponentIndexer componentIndexer) {
081 this(moduleInputFileCache, project.getKey(), settings, indexer, initializer, componentIndexer);
082 }
083
084 private DefaultModuleFileSystem(ModuleInputFileCache moduleInputFileCache, String moduleKey, Settings settings,
085 FileIndexer indexer, ModuleFileSystemInitializer initializer,
086 @Nullable ComponentIndexer componentIndexer) {
087 super(initializer.baseDir(), moduleInputFileCache);
088 this.componentIndexer = componentIndexer;
089 this.moduleKey = moduleKey;
090 this.settings = settings;
091 this.indexer = indexer;
092 setWorkDir(initializer.workingDir());
093 this.buildDir = initializer.buildDir();
094 this.sourceDirsOrFiles = initializer.sources();
095 this.testDirsOrFiles = initializer.tests();
096 this.binaryDirs = initializer.binaryDirs();
097 }
098
099 public boolean isInitialized() {
100 return initialized;
101 }
102
103 public String moduleKey() {
104 return moduleKey;
105 }
106
107 @Override
108 @CheckForNull
109 public File buildDir() {
110 return buildDir;
111 }
112
113 @Override
114 public List<File> sourceDirs() {
115 return keepOnlyDirs(sourceDirsOrFiles);
116 }
117
118 public List<File> sources() {
119 return sourceDirsOrFiles;
120 }
121
122 @Override
123 public List<File> testDirs() {
124 return keepOnlyDirs(testDirsOrFiles);
125 }
126
127 public List<File> tests() {
128 return testDirsOrFiles;
129 }
130
131 private List<File> keepOnlyDirs(List<File> dirsOrFiles) {
132 List<File> result = new ArrayList<File>();
133 for (File f : dirsOrFiles) {
134 if (f.isDirectory()) {
135 result.add(f);
136 }
137 }
138 return result;
139 }
140
141 @Override
142 public List<File> binaryDirs() {
143 return binaryDirs;
144 }
145
146 @Override
147 public Charset encoding() {
148 final Charset charset;
149 String encoding = settings.getString(CoreProperties.ENCODING_PROPERTY);
150 if (StringUtils.isNotEmpty(encoding)) {
151 charset = Charset.forName(StringUtils.trim(encoding));
152 } else {
153 charset = Charset.defaultCharset();
154 }
155 return charset;
156 }
157
158 @Override
159 public boolean isDefaultJvmEncoding() {
160 return !settings.hasKey(CoreProperties.ENCODING_PROPERTY);
161 }
162
163 /**
164 * Should not be used - only for old plugins
165 *
166 * @deprecated since 4.0
167 */
168 @Deprecated
169 void addSourceDir(File dir) {
170 throw modificationNotPermitted();
171 }
172
173 /**
174 * Should not be used - only for old plugins
175 *
176 * @deprecated since 4.0
177 */
178 @Deprecated
179 void addTestDir(File dir) {
180 throw modificationNotPermitted();
181 }
182
183 private UnsupportedOperationException modificationNotPermitted() {
184 return new UnsupportedOperationException("Modifications of the file system are not permitted");
185 }
186
187 /**
188 * @return
189 * @deprecated in 4.2. Replaced by {@link #encoding()}
190 */
191 @Override
192 @Deprecated
193 public Charset sourceCharset() {
194 return encoding();
195 }
196
197 /**
198 * @deprecated in 4.2. Replaced by {@link #workDir()}
199 */
200 @Deprecated
201 @Override
202 public File workingDir() {
203 return workDir();
204 }
205
206 @Override
207 public List<File> files(FileQuery query) {
208 doPreloadFiles();
209 Collection<FilePredicate> predicates = Lists.newArrayList();
210 for (Map.Entry<String, Collection<String>> entry : query.attributes().entrySet()) {
211 predicates.add(fromDeprecatedAttribute(entry.getKey(), entry.getValue()));
212 }
213 return ImmutableList.copyOf(files(predicates().and(predicates)));
214 }
215
216 @Override
217 protected void doPreloadFiles() {
218 if (!initialized) {
219 LOG.warn("Accessing the filesystem before the Sensor phase is deprecated and will not be supported in the future. Please update your plugin.");
220 indexer.index(this);
221 }
222 }
223
224 public void resetDirs(File basedir, File buildDir, List<File> sourceDirs, List<File> testDirs, List<File> binaryDirs) {
225 if (initialized) {
226 throw MessageException.of("Module filesystem is already initialized. Modifications of filesystem are only allowed during Initializer phase.");
227 }
228 setBaseDir(basedir);
229 this.buildDir = buildDir;
230 this.sourceDirsOrFiles = existingDirsOrFiles(sourceDirs);
231 this.testDirsOrFiles = existingDirsOrFiles(testDirs);
232 this.binaryDirs = existingDirsOrFiles(binaryDirs);
233 }
234
235 public void index() {
236 if (initialized) {
237 throw MessageException.of("Module filesystem can only be indexed once");
238 }
239 initialized = true;
240 indexer.index(this);
241 if (componentIndexer != null) {
242 componentIndexer.execute(this);
243 }
244 }
245
246 private List<File> existingDirsOrFiles(List<File> dirsOrFiles) {
247 ImmutableList.Builder<File> builder = ImmutableList.builder();
248 for (File dirOrFile : dirsOrFiles) {
249 if (dirOrFile.exists()) {
250 builder.add(dirOrFile);
251 }
252 }
253 return builder.build();
254 }
255
256 private FilePredicate fromDeprecatedAttribute(String key, Collection<String> value) {
257 if ("TYPE".equals(key)) {
258 return predicates().or(Collections2.transform(value, new Function<String, FilePredicate>() {
259 @Override
260 public FilePredicate apply(@Nullable String s) {
261 return s == null ? predicates().all() : predicates().hasType(org.sonar.api.batch.fs.InputFile.Type.valueOf(s));
262 }
263 }));
264 }
265 if ("STATUS".equals(key)) {
266 return predicates().or(Collections2.transform(value, new Function<String, FilePredicate>() {
267 @Override
268 public FilePredicate apply(@Nullable String s) {
269 return s == null ? predicates().all() : predicates().hasStatus(org.sonar.api.batch.fs.InputFile.Status.valueOf(s));
270 }
271 }));
272 }
273 if ("LANG".equals(key)) {
274 return predicates().or(Collections2.transform(value, new Function<String, FilePredicate>() {
275 @Override
276 public FilePredicate apply(@Nullable String s) {
277 return s == null ? predicates().all() : predicates().hasLanguage(s);
278 }
279 }));
280 }
281 if ("CMP_KEY".equals(key)) {
282 return predicates().or(Collections2.transform(value, new Function<String, FilePredicate>() {
283 @Override
284 public FilePredicate apply(@Nullable String s) {
285 return s == null ? predicates().all() : new AdditionalFilePredicates.KeyPredicate(s);
286 }
287 }));
288 }
289 if ("CMP_DEPRECATED_KEY".equals(key)) {
290 return predicates().or(Collections2.transform(value, new Function<String, FilePredicate>() {
291 @Override
292 public FilePredicate apply(@Nullable String s) {
293 return s == null ? predicates().all() : new AdditionalFilePredicates.DeprecatedKeyPredicate(s);
294 }
295 }));
296 }
297 if ("SRC_REL_PATH".equals(key)) {
298 return predicates().or(Collections2.transform(value, new Function<String, FilePredicate>() {
299 @Override
300 public FilePredicate apply(@Nullable String s) {
301 return s == null ? predicates().all() : new AdditionalFilePredicates.SourceRelativePathPredicate(s);
302 }
303 }));
304 }
305 if ("SRC_DIR_PATH".equals(key)) {
306 return predicates().or(Collections2.transform(value, new Function<String, FilePredicate>() {
307 @Override
308 public FilePredicate apply(@Nullable String s) {
309 return s == null ? predicates().all() : new AdditionalFilePredicates.SourceDirPredicate(s);
310 }
311 }));
312 }
313 throw new IllegalArgumentException("Unsupported file attribute: " + key);
314 }
315
316 @Override
317 public boolean equals(Object o) {
318 if (this == o) {
319 return true;
320 }
321 if (o == null || getClass() != o.getClass()) {
322 return false;
323 }
324 DefaultModuleFileSystem that = (DefaultModuleFileSystem) o;
325 return moduleKey.equals(that.moduleKey);
326 }
327
328 @Override
329 public int hashCode() {
330 return moduleKey.hashCode();
331 }
332 }