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