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