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