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