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}