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