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