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.bootstrap; 021 022import com.google.common.collect.Lists; 023import org.apache.commons.lang.StringUtils; 024import org.sonar.api.CoreProperties; 025 026import java.io.File; 027import java.util.List; 028import java.util.Properties; 029 030/** 031 * Defines project metadata (key, name, source directories, ...). It's generally used by the 032 * {@link org.sonar.api.batch.bootstrap.ProjectBuilder extension point} and must not be used 033 * by other standard extensions. 034 * 035 * @since 2.9 036 */ 037public class ProjectDefinition { 038 039 public static final String SOURCE_DIRS_PROPERTY = "sonar.sources"; 040 public static final String SOURCE_FILES_PROPERTY = "sonar.sourceFiles"; 041 public static final String TEST_DIRS_PROPERTY = "sonar.tests"; 042 public static final String TEST_FILES_PROPERTY = "sonar.testFiles"; 043 public static final String BINARIES_PROPERTY = "sonar.binaries"; 044 public static final String LIBRARIES_PROPERTY = "sonar.libraries"; 045 public static final String BUILD_DIR_PROPERTY = "sonar.buildDir"; 046 047 private static final char SEPARATOR = ','; 048 049 private File baseDir, workDir, buildDir; 050 private Properties properties = new Properties(); 051 private ProjectDefinition parent = null; 052 private List<ProjectDefinition> subProjects = Lists.newArrayList(); 053 private List<Object> containerExtensions = Lists.newArrayList(); 054 055 private ProjectDefinition(Properties p) { 056 this.properties = p; 057 } 058 059 /** 060 * @deprecated in 2.12, because it uses external object to represent internal state. 061 * To ensure backward-compatibility with Ant task this method cannot clone properties, 062 * so other callers must explicitly make clone of properties before passing into this method. 063 * Thus better to use {@link #create()} with combination of other methods like {@link #setProperties(Properties)} and {@link #setProperty(String, String)}. 064 */ 065 @Deprecated 066 public static ProjectDefinition create(Properties properties) { 067 return new ProjectDefinition(properties); 068 } 069 070 public static ProjectDefinition create() { 071 return new ProjectDefinition(new Properties()); 072 } 073 074 public ProjectDefinition setBaseDir(File baseDir) { 075 this.baseDir = baseDir; 076 return this; 077 } 078 079 public File getBaseDir() { 080 return baseDir; 081 } 082 083 public ProjectDefinition setWorkDir(File workDir) { 084 this.workDir = workDir; 085 return this; 086 } 087 088 public File getWorkDir() { 089 return workDir; 090 } 091 092 public ProjectDefinition setBuildDir(File d) { 093 this.buildDir = d; 094 return this; 095 } 096 097 public File getBuildDir() { 098 return buildDir; 099 } 100 101 public Properties getProperties() { 102 return properties; 103 } 104 105 /** 106 * Copies specified properties into this object. 107 * 108 * @since 2.12 109 */ 110 public ProjectDefinition setProperties(Properties properties) { 111 this.properties.putAll(properties); 112 return this; 113 } 114 115 public ProjectDefinition setProperty(String key, String value) { 116 properties.setProperty(key, value); 117 return this; 118 } 119 120 public ProjectDefinition setKey(String key) { 121 properties.setProperty(CoreProperties.PROJECT_KEY_PROPERTY, key); 122 return this; 123 } 124 125 public ProjectDefinition setVersion(String s) { 126 properties.setProperty(CoreProperties.PROJECT_VERSION_PROPERTY, StringUtils.defaultString(s)); 127 return this; 128 } 129 130 public ProjectDefinition setName(String s) { 131 properties.setProperty(CoreProperties.PROJECT_NAME_PROPERTY, StringUtils.defaultString(s)); 132 return this; 133 } 134 135 public ProjectDefinition setDescription(String s) { 136 properties.setProperty(CoreProperties.PROJECT_DESCRIPTION_PROPERTY, StringUtils.defaultString(s)); 137 return this; 138 } 139 140 public String getKey() { 141 return properties.getProperty(CoreProperties.PROJECT_KEY_PROPERTY); 142 } 143 144 public String getVersion() { 145 return properties.getProperty(CoreProperties.PROJECT_VERSION_PROPERTY); 146 } 147 148 public String getName() { 149 String name = properties.getProperty(CoreProperties.PROJECT_NAME_PROPERTY); 150 if (StringUtils.isBlank(name)) { 151 name = "Unnamed - " + getKey(); 152 } 153 return name; 154 } 155 156 public String getDescription() { 157 return properties.getProperty(CoreProperties.PROJECT_DESCRIPTION_PROPERTY); 158 } 159 160 private void appendProperty(String key, String value) { 161 String newValue = properties.getProperty(key, "") + SEPARATOR + value; 162 properties.put(key, newValue); 163 } 164 165 public List<String> getSourceDirs() { 166 String sources = properties.getProperty(SOURCE_DIRS_PROPERTY, ""); 167 return trim(StringUtils.split(sources, SEPARATOR)); 168 } 169 170 /** 171 * @param paths paths to directory with main sources. 172 * They can be absolute or relative to project base directory. 173 */ 174 public ProjectDefinition addSourceDirs(String... paths) { 175 for (String path : paths) { 176 appendProperty(SOURCE_DIRS_PROPERTY, path); 177 } 178 return this; 179 } 180 181 public ProjectDefinition addSourceDirs(File... dirs) { 182 for (File dir : dirs) { 183 addSourceDirs(dir.getAbsolutePath()); 184 } 185 return this; 186 } 187 188 public ProjectDefinition resetSourceDirs() { 189 properties.remove(SOURCE_DIRS_PROPERTY); 190 return this; 191 } 192 193 public ProjectDefinition setSourceDirs(String... paths) { 194 resetSourceDirs(); 195 return addSourceDirs(paths); 196 } 197 198 public ProjectDefinition setSourceDirs(File... dirs) { 199 resetSourceDirs(); 200 for (File dir : dirs) { 201 addSourceDirs(dir.getAbsolutePath()); 202 } 203 return this; 204 } 205 206 /** 207 * Adding source files is possible only if no source directories have been set. 208 * Absolute path or relative path from project base dir. 209 */ 210 public ProjectDefinition addSourceFiles(String... paths) { 211 for (String path : paths) { 212 appendProperty(SOURCE_FILES_PROPERTY, path); 213 } 214 return this; 215 } 216 217 /** 218 * Adding source files is possible only if no source directories have been set. 219 */ 220 public ProjectDefinition addSourceFiles(File... files) { 221 for (File file : files) { 222 addSourceFiles(file.getAbsolutePath()); 223 } 224 return this; 225 } 226 227 public List<String> getSourceFiles() { 228 String sources = properties.getProperty(SOURCE_FILES_PROPERTY, ""); 229 return trim(StringUtils.split(sources, SEPARATOR)); 230 } 231 232 public List<String> getTestDirs() { 233 String sources = properties.getProperty(TEST_DIRS_PROPERTY, ""); 234 return trim(StringUtils.split(sources, SEPARATOR)); 235 } 236 237 /** 238 * @param paths path to directory with test sources. 239 * It can be absolute or relative to project directory. 240 */ 241 public ProjectDefinition addTestDirs(String... paths) { 242 for (String path : paths) { 243 appendProperty(TEST_DIRS_PROPERTY, path); 244 } 245 return this; 246 } 247 248 public ProjectDefinition addTestDirs(File... dirs) { 249 for (File dir : dirs) { 250 addTestDirs(dir.getAbsolutePath()); 251 } 252 return this; 253 } 254 255 public ProjectDefinition setTestDirs(String... paths) { 256 resetTestDirs(); 257 return addTestDirs(paths); 258 } 259 260 public ProjectDefinition setTestDirs(File... dirs) { 261 resetTestDirs(); 262 for (File dir : dirs) { 263 addTestDirs(dir.getAbsolutePath()); 264 } 265 return this; 266 } 267 268 public ProjectDefinition resetTestDirs() { 269 properties.remove(TEST_DIRS_PROPERTY); 270 return this; 271 } 272 273 /** 274 * Adding source files is possible only if no source directories have been set. 275 * Absolute path or relative path from project base dir. 276 */ 277 public ProjectDefinition addTestFiles(String... paths) { 278 for (String path : paths) { 279 appendProperty(TEST_FILES_PROPERTY, path); 280 } 281 return this; 282 } 283 284 /** 285 * Adding source files is possible only if no source directories have been set. 286 */ 287 public ProjectDefinition addTestFiles(File... files) { 288 for (File file : files) { 289 addTestFiles(file.getAbsolutePath()); 290 } 291 return this; 292 } 293 294 public List<String> getTestFiles() { 295 String sources = properties.getProperty(TEST_FILES_PROPERTY, ""); 296 return trim(StringUtils.split(sources, SEPARATOR)); 297 } 298 299 public List<String> getBinaries() { 300 String sources = properties.getProperty(BINARIES_PROPERTY, ""); 301 return trim(StringUtils.split(sources, SEPARATOR)); 302 } 303 304 /** 305 * @param path path to directory with compiled source. In case of Java this is directory with class files. 306 * It can be absolute or relative to project directory. 307 * TODO currently Sonar supports only one such directory due to dependency on MavenProject 308 */ 309 public ProjectDefinition addBinaryDir(String path) { 310 appendProperty(BINARIES_PROPERTY, path); 311 return this; 312 } 313 314 public ProjectDefinition addBinaryDir(File f) { 315 return addBinaryDir(f.getAbsolutePath()); 316 } 317 318 public List<String> getLibraries() { 319 String sources = properties.getProperty(LIBRARIES_PROPERTY, ""); 320 return trim(StringUtils.split(sources, SEPARATOR)); 321 } 322 323 /** 324 * @param path path to file with third-party library. In case of Java this is path to jar file. 325 * It can be absolute or relative to project directory. 326 */ 327 public void addLibrary(String path) { 328 appendProperty(LIBRARIES_PROPERTY, path); 329 } 330 331 /** 332 * Adds an extension, which would be available in PicoContainer during analysis of this project. 333 * 334 * @since 2.8 335 */ 336 public ProjectDefinition addContainerExtension(Object extension) { 337 containerExtensions.add(extension); 338 return this; 339 } 340 341 /** 342 * @since 2.8 343 */ 344 public List<Object> getContainerExtensions() { 345 return containerExtensions; 346 } 347 348 /** 349 * @since 2.8 350 */ 351 public ProjectDefinition addSubProject(ProjectDefinition child) { 352 subProjects.add(child); 353 child.setParent(this); 354 return this; 355 } 356 357 public ProjectDefinition getParent() { 358 return parent; 359 } 360 361 public void remove() { 362 if (parent != null) { 363 parent.subProjects.remove(this); 364 parent = null; 365 subProjects.clear(); 366 } 367 } 368 369 private void setParent(ProjectDefinition parent) { 370 this.parent = parent; 371 } 372 373 /** 374 * @since 2.8 375 */ 376 public List<ProjectDefinition> getSubProjects() { 377 return subProjects; 378 } 379 380 private static List<String> trim(String[] strings) { 381 List<String> result = Lists.newArrayList(); 382 for (String s : strings) { 383 result.add(StringUtils.trim(s)); 384 } 385 return result; 386 } 387 388 @Override 389 public boolean equals(Object o) { 390 if (this == o) { 391 return true; 392 } 393 if (o == null || getClass() != o.getClass()) { 394 return false; 395 } 396 ProjectDefinition that = (ProjectDefinition) o; 397 String key = getKey(); 398 if (key != null ? !key.equals(that.getKey()) : that.getKey() != null) { 399 return false; 400 } 401 402 return true; 403 } 404 405 @Override 406 public int hashCode() { 407 String key = getKey(); 408 return key != null ? key.hashCode() : 0; 409 } 410}