aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>2013-10-07 11:09:14 +0200
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>2013-10-08 11:28:13 +0200
commit62ef92603ae2ffc1403d42ddcc0cad7d252daa2c (patch)
treeb01990419c931dc117811368332b3f5012a467c1
parent2d3f25c76a3f0bd604ac457f08afd2a5a5a50b45 (diff)
downloadsonarqube-62ef92603ae2ffc1403d42ddcc0cad7d252daa2c.tar.gz
sonarqube-62ef92603ae2ffc1403d42ddcc0cad7d252daa2c.zip
SONAR-4679 Introduce a chain of responsibility for issue filtering
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssueFilterChain.java53
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java33
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java77
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/issue/IssueFiltersTest.java16
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java1
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/batch/IssueFilter.java44
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/issue/batch/IssueFilterChain.java42
7 files changed, 247 insertions, 19 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssueFilterChain.java b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssueFilterChain.java
new file mode 100644
index 00000000000..2644561e422
--- /dev/null
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/DefaultIssueFilterChain.java
@@ -0,0 +1,53 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.batch.issue;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.batch.IssueFilter;
+import org.sonar.api.issue.batch.IssueFilterChain;
+
+import java.util.List;
+
+public class DefaultIssueFilterChain implements IssueFilterChain {
+
+ private final List<IssueFilter> filters;
+
+ public DefaultIssueFilterChain(IssueFilter... filters) {
+ this.filters = ImmutableList.copyOf(filters);
+ }
+
+ public DefaultIssueFilterChain() {
+ this.filters = ImmutableList.of();
+ }
+
+ private DefaultIssueFilterChain(List<IssueFilter> filters) {
+ this.filters = filters;
+ }
+
+ @Override
+ public boolean accept(Issue issue) {
+ if (filters.isEmpty()) {
+ return true;
+ } else {
+ return filters.get(0).accept(issue, new DefaultIssueFilterChain(filters.subList(1, filters.size())));
+ }
+ }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java
index 190b4ea1916..dc79211d39c 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/issue/IssueFilters.java
@@ -20,7 +20,7 @@
package org.sonar.batch.issue;
import org.sonar.api.BatchExtension;
-import org.sonar.api.issue.IssueFilter;
+import org.sonar.api.issue.batch.IssueFilter;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.rules.Violation;
import org.sonar.batch.ViolationFilters;
@@ -31,28 +31,39 @@ public class IssueFilters implements BatchExtension {
private final ViolationFilters deprecatedFilters;
private final DeprecatedViolations deprecatedViolations;
+ private final org.sonar.api.issue.IssueFilter[] exclusionFilters;
private final IssueFilter[] filters;
- public IssueFilters(ViolationFilters deprecatedFilters, DeprecatedViolations deprecatedViolations, IssueFilter[] filters) {
+ public IssueFilters(ViolationFilters deprecatedFilters, DeprecatedViolations deprecatedViolations, org.sonar.api.issue.IssueFilter[] exclusionFilters, IssueFilter[] filters) {
this.deprecatedFilters = deprecatedFilters;
this.deprecatedViolations = deprecatedViolations;
+ this.exclusionFilters = exclusionFilters;
this.filters = filters;
}
+ public IssueFilters(ViolationFilters deprecatedFilters, DeprecatedViolations deprecatedViolations, org.sonar.api.issue.IssueFilter[] exclusionFilters) {
+ this(deprecatedFilters, deprecatedViolations, exclusionFilters, new IssueFilter[0]);
+ }
+
public IssueFilters(ViolationFilters deprecatedFilters, DeprecatedViolations deprecatedViolations) {
- this(deprecatedFilters, deprecatedViolations, new IssueFilter[0]);
+ this(deprecatedFilters, deprecatedViolations, new org.sonar.api.issue.IssueFilter[0]);
}
public boolean accept(DefaultIssue issue, @Nullable Violation violation) {
- for (IssueFilter filter : filters) {
- if (!filter.accept(issue)) {
- return false;
+ if(new DefaultIssueFilterChain(filters).accept(issue)) {
+ // Apply deprecated rules only if filter chain accepts the current issue
+ for (org.sonar.api.issue.IssueFilter filter : exclusionFilters) {
+ if (!filter.accept(issue)) {
+ return false;
+ }
}
+ if (!deprecatedFilters.isEmpty()) {
+ Violation v = violation != null ? violation : deprecatedViolations.toViolation(issue);
+ return !deprecatedFilters.isIgnored(v);
+ }
+ return true;
+ } else {
+ return false;
}
- if (!deprecatedFilters.isEmpty()) {
- Violation v = violation != null ? violation : deprecatedViolations.toViolation(issue);
- return !deprecatedFilters.isIgnored(v);
- }
- return true;
}
}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java
new file mode 100644
index 00000000000..ce8ff6a1b63
--- /dev/null
+++ b/sonar-batch/src/test/java/org/sonar/batch/issue/DefaultIssueFilterChainTest.java
@@ -0,0 +1,77 @@
+package org.sonar.batch.issue;
+
+import org.fest.assertions.Fail;
+
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.batch.IssueFilter;
+import org.sonar.api.issue.batch.IssueFilterChain;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class DefaultIssueFilterChainTest {
+
+ private final Issue issue = mock(Issue.class);
+
+ @Test
+ public void should_accept_when_no_filter() throws Exception {
+ assertThat(new DefaultIssueFilterChain().accept(issue)).isTrue();
+ }
+
+ class PassingFilter implements IssueFilter {
+ @Override
+ public boolean accept(Issue issue, IssueFilterChain chain) {
+ return chain.accept(issue);
+ }
+ }
+
+ class AcceptingFilter implements IssueFilter {
+ @Override
+ public boolean accept(Issue issue, IssueFilterChain chain) {
+ return true;
+ }
+ }
+
+ class RefusingFilter implements IssueFilter {
+ @Override
+ public boolean accept(Issue issue, IssueFilterChain chain) {
+ return false;
+ }
+ }
+
+ class FailingFilter implements IssueFilter {
+ @Override
+ public boolean accept(Issue issue, IssueFilterChain chain) {
+ Fail.fail();
+ return false;
+ }
+
+ }
+
+ @Test
+ public void should_accept_if_all_filters_pass() throws Exception {
+ assertThat(new DefaultIssueFilterChain(
+ new PassingFilter(),
+ new PassingFilter(),
+ new PassingFilter()
+ ).accept(issue)).isTrue();
+ }
+
+ @Test
+ public void should_accept_and_not_go_further_if_filter_accepts() throws Exception {
+ assertThat(new DefaultIssueFilterChain(
+ new PassingFilter(),
+ new AcceptingFilter(),
+ new FailingFilter()
+ ).accept(issue)).isTrue();
+ }
+
+ @Test
+ public void should_refuse_and_not_go_further_if_filter_refuses() throws Exception {
+ assertThat(new DefaultIssueFilterChain(
+ new PassingFilter(),
+ new RefusingFilter(),
+ new FailingFilter()
+ ).accept(issue)).isFalse();
+ }
+}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/issue/IssueFiltersTest.java b/sonar-batch/src/test/java/org/sonar/batch/issue/IssueFiltersTest.java
index b9583a20285..d1e35cf5492 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/issue/IssueFiltersTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/issue/IssueFiltersTest.java
@@ -21,7 +21,7 @@ package org.sonar.batch.issue;
import org.junit.Test;
import org.sonar.api.issue.Issue;
-import org.sonar.api.issue.IssueFilter;
+import org.sonar.api.issue.batch.IssueFilter;
import org.sonar.api.issue.internal.DefaultIssue;
import org.sonar.api.rules.Violation;
import org.sonar.batch.ViolationFilters;
@@ -37,21 +37,21 @@ public class IssueFiltersTest {
ViolationFilters deprecatedFilters = mock(ViolationFilters.class);
@Test
- public void accept() throws Exception {
- IssueFilter ok = mock(IssueFilter.class);
+ public void accept_when_filter_chain_is_empty() throws Exception {
+ org.sonar.api.issue.IssueFilter ok = mock(org.sonar.api.issue.IssueFilter.class);
when(ok.accept(any(Issue.class))).thenReturn(true);
- IssueFilter ko = mock(IssueFilter.class);
+ org.sonar.api.issue.IssueFilter ko = mock(org.sonar.api.issue.IssueFilter.class);
when(ko.accept(any(Issue.class))).thenReturn(false);
when(deprecatedFilters.isEmpty()).thenReturn(true);
- IssueFilters filters = new IssueFilters(deprecatedFilters, deprecatedViolations, new IssueFilter[]{ok, ko});
+ IssueFilters filters = new IssueFilters(deprecatedFilters, deprecatedViolations, new org.sonar.api.issue.IssueFilter[]{ok, ko});
assertThat(filters.accept(new DefaultIssue(), null)).isFalse();
- filters = new IssueFilters(deprecatedFilters, deprecatedViolations, new IssueFilter[]{ok});
+ filters = new IssueFilters(deprecatedFilters, deprecatedViolations, new org.sonar.api.issue.IssueFilter[]{ok});
assertThat(filters.accept(new DefaultIssue(), null)).isTrue();
- filters = new IssueFilters(deprecatedFilters, deprecatedViolations, new IssueFilter[]{ko});
+ filters = new IssueFilters(deprecatedFilters, deprecatedViolations, new org.sonar.api.issue.IssueFilter[]{ko});
assertThat(filters.accept(new DefaultIssue(), null)).isFalse();
}
@@ -66,7 +66,7 @@ public class IssueFiltersTest {
public void should_check_deprecated_violation_filters() throws Exception {
when(deprecatedFilters.isEmpty()).thenReturn(false);
when(deprecatedFilters.isIgnored(any(Violation.class))).thenReturn(true);
- IssueFilters filters = new IssueFilters(deprecatedFilters, deprecatedViolations, new IssueFilter[0]);
+ IssueFilters filters = new IssueFilters(deprecatedFilters, deprecatedViolations, new org.sonar.api.issue.IssueFilter[0]);
assertThat(filters.accept(new DefaultIssue(), null)).isFalse();
}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java
index 66d3fb9e0f0..fbde9018825 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueFilter.java
@@ -23,6 +23,7 @@ import org.sonar.api.BatchExtension;
/**
* @since 3.6
+ * @deprecated since 4.0
*/
public interface IssueFilter extends BatchExtension {
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/batch/IssueFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/batch/IssueFilter.java
new file mode 100644
index 00000000000..1b4a765492b
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/batch/IssueFilter.java
@@ -0,0 +1,44 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.issue.batch;
+
+import org.sonar.api.issue.Issue;
+
+/**
+ * <p>An issue filter is an object that allows filtering of {@link Issue}s on batch side, preventing them from being persisted.</p>
+ * @since 4.0
+ *
+ */
+public interface IssueFilter {
+
+ /**
+ * The <code>accept</code> method is called for each {@link Issue} created during analysis, to check if it has to be persisted. Examples of use cases are:
+ * <ul>
+ * <li>Ignoring or enforcing rules on specific resources</li>
+ * <li>Switching-off an issue based on its context (<code>//NOSONAR</code> comments, semantic annotations)</li>
+ * </ul>
+ * The <code>chain</code> parameter allows for fine control of the filtering logic: it is each filter's duty to either pass the issue to the next filter, by calling
+ * the {@link IssueFilterChain#accept()} method, or return directly if the issue has to be accepted or not
+ * @param issue the issue being filtered
+ * @param chain the rest of the filters
+ * @return <code>true</code> to accept the issue, <code>false</code> to reject it, {@link IssueFilterChain#accept()} to let the other filters decide.
+ */
+ boolean accept(Issue issue, IssueFilterChain chain);
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/batch/IssueFilterChain.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/batch/IssueFilterChain.java
new file mode 100644
index 00000000000..b147e2761e3
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/batch/IssueFilterChain.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 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.issue.batch;
+
+import org.sonar.api.issue.Issue;
+
+/**
+ * A filter chain is an object provided to issues filters for fine control over the filtering logic. Each filter has the choice to:
+ * <ul>
+ * <li>Accept the issue</li>
+ * <li>Reject the issue</li>
+ * <li>Let downstream filters decide by passing the issue to the rest of the chain</li>
+ * </ul>
+ * @since 4.0
+ *
+ */
+public interface IssueFilterChain {
+
+ /**
+ * Called by a filter to let downstream filters decide the fate of the issue
+ * @param issue
+ * @return
+ */
+ boolean accept(Issue issue);
+}