001/* 002 * SonarQube, open source software quality management tool. 003 * Copyright (C) 2008-2014 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.ObjectUtils; 024import org.apache.commons.lang.StringUtils; 025import org.sonar.api.CoreProperties; 026 027import javax.annotation.CheckForNull; 028import javax.annotation.Nullable; 029 030import java.io.File; 031import java.util.*; 032import java.util.Map.Entry; 033 034/** 035 * Defines project metadata (key, name, source directories, ...). It's generally used by the 036 * {@link org.sonar.api.batch.bootstrap.ProjectBuilder extension point} and must not be used 037 * by other standard extensions. 038 * 039 * @since 2.9 040 */ 041public class ProjectDefinition { 042 043 public static final String SOURCES_PROPERTY = "sonar.sources"; 044 /** 045 * @deprecated since 4.5 use {@link #SOURCES_PROPERTY} 046 */ 047 @Deprecated 048 public static final String SOURCE_DIRS_PROPERTY = SOURCES_PROPERTY; 049 /** 050 * @deprecated since 4.5 use {@link #SOURCES_PROPERTY} 051 */ 052 @Deprecated 053 public static final String SOURCE_FILES_PROPERTY = "sonar.sourceFiles"; 054 055 public static final String TESTS_PROPERTY = "sonar.tests"; 056 /** 057 * @deprecated since 4.5 use {@link #TESTS_PROPERTY} 058 */ 059 @Deprecated 060 public static final String TEST_DIRS_PROPERTY = TESTS_PROPERTY; 061 /** 062 * @deprecated since 4.5 use {@link #TESTS_PROPERTY} 063 */ 064 @Deprecated 065 public static final String TEST_FILES_PROPERTY = "sonar.testFiles"; 066 /** 067 * @deprecated since 4.5.1 use SonarQube Java specific API 068 */ 069 @Deprecated 070 public static final String BINARIES_PROPERTY = "sonar.binaries"; 071 /** 072 * @deprecated since 4.5.1 use SonarQube Java specific API 073 */ 074 @Deprecated 075 public static final String LIBRARIES_PROPERTY = "sonar.libraries"; 076 public static final String BUILD_DIR_PROPERTY = "sonar.buildDir"; 077 078 private static final char SEPARATOR = ','; 079 080 private File baseDir, workDir, buildDir; 081 private Map<String, String> properties = new HashMap<String, String>(); 082 private ProjectDefinition parent = null; 083 private List<ProjectDefinition> subProjects = Lists.newArrayList(); 084 private List<Object> containerExtensions = Lists.newArrayList(); 085 086 private ProjectDefinition(Properties p) { 087 for (Entry<Object, Object> entry : p.entrySet()) { 088 this.properties.put(entry.getKey().toString(), entry.getValue().toString()); 089 } 090 } 091 092 /** 093 * @deprecated in 2.12, because it uses external object to represent internal state. 094 * To ensure backward-compatibility with Ant task this method cannot clone properties, 095 * so other callers must explicitly make clone of properties before passing into this method. 096 * Thus better to use {@link #create()} with combination of other methods like {@link #setProperties(Properties)} and {@link #setProperty(String, String)}. 097 */ 098 @Deprecated 099 public static ProjectDefinition create(Properties properties) { 100 return new ProjectDefinition(properties); 101 } 102 103 public static ProjectDefinition create() { 104 return new ProjectDefinition(new Properties()); 105 } 106 107 public ProjectDefinition setBaseDir(File baseDir) { 108 this.baseDir = baseDir; 109 return this; 110 } 111 112 public File getBaseDir() { 113 return baseDir; 114 } 115 116 public ProjectDefinition setWorkDir(@Nullable File workDir) { 117 this.workDir = workDir; 118 return this; 119 } 120 121 @CheckForNull 122 public File getWorkDir() { 123 return workDir; 124 } 125 126 public ProjectDefinition setBuildDir(@Nullable File d) { 127 this.buildDir = d; 128 return this; 129 } 130 131 @CheckForNull 132 public File getBuildDir() { 133 return buildDir; 134 } 135 136 /** 137 * @deprecated since 5.0 use {@link #properties()} 138 */ 139 @Deprecated 140 public Properties getProperties() { 141 Properties result = new Properties(); 142 for (Map.Entry<String, String> entry : properties.entrySet()) { 143 result.setProperty(entry.getKey(), entry.getValue()); 144 } 145 return result; 146 } 147 148 public Map<String, String> properties() { 149 return properties; 150 } 151 152 /** 153 * Copies specified properties into this object. 154 * 155 * @since 2.12 156 * @deprecated since 5.0 use {@link #setProperties(Map)} 157 */ 158 @Deprecated 159 public ProjectDefinition setProperties(Properties properties) { 160 for (Entry<Object, Object> entry : properties.entrySet()) { 161 this.properties.put(entry.getKey().toString(), entry.getValue().toString()); 162 } 163 return this; 164 } 165 166 public ProjectDefinition setProperties(Map<String, String> properties) { 167 this.properties.putAll(properties); 168 return this; 169 } 170 171 public ProjectDefinition setProperty(String key, String value) { 172 properties.put(key, value); 173 return this; 174 } 175 176 public ProjectDefinition setKey(String key) { 177 properties.put(CoreProperties.PROJECT_KEY_PROPERTY, key); 178 return this; 179 } 180 181 public ProjectDefinition setVersion(String s) { 182 properties.put(CoreProperties.PROJECT_VERSION_PROPERTY, StringUtils.defaultString(s)); 183 return this; 184 } 185 186 public ProjectDefinition setName(String s) { 187 properties.put(CoreProperties.PROJECT_NAME_PROPERTY, StringUtils.defaultString(s)); 188 return this; 189 } 190 191 public ProjectDefinition setDescription(String s) { 192 properties.put(CoreProperties.PROJECT_DESCRIPTION_PROPERTY, StringUtils.defaultString(s)); 193 return this; 194 } 195 196 public String getKey() { 197 return properties.get(CoreProperties.PROJECT_KEY_PROPERTY); 198 } 199 200 /** 201 * @since 4.5 202 */ 203 public String getKeyWithBranch() { 204 String branch = getBranch(); 205 String projectKey = getKey(); 206 if (StringUtils.isNotBlank(branch)) { 207 projectKey = String.format("%s:%s", projectKey, branch); 208 } 209 return projectKey; 210 } 211 212 @CheckForNull 213 private String getBranch() { 214 String branch = properties.get(CoreProperties.PROJECT_BRANCH_PROPERTY); 215 if (StringUtils.isNotBlank(branch)) { 216 return branch; 217 } else if (getParent() != null) { 218 return getParent().getBranch(); 219 } 220 return null; 221 } 222 223 public String getVersion() { 224 return properties.get(CoreProperties.PROJECT_VERSION_PROPERTY); 225 } 226 227 public String getName() { 228 String name = properties.get(CoreProperties.PROJECT_NAME_PROPERTY); 229 if (StringUtils.isBlank(name)) { 230 name = "Unnamed - " + getKey(); 231 } 232 return name; 233 } 234 235 public String getDescription() { 236 return properties.get(CoreProperties.PROJECT_DESCRIPTION_PROPERTY); 237 } 238 239 private void appendProperty(String key, String value) { 240 String current = (String) ObjectUtils.defaultIfNull(properties.get(key), ""); 241 if (StringUtils.isBlank(current)) { 242 properties.put(key, value); 243 } else { 244 properties.put(key, current + SEPARATOR + value); 245 } 246 } 247 248 /** 249 * @return Source files and folders. 250 */ 251 public List<String> sources() { 252 String sources = (String) ObjectUtils.defaultIfNull(properties.get(SOURCES_PROPERTY), ""); 253 return trim(StringUtils.split(sources, SEPARATOR)); 254 } 255 256 /** 257 * @deprecated since 4.5 use {@link #sources()} 258 */ 259 @Deprecated 260 public List<String> getSourceDirs() { 261 return sources(); 262 } 263 264 /** 265 * @param paths paths to file or directory with main sources. 266 * They can be absolute or relative to project base directory. 267 */ 268 public ProjectDefinition addSources(String... paths) { 269 for (String path : paths) { 270 appendProperty(SOURCES_PROPERTY, path); 271 } 272 return this; 273 } 274 275 /** 276 * @deprecated since 4.5 use {@link #addSources(String...)} 277 */ 278 @Deprecated 279 public ProjectDefinition addSourceDirs(String... paths) { 280 return addSources(paths); 281 } 282 283 public ProjectDefinition addSources(File... fileOrDirs) { 284 for (File fileOrDir : fileOrDirs) { 285 addSources(fileOrDir.getAbsolutePath()); 286 } 287 return this; 288 } 289 290 /** 291 * @deprecated since 4.5 use {@link #addSources(File...)} 292 */ 293 @Deprecated 294 public ProjectDefinition addSourceDirs(File... dirs) { 295 return addSources(dirs); 296 } 297 298 public ProjectDefinition resetSources() { 299 properties.remove(SOURCES_PROPERTY); 300 return this; 301 } 302 303 /** 304 * @deprecated since 4.5 use {@link #resetSources()} 305 */ 306 @Deprecated 307 public ProjectDefinition resetSourceDirs() { 308 return resetSources(); 309 } 310 311 public ProjectDefinition setSources(String... paths) { 312 resetSources(); 313 return addSources(paths); 314 } 315 316 /** 317 * @deprecated since 4.5 use {@link #setSources(String...)} 318 */ 319 @Deprecated 320 public ProjectDefinition setSourceDirs(String... paths) { 321 return setSources(paths); 322 } 323 324 public ProjectDefinition setSources(File... filesOrDirs) { 325 resetSources(); 326 for (File fileOrDir : filesOrDirs) { 327 addSources(fileOrDir.getAbsolutePath()); 328 } 329 return this; 330 } 331 332 /** 333 * @deprecated since 4.5 use {@link #setSources(File...)} 334 */ 335 @Deprecated 336 public ProjectDefinition setSourceDirs(File... dirs) { 337 resetSourceDirs(); 338 for (File dir : dirs) { 339 addSourceDirs(dir.getAbsolutePath()); 340 } 341 return this; 342 } 343 344 /** 345 * @deprecated since 4.5 use {@link #addSources(File...)} 346 */ 347 @Deprecated 348 public ProjectDefinition addSourceFiles(String... paths) { 349 // Hack for visual studio project builder that used to add baseDir first as source dir 350 List<String> sourceDirs = getSourceDirs(); 351 if (sourceDirs.size() == 1 && isDirectory(sourceDirs.get(0))) { 352 resetSources(); 353 } 354 return addSources(paths); 355 } 356 357 /** 358 * @deprecated since 4.5 use {@link #addSources(File...)} 359 */ 360 @Deprecated 361 public ProjectDefinition addSourceFiles(File... files) { 362 // Hack for visual studio project builder that used to add baseDir first as source dir 363 List<String> sourceDirs = getSourceDirs(); 364 if (sourceDirs.size() == 1 && isDirectory(sourceDirs.get(0))) { 365 resetSources(); 366 } 367 return addSources(files); 368 } 369 370 /** 371 * @deprecated since 4.5 use {@link #sources()} 372 */ 373 @Deprecated 374 public List<String> getSourceFiles() { 375 return sources(); 376 } 377 378 public List<String> tests() { 379 String sources = (String) ObjectUtils.defaultIfNull(properties.get(TESTS_PROPERTY), ""); 380 return trim(StringUtils.split(sources, SEPARATOR)); 381 } 382 383 /** 384 * @deprecated since 4.5 use {@link #tests()} 385 */ 386 @Deprecated 387 public List<String> getTestDirs() { 388 return tests(); 389 } 390 391 /** 392 * @param paths path to files or directories with test sources. 393 * It can be absolute or relative to project directory. 394 */ 395 public ProjectDefinition addTests(String... paths) { 396 for (String path : paths) { 397 appendProperty(TESTS_PROPERTY, path); 398 } 399 return this; 400 } 401 402 /** 403 * @deprecated since 4.5 use {@link #addTests(String...)} 404 */ 405 @Deprecated 406 public ProjectDefinition addTestDirs(String... paths) { 407 return addTests(paths); 408 } 409 410 public ProjectDefinition addTests(File... fileOrDirs) { 411 for (File fileOrDir : fileOrDirs) { 412 addTests(fileOrDir.getAbsolutePath()); 413 } 414 return this; 415 } 416 417 /** 418 * @deprecated since 4.5 use {@link #addTests(File...)} 419 */ 420 @Deprecated 421 public ProjectDefinition addTestDirs(File... dirs) { 422 return addTests(dirs); 423 } 424 425 public ProjectDefinition setTests(String... paths) { 426 resetTests(); 427 return addTests(paths); 428 } 429 430 /** 431 * @deprecated since 4.5 use {@link #setTests(String...)} 432 */ 433 @Deprecated 434 public ProjectDefinition setTestDirs(String... paths) { 435 return setTests(paths); 436 } 437 438 public ProjectDefinition setTests(File... fileOrDirs) { 439 resetTests(); 440 for (File dir : fileOrDirs) { 441 addTests(dir.getAbsolutePath()); 442 } 443 return this; 444 } 445 446 /** 447 * @deprecated since 4.5 use {@link #setTests(File...)} 448 */ 449 @Deprecated 450 public ProjectDefinition setTestDirs(File... dirs) { 451 return setTests(dirs); 452 } 453 454 public ProjectDefinition resetTests() { 455 properties.remove(TESTS_PROPERTY); 456 return this; 457 } 458 459 /** 460 * @deprecated since 4.5 use {@link #resetTests()} 461 */ 462 @Deprecated 463 public ProjectDefinition resetTestDirs() { 464 return resetTests(); 465 } 466 467 /** 468 * @deprecated since 4.5 use {@link #addTests(String...)} 469 */ 470 @Deprecated 471 public ProjectDefinition addTestFiles(String... paths) { 472 // Hack for visual studio project builder that used to add baseDir first as test dir 473 List<String> testDirs = getTestDirs(); 474 if (testDirs.size() == 1 && isDirectory(testDirs.get(0))) { 475 resetTests(); 476 } 477 return addTests(paths); 478 } 479 480 private boolean isDirectory(String relativeOrAbsoluteDir) { 481 File file = new File(relativeOrAbsoluteDir); 482 if (!file.isAbsolute()) { 483 file = new File(baseDir, relativeOrAbsoluteDir); 484 } 485 return file.isDirectory(); 486 } 487 488 /** 489 * @deprecated since 4.5 use {@link #addTests(File...)} 490 */ 491 @Deprecated 492 public ProjectDefinition addTestFiles(File... files) { 493 // Hack for visual studio project builder that used to add baseDir first as test dir 494 List<String> testDirs = getTestDirs(); 495 if (testDirs.size() == 1 && isDirectory(testDirs.get(0))) { 496 resetTests(); 497 } 498 return addTests(files); 499 } 500 501 /** 502 * @deprecated since 4.5 use {@link #tests()} 503 */ 504 @Deprecated 505 public List<String> getTestFiles() { 506 return tests(); 507 } 508 509 /** 510 * @deprecated since 4.5.1 use SonarQube Java specific API 511 */ 512 @Deprecated 513 public List<String> getBinaries() { 514 String sources = (String) ObjectUtils.defaultIfNull(properties.get(BINARIES_PROPERTY), ""); 515 return trim(StringUtils.split(sources, SEPARATOR)); 516 } 517 518 /** 519 * @param path path to directory with compiled source. In case of Java this is directory with class files. 520 * It can be absolute or relative to project directory. 521 * @deprecated since 4.5.1 use SonarQube Java specific API 522 */ 523 @Deprecated 524 public ProjectDefinition addBinaryDir(String path) { 525 appendProperty(BINARIES_PROPERTY, path); 526 return this; 527 } 528 529 /** 530 * @deprecated since 4.5.1 use SonarQube Java specific API 531 */ 532 @Deprecated 533 public ProjectDefinition addBinaryDir(File f) { 534 return addBinaryDir(f.getAbsolutePath()); 535 } 536 537 /** 538 * @deprecated since 4.5.1 use SonarQube Java specific API 539 */ 540 @Deprecated 541 public List<String> getLibraries() { 542 String sources = (String) ObjectUtils.defaultIfNull(properties.get(LIBRARIES_PROPERTY), ""); 543 return trim(StringUtils.split(sources, SEPARATOR)); 544 } 545 546 /** 547 * @param path path to file with third-party library. In case of Java this is path to jar file. 548 * It can be absolute or relative to project directory. 549 * @deprecated since 4.5.1 use SonarQube Java specific API 550 */ 551 @Deprecated 552 public void addLibrary(String path) { 553 appendProperty(LIBRARIES_PROPERTY, path); 554 } 555 556 /** 557 * Adds an extension, which would be available in PicoContainer during analysis of this project. 558 * 559 * @since 2.8 560 */ 561 public ProjectDefinition addContainerExtension(Object extension) { 562 containerExtensions.add(extension); 563 return this; 564 } 565 566 /** 567 * @since 2.8 568 */ 569 public List<Object> getContainerExtensions() { 570 return containerExtensions; 571 } 572 573 /** 574 * @since 2.8 575 */ 576 public ProjectDefinition addSubProject(ProjectDefinition child) { 577 subProjects.add(child); 578 child.setParent(this); 579 return this; 580 } 581 582 public ProjectDefinition getParent() { 583 return parent; 584 } 585 586 public void remove() { 587 if (parent != null) { 588 parent.subProjects.remove(this); 589 parent = null; 590 subProjects.clear(); 591 } 592 } 593 594 private void setParent(ProjectDefinition parent) { 595 this.parent = parent; 596 } 597 598 /** 599 * @since 2.8 600 */ 601 public List<ProjectDefinition> getSubProjects() { 602 return subProjects; 603 } 604 605 private static List<String> trim(String[] strings) { 606 List<String> result = Lists.newArrayList(); 607 for (String s : strings) { 608 result.add(StringUtils.trim(s)); 609 } 610 return result; 611 } 612 613 @Override 614 public boolean equals(Object o) { 615 if (this == o) { 616 return true; 617 } 618 if (o == null || getClass() != o.getClass()) { 619 return false; 620 } 621 ProjectDefinition that = (ProjectDefinition) o; 622 String key = getKey(); 623 if (key != null ? !key.equals(that.getKey()) : that.getKey() != null) { 624 return false; 625 } 626 627 return true; 628 } 629 630 @Override 631 public int hashCode() { 632 String key = getKey(); 633 return key != null ? key.hashCode() : 0; 634 } 635}