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 */
020package org.sonar.api.batch.bootstrap;
021
022import com.google.common.collect.Lists;
023import org.apache.commons.lang.StringUtils;
024import org.sonar.api.BatchComponent;
025import org.sonar.api.CoreProperties;
026
027import java.io.File;
028import java.util.List;
029import 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 */
038public 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}