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 }