001/* 002 * SonarQube 003 * Copyright (C) 2009-2016 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * This program 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 * This program 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.batch.bootstrap; 021 022import java.io.File; 023import java.util.ArrayList; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.Map.Entry; 028import java.util.Properties; 029import javax.annotation.CheckForNull; 030import org.apache.commons.lang.ObjectUtils; 031import org.apache.commons.lang.StringUtils; 032import org.sonar.api.CoreProperties; 033 034/** 035 * Defines project metadata (key, name, source directories, ...). It's generally used by the 036 * {@link org.sonar.api.batch.bootstrap.ProjectBuilder extension point} and must not be used 037 * by other standard extensions. 038 * 039 * @since 2.9 040 */ 041public class ProjectDefinition { 042 043 public static final String SOURCES_PROPERTY = "sonar.sources"; 044 045 public static final String TESTS_PROPERTY = "sonar.tests"; 046 047 public static final String BUILD_DIR_PROPERTY = "sonar.buildDir"; 048 049 private static final char SEPARATOR = ','; 050 051 private File baseDir; 052 private File workDir; 053 private File buildDir; 054 private Map<String, String> properties = new HashMap<>(); 055 private ProjectDefinition parent = null; 056 private List<ProjectDefinition> subProjects = new ArrayList<>(); 057 058 private ProjectDefinition(Properties p) { 059 for (Entry<Object, Object> entry : p.entrySet()) { 060 this.properties.put(entry.getKey().toString(), entry.getValue().toString()); 061 } 062 } 063 064 public static ProjectDefinition create() { 065 return new ProjectDefinition(new Properties()); 066 } 067 068 public ProjectDefinition setBaseDir(File baseDir) { 069 this.baseDir = baseDir; 070 return this; 071 } 072 073 public File getBaseDir() { 074 return baseDir; 075 } 076 077 public ProjectDefinition setWorkDir(File workDir) { 078 this.workDir = workDir; 079 return this; 080 } 081 082 public File getWorkDir() { 083 return workDir; 084 } 085 086 public ProjectDefinition setBuildDir(File d) { 087 this.buildDir = d; 088 return this; 089 } 090 091 public File getBuildDir() { 092 return buildDir; 093 } 094 095 /** 096 * @deprecated since 5.0 use {@link #properties()} 097 */ 098 @Deprecated 099 public Properties getProperties() { 100 Properties result = new Properties(); 101 for (Map.Entry<String, String> entry : properties.entrySet()) { 102 result.setProperty(entry.getKey(), entry.getValue()); 103 } 104 return result; 105 } 106 107 public Map<String, String> properties() { 108 return properties; 109 } 110 111 /** 112 * Copies specified properties into this object. 113 * 114 * @since 2.12 115 * @deprecated since 5.0 use {@link #setProperties(Map)} 116 */ 117 @Deprecated 118 public ProjectDefinition setProperties(Properties properties) { 119 for (Entry<Object, Object> entry : properties.entrySet()) { 120 this.properties.put(entry.getKey().toString(), entry.getValue().toString()); 121 } 122 return this; 123 } 124 125 public ProjectDefinition setProperties(Map<String, String> properties) { 126 this.properties.putAll(properties); 127 return this; 128 } 129 130 public ProjectDefinition setProperty(String key, String value) { 131 properties.put(key, value); 132 return this; 133 } 134 135 public ProjectDefinition setKey(String key) { 136 properties.put(CoreProperties.PROJECT_KEY_PROPERTY, key); 137 return this; 138 } 139 140 public ProjectDefinition setVersion(String s) { 141 properties.put(CoreProperties.PROJECT_VERSION_PROPERTY, StringUtils.defaultString(s)); 142 return this; 143 } 144 145 public ProjectDefinition setName(String s) { 146 properties.put(CoreProperties.PROJECT_NAME_PROPERTY, StringUtils.defaultString(s)); 147 return this; 148 } 149 150 public ProjectDefinition setDescription(String s) { 151 properties.put(CoreProperties.PROJECT_DESCRIPTION_PROPERTY, StringUtils.defaultString(s)); 152 return this; 153 } 154 155 public String getKey() { 156 return properties.get(CoreProperties.PROJECT_KEY_PROPERTY); 157 } 158 159 /** 160 * @since 4.5 161 */ 162 public String getKeyWithBranch() { 163 String branch = getBranch(); 164 String projectKey = getKey(); 165 if (StringUtils.isNotBlank(branch)) { 166 projectKey = String.format("%s:%s", projectKey, branch); 167 } 168 return projectKey; 169 } 170 171 @CheckForNull 172 public String getBranch() { 173 String branch = properties.get(CoreProperties.PROJECT_BRANCH_PROPERTY); 174 if (StringUtils.isNotBlank(branch)) { 175 return branch; 176 } else if (getParent() != null) { 177 return getParent().getBranch(); 178 } 179 return null; 180 } 181 182 public String getVersion() { 183 return properties.get(CoreProperties.PROJECT_VERSION_PROPERTY); 184 } 185 186 public String getName() { 187 String name = properties.get(CoreProperties.PROJECT_NAME_PROPERTY); 188 if (StringUtils.isBlank(name)) { 189 name = "Unnamed - " + getKey(); 190 } 191 return name; 192 } 193 194 public String getDescription() { 195 return properties.get(CoreProperties.PROJECT_DESCRIPTION_PROPERTY); 196 } 197 198 private void appendProperty(String key, String value) { 199 String current = (String) ObjectUtils.defaultIfNull(properties.get(key), ""); 200 if (StringUtils.isBlank(current)) { 201 properties.put(key, value); 202 } else { 203 properties.put(key, current + SEPARATOR + value); 204 } 205 } 206 207 /** 208 * @return Source files and folders. 209 */ 210 public List<String> sources() { 211 String sources = (String) ObjectUtils.defaultIfNull(properties.get(SOURCES_PROPERTY), ""); 212 return trim(StringUtils.split(sources, SEPARATOR)); 213 } 214 215 /** 216 * @param paths paths to file or directory with main sources. 217 * They can be absolute or relative to project base directory. 218 */ 219 public ProjectDefinition addSources(String... paths) { 220 for (String path : paths) { 221 appendProperty(SOURCES_PROPERTY, path); 222 } 223 return this; 224 } 225 226 public ProjectDefinition addSources(File... fileOrDirs) { 227 for (File fileOrDir : fileOrDirs) { 228 addSources(fileOrDir.getAbsolutePath()); 229 } 230 return this; 231 } 232 233 public ProjectDefinition resetSources() { 234 properties.remove(SOURCES_PROPERTY); 235 return this; 236 } 237 238 public ProjectDefinition setSources(String... paths) { 239 resetSources(); 240 return addSources(paths); 241 } 242 243 public ProjectDefinition setSources(File... filesOrDirs) { 244 resetSources(); 245 for (File fileOrDir : filesOrDirs) { 246 addSources(fileOrDir.getAbsolutePath()); 247 } 248 return this; 249 } 250 251 public List<String> tests() { 252 String sources = (String) ObjectUtils.defaultIfNull(properties.get(TESTS_PROPERTY), ""); 253 return trim(StringUtils.split(sources, SEPARATOR)); 254 } 255 256 /** 257 * @param paths path to files or directories with test sources. 258 * It can be absolute or relative to project directory. 259 */ 260 public ProjectDefinition addTests(String... paths) { 261 for (String path : paths) { 262 appendProperty(TESTS_PROPERTY, path); 263 } 264 return this; 265 } 266 267 public ProjectDefinition addTests(File... fileOrDirs) { 268 for (File fileOrDir : fileOrDirs) { 269 addTests(fileOrDir.getAbsolutePath()); 270 } 271 return this; 272 } 273 274 public ProjectDefinition setTests(String... paths) { 275 resetTests(); 276 return addTests(paths); 277 } 278 279 public ProjectDefinition setTests(File... fileOrDirs) { 280 resetTests(); 281 for (File dir : fileOrDirs) { 282 addTests(dir.getAbsolutePath()); 283 } 284 return this; 285 } 286 287 public ProjectDefinition resetTests() { 288 properties.remove(TESTS_PROPERTY); 289 return this; 290 } 291 292 /** 293 * @since 2.8 294 */ 295 public ProjectDefinition addSubProject(ProjectDefinition child) { 296 subProjects.add(child); 297 child.setParent(this); 298 return this; 299 } 300 301 public ProjectDefinition getParent() { 302 return parent; 303 } 304 305 public void remove() { 306 if (parent != null) { 307 parent.subProjects.remove(this); 308 parent = null; 309 subProjects.clear(); 310 } 311 } 312 313 private void setParent(ProjectDefinition parent) { 314 this.parent = parent; 315 } 316 317 /** 318 * @since 2.8 319 */ 320 public List<ProjectDefinition> getSubProjects() { 321 return subProjects; 322 } 323 324 private static List<String> trim(String[] strings) { 325 List<String> result = new ArrayList<>(); 326 for (String s : strings) { 327 result.add(StringUtils.trim(s)); 328 } 329 return result; 330 } 331 332 @Override 333 public boolean equals(Object o) { 334 if (this == o) { 335 return true; 336 } 337 if (o == null || getClass() != o.getClass()) { 338 return false; 339 } 340 ProjectDefinition that = (ProjectDefinition) o; 341 String key = getKey(); 342 return !((key != null) ? !key.equals(that.getKey()) : (that.getKey() != null)); 343 344 } 345 346 @Override 347 public int hashCode() { 348 String key = getKey(); 349 return key != null ? key.hashCode() : 0; 350 } 351}