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