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