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