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 */
020package org.sonar.api.resources;
021
022import java.util.ArrayList;
023import java.util.Date;
024import java.util.List;
025import javax.annotation.CheckForNull;
026import javax.annotation.Nullable;
027import org.apache.commons.lang.StringUtils;
028import org.apache.commons.lang.builder.ToStringBuilder;
029import org.sonar.api.CoreProperties;
030import org.sonar.api.batch.fs.FileSystem;
031import org.sonar.api.component.Component;
032import org.sonar.api.config.Settings;
033
034/**
035 * A class that manipulates Projects in the Sonar way.
036 *
037 * @since 1.10
038 */
039public class Project extends Resource implements Component {
040
041  /**
042   * Internal use
043   */
044  public static final Language NONE_LANGUAGE = new AbstractLanguage("none", "None") {
045    @Override
046    public String[] getFileSuffixes() {
047      return new String[0];
048    }
049  };
050
051  static final String MAVEN_KEY_FORMAT = "%s:%s";
052  private static final String BRANCH_KEY_FORMAT = "%s:%s";
053
054  public static final String SCOPE = Scopes.PROJECT;
055
056  /**
057   * Enumerates the type of possible analysis
058   * @deprecated since 4.4 Since 4.3 SQ will no more run tests. So basically it's always reuse report.
059   */
060  @Deprecated
061  public enum AnalysisType {
062    STATIC, DYNAMIC, REUSE_REPORTS;
063
064    /**
065     * @param includeReuseReportMode whether to count report reuse as dynamic or not
066     * @return whether this a dynamic analysis
067     */
068    public boolean isDynamic(boolean includeReuseReportMode) {
069      return equals(Project.AnalysisType.DYNAMIC) ||
070        (equals(Project.AnalysisType.REUSE_REPORTS) && includeReuseReportMode);
071    }
072  }
073
074  private String branch;
075  private ProjectFileSystem fileSystem;
076  private String name;
077  private String description;
078  private Language language;
079  private Date analysisDate;
080  private AnalysisType analysisType;
081  private String analysisVersion;
082  private Settings settings;
083
084  // For internal use
085  private java.io.File baseDir;
086
087  // modules tree
088  private Project parent;
089  private List<Project> modules = new ArrayList<>();
090
091  public Project(String key) {
092    setKey(key);
093    setEffectiveKey(key);
094  }
095
096  public Project(String key, String branch, String name) {
097    if (StringUtils.isNotBlank(branch)) {
098      setKey(String.format(BRANCH_KEY_FORMAT, key, branch));
099      this.name = String.format("%s %s", name, branch);
100    } else {
101      setKey(key);
102      this.name = name;
103    }
104    setEffectiveKey(getKey());
105    this.branch = branch;
106  }
107
108  public String getBranch() {
109    return branch;
110  }
111
112  /**
113   * For internal use only.
114   */
115  public Project setBranch(String branch) {
116    this.branch = branch;
117    return this;
118  }
119
120  @Override
121  public String getName() {
122    return name;
123  }
124
125  @Override
126  public String getLongName() {
127    return name;
128  }
129
130  @Override
131  public String getDescription() {
132    return description;
133  }
134
135  /**
136   * For internal use only.
137   */
138  public Project setName(String name) {
139    this.name = name;
140    return this;
141  }
142
143  /**
144   * For internal use only.
145   */
146  public Project setDescription(String description) {
147    this.description = description;
148    return this;
149  }
150
151  /**
152   * @return whether the current project is root project
153   */
154  public boolean isRoot() {
155    return getParent() == null;
156  }
157
158  public Project getRoot() {
159    return parent == null ? this : parent.getRoot();
160  }
161
162  /**
163   * @return whether the current project is a module
164   */
165  public boolean isModule() {
166    return !isRoot();
167  }
168
169  /**
170   * @deprecated since 4.4 Since 4.3 SQ will no more run tests. So basically it's always reuse report.
171   */
172  @Deprecated
173  public AnalysisType getAnalysisType() {
174    return analysisType;
175  }
176
177  /**
178   * @deprecated since 4.4 Since 4.3 SQ will no more run tests. So basically it's always reuse report.
179   */
180  @Deprecated
181  public Project setAnalysisType(AnalysisType at) {
182    this.analysisType = at;
183    return this;
184  }
185
186  /**
187   * whether it's the latest analysis done on this project (displayed in sonar dashboard) or an analysis on a past revision.
188   *
189   * @since 2.0
190   * @deprecated in 3.6. The analysis is now always the latest one (past analysis must be done in a chronological order). See http://jira.sonarsource.com/browse/SONAR-4334
191   */
192  @Deprecated
193  public boolean isLatestAnalysis() {
194    return true;
195  }
196
197  /**
198   * For internal use only.
199   *
200   * @deprecated in 3.6. It's not possible to analyze a project before the latest known quality snapshot.
201   * See http://jira.sonarsource.com/browse/SONAR-4334
202   */
203  @Deprecated
204  public Project setLatestAnalysis(boolean b) {
205    if (!b) {
206      throw new UnsupportedOperationException("The analysis is always the latest one. " +
207        "Past analysis must be done in a chronological order.");
208    }
209    return this;
210  }
211
212  /**
213   * @return the project language when there is only one language
214   * @deprecated since 4.2 use {@link org.sonar.api.batch.fs.FileSystem#languages()}
215   */
216  @Deprecated
217  @Override
218  public Language getLanguage() {
219    return language;
220  }
221
222  /**
223   * Internal use
224   */
225  public Project setLanguage(Language language) {
226    this.language = language;
227    return this;
228  }
229
230  /**
231   * @return the language key or empty if no language is specified
232   * @deprecated since 4.2 use {@link org.sonar.api.batch.fs.FileSystem#languages()}
233   */
234  @Deprecated
235  public String getLanguageKey() {
236    if (settings == null) {
237      throw new IllegalStateException("Project is not yet initialized");
238    }
239    return StringUtils.defaultIfEmpty(settings.getString(CoreProperties.PROJECT_LANGUAGE_PROPERTY), "");
240  }
241
242  /**
243   * Internal use
244   */
245  public Project setSettings(Settings settings) {
246    this.settings = settings;
247    return this;
248  }
249
250  /**
251   * Internal use for backward compatibility. Settings should be retrieved as an IoC dependency.
252   * @deprecated since 5.0
253   */
254  @Deprecated
255  public Settings getSettings() {
256    return settings;
257  }
258
259  /**
260   * For internal use only.
261   */
262  public Project setAnalysisDate(Date analysisDate) {
263    this.analysisDate = analysisDate;
264    return this;
265  }
266
267  /**
268   * For internal use only.
269   */
270  public Project setAnalysisVersion(String analysisVersion) {
271    this.analysisVersion = analysisVersion;
272    return this;
273  }
274
275  /**
276   * @return the scope of the current object
277   */
278  @Override
279  public String getScope() {
280    return Scopes.PROJECT;
281  }
282
283  /**
284   * @return the qualifier of the current object
285   */
286  @Override
287  public String getQualifier() {
288    return isRoot() ? Qualifiers.PROJECT : Qualifiers.MODULE;
289  }
290
291  @Override
292  public boolean matchFilePattern(String antPattern) {
293    return false;
294  }
295
296  @CheckForNull
297  @Override
298  public Project getParent() {
299    return parent;
300  }
301
302  /**
303   * For internal use only.
304   */
305  public Project setParent(Project parent) {
306    this.parent = parent;
307    if (parent != null) {
308      parent.modules.add(this);
309    }
310    return this;
311  }
312
313  /**
314   * For internal use only.
315   */
316  public void removeFromParent() {
317    if (parent != null) {
318      parent.modules.remove(this);
319    }
320  }
321
322  /**
323   * @return the list of modules
324   */
325  public List<Project> getModules() {
326    return modules;
327  }
328
329  /**
330   * @return the current version of the project
331   */
332  public String getAnalysisVersion() {
333    return analysisVersion;
334  }
335
336  /**
337   * @return the analysis date, i.e. the date that will be used to store the snapshot
338   */
339  public Date getAnalysisDate() {
340    return analysisDate;
341  }
342
343  /**
344   * Note: it's better to get a reference on ProjectFileSystem as an IoC dependency (constructor parameter)
345   * @deprecated since 3.5 use {@link FileSystem} instead
346   */
347  @Deprecated
348  public ProjectFileSystem getFileSystem() {
349    return fileSystem;
350  }
351
352  /**
353   * For internal use only.
354   *
355   * @deprecated since 2.6. See http://jira.sonarsource.com/browse/SONAR-2126
356   */
357  @Deprecated
358  public Project setFileSystem(ProjectFileSystem fs) {
359    this.fileSystem = fs;
360    return this;
361  }
362
363  public static Project createFromMavenIds(String groupId, String artifactId) {
364    return createFromMavenIds(groupId, artifactId, null);
365  }
366
367  public static Project createFromMavenIds(String groupId, String artifactId, @Nullable String branch) {
368    return new Project(String.format(MAVEN_KEY_FORMAT, groupId, artifactId), branch, "");
369  }
370
371  @Override
372  public String toString() {
373    return new ToStringBuilder(this)
374      .append("id", getId())
375      .append("key", getKey())
376      .append("qualifier", getQualifier())
377      .toString();
378  }
379
380  @Override
381  public String key() {
382    return getKey();
383  }
384
385  @Override
386  public String name() {
387    return getName();
388  }
389
390  @Override
391  public String path() {
392    return getPath();
393  }
394
395  @Override
396  public String longName() {
397    return getLongName();
398  }
399
400  @Override
401  public String qualifier() {
402    return getQualifier();
403  }
404
405  // For internal use
406  public void setBaseDir(java.io.File baseDir) {
407    this.baseDir = baseDir;
408  }
409
410  java.io.File getBaseDir() {
411    return baseDir;
412  }
413}