diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2015-07-29 17:42:20 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2015-07-31 11:00:25 +0200 |
commit | 93420cb74009febd28256484ca73241442bc1ff9 (patch) | |
tree | 612fc1f176e3b41cff95dd3038c2726d14725ee2 /sonar-plugin-api | |
parent | 5b93180b18e756c6a246b8d22307785d333d0002 (diff) | |
download | sonarqube-93420cb74009febd28256484ca73241442bc1ff9.tar.gz sonarqube-93420cb74009febd28256484ca73241442bc1ff9.zip |
SONAR-6052 Extract primaryLocation as a special attribute
Also rework new issue API on batch side.
Diffstat (limited to 'sonar-plugin-api')
18 files changed, 289 insertions, 129 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputComponent.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputComponent.java new file mode 100644 index 00000000000..e4f66be54b0 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputComponent.java @@ -0,0 +1,36 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs; + +/** + * Common interface for all input components. + * + * @since 5.2 + * @see InputFile + * @see InputDir + */ +public interface InputComponent { + + /** + * Is the component an {@link InputFile} + */ + boolean isFile(); + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java new file mode 100644 index 00000000000..a92dc3655f2 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputModule.java @@ -0,0 +1,29 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs; + +/** + * Used to create issues and measures on modules. + * + * @since 5.2 + */ +public interface InputModule extends InputComponent { + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java index 121df157028..b63d955a4c2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/InputPath.java @@ -20,7 +20,6 @@ package org.sonar.api.batch.fs; import java.io.File; -import java.io.Serializable; import java.nio.file.Path; /** @@ -30,7 +29,7 @@ import java.nio.file.Path; * @see InputFile * @see InputDir */ -public interface InputPath extends Serializable { +public interface InputPath extends InputComponent { /** * @see InputFile#relativePath() diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputComponent.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputComponent.java new file mode 100644 index 00000000000..9a49dd4de66 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputComponent.java @@ -0,0 +1,53 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs.internal; + +import org.sonar.api.batch.fs.InputComponent; + +/** + * @since 5.2 + */ +public abstract class DefaultInputComponent implements InputComponent { + + public abstract String key(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + + DefaultInputComponent that = (DefaultInputComponent) o; + return key().equals(that.key()); + } + + @Override + public int hashCode() { + return key().hashCode(); + } + + @Override + public String toString() { + return "[key=" + key() + "]"; + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java index 8979e1f3468..5e1ebb651c2 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputDir.java @@ -19,16 +19,15 @@ */ package org.sonar.api.batch.fs.internal; -import org.sonar.api.batch.fs.InputDir; -import org.sonar.api.utils.PathUtils; - import java.io.File; import java.nio.file.Path; +import org.sonar.api.batch.fs.InputDir; +import org.sonar.api.utils.PathUtils; /** * @since 4.5 */ -public class DefaultInputDir implements InputDir { +public class DefaultInputDir extends DefaultInputComponent implements InputDir { private final String relativePath; private final String moduleKey; @@ -66,6 +65,7 @@ public class DefaultInputDir implements InputDir { return moduleKey; } + @Override public String key() { return new StringBuilder().append(moduleKey).append(":").append(relativePath).toString(); } @@ -79,6 +79,11 @@ public class DefaultInputDir implements InputDir { } @Override + public boolean isFile() { + return false; + } + + @Override public boolean equals(Object o) { if (this == o) { return true; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java index 3c0cb4a243b..a2e8e992472 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputFile.java @@ -39,7 +39,7 @@ import org.sonar.api.utils.PathUtils; /** * @since 4.2 */ -public class DefaultInputFile implements InputFile, org.sonar.api.resources.InputFile { +public class DefaultInputFile extends DefaultInputComponent implements InputFile, org.sonar.api.resources.InputFile { private final String relativePath; private final String moduleKey; @@ -114,6 +114,7 @@ public class DefaultInputFile implements InputFile, org.sonar.api.resources.Inpu /** * Component key. */ + @Override public String key() { return new StringBuilder().append(moduleKey).append(":").append(relativePath).toString(); } @@ -327,4 +328,9 @@ public class DefaultInputFile implements InputFile, org.sonar.api.resources.Inpu return new BufferedInputStream(new FileInputStream(file())); } + @Override + public boolean isFile() { + return true; + } + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java new file mode 100644 index 00000000000..e1dd688d6c8 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/fs/internal/DefaultInputModule.java @@ -0,0 +1,45 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.api.batch.fs.internal; + +import org.sonar.api.batch.fs.InputModule; + +/** + * @since 5.2 + */ +public class DefaultInputModule extends DefaultInputComponent implements InputModule { + + private final String moduleKey; + + public DefaultInputModule(String moduleKey) { + this.moduleKey = moduleKey; + } + + @Override + public String key() { + return moduleKey; + } + + @Override + public boolean isFile() { + return false; + } + +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/postjob/issue/Issue.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/postjob/issue/Issue.java index ccf79d800fb..6027568a0cf 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/postjob/issue/Issue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/postjob/issue/Issue.java @@ -20,12 +20,11 @@ package org.sonar.api.batch.postjob.issue; import com.google.common.annotations.Beta; -import org.sonar.api.batch.fs.InputPath; +import javax.annotation.CheckForNull; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.rule.Severity; import org.sonar.api.rule.RuleKey; -import javax.annotation.CheckForNull; - /** * Represents an issue state at the end of the batch analysis. Only available after local issue tracking in preview mode. * @@ -50,10 +49,10 @@ public interface Issue { String componentKey(); /** - * The {@link InputPath} this issue belongs to. Returns null if issue is global to the project or if component was deleted (for resolved issues). + * The {@link InputComponent} this issue belongs to. Returns null if component was deleted (for resolved issues). */ @CheckForNull - InputPath inputPath(); + InputComponent inputComponent(); /** * Line of the issue. Null for global issues and issues on directories. Can also be null diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/Issue.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/Issue.java index 1c519115e29..563c848ce0f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/Issue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/Issue.java @@ -21,6 +21,7 @@ package org.sonar.api.batch.sensor.issue; import com.google.common.annotations.Beta; import java.util.List; +import java.util.Map; import javax.annotation.CheckForNull; import org.sonar.api.batch.rule.Severity; import org.sonar.api.batch.sensor.Sensor; @@ -59,7 +60,13 @@ public interface Issue { Severity overriddenSeverity(); /** - * List of locations for this issue. Returns at least one location. + * Primary locations for this issue. + * @since 5.2 + */ + IssueLocation primaryLocation(); + + /** + * List of additional locations for this issue. * @since 5.2 */ List<IssueLocation> locations(); @@ -70,4 +77,10 @@ public interface Issue { */ List<ExecutionFlow> executionFlows(); + /** + * Key/value pair of attributes that are attached to the issue. + * @since 5.2 + */ + Map<String, String> attributes(); + } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/IssueLocation.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/IssueLocation.java index fa587f89cb8..aff5cd8947d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/IssueLocation.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/IssueLocation.java @@ -21,7 +21,7 @@ package org.sonar.api.batch.sensor.issue; import com.google.common.annotations.Beta; import javax.annotation.CheckForNull; -import org.sonar.api.batch.fs.InputPath; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.TextRange; /** @@ -33,10 +33,9 @@ import org.sonar.api.batch.fs.TextRange; public interface IssueLocation { /** - * The {@link InputPath} this location belongs to. Returns null if location is global to the project. + * The {@link InputComponent} this location belongs to. */ - @CheckForNull - InputPath inputPath(); + InputComponent inputComponent(); /** * Range of the issue. Null for global issues and issues on directories. Can also be null diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssue.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssue.java index 7c44f331163..8ad694545b3 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssue.java @@ -51,7 +51,13 @@ public interface NewIssue { /** * @since 5.2 - * Register a new location for this issue. First registered location is considered as primary location. + * Primary for this issue. + */ + NewIssue at(NewIssueLocation primaryLocation); + + /** + * @since 5.2 + * Register an additional location for this issue. */ NewIssue addLocation(NewIssueLocation location); @@ -68,6 +74,12 @@ public interface NewIssue { NewIssueLocation newLocation(); /** + * @since 5.2 + * Attach a new attribute to the issue. Not used by SQ but can be reused later for integration needs (for example it is returned by WS). + */ + NewIssue addAttribute(String key, String value); + + /** * Save the issue. If rule key is unknown or rule not enabled in the current quality profile then a warning is logged but no exception * is thrown. */ diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssueLocation.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssueLocation.java index 3ead59ed7f5..e0f4281a65a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssueLocation.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssueLocation.java @@ -20,7 +20,7 @@ package org.sonar.api.batch.sensor.issue; import com.google.common.annotations.Beta; -import org.sonar.api.batch.fs.InputDir; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.TextRange; @@ -38,22 +38,12 @@ public interface NewIssueLocation { int MESSAGE_MAX_SIZE = 4000; /** - * The {@link InputFile} the issue location belongs to. For global issues call {@link #onProject()}. + * The {@link InputComponent} the issue location belongs to. Mandatory. */ - NewIssueLocation onFile(InputFile file); + NewIssueLocation on(InputComponent component); /** - * The {@link InputDir} the issue location belongs to. For global issues call {@link #onProject()}. - */ - NewIssueLocation onDir(InputDir inputDir); - - /** - * Tell that the issue location is global to the project. - */ - NewIssueLocation onProject(); - - /** - * Position in the file. Only valid when {@link #onFile(InputFile)} has been called. + * Position in the file. Only applicable when {@link #on(InputComponent)} has been called with an InputFile. * See {@link InputFile#newRange(org.sonar.api.batch.fs.TextPointer, org.sonar.api.batch.fs.TextPointer)} */ NewIssueLocation at(TextRange location); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java index 686677ffb29..2018f7ac0ed 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java @@ -21,11 +21,13 @@ package org.sonar.api.batch.sensor.issue.internal; import com.google.common.base.Function; import com.google.common.base.Preconditions; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import javax.annotation.Nullable; import org.sonar.api.batch.rule.Severity; import org.sonar.api.batch.sensor.internal.DefaultStorable; @@ -35,25 +37,23 @@ import org.sonar.api.batch.sensor.issue.IssueLocation; import org.sonar.api.batch.sensor.issue.NewIssue; import org.sonar.api.batch.sensor.issue.NewIssueLocation; import org.sonar.api.rule.RuleKey; -import org.sonar.api.utils.internal.Uuids; public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { - private String key; private RuleKey ruleKey; private Double effortToFix; private Severity overriddenSeverity; + private IssueLocation primaryLocation; private List<IssueLocation> locations = new ArrayList<>(); private List<List<IssueLocation>> executionFlows = new ArrayList<>(); + private final Map<String, String> attributes = new LinkedHashMap<>(); public DefaultIssue() { super(null); - this.key = Uuids.create(); } public DefaultIssue(SensorStorage storage) { super(storage); - this.key = Uuids.create(); } @Override @@ -80,6 +80,14 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { } @Override + public DefaultIssue at(NewIssueLocation primaryLocation) { + Preconditions.checkArgument(primaryLocation != null, "Cannot use a location that is null"); + Preconditions.checkState(this.primaryLocation == null, "at() already called"); + this.primaryLocation = (DefaultIssueLocation) primaryLocation; + return this; + } + + @Override public DefaultIssue addLocation(NewIssueLocation location) { locations.add((DefaultIssueLocation) location); return this; @@ -96,6 +104,17 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { } @Override + public DefaultIssue addAttribute(String key, String value) { + attributes.put(key, value); + return this; + } + + @Override + public Map<String, String> attributes() { + return ImmutableMap.copyOf(attributes); + } + + @Override public RuleKey ruleKey() { return this.ruleKey; } @@ -110,8 +129,9 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { return this.effortToFix; } - public String key() { - return this.key; + @Override + public IssueLocation primaryLocation() { + return primaryLocation; } @Override @@ -137,34 +157,8 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue { @Override public void doSave() { Preconditions.checkNotNull(this.ruleKey, "ruleKey is mandatory on issue"); - Preconditions.checkState(!Strings.isNullOrEmpty(key), "Fail to generate issue key"); - Preconditions.checkState(!locations.isEmpty(), "At least one location is mandatory on every issue"); + Preconditions.checkState(primaryLocation != null, "Primary location is mandatory on every issue"); storage.store(this); } - /** - * For testing only. - */ - public DefaultIssue withKey(String key) { - this.key = key; - return this; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - DefaultIssue that = (DefaultIssue) o; - return !key.equals(that.key); - } - - @Override - public int hashCode() { - return key.hashCode(); - } - } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueLocation.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueLocation.java index 3840cd595f8..87b6ff67d44 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueLocation.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueLocation.java @@ -20,10 +20,7 @@ package org.sonar.api.batch.sensor.issue.internal; import com.google.common.base.Preconditions; -import javax.annotation.CheckForNull; -import org.sonar.api.batch.fs.InputDir; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.InputPath; +import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.TextRange; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.sensor.issue.IssueLocation; @@ -31,46 +28,23 @@ import org.sonar.api.batch.sensor.issue.NewIssueLocation; public class DefaultIssueLocation implements NewIssueLocation, IssueLocation { - private static final String INPUT_DIR_SHOULD_BE_NON_NULL = "InputDir should be non null"; - private static final String INPUT_FILE_SHOULD_BE_NON_NULL = "InputFile should be non null"; - private static final String ON_FILE_OR_ON_DIR_ALREADY_CALLED = "onFile or onDir already called"; - private static final String ON_PROJECT_ALREADY_CALLED = "onProject already called"; - - private boolean onProject = false; - private InputPath path; + private InputComponent component; private TextRange textRange; private String message; @Override - public NewIssueLocation onFile(InputFile file) { - Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); - Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); - Preconditions.checkNotNull(file, INPUT_FILE_SHOULD_BE_NON_NULL); - this.path = file; - return this; - } - - @Override - public NewIssueLocation onDir(InputDir dir) { - Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); - Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); - Preconditions.checkNotNull(dir, INPUT_DIR_SHOULD_BE_NON_NULL); - this.path = dir; - return this; - } - - @Override - public NewIssueLocation onProject() { - Preconditions.checkState(!this.onProject, ON_PROJECT_ALREADY_CALLED); - Preconditions.checkState(this.path == null, ON_FILE_OR_ON_DIR_ALREADY_CALLED); - this.onProject = true; + public NewIssueLocation on(InputComponent component) { + Preconditions.checkArgument(component != null, "Component can't be null"); + Preconditions.checkState(this.component == null, "on() already called"); + this.component = component; return this; } @Override public NewIssueLocation at(TextRange location) { - Preconditions.checkState(this.path != null && this.path instanceof InputFile, "at() should be called after onFile."); - DefaultInputFile file = (DefaultInputFile) this.path; + Preconditions.checkState(this.component != null, "at() should be called after on()"); + Preconditions.checkState(this.component.isFile(), "at() should be called only for an InputFile."); + DefaultInputFile file = (DefaultInputFile) this.component; file.validate(location); this.textRange = location; return this; @@ -86,9 +60,8 @@ public class DefaultIssueLocation implements NewIssueLocation, IssueLocation { } @Override - @CheckForNull - public InputPath inputPath() { - return this.path; + public InputComponent inputComponent() { + return this.component; } @Override diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java index 7cf03cb0579..c2adf0e5e11 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/Issuable.java @@ -67,7 +67,7 @@ public interface Issuable extends Perspective { /** * Optional line index, starting from 1. It must not be zero or negative. - * @deprecated since 5.2 use {@link #addLocation(NewIssueLocation)} + * @deprecated since 5.2 use {@link #at(NewIssueLocation)} */ @Deprecated IssueBuilder line(@Nullable Integer line); @@ -76,7 +76,7 @@ public interface Issuable extends Perspective { * Optional, but recommended, plain-text message. * <p/> * Formats like Markdown or HTML are not supported. Size must not be greater than {@link Issue#MESSAGE_MAX_SIZE} characters. - * @deprecated since 5.2 use {@link #addLocation(NewIssueLocation)} + * @deprecated since 5.2 use {@link #at(NewIssueLocation)} */ @Deprecated IssueBuilder message(@Nullable String message); @@ -89,6 +89,12 @@ public interface Issuable extends Perspective { /** * @since 5.2 + * Register primary location for this issue. + */ + IssueBuilder at(NewIssueLocation primaryLocation); + + /** + * @since 5.2 * Register a new secondary location for this issue. */ IssueBuilder addLocation(NewIssueLocation location); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java index 8837c6eb6da..7c15bd2c3f9 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/internal/SensorContextTesterTest.java @@ -89,12 +89,12 @@ public class SensorContextTesterTest { assertThat(tester.allIssues()).isEmpty(); NewIssue newIssue = tester.newIssue(); newIssue - .addLocation(newIssue.newLocation().onFile(new DefaultInputFile("foo", "src/Foo.java"))) + .at(newIssue.newLocation().on(new DefaultInputFile("foo", "src/Foo.java"))) .forRule(RuleKey.of("repo", "rule")) .save(); newIssue = tester.newIssue(); newIssue - .addLocation(newIssue.newLocation().onFile(new DefaultInputFile("foo", "src/Foo.java"))) + .at(newIssue.newLocation().on(new DefaultInputFile("foo", "src/Foo.java"))) .forRule(RuleKey.of("repo", "rule")) .save(); assertThat(tester.allIssues()).hasSize(2); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueLocationTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueLocationTest.java index 6ed1a7aa686..cffb6367baf 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueLocationTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueLocationTest.java @@ -35,19 +35,19 @@ public class DefaultIssueLocationTest { private DefaultInputFile inputFile = new DefaultInputFile("foo", "src/Foo.php").initMetadata(new FileMetadata().readMetadata(new StringReader("Foo\nBar\n"))); @Test - public void not_allowed_to_call_onFile_and_onProject() { + public void not_allowed_to_call_on_twice() { thrown.expect(IllegalStateException.class); - thrown.expectMessage("onProject already called"); + thrown.expectMessage("on() already called"); new DefaultIssueLocation() - .onProject() - .onFile(inputFile) + .on(inputFile) + .on(inputFile) .message("Wrong way!"); } @Test public void prevent_too_long_messages() { new DefaultIssueLocation() - .onFile(inputFile) + .on(inputFile) .message(StringUtils.repeat("a", 4000)); thrown.expect(IllegalArgumentException.class); @@ -55,7 +55,7 @@ public class DefaultIssueLocationTest { thrown.expectMessage("aaa] size is 4001"); new DefaultIssueLocation() - .onFile(inputFile) + .on(inputFile) .message(StringUtils.repeat("a", 4001)); } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java index e9b27548b35..011683854e1 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssueTest.java @@ -23,6 +23,7 @@ import java.io.StringReader; import org.junit.Test; import org.sonar.api.batch.fs.internal.DefaultInputDir; import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.FileMetadata; import org.sonar.api.batch.rule.Severity; import org.sonar.api.batch.sensor.internal.SensorStorage; @@ -40,18 +41,18 @@ public class DefaultIssueTest { public void build_file_issue() { SensorStorage storage = mock(SensorStorage.class); DefaultIssue issue = new DefaultIssue(storage) - .addLocation(new DefaultIssueLocation() - .onFile(inputFile) + .at(new DefaultIssueLocation() + .on(inputFile) .at(inputFile.selectLine(1)) .message("Wrong way!")) .forRule(RuleKey.of("repo", "rule")) .effortToFix(10.0); - assertThat(issue.locations().get(0).inputPath()).isEqualTo(inputFile); + assertThat(issue.primaryLocation().inputComponent()).isEqualTo(inputFile); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); - assertThat(issue.locations().get(0).textRange().start().line()).isEqualTo(1); + assertThat(issue.primaryLocation().textRange().start().line()).isEqualTo(1); assertThat(issue.effortToFix()).isEqualTo(10.0); - assertThat(issue.locations().get(0).message()).isEqualTo("Wrong way!"); + assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!"); issue.save(); @@ -62,16 +63,16 @@ public class DefaultIssueTest { public void build_directory_issue() { SensorStorage storage = mock(SensorStorage.class); DefaultIssue issue = new DefaultIssue(storage) - .addLocation(new DefaultIssueLocation() - .onDir(new DefaultInputDir("foo", "src")) + .at(new DefaultIssueLocation() + .on(new DefaultInputDir("foo", "src")) .message("Wrong way!")) .forRule(RuleKey.of("repo", "rule")) .overrideSeverity(Severity.BLOCKER); - assertThat(issue.locations().get(0).inputPath()).isEqualTo(new DefaultInputDir("foo", "src")); + assertThat(issue.primaryLocation().inputComponent()).isEqualTo(new DefaultInputDir("foo", "src")); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); - assertThat(issue.locations().get(0).textRange()).isNull(); - assertThat(issue.locations().get(0).message()).isEqualTo("Wrong way!"); + assertThat(issue.primaryLocation().textRange()).isNull(); + assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!"); assertThat(issue.overriddenSeverity()).isEqualTo(Severity.BLOCKER); issue.save(); @@ -83,17 +84,17 @@ public class DefaultIssueTest { public void build_project_issue() { SensorStorage storage = mock(SensorStorage.class); DefaultIssue issue = new DefaultIssue(storage) - .addLocation(new DefaultIssueLocation() - .onProject() + .at(new DefaultIssueLocation() + .on(new DefaultInputModule("foo")) .message("Wrong way!")) .forRule(RuleKey.of("repo", "rule")) .effortToFix(10.0); - assertThat(issue.locations().get(0).inputPath()).isNull(); + assertThat(issue.primaryLocation().inputComponent()).isEqualTo(new DefaultInputModule("foo")); assertThat(issue.ruleKey()).isEqualTo(RuleKey.of("repo", "rule")); - assertThat(issue.locations().get(0).textRange()).isNull(); + assertThat(issue.primaryLocation().textRange()).isNull(); assertThat(issue.effortToFix()).isEqualTo(10.0); - assertThat(issue.locations().get(0).message()).isEqualTo("Wrong way!"); + assertThat(issue.primaryLocation().message()).isEqualTo("Wrong way!"); issue.save(); |