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