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