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