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 com.google.common.collect.ImmutableList;
023import com.google.common.collect.Lists;
024import org.apache.commons.configuration.Configuration;
025import org.apache.commons.lang.StringUtils;
026import org.apache.commons.lang.builder.ToStringBuilder;
027import org.apache.maven.project.MavenProject;
028import org.sonar.api.CoreProperties;
029import org.sonar.api.component.Component;
030
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   * @deprecated since version 1.11. Constant moved to CoreProperties
061   */
062  @Deprecated
063  public static final String PARAM_REUSE_RULES_CONFIG = CoreProperties.REUSE_RULES_CONFIGURATION_PROPERTY;
064
065  /**
066   * Enumerates the type of possible analysis
067   * @deprecated since 4.4 Since 4.3 SQ will no more run tests. So basically it's always reuse report.
068   */
069  @Deprecated
070  public enum AnalysisType {
071    STATIC, DYNAMIC, REUSE_REPORTS;
072
073    /**
074     * @param includeReuseReportMode whether to count report reuse as dynamic or not
075     * @return whether this a dynamic analysis
076     */
077    public boolean isDynamic(boolean includeReuseReportMode) {
078      return equals(Project.AnalysisType.DYNAMIC) ||
079        (equals(Project.AnalysisType.REUSE_REPORTS) && includeReuseReportMode);
080    }
081  }
082
083  private MavenProject pom;
084  private String branch;
085  private ProjectFileSystem fileSystem;
086  private Configuration configuration;
087  private String name;
088  private String description;
089  private String packaging;
090  private Language language;
091  private Date analysisDate;
092  private AnalysisType analysisType;
093  private String analysisVersion;
094
095  // modules tree
096  private Project parent;
097  private List<Project> modules = new ArrayList<Project>();
098
099  public Project(String key) {
100    setKey(key);
101    setDeprecatedKey(key);
102    setEffectiveKey(key);
103  }
104
105  public Project(String key, String branch, String name) {
106    if (StringUtils.isNotBlank(branch)) {
107      setKey(String.format(BRANCH_KEY_FORMAT, key, branch));
108      this.name = String.format("%s %s", name, branch);
109    } else {
110      setKey(key);
111      this.name = name;
112    }
113    setDeprecatedKey(getKey());
114    setEffectiveKey(getKey());
115    this.branch = branch;
116  }
117
118  public String getBranch() {
119    return branch;
120  }
121
122  /**
123   * For internal use only.
124   */
125  public Project setBranch(String branch) {
126    this.branch = branch;
127    return this;
128  }
129
130  /**
131   * For internal use only.
132   */
133  public final Project setPom(MavenProject pom) {
134    this.pom = pom;
135    return this;
136  }
137
138  /**
139   * @return the project's packaging
140   * @deprecated in 2.8. See http://jira.codehaus.org/browse/SONAR-2341
141   */
142  @Deprecated
143  public String getPackaging() {
144    return packaging;
145  }
146
147  @Override
148  public String getName() {
149    return name;
150  }
151
152  @Override
153  public String getLongName() {
154    return name;
155  }
156
157  @Override
158  public String getDescription() {
159    return description;
160  }
161
162  /**
163   * For internal use only.
164   */
165  public Project setName(String name) {
166    this.name = name;
167    return this;
168  }
169
170  /**
171   * For internal use only.
172   */
173  public Project setDescription(String description) {
174    this.description = description;
175    return this;
176  }
177
178  /**
179   * For internal use only.
180   *
181   * @deprecated in 2.8. See http://jira.codehaus.org/browse/SONAR-2341
182   */
183  @Deprecated
184  public Project setPackaging(String packaging) {
185    this.packaging = packaging;
186    return this;
187  }
188
189  /**
190   * @return whether the current project is root project
191   */
192  public boolean isRoot() {
193    return getParent() == null;
194  }
195
196  public Project getRoot() {
197    return parent == null ? this : parent.getRoot();
198  }
199
200  /**
201   * @return whether the current project is a module
202   */
203  public boolean isModule() {
204    return !isRoot();
205  }
206
207  /**
208   * @deprecated since 4.4 Since 4.3 SQ will no more run tests. So basically it's always reuse report.
209   */
210  @Deprecated
211  public AnalysisType getAnalysisType() {
212    return analysisType;
213  }
214
215  /**
216   * @deprecated since 4.4 Since 4.3 SQ will no more run tests. So basically it's always reuse report.
217   */
218  @Deprecated
219  public Project setAnalysisType(AnalysisType at) {
220    this.analysisType = at;
221    return this;
222  }
223
224  /**
225   * whether it's the latest analysis done on this project (displayed in sonar dashboard) or an analysis on a past revision.
226   *
227   * @since 2.0
228   * @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
229   */
230  @Deprecated
231  public boolean isLatestAnalysis() {
232    return true;
233  }
234
235  /**
236   * For internal use only.
237   *
238   * @deprecated in 3.6. It's not possible to analyze a project before the latest known quality snapshot.
239   * See http://jira.codehaus.org/browse/SONAR-4334
240   */
241  @Deprecated
242  public Project setLatestAnalysis(boolean b) {
243    if (!b) {
244      throw new UnsupportedOperationException("The analysis is always the latest one. " +
245        "Past analysis must be done in a chronological order.");
246    }
247    return this;
248  }
249
250  /**
251   * @return the project language when there is only one language
252   * @deprecated since 4.2 use {@link org.sonar.api.batch.fs.FileSystem#languages()}
253   */
254  @Deprecated
255  @Override
256  public Language getLanguage() {
257    return language;
258  }
259
260  /**
261   * Internal use
262   */
263  public Project setLanguage(Language language) {
264    this.language = language;
265    return this;
266  }
267
268  /**
269   * @return the language key or empty if no language is specified
270   * @deprecated since 4.2 use {@link org.sonar.api.batch.fs.FileSystem#languages()}
271   */
272  @Deprecated
273  public String getLanguageKey() {
274    return configuration.getString(CoreProperties.PROJECT_LANGUAGE_PROPERTY, "");
275  }
276
277  /**
278   * For internal use only.
279   */
280  public Project setAnalysisDate(Date analysisDate) {
281    this.analysisDate = analysisDate;
282    return this;
283  }
284
285  /**
286   * For internal use only.
287   */
288  public Project setAnalysisVersion(String analysisVersion) {
289    this.analysisVersion = analysisVersion;
290    return this;
291  }
292
293  /**
294   * @return the scope of the current object
295   */
296  @Override
297  public String getScope() {
298    return Scopes.PROJECT;
299  }
300
301  /**
302   * @return the qualifier of the current object
303   */
304  @Override
305  public String getQualifier() {
306    return isRoot() ? Qualifiers.PROJECT : Qualifiers.MODULE;
307  }
308
309  @Override
310  public boolean matchFilePattern(String antPattern) {
311    return false;
312  }
313
314  @Override
315  public Project getParent() {
316    return parent;
317  }
318
319  /**
320   * For internal use only.
321   */
322  public Project setParent(Project parent) {
323    this.parent = parent;
324    if (parent != null) {
325      parent.modules.add(this);
326    }
327    return this;
328  }
329
330  /**
331   * For internal use only.
332   */
333  public void removeFromParent() {
334    if (parent != null) {
335      parent.modules.remove(this);
336    }
337  }
338
339  /**
340   * @return the list of modules
341   */
342  public List<Project> getModules() {
343    return modules;
344  }
345
346  /**
347   * @return whether to use external source for rules configuration
348   * @deprecated since 2.5. See discussion from http://jira.codehaus.org/browse/SONAR-1873
349   */
350  @Deprecated
351  public boolean getReuseExistingRulesConfig() {
352    return configuration != null && configuration.getBoolean(CoreProperties.REUSE_RULES_CONFIGURATION_PROPERTY, false);
353  }
354
355  /**
356   * @return the current version of the project
357   */
358  public String getAnalysisVersion() {
359    return analysisVersion;
360  }
361
362  /**
363   * @return the analysis date, i.e. the date that will be used to store the snapshot
364   */
365  public Date getAnalysisDate() {
366    return analysisDate;
367  }
368
369  /**
370   * Patterns of resource exclusion as defined in project settings page.
371   *
372   * @since 3.3 also applies exclusions in general settings page and global exclusions.
373   * @deprecated replaced by {@link org.sonar.api.scan.filesystem.FileExclusions} in version 3.5
374   */
375  @Deprecated
376  public String[] getExclusionPatterns() {
377    return trimExclusions(ImmutableList.<String>builder()
378      .add(configuration.getStringArray(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY))
379      .add(configuration.getStringArray(CoreProperties.GLOBAL_EXCLUSIONS_PROPERTY)).build());
380  }
381
382  /**
383   * Patterns of test exclusion as defined in project settings page.
384   * Also applies exclusions in general settings page and global exclusions.
385   *
386   * @since 3.3
387   * @deprecated replaced by {@link org.sonar.api.scan.filesystem.FileExclusions} in version 3.5
388   */
389  @Deprecated
390  public String[] getTestExclusionPatterns() {
391    return trimExclusions(ImmutableList.<String>builder()
392      .add(configuration.getStringArray(CoreProperties.PROJECT_TEST_EXCLUSIONS_PROPERTY))
393      .add(configuration.getStringArray(CoreProperties.GLOBAL_TEST_EXCLUSIONS_PROPERTY)).build());
394  }
395
396  // http://jira.codehaus.org/browse/SONAR-2261 - exclusion must be trimmed
397  private static String[] trimExclusions(List<String> exclusions) {
398    List<String> trimmed = Lists.newArrayList();
399    for (String exclusion : exclusions) {
400      trimmed.add(StringUtils.trim(exclusion));
401    }
402    return trimmed.toArray(new String[trimmed.size()]);
403  }
404
405  /**
406   * Set exclusion patterns. Configuration is not saved, so this method must be used ONLY IN UNIT TESTS.
407   * @deprecated replaced by {@link org.sonar.api.scan.filesystem.FileExclusions} in version 3.5
408   */
409  @Deprecated
410  public Project setExclusionPatterns(String[] s) {
411    throw new UnsupportedOperationException("deprecated in 3.5");
412  }
413
414  /**
415   * Note: it's better to get a reference on ProjectFileSystem as an IoC dependency (constructor parameter)
416   * @deprecated replaced by {@link org.sonar.api.scan.filesystem.ModuleFileSystem} in 3.5
417   */
418  @Deprecated
419  public ProjectFileSystem getFileSystem() {
420    return fileSystem;
421  }
422
423  /**
424   * For internal use only.
425   *
426   * @deprecated since 2.6. See http://jira.codehaus.org/browse/SONAR-2126
427   */
428  @Deprecated
429  public Project setFileSystem(ProjectFileSystem fs) {
430    this.fileSystem = fs;
431    return this;
432  }
433
434  /**
435   * @deprecated since 2.5. See http://jira.codehaus.org/browse/SONAR-2011
436   */
437  @Deprecated
438  public String getGroupId() {
439    return pom.getGroupId();
440  }
441
442  /**
443   * @deprecated since 2.5. See http://jira.codehaus.org/browse/SONAR-2011
444   */
445  @Deprecated
446  public String getArtifactId() {
447    return pom.getArtifactId();
448  }
449
450  /**
451   * @return the underlying Maven project
452   * @deprecated since 2.5. See http://jira.codehaus.org/browse/SONAR-2011 ,
453   *             MavenProject can be retrieved as an IoC dependency
454   */
455  @Deprecated
456  public MavenProject getPom() {
457    return pom;
458  }
459
460  /**
461   * @return the project configuration
462   * @deprecated since 2.12. The component org.sonar.api.config.Settings must be used.
463   */
464  @Deprecated
465  public Configuration getConfiguration() {
466    return configuration;
467  }
468
469  /**
470   * For internal use only.
471   */
472  public final Project setConfiguration(Configuration configuration) {
473    this.configuration = configuration;
474    return this;
475  }
476
477  /**
478   * @deprecated since 3.6. Replaced by {@link org.sonar.api.config.Settings}.
479   */
480  @Deprecated
481  public Object getProperty(String key) {
482    return configuration != null ? configuration.getProperty(key) : null;
483  }
484
485  public static Project createFromMavenIds(String groupId, String artifactId) {
486    return createFromMavenIds(groupId, artifactId, null);
487  }
488
489  public static Project createFromMavenIds(String groupId, String artifactId, @Nullable String branch) {
490    return new Project(String.format(MAVEN_KEY_FORMAT, groupId, artifactId), branch, "");
491  }
492
493  @Override
494  public String toString() {
495    return new ToStringBuilder(this)
496      .append("id", getId())
497      .append("key", getKey())
498      .append("qualifier", getQualifier())
499      .toString();
500  }
501
502  @Override
503  public String key() {
504    return getKey();
505  }
506
507  @Override
508  public String name() {
509    return getName();
510  }
511
512  @Override
513  public String path() {
514    return getPath();
515  }
516
517  @Override
518  public String longName() {
519    return getLongName();
520  }
521
522  @Override
523  public String qualifier() {
524    return getQualifier();
525  }
526}