001/*
002 * SonarQube
003 * Copyright (C) 2009-2017 SonarSource SA
004 * mailto:info AT sonarsource DOT com
005 *
006 * This program 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 * This program 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 */
020package org.sonar.api.batch.bootstrap;
021
022import java.io.File;
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.Properties;
029import javax.annotation.CheckForNull;
030import org.apache.commons.lang.ObjectUtils;
031import org.apache.commons.lang.StringUtils;
032import org.sonar.api.CoreProperties;
033
034/**
035 * Defines project metadata (key, name, source directories, ...). It's generally used by the
036 * {@link org.sonar.api.batch.bootstrap.ProjectBuilder extension point} and must not be used
037 * by other standard extensions.
038 *
039 * @since 2.9
040 */
041public class ProjectDefinition {
042
043  public static final String SOURCES_PROPERTY = "sonar.sources";
044
045  public static final String TESTS_PROPERTY = "sonar.tests";
046
047  public static final String BUILD_DIR_PROPERTY = "sonar.buildDir";
048
049  private static final char SEPARATOR = ',';
050
051  private File baseDir;
052  private File workDir;
053  private File buildDir;
054  private Map<String, String> properties = new HashMap<>();
055  private ProjectDefinition parent = null;
056  private List<ProjectDefinition> subProjects = new ArrayList<>();
057
058  private ProjectDefinition(Properties p) {
059    for (Entry<Object, Object> entry : p.entrySet()) {
060      this.properties.put(entry.getKey().toString(), entry.getValue().toString());
061    }
062  }
063
064  public static ProjectDefinition create() {
065    return new ProjectDefinition(new Properties());
066  }
067
068  public ProjectDefinition setBaseDir(File baseDir) {
069    this.baseDir = baseDir;
070    return this;
071  }
072
073  public File getBaseDir() {
074    return baseDir;
075  }
076
077  public ProjectDefinition setWorkDir(File workDir) {
078    this.workDir = workDir;
079    return this;
080  }
081
082  public File getWorkDir() {
083    return workDir;
084  }
085
086  /**
087   * @deprecated since 6.1 notion of buildDir is not well defined
088   */
089  @Deprecated
090  public ProjectDefinition setBuildDir(File d) {
091    this.buildDir = d;
092    return this;
093  }
094
095  /**
096   * @deprecated since 6.1 notion of buildDir is not well defined
097   */
098  @Deprecated
099  public File getBuildDir() {
100    return buildDir;
101  }
102
103  /**
104   * @deprecated since 5.0 use {@link #properties()}
105   */
106  @Deprecated
107  public Properties getProperties() {
108    Properties result = new Properties();
109    for (Map.Entry<String, String> entry : properties.entrySet()) {
110      result.setProperty(entry.getKey(), entry.getValue());
111    }
112    return result;
113  }
114
115  public Map<String, String> properties() {
116    return properties;
117  }
118
119  /**
120   * Copies specified properties into this object.
121   *
122   * @since 2.12
123   * @deprecated since 5.0 use {@link #setProperties(Map)}
124   */
125  @Deprecated
126  public ProjectDefinition setProperties(Properties properties) {
127    for (Entry<Object, Object> entry : properties.entrySet()) {
128      this.properties.put(entry.getKey().toString(), entry.getValue().toString());
129    }
130    return this;
131  }
132
133  public ProjectDefinition setProperties(Map<String, String> properties) {
134    this.properties.putAll(properties);
135    return this;
136  }
137
138  public ProjectDefinition setProperty(String key, String value) {
139    properties.put(key, value);
140    return this;
141  }
142
143  public ProjectDefinition setKey(String key) {
144    properties.put(CoreProperties.PROJECT_KEY_PROPERTY, key);
145    return this;
146  }
147
148  public ProjectDefinition setVersion(String s) {
149    properties.put(CoreProperties.PROJECT_VERSION_PROPERTY, StringUtils.defaultString(s));
150    return this;
151  }
152
153  public ProjectDefinition setName(String s) {
154    properties.put(CoreProperties.PROJECT_NAME_PROPERTY, StringUtils.defaultString(s));
155    return this;
156  }
157
158  public ProjectDefinition setDescription(String s) {
159    properties.put(CoreProperties.PROJECT_DESCRIPTION_PROPERTY, StringUtils.defaultString(s));
160    return this;
161  }
162
163  public String getKey() {
164    return properties.get(CoreProperties.PROJECT_KEY_PROPERTY);
165  }
166
167  /**
168   * @since 4.5
169   */
170  public String getKeyWithBranch() {
171    String branch = getBranch();
172    String projectKey = getKey();
173    if (StringUtils.isNotBlank(branch)) {
174      projectKey = String.format("%s:%s", projectKey, branch);
175    }
176    return projectKey;
177  }
178
179  @CheckForNull
180  public String getBranch() {
181    String branch = properties.get(CoreProperties.PROJECT_BRANCH_PROPERTY);
182    if (StringUtils.isNotBlank(branch)) {
183      return branch;
184    } else if (getParent() != null) {
185      return getParent().getBranch();
186    }
187    return null;
188  }
189  
190  @CheckForNull
191  public String getOriginalVersion() {
192    return properties.get(CoreProperties.PROJECT_VERSION_PROPERTY);
193  }
194
195  public String getVersion() {
196    String version = properties.get(CoreProperties.PROJECT_VERSION_PROPERTY);
197    if (StringUtils.isBlank(version)) {
198      version = "not provided";
199    }
200    return version;
201  }
202  
203  @CheckForNull
204  public String getOriginalName() {
205    return properties.get(CoreProperties.PROJECT_NAME_PROPERTY);
206  }
207
208  public String getName() {
209    String name = properties.get(CoreProperties.PROJECT_NAME_PROPERTY);
210    if (StringUtils.isBlank(name)) {
211      name = getKey();
212    }
213    return name;
214  }
215
216  public String getDescription() {
217    return properties.get(CoreProperties.PROJECT_DESCRIPTION_PROPERTY);
218  }
219
220  private void appendProperty(String key, String value) {
221    String current = (String) ObjectUtils.defaultIfNull(properties.get(key), "");
222    if (StringUtils.isBlank(current)) {
223      properties.put(key, value);
224    } else {
225      properties.put(key, current + SEPARATOR + value);
226    }
227  }
228
229  /**
230   * @return Source files and folders.
231   */
232  public List<String> sources() {
233    String sources = (String) ObjectUtils.defaultIfNull(properties.get(SOURCES_PROPERTY), "");
234    return trim(StringUtils.split(sources, SEPARATOR));
235  }
236
237  /**
238   * @param paths paths to file or directory with main sources.
239   *              They can be absolute or relative to project base directory.
240   */
241  public ProjectDefinition addSources(String... paths) {
242    for (String path : paths) {
243      appendProperty(SOURCES_PROPERTY, path);
244    }
245    return this;
246  }
247
248  public ProjectDefinition addSources(File... fileOrDirs) {
249    for (File fileOrDir : fileOrDirs) {
250      addSources(fileOrDir.getAbsolutePath());
251    }
252    return this;
253  }
254
255  public ProjectDefinition resetSources() {
256    properties.remove(SOURCES_PROPERTY);
257    return this;
258  }
259
260  public ProjectDefinition setSources(String... paths) {
261    resetSources();
262    return addSources(paths);
263  }
264
265  public ProjectDefinition setSources(File... filesOrDirs) {
266    resetSources();
267    for (File fileOrDir : filesOrDirs) {
268      addSources(fileOrDir.getAbsolutePath());
269    }
270    return this;
271  }
272
273  public List<String> tests() {
274    String sources = (String) ObjectUtils.defaultIfNull(properties.get(TESTS_PROPERTY), "");
275    return trim(StringUtils.split(sources, SEPARATOR));
276  }
277
278  /**
279   * @param paths path to files or directories with test sources.
280   *              It can be absolute or relative to project directory.
281   */
282  public ProjectDefinition addTests(String... paths) {
283    for (String path : paths) {
284      appendProperty(TESTS_PROPERTY, path);
285    }
286    return this;
287  }
288
289  public ProjectDefinition addTests(File... fileOrDirs) {
290    for (File fileOrDir : fileOrDirs) {
291      addTests(fileOrDir.getAbsolutePath());
292    }
293    return this;
294  }
295
296  public ProjectDefinition setTests(String... paths) {
297    resetTests();
298    return addTests(paths);
299  }
300
301  public ProjectDefinition setTests(File... fileOrDirs) {
302    resetTests();
303    for (File dir : fileOrDirs) {
304      addTests(dir.getAbsolutePath());
305    }
306    return this;
307  }
308
309  public ProjectDefinition resetTests() {
310    properties.remove(TESTS_PROPERTY);
311    return this;
312  }
313
314  /**
315   * @since 2.8
316   */
317  public ProjectDefinition addSubProject(ProjectDefinition child) {
318    subProjects.add(child);
319    child.setParent(this);
320    return this;
321  }
322
323  public ProjectDefinition getParent() {
324    return parent;
325  }
326
327  public void remove() {
328    if (parent != null) {
329      parent.subProjects.remove(this);
330      parent = null;
331      subProjects.clear();
332    }
333  }
334
335  private void setParent(ProjectDefinition parent) {
336    this.parent = parent;
337  }
338
339  /**
340   * @since 2.8
341   */
342  public List<ProjectDefinition> getSubProjects() {
343    return subProjects;
344  }
345
346  private static List<String> trim(String[] strings) {
347    List<String> result = new ArrayList<>();
348    for (String s : strings) {
349      result.add(StringUtils.trim(s));
350    }
351    return result;
352  }
353
354  @Override
355  public boolean equals(Object o) {
356    if (this == o) {
357      return true;
358    }
359    if (o == null || getClass() != o.getClass()) {
360      return false;
361    }
362    ProjectDefinition that = (ProjectDefinition) o;
363    String key = getKey();
364    return !((key != null) ? !key.equals(that.getKey()) : (that.getKey() != null));
365
366  }
367
368  @Override
369  public int hashCode() {
370    String key = getKey();
371    return key != null ? key.hashCode() : 0;
372  }
373}