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