001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2009 SonarSource SA
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar 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 * Sonar 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
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.api.resources;
021
022 import org.apache.commons.io.FileUtils;
023 import org.apache.commons.io.FilenameUtils;
024 import org.apache.commons.io.filefilter.*;
025 import org.apache.commons.lang.StringUtils;
026 import org.sonar.api.batch.maven.MavenUtils;
027 import org.sonar.api.utils.SonarException;
028 import org.sonar.api.utils.WildcardPattern;
029
030 import java.io.File;
031 import java.io.IOException;
032 import java.nio.charset.Charset;
033 import java.util.ArrayList;
034 import java.util.Arrays;
035 import java.util.List;
036
037 /**
038 * @since 1.10
039 */
040 public class DefaultProjectFileSystem implements ProjectFileSystem {
041
042 private Project project;
043
044 public DefaultProjectFileSystem(Project project) {
045 this.project = project;
046 }
047
048 /**
049 * Source encoding. Never null, it returns the default plateform charset if it is not defined in project.
050 */
051 public Charset getSourceCharset() {
052 return MavenUtils.getSourceCharset(project.getPom());
053 }
054
055
056 /**
057 * Basedir is the project root directory.
058 */
059 public File getBasedir() {
060 return project.getPom().getBasedir();
061 }
062
063 /**
064 * Build directory is by default "target" in maven projects.
065 */
066 public File getBuildDir() {
067 return resolvePath(project.getPom().getBuild().getDirectory());
068 }
069
070 public File getBuildOutputDir() {
071 return resolvePath(project.getPom().getBuild().getOutputDirectory());
072 }
073
074 public List<File> getSourceDirs() {
075 return resolvePaths(project.getPom().getCompileSourceRoots());
076 }
077
078 public DefaultProjectFileSystem addSourceDir(File dir) {
079 if (dir == null) {
080 throw new IllegalArgumentException("Can not add null to project source dirs");
081 }
082 project.getPom().getCompileSourceRoots().add(0, dir.getAbsolutePath());
083 return this;
084 }
085
086 public List<File> getTestDirs() {
087 return resolvePaths(project.getPom().getTestCompileSourceRoots());
088 }
089
090 public DefaultProjectFileSystem addTestDir(File dir) {
091 if (dir == null) {
092 throw new IllegalArgumentException("Can not add null to project test dirs");
093 }
094 project.getPom().getTestCompileSourceRoots().add(0, dir.getAbsolutePath());
095 return this;
096 }
097
098 public File getReportOutputDir() {
099 return resolvePath(project.getPom().getReporting().getOutputDirectory());
100 }
101
102 public File getSonarWorkingDirectory() {
103 try {
104 File dir = new File(project.getPom().getBuild().getDirectory(), "sonar");
105 FileUtils.forceMkdir(dir);
106 return dir;
107
108 } catch (IOException e) {
109 throw new SonarException("Unable to retrieve Sonar working directory.", e);
110 }
111 }
112
113 public File resolvePath(String path) {
114 File file = new File(path);
115 if (!file.isAbsolute()) {
116 file = new File(project.getPom().getBasedir(), path);
117 }
118 return file;
119 }
120
121 private List<File> resolvePaths(List<String> paths) {
122 List<File> result = new ArrayList<File>();
123 if (paths != null) {
124 for (String path : paths) {
125 result.add(resolvePath(path));
126 }
127 }
128
129 return result;
130 }
131
132 public List<File> getSourceFiles(Language... langs) {
133 return getFiles(getSourceDirs(), true, langs);
134 }
135
136 public List<File> getJavaSourceFiles() {
137 return getSourceFiles(Java.INSTANCE);
138 }
139
140 public boolean hasJavaSourceFiles() {
141 return !getJavaSourceFiles().isEmpty();
142 }
143
144 public List<File> getTestFiles(Language... langs) {
145 return getFiles(getTestDirs(), false, langs);
146 }
147
148 public boolean hasTestFiles(Language lang) {
149 return !getTestFiles(lang).isEmpty();
150 }
151
152 private List<File> getFiles(List<File> directories, boolean applyExclusionPatterns, Language... langs) {
153 List<File> result = new ArrayList<File>();
154 if (directories == null || langs == null) {
155 return result;
156 }
157
158 IOFileFilter suffixFilter = getFileSuffixFilter(langs);
159 WildcardPattern[] exclusionPatterns = getExclusionPatterns(applyExclusionPatterns);
160
161 for (File dir : directories) {
162 if (dir.exists()) {
163 IOFileFilter exclusionFilter = new ExclusionFilter(dir, exclusionPatterns);
164 result.addAll(FileUtils.listFiles(dir, new AndFileFilter(suffixFilter, exclusionFilter), TrueFileFilter.INSTANCE));
165 }
166 }
167 return result;
168 }
169
170 private WildcardPattern[] getExclusionPatterns(boolean applyExclusionPatterns) {
171 WildcardPattern[] exclusionPatterns;
172 if (applyExclusionPatterns) {
173 exclusionPatterns = WildcardPattern.create(project.getExclusionPatterns());
174 } else {
175 exclusionPatterns = new WildcardPattern[0];
176 }
177 return exclusionPatterns;
178 }
179
180 private IOFileFilter getFileSuffixFilter(Language... langs) {
181 IOFileFilter suffixFilter;
182 if (langs.length == 0) {
183 suffixFilter = FileFilterUtils.trueFileFilter();
184
185 } else {
186 List<String> suffixes = new ArrayList<String>();
187 for (Language lang : langs) {
188 if (lang.getFileSuffixes() != null) {
189 suffixes.addAll(Arrays.asList(lang.getFileSuffixes()));
190 }
191 }
192 suffixFilter = new SuffixFileFilter(suffixes);
193 }
194 return suffixFilter;
195 }
196
197 private static class ExclusionFilter implements IOFileFilter {
198 File sourceDir;
199 WildcardPattern[] patterns;
200
201 ExclusionFilter(File sourceDir, WildcardPattern[] patterns) {
202 this.sourceDir = sourceDir;
203 this.patterns = patterns;
204 }
205
206 public boolean accept(File file) {
207 String relativePath = getRelativePath(file, sourceDir);
208 if (relativePath == null) {
209 return false;
210 }
211 for (WildcardPattern pattern : patterns) {
212 if (pattern.match(relativePath)) {
213 return false;
214 }
215 }
216 return true;
217 }
218
219 public boolean accept(File file, String name) {
220 return accept(file);
221 }
222 }
223
224 /**
225 * Save data into a new file of Sonar working directory.
226 *
227 * @return the created file
228 */
229 public File writeToWorkingDirectory(String content, String fileName) throws IOException {
230 return writeToFile(content, getSonarWorkingDirectory(), fileName);
231 }
232
233 protected static File writeToFile(String content, File dir, String fileName) throws IOException {
234 File file = new File(dir, fileName);
235 FileUtils.writeStringToFile(file, content, "UTF-8");
236 return file;
237 }
238
239 /**
240 * getRelativePath("c:/foo/src/my/package/Hello.java", "c:/foo/src") is "my/package/Hello.java"
241 *
242 * @return null if file is not in dir (including recursive subdirectories)
243 */
244 public static String getRelativePath(File file, File dir) {
245 return getRelativePath(file, Arrays.asList(dir));
246 }
247
248 /**
249 * getRelativePath("c:/foo/src/my/package/Hello.java", ["c:/bar", "c:/foo/src"]) is "my/package/Hello.java".
250 * <p/>
251 * <p>Relative path is composed of slashes. Windows backslaches are replaced by /</p>
252 *
253 * @return null if file is not in dir (including recursive subdirectories)
254 */
255 public static String getRelativePath(File file, List<File> dirs) {
256 List<String> stack = new ArrayList<String>();
257 String path = FilenameUtils.normalize(file.getAbsolutePath());
258 File cursor = new File(path);
259 while (cursor != null) {
260 if (containsFile(dirs, cursor)) {
261 return StringUtils.join(stack, "/");
262 }
263 stack.add(0, cursor.getName());
264 cursor = cursor.getParentFile();
265 }
266 return null;
267 }
268
269 public File getFileFromBuildDirectory(String filename) {
270 File file = new File(getBuildDir(), filename);
271 return (file.exists() ? file : null);
272 }
273
274 public Resource toResource(File file) {
275 if (file == null || !file.exists()) {
276 return null;
277 }
278
279 String relativePath = getRelativePath(file, getSourceDirs());
280 if (relativePath == null) {
281 return null;
282 }
283
284 return (file.isFile() ? new org.sonar.api.resources.File(relativePath) : new org.sonar.api.resources.Directory(relativePath));
285 }
286
287 private static boolean containsFile(List<File> dirs, File cursor) {
288 for (File dir : dirs) {
289 if (FilenameUtils.equalsNormalizedOnSystem(dir.getAbsolutePath(), cursor.getAbsolutePath())) {
290 return true;
291 }
292 }
293 return false;
294 }
295
296 }