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    }