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