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  // 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}