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