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