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