001/* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2012 SonarSource 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 */ 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 046 private static final char SEPARATOR = ','; 047 048 private File baseDir; 049 private File workDir; 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 Properties getProperties() { 093 return properties; 094 } 095 096 /** 097 * Copies specified properties into this object. 098 * 099 * @since 2.12 100 */ 101 public ProjectDefinition setProperties(Properties properties) { 102 this.properties.putAll(properties); 103 return this; 104 } 105 106 public ProjectDefinition setProperty(String key, String value) { 107 properties.setProperty(key, value); 108 return this; 109 } 110 111 public ProjectDefinition setKey(String key) { 112 properties.setProperty(CoreProperties.PROJECT_KEY_PROPERTY, key); 113 return this; 114 } 115 116 public ProjectDefinition setVersion(String s) { 117 properties.setProperty(CoreProperties.PROJECT_VERSION_PROPERTY, StringUtils.defaultString(s)); 118 return this; 119 } 120 121 public ProjectDefinition setName(String s) { 122 properties.setProperty(CoreProperties.PROJECT_NAME_PROPERTY, StringUtils.defaultString(s)); 123 return this; 124 } 125 126 public ProjectDefinition setDescription(String s) { 127 properties.setProperty(CoreProperties.PROJECT_DESCRIPTION_PROPERTY, StringUtils.defaultString(s)); 128 return this; 129 } 130 131 public String getKey() { 132 return properties.getProperty(CoreProperties.PROJECT_KEY_PROPERTY); 133 } 134 135 public String getVersion() { 136 return properties.getProperty(CoreProperties.PROJECT_VERSION_PROPERTY); 137 } 138 139 public String getName() { 140 String name = properties.getProperty(CoreProperties.PROJECT_NAME_PROPERTY); 141 if (StringUtils.isBlank(name)) { 142 name = "Unnamed - " + getKey(); 143 } 144 return name; 145 } 146 147 public String getDescription() { 148 return properties.getProperty(CoreProperties.PROJECT_DESCRIPTION_PROPERTY); 149 } 150 151 private void appendProperty(String key, String value) { 152 String newValue = properties.getProperty(key, "") + SEPARATOR + value; 153 properties.put(key, newValue); 154 } 155 156 public List<String> getSourceDirs() { 157 String sources = properties.getProperty(SOURCE_DIRS_PROPERTY, ""); 158 return trim(StringUtils.split(sources, SEPARATOR)); 159 } 160 161 /** 162 * @param paths paths to directory with main sources. 163 * They can be absolute or relative to project base directory. 164 */ 165 public ProjectDefinition addSourceDirs(String... paths) { 166 for (String path : paths) { 167 appendProperty(SOURCE_DIRS_PROPERTY, path); 168 } 169 return this; 170 } 171 172 public ProjectDefinition addSourceDirs(File... dirs) { 173 for (File dir : dirs) { 174 addSourceDirs(dir.getAbsolutePath()); 175 } 176 return this; 177 } 178 179 public ProjectDefinition resetSourceDirs() { 180 properties.remove(SOURCE_DIRS_PROPERTY); 181 return this; 182 } 183 184 public ProjectDefinition setSourceDirs(String... paths) { 185 resetSourceDirs(); 186 return addSourceDirs(paths); 187 } 188 189 public ProjectDefinition setSourceDirs(File... dirs) { 190 resetSourceDirs(); 191 for (File dir : dirs) { 192 addSourceDirs(dir.getAbsolutePath()); 193 } 194 return this; 195 } 196 197 /** 198 * Adding source files is possible only if no source directories have been set. 199 * Absolute path or relative path from project base dir. 200 */ 201 public ProjectDefinition addSourceFiles(String... paths) { 202 for (String path : paths) { 203 appendProperty(SOURCE_FILES_PROPERTY, path); 204 } 205 return this; 206 } 207 208 /** 209 * Adding source files is possible only if no source directories have been set. 210 */ 211 public ProjectDefinition addSourceFiles(File... files) { 212 for (File file : files) { 213 addSourceFiles(file.getAbsolutePath()); 214 } 215 return this; 216 } 217 218 public List<String> getSourceFiles() { 219 String sources = properties.getProperty(SOURCE_FILES_PROPERTY, ""); 220 return trim(StringUtils.split(sources, SEPARATOR)); 221 } 222 223 public List<String> getTestDirs() { 224 String sources = properties.getProperty(TEST_DIRS_PROPERTY, ""); 225 return trim(StringUtils.split(sources, SEPARATOR)); 226 } 227 228 /** 229 * @param paths path to directory with test sources. 230 * It can be absolute or relative to project directory. 231 */ 232 public ProjectDefinition addTestDirs(String... paths) { 233 for (String path : paths) { 234 appendProperty(TEST_DIRS_PROPERTY, path); 235 } 236 return this; 237 } 238 239 public ProjectDefinition addTestDirs(File... dirs) { 240 for (File dir : dirs) { 241 addTestDirs(dir.getAbsolutePath()); 242 } 243 return this; 244 } 245 246 public ProjectDefinition setTestDirs(String... paths) { 247 resetTestDirs(); 248 return addTestDirs(paths); 249 } 250 251 public ProjectDefinition setTestDirs(File... dirs) { 252 resetTestDirs(); 253 for (File dir : dirs) { 254 addTestDirs(dir.getAbsolutePath()); 255 } 256 return this; 257 } 258 259 public ProjectDefinition resetTestDirs() { 260 properties.remove(TEST_DIRS_PROPERTY); 261 return this; 262 } 263 264 /** 265 * Adding source files is possible only if no source directories have been set. 266 * Absolute path or relative path from project base dir. 267 */ 268 public ProjectDefinition addTestFiles(String... paths) { 269 for (String path : paths) { 270 appendProperty(TEST_FILES_PROPERTY, path); 271 } 272 return this; 273 } 274 275 /** 276 * Adding source files is possible only if no source directories have been set. 277 */ 278 public ProjectDefinition addTestFiles(File... files) { 279 for (File file : files) { 280 addTestFiles(file.getAbsolutePath()); 281 } 282 return this; 283 } 284 285 public List<String> getTestFiles() { 286 String sources = properties.getProperty(TEST_FILES_PROPERTY, ""); 287 return trim(StringUtils.split(sources, SEPARATOR)); 288 } 289 290 public List<String> getBinaries() { 291 String sources = properties.getProperty(BINARIES_PROPERTY, ""); 292 return trim(StringUtils.split(sources, SEPARATOR)); 293 } 294 295 /** 296 * @param path path to directory with compiled source. In case of Java this is directory with class files. 297 * It can be absolute or relative to project directory. 298 * @TODO currently Sonar supports only one such directory due to dependency on MavenProject 299 */ 300 public ProjectDefinition addBinaryDir(String path) { 301 appendProperty(BINARIES_PROPERTY, path); 302 return this; 303 } 304 305 public ProjectDefinition addBinaryDir(File f) { 306 return addBinaryDir(f.getAbsolutePath()); 307 } 308 309 public List<String> getLibraries() { 310 String sources = properties.getProperty(LIBRARIES_PROPERTY, ""); 311 return trim(StringUtils.split(sources, SEPARATOR)); 312 } 313 314 /** 315 * @param path path to file with third-party library. In case of Java this is path to jar file. 316 * It can be absolute or relative to project directory. 317 */ 318 public void addLibrary(String path) { 319 appendProperty(LIBRARIES_PROPERTY, path); 320 } 321 322 /** 323 * Adds an extension, which would be available in PicoContainer during analysis of this project. 324 * 325 * @since 2.8 326 */ 327 public ProjectDefinition addContainerExtension(Object extension) { 328 containerExtensions.add(extension); 329 return this; 330 } 331 332 /** 333 * @since 2.8 334 */ 335 public List<Object> getContainerExtensions() { 336 return containerExtensions; 337 } 338 339 /** 340 * @since 2.8 341 */ 342 public ProjectDefinition addSubProject(ProjectDefinition child) { 343 subProjects.add(child); 344 child.setParent(this); 345 return this; 346 } 347 348 public ProjectDefinition getParent() { 349 return parent; 350 } 351 352 public void remove() { 353 if (parent != null) { 354 parent.subProjects.remove(this); 355 parent = null; 356 subProjects.clear(); 357 } 358 } 359 360 private void setParent(ProjectDefinition parent) { 361 this.parent = parent; 362 } 363 364 /** 365 * @since 2.8 366 */ 367 public List<ProjectDefinition> getSubProjects() { 368 return subProjects; 369 } 370 371 private static List<String> trim(String[] strings) { 372 List<String> result = Lists.newArrayList(); 373 for (String s : strings) { 374 result.add(StringUtils.trim(s)); 375 } 376 return result; 377 } 378 379 @Override 380 public boolean equals(Object o) { 381 if (this == o) { 382 return true; 383 } 384 if (o == null || getClass() != o.getClass()) { 385 return false; 386 } 387 ProjectDefinition that = (ProjectDefinition) o; 388 String key = getKey(); 389 if (key != null ? !key.equals(that.getKey()) : that.getKey() != null) { 390 return false; 391 } 392 393 return true; 394 } 395 396 @Override 397 public int hashCode() { 398 String key = getKey(); 399 return key != null ? key.hashCode() : 0; 400 } 401}