001/*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 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.batch.sensor.issue.internal;
021
022import com.google.common.base.Preconditions;
023import com.google.common.base.Strings;
024import org.sonar.api.batch.fs.InputDir;
025import org.sonar.api.batch.fs.InputFile;
026import org.sonar.api.batch.fs.InputPath;
027import org.sonar.api.batch.sensor.internal.DefaultStorable;
028import org.sonar.api.batch.sensor.internal.SensorStorage;
029import org.sonar.api.batch.sensor.issue.Issue;
030import org.sonar.api.batch.sensor.issue.NewIssue;
031import org.sonar.api.rule.RuleKey;
032import org.sonar.api.utils.internal.Uuids;
033
034import javax.annotation.CheckForNull;
035import javax.annotation.Nullable;
036
037public class DefaultIssue extends DefaultStorable implements Issue, NewIssue {
038
039  private static final String INPUT_DIR_SHOULD_BE_NON_NULL = "InputDir should be non null";
040  private static final String INPUT_FILE_SHOULD_BE_NON_NULL = "InputFile should be non null";
041  private static final String ON_FILE_OR_ON_DIR_ALREADY_CALLED = "onFile or onDir already called";
042  private static final String ON_PROJECT_ALREADY_CALLED = "onProject already called";
043  private String key;
044  private boolean onProject = false;
045  private InputPath path;
046  private RuleKey ruleKey;
047  private String message;
048  private Integer line;
049  private Double effortToFix;
050  private Severity overridenSeverity;
051
052  public DefaultIssue() {
053    super(null);
054    this.key = Uuids.create();
055  }
056
057  public DefaultIssue(SensorStorage storage) {
058    super(storage);
059    this.key = Uuids.create();
060  }
061
062  @Override
063  public DefaultIssue forRule(RuleKey ruleKey) {
064    this.ruleKey = ruleKey;
065    return this;
066  }
067
068  @Override
069  public DefaultIssue onFile(InputFile file) {
070    Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED);
071    Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED);
072    Preconditions.checkNotNull(file, INPUT_FILE_SHOULD_BE_NON_NULL);
073    this.path = file;
074    return this;
075  }
076
077  @Override
078  public DefaultIssue onDir(InputDir dir) {
079    Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED);
080    Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED);
081    Preconditions.checkNotNull(dir, INPUT_DIR_SHOULD_BE_NON_NULL);
082    this.path = dir;
083    return this;
084  }
085
086  @Override
087  public DefaultIssue onProject() {
088    Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED);
089    Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED);
090    this.onProject = true;
091    return this;
092  }
093
094  @Override
095  public DefaultIssue atLine(int line) {
096    Preconditions.checkState(this.path != null && this.path instanceof InputFile, "atLine should be called after onFile.");
097    Preconditions.checkArgument(line > 0, "line starts at 1, invalid value " + line + ".");
098    int lines = ((InputFile) path).lines();
099    Preconditions.checkArgument(line <= lines, "File " + path + " has " + lines + " lines. Unable to create issue at line " + line + ".");
100    this.line = line;
101    return this;
102  }
103
104  @Override
105  public DefaultIssue effortToFix(@Nullable Double effortToFix) {
106    this.effortToFix = effortToFix;
107    return this;
108  }
109
110  @Override
111  public DefaultIssue message(String message) {
112    this.message = message;
113    return this;
114  }
115
116  @Override
117  public DefaultIssue overrideSeverity(@Nullable Severity severity) {
118    this.overridenSeverity = severity;
119    return this;
120  }
121
122  @Override
123  public RuleKey ruleKey() {
124    return this.ruleKey;
125  }
126
127  @CheckForNull
128  @Override
129  public InputPath inputPath() {
130    return this.path;
131  }
132
133  @Override
134  public Integer line() {
135    return this.line;
136  }
137
138  @Override
139  public String message() {
140    return this.message;
141  }
142
143  @Override
144  public Severity overridenSeverity() {
145    return this.overridenSeverity;
146  }
147
148  @Override
149  public Double effortToFix() {
150    return this.effortToFix;
151  }
152
153  public String key() {
154    return this.key;
155  }
156
157  @Override
158  public void doSave() {
159    Preconditions.checkNotNull(this.ruleKey, "ruleKey is mandatory on issue");
160    Preconditions.checkState(!Strings.isNullOrEmpty(key), "Fail to generate issue key");
161    storage.store(this);
162  }
163
164  /**
165   * For testing only.
166   */
167  public DefaultIssue withKey(String key) {
168    this.key = key;
169    return this;
170  }
171
172  @Override
173  public boolean equals(Object o) {
174    if (this == o) {
175      return true;
176    }
177    if (o == null || getClass() != o.getClass()) {
178      return false;
179    }
180    DefaultIssue that = (DefaultIssue) o;
181    return !key.equals(that.key);
182  }
183
184  @Override
185  public int hashCode() {
186    return key.hashCode();
187  }
188
189}