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