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