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.api.batch.fs.internal; 021 022 import com.google.common.base.Function; 023 import com.google.common.collect.Iterables; 024 import org.sonar.api.batch.fs.FilePredicate; 025 import org.sonar.api.batch.fs.FilePredicates; 026 import org.sonar.api.batch.fs.FileSystem; 027 import org.sonar.api.batch.fs.InputDir; 028 import org.sonar.api.batch.fs.InputFile; 029 import org.sonar.api.scan.filesystem.PathResolver; 030 import org.sonar.api.utils.PathUtils; 031 032 import javax.annotation.CheckForNull; 033 import javax.annotation.Nullable; 034 035 import java.io.File; 036 import java.nio.charset.Charset; 037 import java.util.ArrayList; 038 import java.util.Collections; 039 import java.util.HashMap; 040 import java.util.Iterator; 041 import java.util.Map; 042 import java.util.SortedSet; 043 import java.util.TreeSet; 044 045 /** 046 * @since 4.2 047 */ 048 public class DefaultFileSystem implements FileSystem { 049 050 private final Cache cache; 051 private final SortedSet<String> languages = new TreeSet<String>(); 052 private File baseDir; 053 private File workDir; 054 private Charset encoding; 055 private final FilePredicates predicates; 056 057 /** 058 * Only for testing 059 */ 060 public DefaultFileSystem(File baseDir) { 061 this(baseDir, new MapCache()); 062 } 063 064 protected DefaultFileSystem(File baseDir, Cache cache) { 065 // Basedir can be null with views 066 this.baseDir = baseDir != null ? baseDir.getAbsoluteFile() : new File("."); 067 this.cache = cache; 068 this.predicates = new DefaultFilePredicates(baseDir); 069 } 070 071 @Override 072 public File baseDir() { 073 return baseDir; 074 } 075 076 public void setBaseDir(File baseDir) { 077 this.baseDir = baseDir; 078 } 079 080 public DefaultFileSystem setEncoding(@Nullable Charset e) { 081 this.encoding = e; 082 return this; 083 } 084 085 @Override 086 public Charset encoding() { 087 return encoding == null ? Charset.defaultCharset() : encoding; 088 } 089 090 public boolean isDefaultJvmEncoding() { 091 return encoding == null; 092 } 093 094 public DefaultFileSystem setWorkDir(File d) { 095 this.workDir = d.getAbsoluteFile(); 096 return this; 097 } 098 099 @Override 100 public File workDir() { 101 return workDir; 102 } 103 104 @Override 105 public InputFile inputFile(FilePredicate predicate) { 106 Iterable<InputFile> files = inputFiles(predicate); 107 Iterator<InputFile> iterator = files.iterator(); 108 if (!iterator.hasNext()) { 109 return null; 110 } 111 InputFile first = iterator.next(); 112 if (!iterator.hasNext()) { 113 return first; 114 } 115 116 StringBuilder sb = new StringBuilder(); 117 sb.append("expected one element but was: <" + first); 118 for (int i = 0; i < 4 && iterator.hasNext(); i++) { 119 sb.append(", " + iterator.next()); 120 } 121 if (iterator.hasNext()) { 122 sb.append(", ..."); 123 } 124 sb.append('>'); 125 126 throw new IllegalArgumentException(sb.toString()); 127 128 } 129 130 @Override 131 public Iterable<InputFile> inputFiles(FilePredicate predicate) { 132 doPreloadFiles(); 133 return OptimizedFilePredicateAdapter.create(predicate).get(cache); 134 } 135 136 @Override 137 public boolean hasFiles(FilePredicate predicate) { 138 return inputFiles(predicate).iterator().hasNext(); 139 } 140 141 @Override 142 public Iterable<File> files(FilePredicate predicate) { 143 doPreloadFiles(); 144 return Iterables.transform(inputFiles(predicate), new Function<InputFile, File>() { 145 @Override 146 public File apply(InputFile input) { 147 return input.file(); 148 } 149 }); 150 } 151 152 @Override 153 public InputDir inputDir(File dir) { 154 doPreloadFiles(); 155 String relativePath = PathUtils.sanitize(new PathResolver().relativePath(baseDir, dir)); 156 if (relativePath == null) { 157 return null; 158 } 159 return cache.inputDir(relativePath); 160 } 161 162 /** 163 * Adds InputFile to the list and registers its language, if present. 164 */ 165 public DefaultFileSystem add(InputFile inputFile) { 166 cache.add(inputFile); 167 if (inputFile.language() != null) { 168 languages.add(inputFile.language()); 169 } 170 return this; 171 } 172 173 /** 174 * Adds InputDir to the list. 175 */ 176 public DefaultFileSystem add(InputDir inputDir) { 177 cache.add(inputDir); 178 return this; 179 } 180 181 /** 182 * Adds a language to the list. To be used only for unit tests that need to use {@link #languages()} without 183 * using {@link #add(org.sonar.api.batch.fs.InputFile)}. 184 */ 185 public DefaultFileSystem addLanguages(String language, String... others) { 186 languages.add(language); 187 Collections.addAll(languages, others); 188 return this; 189 } 190 191 @Override 192 public SortedSet<String> languages() { 193 doPreloadFiles(); 194 return languages; 195 } 196 197 @Override 198 public FilePredicates predicates() { 199 return predicates; 200 } 201 202 /** 203 * This method is called before each search of files. 204 */ 205 protected void doPreloadFiles() { 206 // nothing to do by default 207 } 208 209 public abstract static class Cache implements Index { 210 @Override 211 public abstract Iterable<InputFile> inputFiles(); 212 213 @Override 214 @CheckForNull 215 public abstract InputFile inputFile(String relativePath); 216 217 @Override 218 @CheckForNull 219 public abstract InputDir inputDir(String relativePath); 220 221 protected abstract void doAdd(InputFile inputFile); 222 223 protected abstract void doAdd(InputDir inputDir); 224 225 final void add(InputFile inputFile) { 226 doAdd(inputFile); 227 } 228 229 public void add(InputDir inputDir) { 230 doAdd(inputDir); 231 } 232 233 } 234 235 /** 236 * Used only for testing 237 */ 238 private static class MapCache extends Cache { 239 private final Map<String, InputFile> fileMap = new HashMap<String, InputFile>(); 240 private final Map<String, InputDir> dirMap = new HashMap<String, InputDir>(); 241 242 @Override 243 public Iterable<InputFile> inputFiles() { 244 return new ArrayList<InputFile>(fileMap.values()); 245 } 246 247 @Override 248 public InputFile inputFile(String relativePath) { 249 return fileMap.get(relativePath); 250 } 251 252 @Override 253 public InputDir inputDir(String relativePath) { 254 return dirMap.get(relativePath); 255 } 256 257 @Override 258 protected void doAdd(InputFile inputFile) { 259 fileMap.put(inputFile.relativePath(), inputFile); 260 } 261 262 @Override 263 protected void doAdd(InputDir inputDir) { 264 dirMap.put(inputDir.relativePath(), inputDir); 265 } 266 } 267 268 }