001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2009 SonarSource SA
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     */
020    package org.sonar.api.resources;
021    
022    import org.apache.commons.configuration.Configuration;
023    import org.apache.commons.configuration.MapConfiguration;
024    import org.apache.commons.lang.StringUtils;
025    import org.apache.commons.lang.time.DateUtils;
026    import org.apache.maven.project.MavenProject;
027    import org.sonar.api.CoreProperties;
028    import org.sonar.api.database.model.Snapshot;
029    import org.sonar.api.utils.SonarException;
030    
031    import java.text.DateFormat;
032    import java.text.ParseException;
033    import java.text.SimpleDateFormat;
034    import java.util.ArrayList;
035    import java.util.Date;
036    import java.util.List;
037    
038    /**
039     * A class that manipulates Projects in the Sonar way, i.e. mixing MavenProjects with the way it should be analyzed
040     *
041     * @since 1.10
042     */
043    public class Project implements Resource {
044    
045      /**
046       * @deprecated since version 1.11. Constant moved to CoreProperties
047       */
048      @Deprecated
049      public static final String PARAM_DEPRECATED_BRANCH = "branch";
050    
051      /**
052       * @deprecated since version 1.11. Constant moved to CoreProperties
053       */
054      @Deprecated
055      public static final String PARAM_BRANCH = "sonar.branch";
056    
057      /**
058       * @deprecated since version 1.11. Constant moved to CoreProperties
059       */
060      @Deprecated
061      public static final String PARAM_VERSION = "sonar.projectVersion";
062    
063      /**
064       * @deprecated since version 1.11. Constant moved to CoreProperties
065       */
066      @Deprecated
067      public static final String PARAM_DATE = "sonar.projectDate";
068    
069      /**
070       * @deprecated since version 1.11. Constant moved to CoreProperties
071       */
072      @Deprecated
073      public static final String PARAM_LANGUAGE = "sonar.language";
074    
075      /**
076       * @deprecated since version 1.11. Constant moved to CoreProperties
077       */
078      @Deprecated
079      public static final String PARAM_DYNAMIC_ANALYSIS = "sonar.dynamicAnalysis";
080    
081      /**
082       * @deprecated since version 1.11. Constant moved to CoreProperties
083       */
084      @Deprecated
085      public static final String PARAM_EXCLUSIONS = "sonar.exclusions";
086    
087      /**
088       * @deprecated since version 1.11. Constant moved to CoreProperties
089       */
090      @Deprecated
091      public static final String PARAM_REUSE_RULES_CONFIG = "sonar.reuseExistingRulesConfiguration";
092    
093      /**
094       * Enumerates the type of possible analysis
095       */
096      public enum AnalysisType {
097        STATIC, DYNAMIC, REUSE_REPORTS;
098    
099        /**
100         * @param includeReuseReportMode whether to count report reuse as dynamic or not
101         * @return whether this a dynamic analysis
102         */
103        public boolean isDynamic(boolean includeReuseReportMode) {
104          return equals(Project.AnalysisType.DYNAMIC) ||
105            (equals(Project.AnalysisType.REUSE_REPORTS) && includeReuseReportMode);
106        }
107      }
108    
109      private MavenProject mavenProject;
110      private DefaultProjectFileSystem fileSystem;
111      private Configuration configuration;
112      private String key;
113      private String name;
114      private String packaging;
115      private String description;
116    
117      // modules tree
118      private Project root;
119      private Project parent;
120      private List<Project> modules = new ArrayList<Project>();
121    
122      // internal use
123      private Snapshot snapshot;
124      private Integer id;
125      private Languages languages;
126    
127    
128      /**
129       * Creates a Project from a MavenProject (pom)
130       */
131      public Project(MavenProject mavenProject) {
132        this(mavenProject, new MapConfiguration(mavenProject.getProperties()));
133      }
134    
135      /**
136       * Creates a project from MavenProject and a configuration
137       */
138      public Project(MavenProject pom, Configuration configuration) {
139        this.mavenProject = pom;
140        this.fileSystem = new DefaultProjectFileSystem(this);
141        this.configuration = configuration;
142    
143        String branch = getBranch(configuration);
144        this.key = getMavenKey(pom, branch);
145        this.name = getMavenName(pom, branch);
146        this.packaging = pom.getPackaging();
147        this.description = pom.getDescription();
148      }
149    
150      /**
151       * Creates a Project from key, name, packaging and configuration
152       */
153      public Project(String key, String name, String packaging, Configuration conf) {
154        this.key = key;
155        this.name = name;
156        this.packaging = packaging;
157        this.configuration = conf;
158      }
159    
160      private static String getMavenKey(MavenProject pom, String branch) {
161        StringBuilder sb = new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId());
162        if (StringUtils.isNotBlank(branch)) {
163          sb.append(":").append(branch);
164        }
165        return sb.toString();
166      }
167    
168      private static String getMavenName(MavenProject pom, String branch) {
169        StringBuilder sb = new StringBuilder().append(pom.getName());
170        if (StringUtils.isNotBlank(branch)) {
171          sb.append(" ").append(branch);
172        }
173        return sb.toString();
174      }
175    
176      private static String getBranch(Configuration configuration) {
177        if (configuration != null) {
178          return configuration.getString(CoreProperties.PROJECT_BRANCH_PROPERTY, configuration.getString(PARAM_DEPRECATED_BRANCH));
179        }
180        return null;
181      }
182    
183      /**
184       * Internal use
185       */
186      public Snapshot getSnapshot() {
187        return snapshot;
188      }
189    
190      /**
191       * Internal use
192       */
193      public Integer getId() {
194        return id;
195      }
196    
197      /**
198       * Internal use
199       */
200      public Project setDatabaseSettings(Integer projectId, Snapshot snapshot) {
201        this.snapshot = snapshot;
202        this.id = projectId;
203        return this;
204      }
205    
206      /**
207       * Sets the project languaage
208       *
209       * @return the current object
210       */
211      public Project setLanguages(Languages languages) {
212        this.languages = languages;
213        return this;
214      }
215    
216      /**
217       * @return the project's root project
218       */
219      public Project getRoot() {
220        return root;
221      }
222    
223      /**
224       * @return the project's packaging
225       */
226      public String getPackaging() {
227        return packaging;
228      }
229    
230      /**
231       * @return whether the current project is root project
232       */
233      public boolean isRoot() {
234        return mavenProject.isExecutionRoot();
235      }
236    
237      /**
238       * @return whether the current project is a module
239       */
240      public boolean isModule() {
241        return !isRoot();
242      }
243    
244      /**
245       * @return the type of analysis of the project
246       */
247      public AnalysisType getAnalysisType() {
248        String value = getConfiguration().getString(CoreProperties.DYNAMIC_ANALYSIS_PROPERTY);
249        if (value == null) {
250          return (isSonarLightMode() ? AnalysisType.STATIC : AnalysisType.DYNAMIC);
251        }
252        if ("true".equals(value)) {
253          return AnalysisType.DYNAMIC;
254        }
255        if ("reuseReports".equals(value)) {
256          return AnalysisType.REUSE_REPORTS;
257        }
258        return AnalysisType.STATIC;
259      }
260    
261    
262      /**
263       * @deprecated since 1.12. Avoid coupling with Maven concepts.
264       */
265      @Deprecated
266      public String getGroupId() {
267        return mavenProject.getGroupId();
268      }
269    
270      /**
271       * @deprecated since 1.12. Avoid coupling with Maven concepts.
272       */
273      @Deprecated
274      public String getArtifactId() {
275        return mavenProject.getArtifactId();
276      }
277    
278      /**
279       * @return project's name
280       */
281      public String getName() {
282        return name;
283      }
284    
285      /**
286       * @return project's long name
287       */
288      public String getLongName() {
289        return null;
290      }
291    
292      /**
293       * @return project's description
294       */
295      public String getDescription() {
296        return description;
297      }
298    
299      /**
300       * @return the project language
301       */
302      public Language getLanguage() {
303        String key = getLanguageKey();
304        if (languages != null) {
305          return languages.get(key);
306        }
307        if (Java.KEY.equals(key)) {
308          return Java.INSTANCE;
309        }
310        return null;
311      }
312    
313      /**
314       * @return the language key
315       */
316      public String getLanguageKey() {
317        String key = configuration.getString(CoreProperties.PROJECT_LANGUAGE_PROPERTY);
318        return StringUtils.isBlank(key) ? Java.KEY : key;
319      }
320    
321      /**
322       * @return the scope of the current object
323       */
324      public String getScope() {
325        return SCOPE_SET;
326      }
327    
328      /**
329       * @return the qualifier of the current object
330       */
331      public String getQualifier() {
332        return isRoot() ? QUALIFIER_PROJECT : QUALIFIER_MODULE;
333      }
334    
335      public boolean matchFilePattern(String antPattern) {
336        return false;
337      }
338    
339      /**
340       * @return the current object's parent
341       */
342      public Project getParent() {
343        return parent;
344      }
345    
346      /**
347       * Sets a parent to the current object
348       */
349      public void setParent(Project parent) {
350        this.parent = parent;
351        if (parent != null) {
352          parent.modules.add(this);
353          if (parent.isRoot()) {
354            this.root = parent;
355          } else {
356            this.root = parent.getRoot();
357          }
358        }
359      }
360    
361      /**
362       * @return the list of modules
363       */
364      public List<Project> getModules() {
365        return modules;
366      }
367    
368      /**
369       * @return whether to use external source for rules configuration
370       */
371      public boolean getReuseExistingRulesConfig() {
372        return configuration.getBoolean(CoreProperties.REUSE_RULES_CONFIGURATION_PROPERTY, false);
373      }
374    
375      /**
376       * @return the current version of the project
377       */
378      public String getAnalysisVersion() {
379        String version = configuration.getString(CoreProperties.PROJECT_VERSION_PROPERTY);
380        if (version == null) {
381          version = mavenProject.getVersion();
382        }
383        return version;
384      }
385    
386      /**
387       * @return the analysis date, i.e. the date that will be used to store the snapshot
388       */
389      public Date getAnalysisDate() {
390        String formattedDate = configuration.getString(CoreProperties.PROJECT_DATE_PROPERTY);
391        if (formattedDate == null) {
392          return new Date();
393        }
394    
395        DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
396        try {
397          // see SONAR-908 make sure that a time is defined for the date.
398          Date date = DateUtils.setHours(format.parse(formattedDate), 0);
399          return DateUtils.setMinutes(date, 1);
400    
401        } catch (ParseException e) {
402          throw new SonarException("The property " + PARAM_DATE + " does not respect the format yyyy-MM-dd (for example 2008-05-23) : " + formattedDate, e);
403        }
404      }
405    
406      /**
407       * Project key is "groupId:artifactId[:branch]". Examples : org.struts:struts-core and org.codehaus.sonar:sonar:1.10
408       */
409      public String getKey() {
410        return key;
411      }
412    
413      /**
414       * Patterns of resource exclusion as defined in project settings page.
415       */
416      public String[] getExclusionPatterns() {
417        String[] exclusions = getConfiguration().getStringArray(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY);
418        if (exclusions == null) {
419          return new String[0];
420        }
421        return exclusions;
422      }
423    
424      /**
425       * Set exclusion patterns. Configuration is not saved, so this method must be used ONLY IN UNIT TESTS.
426       */
427      public void setExclusionPatterns(String[] patterns) {
428        getConfiguration().setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, patterns);
429      }
430    
431      public ProjectFileSystem getFileSystem() {
432        return fileSystem;
433      }
434    
435      /**
436       * @return the underlying maven project
437       */
438      public MavenProject getPom() {
439        return mavenProject;
440      }
441    
442      @Deprecated
443      public boolean isSonarLightMode() {
444        return configuration.getBoolean("sonar.light", false);
445      }
446    
447      /**
448       * @return the project configuration
449       */
450      public Configuration getConfiguration() {
451        return configuration;
452      }
453    
454      /**
455       * Sets the configuration
456    
457       * @return the current object
458       */
459      public Project setConfiguration(Configuration configuration) {
460        this.configuration = configuration;
461        return this;
462      }
463    
464      public Object getProperty(String key) {
465        return configuration.getProperty(key);
466      }
467    
468      @Override
469      public boolean equals(Object o) {
470        if (this == o) {
471          return true;
472        }
473        if (o == null || getClass() != o.getClass()) {
474          return false;
475        }
476        return ((Project) o).getKey().equals(getKey());
477      }
478    
479      @Override
480      public int hashCode() {
481        return getKey().hashCode();
482      }
483    
484      @Override
485      public String toString() {
486        return getKey();
487      }
488    }