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      private boolean isNew =true;
117    
118      // modules tree
119      private Project root;
120      private Project parent;
121      private List<Project> modules = new ArrayList<Project>();
122    
123      // internal use
124      private Snapshot snapshot;
125      private Integer id;
126      private Languages languages;
127    
128    
129      /**
130       * Creates a Project from a MavenProject (pom)
131       */
132      public Project(MavenProject mavenProject) {
133        this(mavenProject, new MapConfiguration(mavenProject.getProperties()));
134      }
135    
136      /**
137       * Creates a project from MavenProject and a configuration
138       */
139      public Project(MavenProject pom, Configuration configuration) {
140        this.mavenProject = pom;
141        this.fileSystem = new DefaultProjectFileSystem(this);
142        this.configuration = configuration;
143    
144        String branch = getBranch(configuration);
145        this.key = getMavenKey(pom, branch);
146        this.name = getMavenName(pom, branch);
147        this.packaging = pom.getPackaging();
148        this.description = pom.getDescription();
149      }
150    
151      /**
152       * Creates a Project from key, name, packaging and configuration
153       */
154      public Project(String key, String name, String packaging, Configuration conf) {
155        this.key = key;
156        this.name = name;
157        this.packaging = packaging;
158        this.configuration = conf;
159      }
160    
161      /**
162       * A project is considered as new if it's the first analysis ever done.
163       *
164       * @since 1.12
165       */
166      public boolean isNew() {
167        return isNew;
168      }
169    
170      public Project setIsNew(boolean b) {
171        this.isNew =b;
172        return this;
173      }
174      
175    
176      private static String getMavenKey(MavenProject pom, String branch) {
177        StringBuilder sb = new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId());
178        if (StringUtils.isNotBlank(branch)) {
179          sb.append(":").append(branch);
180        }
181        return sb.toString();
182      }
183    
184      private static String getMavenName(MavenProject pom, String branch) {
185        StringBuilder sb = new StringBuilder().append(pom.getName());
186        if (StringUtils.isNotBlank(branch)) {
187          sb.append(" ").append(branch);
188        }
189        return sb.toString();
190      }
191    
192      private static String getBranch(Configuration configuration) {
193        if (configuration != null) {
194          return configuration.getString(CoreProperties.PROJECT_BRANCH_PROPERTY, configuration.getString(PARAM_DEPRECATED_BRANCH));
195        }
196        return null;
197      }
198    
199      /**
200       * Internal use
201       */
202      public Snapshot getSnapshot() {
203        return snapshot;
204      }
205    
206      /**
207       * Internal use
208       */
209      public Integer getId() {
210        return id;
211      }
212    
213      /**
214       * Internal use
215       */
216      public Project setDatabaseSettings(Integer projectId, Snapshot snapshot) {
217        this.snapshot = snapshot;
218        this.id = projectId;
219        return this;
220      }
221    
222      /**
223       * Sets the project languaage
224       *
225       * @return the current object
226       */
227      public Project setLanguages(Languages languages) {
228        this.languages = languages;
229        return this;
230      }
231    
232      /**
233       * @return the project's root project
234       */
235      public Project getRoot() {
236        return root;
237      }
238    
239      /**
240       * @return the project's packaging
241       */
242      public String getPackaging() {
243        return packaging;
244      }
245    
246      /**
247       * @return whether the current project is root project
248       */
249      public boolean isRoot() {
250        return mavenProject.isExecutionRoot();
251      }
252    
253      /**
254       * @return whether the current project is a module
255       */
256      public boolean isModule() {
257        return !isRoot();
258      }
259    
260      /**
261       * @return the type of analysis of the project
262       */
263      public AnalysisType getAnalysisType() {
264        String value = getConfiguration().getString(CoreProperties.DYNAMIC_ANALYSIS_PROPERTY);
265        if (value == null) {
266          return (isSonarLightMode() ? AnalysisType.STATIC : AnalysisType.DYNAMIC);
267        }
268        if ("true".equals(value)) {
269          return AnalysisType.DYNAMIC;
270        }
271        if ("reuseReports".equals(value)) {
272          return AnalysisType.REUSE_REPORTS;
273        }
274        return AnalysisType.STATIC;
275      }
276    
277    
278      /**
279       * @deprecated since 1.12. Avoid coupling with Maven concepts.
280       */
281      @Deprecated
282      public String getGroupId() {
283        return mavenProject.getGroupId();
284      }
285    
286      /**
287       * @deprecated since 1.12. Avoid coupling with Maven concepts.
288       */
289      @Deprecated
290      public String getArtifactId() {
291        return mavenProject.getArtifactId();
292      }
293    
294      /**
295       * @return project's name
296       */
297      public String getName() {
298        return name;
299      }
300    
301      /**
302       * @return project's long name
303       */
304      public String getLongName() {
305        return null;
306      }
307    
308      /**
309       * @return project's description
310       */
311      public String getDescription() {
312        return description;
313      }
314    
315      /**
316       * @return the project language
317       */
318      public Language getLanguage() {
319        String key = getLanguageKey();
320        if (languages != null) {
321          return languages.get(key);
322        }
323        if (Java.KEY.equals(key)) {
324          return Java.INSTANCE;
325        }
326        return null;
327      }
328    
329      /**
330       * @return the language key
331       */
332      public String getLanguageKey() {
333        String key = configuration.getString(CoreProperties.PROJECT_LANGUAGE_PROPERTY);
334        return StringUtils.isBlank(key) ? Java.KEY : key;
335      }
336    
337      /**
338       * @return the scope of the current object
339       */
340      public String getScope() {
341        return SCOPE_SET;
342      }
343    
344      /**
345       * @return the qualifier of the current object
346       */
347      public String getQualifier() {
348        return isRoot() ? QUALIFIER_PROJECT : QUALIFIER_MODULE;
349      }
350    
351      public boolean matchFilePattern(String antPattern) {
352        return false;
353      }
354    
355      /**
356       * @return the current object's parent
357       */
358      public Project getParent() {
359        return parent;
360      }
361    
362      /**
363       * Sets a parent to the current object
364       */
365      public void setParent(Project parent) {
366        this.parent = parent;
367        if (parent != null) {
368          parent.modules.add(this);
369          if (parent.isRoot()) {
370            this.root = parent;
371          } else {
372            this.root = parent.getRoot();
373          }
374        }
375      }
376    
377      /**
378       * @return the list of modules
379       */
380      public List<Project> getModules() {
381        return modules;
382      }
383    
384      /**
385       * @return whether to use external source for rules configuration
386       */
387      public boolean getReuseExistingRulesConfig() {
388        return configuration.getBoolean(CoreProperties.REUSE_RULES_CONFIGURATION_PROPERTY, false);
389      }
390    
391      /**
392       * @return the current version of the project
393       */
394      public String getAnalysisVersion() {
395        String version = configuration.getString(CoreProperties.PROJECT_VERSION_PROPERTY);
396        if (version == null) {
397          version = mavenProject.getVersion();
398        }
399        return version;
400      }
401    
402      /**
403       * @return the analysis date, i.e. the date that will be used to store the snapshot
404       */
405      public Date getAnalysisDate() {
406        String formattedDate = configuration.getString(CoreProperties.PROJECT_DATE_PROPERTY);
407        if (formattedDate == null) {
408          return new Date();
409        }
410    
411        DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
412        try {
413          // see SONAR-908 make sure that a time is defined for the date.
414          Date date = DateUtils.setHours(format.parse(formattedDate), 0);
415          return DateUtils.setMinutes(date, 1);
416    
417        } catch (ParseException e) {
418          throw new SonarException("The property " + PARAM_DATE + " does not respect the format yyyy-MM-dd (for example 2008-05-23) : " + formattedDate, e);
419        }
420      }
421    
422      /**
423       * Project key is "groupId:artifactId[:branch]". Examples : org.struts:struts-core and org.codehaus.sonar:sonar:1.10
424       */
425      public String getKey() {
426        return key;
427      }
428    
429      /**
430       * Patterns of resource exclusion as defined in project settings page.
431       */
432      public String[] getExclusionPatterns() {
433        String[] exclusions = getConfiguration().getStringArray(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY);
434        if (exclusions == null) {
435          return new String[0];
436        }
437        return exclusions;
438      }
439    
440      /**
441       * Set exclusion patterns. Configuration is not saved, so this method must be used ONLY IN UNIT TESTS.
442       */
443      public void setExclusionPatterns(String[] patterns) {
444        getConfiguration().setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, patterns);
445      }
446    
447      public ProjectFileSystem getFileSystem() {
448        return fileSystem;
449      }
450    
451      /**
452       * @return the underlying maven project
453       */
454      public MavenProject getPom() {
455        return mavenProject;
456      }
457    
458      @Deprecated
459      public boolean isSonarLightMode() {
460        return configuration.getBoolean("sonar.light", false);
461      }
462    
463      /**
464       * @return the project configuration
465       */
466      public Configuration getConfiguration() {
467        return configuration;
468      }
469    
470      /**
471       * Sets the configuration
472    
473       * @return the current object
474       */
475      public Project setConfiguration(Configuration configuration) {
476        this.configuration = configuration;
477        return this;
478      }
479    
480      public Object getProperty(String key) {
481        return configuration.getProperty(key);
482      }
483    
484      @Override
485      public boolean equals(Object o) {
486        if (this == o) {
487          return true;
488        }
489        if (o == null || getClass() != o.getClass()) {
490          return false;
491        }
492        return ((Project) o).getKey().equals(getKey());
493      }
494    
495      @Override
496      public int hashCode() {
497        return getKey().hashCode();
498      }
499    
500      @Override
501      public String toString() {
502        return getKey();
503      }
504    }