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