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 }