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 */ 020package org.sonar.api.resources; 021 022import java.util.ArrayList; 023import java.util.Date; 024import java.util.List; 025import javax.annotation.CheckForNull; 026import javax.annotation.Nullable; 027import org.apache.commons.lang.StringUtils; 028import org.apache.commons.lang.builder.ToStringBuilder; 029import org.sonar.api.CoreProperties; 030import org.sonar.api.batch.fs.FileSystem; 031import org.sonar.api.component.Component; 032import org.sonar.api.config.Settings; 033 034/** 035 * A class that manipulates Projects in the Sonar way. 036 * 037 * @since 1.10 038 */ 039public class Project extends Resource implements Component { 040 041 /** 042 * Internal use 043 */ 044 public static final Language NONE_LANGUAGE = new AbstractLanguage("none", "None") { 045 @Override 046 public String[] getFileSuffixes() { 047 return new String[0]; 048 } 049 }; 050 051 static final String MAVEN_KEY_FORMAT = "%s:%s"; 052 private static final String BRANCH_KEY_FORMAT = "%s:%s"; 053 054 public static final String SCOPE = Scopes.PROJECT; 055 056 /** 057 * Enumerates the type of possible analysis 058 * @deprecated since 4.4 Since 4.3 SQ will no more run tests. So basically it's always reuse report. 059 */ 060 @Deprecated 061 public enum AnalysisType { 062 STATIC, DYNAMIC, REUSE_REPORTS; 063 064 /** 065 * @param includeReuseReportMode whether to count report reuse as dynamic or not 066 * @return whether this a dynamic analysis 067 */ 068 public boolean isDynamic(boolean includeReuseReportMode) { 069 return equals(Project.AnalysisType.DYNAMIC) || 070 (equals(Project.AnalysisType.REUSE_REPORTS) && includeReuseReportMode); 071 } 072 } 073 074 private String branch; 075 private ProjectFileSystem fileSystem; 076 private String name; 077 private String description; 078 private Language language; 079 private Date analysisDate; 080 private AnalysisType analysisType; 081 private String analysisVersion; 082 private Settings settings; 083 084 // For internal use 085 private java.io.File baseDir; 086 087 // modules tree 088 private Project parent; 089 private List<Project> modules = new ArrayList<>(); 090 091 public Project(String key) { 092 setKey(key); 093 setEffectiveKey(key); 094 } 095 096 public Project(String key, String branch, String name) { 097 if (StringUtils.isNotBlank(branch)) { 098 setKey(String.format(BRANCH_KEY_FORMAT, key, branch)); 099 this.name = String.format("%s %s", name, branch); 100 } else { 101 setKey(key); 102 this.name = name; 103 } 104 setEffectiveKey(getKey()); 105 this.branch = branch; 106 } 107 108 public String getBranch() { 109 return branch; 110 } 111 112 /** 113 * For internal use only. 114 */ 115 public Project setBranch(String branch) { 116 this.branch = branch; 117 return this; 118 } 119 120 @Override 121 public String getName() { 122 return name; 123 } 124 125 @Override 126 public String getLongName() { 127 return name; 128 } 129 130 @Override 131 public String getDescription() { 132 return description; 133 } 134 135 /** 136 * For internal use only. 137 */ 138 public Project setName(String name) { 139 this.name = name; 140 return this; 141 } 142 143 /** 144 * For internal use only. 145 */ 146 public Project setDescription(String description) { 147 this.description = description; 148 return this; 149 } 150 151 /** 152 * @return whether the current project is root project 153 */ 154 public boolean isRoot() { 155 return getParent() == null; 156 } 157 158 public Project getRoot() { 159 return parent == null ? this : parent.getRoot(); 160 } 161 162 /** 163 * @return whether the current project is a module 164 */ 165 public boolean isModule() { 166 return !isRoot(); 167 } 168 169 /** 170 * @deprecated since 4.4 Since 4.3 SQ will no more run tests. So basically it's always reuse report. 171 */ 172 @Deprecated 173 public AnalysisType getAnalysisType() { 174 return analysisType; 175 } 176 177 /** 178 * @deprecated since 4.4 Since 4.3 SQ will no more run tests. So basically it's always reuse report. 179 */ 180 @Deprecated 181 public Project setAnalysisType(AnalysisType at) { 182 this.analysisType = at; 183 return this; 184 } 185 186 /** 187 * whether it's the latest analysis done on this project (displayed in sonar dashboard) or an analysis on a past revision. 188 * 189 * @since 2.0 190 * @deprecated in 3.6. The analysis is now always the latest one (past analysis must be done in a chronological order). See http://jira.sonarsource.com/browse/SONAR-4334 191 */ 192 @Deprecated 193 public boolean isLatestAnalysis() { 194 return true; 195 } 196 197 /** 198 * For internal use only. 199 * 200 * @deprecated in 3.6. It's not possible to analyze a project before the latest known quality snapshot. 201 * See http://jira.sonarsource.com/browse/SONAR-4334 202 */ 203 @Deprecated 204 public Project setLatestAnalysis(boolean b) { 205 if (!b) { 206 throw new UnsupportedOperationException("The analysis is always the latest one. " + 207 "Past analysis must be done in a chronological order."); 208 } 209 return this; 210 } 211 212 /** 213 * @return the project language when there is only one language 214 * @deprecated since 4.2 use {@link org.sonar.api.batch.fs.FileSystem#languages()} 215 */ 216 @Deprecated 217 @Override 218 public Language getLanguage() { 219 return language; 220 } 221 222 /** 223 * Internal use 224 */ 225 public Project setLanguage(Language language) { 226 this.language = language; 227 return this; 228 } 229 230 /** 231 * @return the language key or empty if no language is specified 232 * @deprecated since 4.2 use {@link org.sonar.api.batch.fs.FileSystem#languages()} 233 */ 234 @Deprecated 235 public String getLanguageKey() { 236 if (settings == null) { 237 throw new IllegalStateException("Project is not yet initialized"); 238 } 239 return StringUtils.defaultIfEmpty(settings.getString(CoreProperties.PROJECT_LANGUAGE_PROPERTY), ""); 240 } 241 242 /** 243 * Internal use 244 */ 245 public Project setSettings(Settings settings) { 246 this.settings = settings; 247 return this; 248 } 249 250 /** 251 * Internal use for backward compatibility. Settings should be retrieved as an IoC dependency. 252 * @deprecated since 5.0 253 */ 254 @Deprecated 255 public Settings getSettings() { 256 return settings; 257 } 258 259 /** 260 * For internal use only. 261 */ 262 public Project setAnalysisDate(Date analysisDate) { 263 this.analysisDate = analysisDate; 264 return this; 265 } 266 267 /** 268 * For internal use only. 269 */ 270 public Project setAnalysisVersion(String analysisVersion) { 271 this.analysisVersion = analysisVersion; 272 return this; 273 } 274 275 /** 276 * @return the scope of the current object 277 */ 278 @Override 279 public String getScope() { 280 return Scopes.PROJECT; 281 } 282 283 /** 284 * @return the qualifier of the current object 285 */ 286 @Override 287 public String getQualifier() { 288 return isRoot() ? Qualifiers.PROJECT : Qualifiers.MODULE; 289 } 290 291 @Override 292 public boolean matchFilePattern(String antPattern) { 293 return false; 294 } 295 296 @CheckForNull 297 @Override 298 public Project getParent() { 299 return parent; 300 } 301 302 /** 303 * For internal use only. 304 */ 305 public Project setParent(Project parent) { 306 this.parent = parent; 307 if (parent != null) { 308 parent.modules.add(this); 309 } 310 return this; 311 } 312 313 /** 314 * For internal use only. 315 */ 316 public void removeFromParent() { 317 if (parent != null) { 318 parent.modules.remove(this); 319 } 320 } 321 322 /** 323 * @return the list of modules 324 */ 325 public List<Project> getModules() { 326 return modules; 327 } 328 329 /** 330 * @return the current version of the project 331 */ 332 public String getAnalysisVersion() { 333 return analysisVersion; 334 } 335 336 /** 337 * @return the analysis date, i.e. the date that will be used to store the snapshot 338 */ 339 public Date getAnalysisDate() { 340 return analysisDate; 341 } 342 343 /** 344 * Note: it's better to get a reference on ProjectFileSystem as an IoC dependency (constructor parameter) 345 * @deprecated since 3.5 use {@link FileSystem} instead 346 */ 347 @Deprecated 348 public ProjectFileSystem getFileSystem() { 349 return fileSystem; 350 } 351 352 /** 353 * For internal use only. 354 * 355 * @deprecated since 2.6. See http://jira.sonarsource.com/browse/SONAR-2126 356 */ 357 @Deprecated 358 public Project setFileSystem(ProjectFileSystem fs) { 359 this.fileSystem = fs; 360 return this; 361 } 362 363 public static Project createFromMavenIds(String groupId, String artifactId) { 364 return createFromMavenIds(groupId, artifactId, null); 365 } 366 367 public static Project createFromMavenIds(String groupId, String artifactId, @Nullable String branch) { 368 return new Project(String.format(MAVEN_KEY_FORMAT, groupId, artifactId), branch, ""); 369 } 370 371 @Override 372 public String toString() { 373 return new ToStringBuilder(this) 374 .append("id", getId()) 375 .append("key", getKey()) 376 .append("qualifier", getQualifier()) 377 .toString(); 378 } 379 380 @Override 381 public String key() { 382 return getKey(); 383 } 384 385 @Override 386 public String name() { 387 return getName(); 388 } 389 390 @Override 391 public String path() { 392 return getPath(); 393 } 394 395 @Override 396 public String longName() { 397 return getLongName(); 398 } 399 400 @Override 401 public String qualifier() { 402 return getQualifier(); 403 } 404 405 // For internal use 406 public void setBaseDir(java.io.File baseDir) { 407 this.baseDir = baseDir; 408 } 409 410 java.io.File getBaseDir() { 411 return baseDir; 412 } 413}