001/* 002 * SonarQube, open source software quality management tool. 003 * Copyright (C) 2008-2013 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.issue.internal; 021 022import com.google.common.base.Objects; 023import com.google.common.base.Preconditions; 024import com.google.common.base.Strings; 025import com.google.common.collect.ImmutableList; 026import com.google.common.collect.ImmutableMap; 027import com.google.common.collect.Lists; 028import com.google.common.collect.Maps; 029import org.apache.commons.lang.StringUtils; 030import org.apache.commons.lang.builder.ToStringBuilder; 031import org.apache.commons.lang.builder.ToStringStyle; 032import org.sonar.api.issue.Issue; 033import org.sonar.api.issue.IssueComment; 034import org.sonar.api.rule.RuleKey; 035import org.sonar.api.rule.Severity; 036 037import javax.annotation.CheckForNull; 038import javax.annotation.Nullable; 039import java.io.Serializable; 040import java.util.Collections; 041import java.util.Date; 042import java.util.List; 043import java.util.Map; 044 045/** 046 * PLUGINS MUST NOT BE USED THIS CLASS, EXCEPT FOR UNIT TESTING. 047 * 048 * @since 3.6 049 */ 050public class DefaultIssue implements Issue { 051 052 private String key; 053 private String componentKey; 054 private String projectKey; 055 private RuleKey ruleKey; 056 private String severity; 057 private boolean manualSeverity = false; 058 private String message; 059 private Integer line; 060 private Double effortToFix; 061 private String status; 062 private String resolution; 063 private String reporter; 064 private String assignee; 065 private String checksum; 066 private Map<String, String> attributes = null; 067 private String authorLogin = null; 068 private String actionPlanKey; 069 private List<IssueComment> comments = null; 070 071 // FUNCTIONAL DATES 072 private Date creationDate; 073 private Date updateDate; 074 private Date closeDate; 075 076 077 // FOLLOWING FIELDS ARE AVAILABLE ONLY DURING SCAN 078 079 // Current changes 080 private FieldDiffs currentChange = null; 081 082 // true if the the issue did not exist in the previous scan. 083 private boolean isNew = true; 084 085 // True if the the issue did exist in the previous scan but not in the current one. That means 086 // that this issue should be closed. 087 private boolean endOfLife = false; 088 089 private boolean onDisabledRule = false; 090 091 // true if some fields have been changed since the previous scan 092 private boolean isChanged = false; 093 094 // true if notifications have to be sent 095 private boolean sendNotifications = false; 096 097 // Date when issue was loaded from db (only when isNew=false) 098 private Date selectedAt; 099 100 public String key() { 101 return key; 102 } 103 104 public DefaultIssue setKey(String key) { 105 this.key = key; 106 return this; 107 } 108 109 public String componentKey() { 110 return componentKey; 111 } 112 113 public DefaultIssue setComponentKey(String s) { 114 this.componentKey = s; 115 return this; 116 } 117 118 /** 119 * The project key is not always populated, that's why it's not present is the Issue API 120 */ 121 @CheckForNull 122 public String projectKey() { 123 return projectKey; 124 } 125 126 public DefaultIssue setProjectKey(String projectKey) { 127 this.projectKey = projectKey; 128 return this; 129 } 130 131 public RuleKey ruleKey() { 132 return ruleKey; 133 } 134 135 public DefaultIssue setRuleKey(RuleKey k) { 136 this.ruleKey = k; 137 return this; 138 } 139 140 public String severity() { 141 return severity; 142 } 143 144 public DefaultIssue setSeverity(@Nullable String s) { 145 Preconditions.checkArgument(s == null || Severity.ALL.contains(s), "Not a valid severity: " + s); 146 this.severity = s; 147 return this; 148 } 149 150 public boolean manualSeverity() { 151 return manualSeverity; 152 } 153 154 public DefaultIssue setManualSeverity(boolean b) { 155 this.manualSeverity = b; 156 return this; 157 } 158 159 @CheckForNull 160 public String message() { 161 return message; 162 } 163 164 public DefaultIssue setMessage(@Nullable String s) { 165 this.message = StringUtils.abbreviate(StringUtils.trim(s), MESSAGE_MAX_SIZE); 166 return this; 167 } 168 169 @CheckForNull 170 public Integer line() { 171 return line; 172 } 173 174 public DefaultIssue setLine(@Nullable Integer l) { 175 Preconditions.checkArgument(l == null || l > 0, "Line must be null or greater than zero (got " + l + ")"); 176 this.line = l; 177 return this; 178 } 179 180 @CheckForNull 181 public Double effortToFix() { 182 return effortToFix; 183 } 184 185 public DefaultIssue setEffortToFix(@Nullable Double d) { 186 Preconditions.checkArgument(d == null || d >= 0, "Effort to fix must be greater than or equal 0 (got " + d + ")"); 187 this.effortToFix = d; 188 return this; 189 } 190 191 public String status() { 192 return status; 193 } 194 195 public DefaultIssue setStatus(String s) { 196 Preconditions.checkArgument(!Strings.isNullOrEmpty(s), "Status must be set"); 197 this.status = s; 198 return this; 199 } 200 201 @CheckForNull 202 public String resolution() { 203 return resolution; 204 } 205 206 public DefaultIssue setResolution(@Nullable String s) { 207 this.resolution = s; 208 return this; 209 } 210 211 @CheckForNull 212 public String reporter() { 213 return reporter; 214 } 215 216 public DefaultIssue setReporter(@Nullable String s) { 217 this.reporter = s; 218 return this; 219 } 220 221 @CheckForNull 222 public String assignee() { 223 return assignee; 224 } 225 226 public DefaultIssue setAssignee(@Nullable String s) { 227 this.assignee = s; 228 return this; 229 } 230 231 public Date creationDate() { 232 return creationDate; 233 } 234 235 public DefaultIssue setCreationDate(Date d) { 236 this.creationDate = d; 237 return this; 238 } 239 240 @CheckForNull 241 public Date updateDate() { 242 return updateDate; 243 } 244 245 public DefaultIssue setUpdateDate(@Nullable Date d) { 246 this.updateDate = d; 247 return this; 248 } 249 250 @CheckForNull 251 public Date closeDate() { 252 return closeDate; 253 } 254 255 public DefaultIssue setCloseDate(@Nullable Date d) { 256 this.closeDate = d; 257 return this; 258 } 259 260 261 @CheckForNull 262 public String checksum() { 263 return checksum; 264 } 265 266 public DefaultIssue setChecksum(@Nullable String s) { 267 this.checksum = s; 268 return this; 269 } 270 271 public boolean isNew() { 272 return isNew; 273 } 274 275 public DefaultIssue setNew(boolean b) { 276 isNew = b; 277 return this; 278 } 279 280 /** 281 * True when one of the following conditions is true : 282 * <ul> 283 * <li>the related component has been deleted or renamed</li> 284 * <li>the rule has been deleted (eg. on plugin uninstall)</li> 285 * <li>the rule has been disabled in the Quality profile</li> 286 * </ul> 287 */ 288 public boolean isEndOfLife() { 289 return endOfLife; 290 } 291 292 public DefaultIssue setEndOfLife(boolean b) { 293 endOfLife = b; 294 return this; 295 } 296 297 public boolean isOnDisabledRule() { 298 return onDisabledRule; 299 } 300 301 public DefaultIssue setOnDisabledRule(boolean b) { 302 onDisabledRule = b; 303 return this; 304 } 305 306 public boolean isChanged() { 307 return isChanged; 308 } 309 310 public DefaultIssue setChanged(boolean b) { 311 isChanged = b; 312 return this; 313 } 314 315 public boolean mustSendNotifications() { 316 return sendNotifications; 317 } 318 319 public DefaultIssue setSendNotifications(boolean b) { 320 sendNotifications = b; 321 return this; 322 } 323 324 @CheckForNull 325 public String attribute(String key) { 326 return attributes == null ? null : attributes.get(key); 327 } 328 329 public DefaultIssue setAttribute(String key, @Nullable String value) { 330 if (attributes == null) { 331 attributes = Maps.newHashMap(); 332 } 333 if (value == null) { 334 attributes.remove(key); 335 } else { 336 attributes.put(key, value); 337 } 338 return this; 339 } 340 341 public Map<String, String> attributes() { 342 return attributes == null ? Collections.<String, String>emptyMap() : ImmutableMap.copyOf(attributes); 343 } 344 345 public DefaultIssue setAttributes(@Nullable Map<String, String> map) { 346 if (map != null) { 347 if (attributes == null) { 348 attributes = Maps.newHashMap(); 349 } 350 attributes.putAll(map); 351 } 352 return this; 353 } 354 355 @CheckForNull 356 public String authorLogin() { 357 return authorLogin; 358 } 359 360 public DefaultIssue setAuthorLogin(@Nullable String s) { 361 this.authorLogin = s; 362 return this; 363 } 364 365 @CheckForNull 366 public String actionPlanKey() { 367 return actionPlanKey; 368 } 369 370 public DefaultIssue setActionPlanKey(@Nullable String actionPlanKey) { 371 this.actionPlanKey = actionPlanKey; 372 return this; 373 } 374 375 public DefaultIssue setFieldChange(IssueChangeContext context, String field, @Nullable Serializable oldValue, @Nullable Serializable newValue) { 376 if (!Objects.equal(oldValue, newValue)) { 377 if (currentChange == null) { 378 currentChange = new FieldDiffs(); 379 currentChange.setUserLogin(context.login()); 380 } 381 currentChange.setDiff(field, oldValue, newValue); 382 } 383 return this; 384 } 385 386 @CheckForNull 387 public FieldDiffs currentChange() { 388 return currentChange; 389 } 390 391 public DefaultIssue addComment(DefaultIssueComment comment) { 392 if (comments == null) { 393 comments = Lists.newArrayList(); 394 } 395 comments.add(comment); 396 return this; 397 } 398 399 @SuppressWarnings("unchcked") 400 public List<IssueComment> comments() { 401 if (comments == null) { 402 return Collections.emptyList(); 403 } 404 return ImmutableList.copyOf(comments); 405 } 406 407 @CheckForNull 408 public Date selectedAt() { 409 return selectedAt; 410 } 411 412 public DefaultIssue setSelectedAt(@Nullable Date d) { 413 this.selectedAt = d; 414 return this; 415 } 416 417 @Override 418 public boolean equals(Object o) { 419 if (this == o) { 420 return true; 421 } 422 if (o == null || getClass() != o.getClass()) { 423 return false; 424 } 425 DefaultIssue that = (DefaultIssue) o; 426 if (key != null ? !key.equals(that.key) : that.key != null) { 427 return false; 428 } 429 return true; 430 } 431 432 @Override 433 public int hashCode() { 434 return key != null ? key.hashCode() : 0; 435 } 436 437 @Override 438 public String toString() { 439 return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); 440 } 441 442 443}