]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3644 Save changes to new API before revert
authorJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Thu, 5 Sep 2013 09:49:20 +0000 (11:49 +0200)
committerJean-Baptiste Lievremont <jean-baptiste.lievremont@sonarsource.com>
Thu, 5 Sep 2013 10:15:17 +0000 (12:15 +0200)
Copy resources from Switch Off Violations plugin to core
Remove references to deprecated APIs

27 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/Constants.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesConfiguration.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilter.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPlugin.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/LineRange.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/Pattern.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoder.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializer.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScanner.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScanner.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilterTest.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPluginTest.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/LineRangeTest.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoderTest.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternTest.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializerTest.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScannerTest.java [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-mess.txt [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-twice.txt [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-unfinished.txt [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-wrong-order.txt [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp.txt [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-no-regexp.txt [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp-and-double-regexp.txt [new file with mode: 0644]
plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp.txt [new file with mode: 0644]

index 7ee64903dc7ed9f08ab1641572bf80953412b004..3f25e46fb09015641737daf357b2c766727e09da 100644 (file)
@@ -47,6 +47,7 @@ import org.sonar.plugins.core.issue.IssueTracking;
 import org.sonar.plugins.core.issue.IssueTrackingDecorator;
 import org.sonar.plugins.core.issue.IssuesDensityDecorator;
 import org.sonar.plugins.core.issue.WeightedIssuesDecorator;
+import org.sonar.plugins.core.issue.ignore.IgnoreIssuesPlugin;
 import org.sonar.plugins.core.issue.notification.ChangesOnMyIssueNotificationDispatcher;
 import org.sonar.plugins.core.issue.notification.IssueChangesEmailTemplate;
 import org.sonar.plugins.core.issue.notification.NewFalsePositiveNotificationDispatcher;
@@ -406,9 +407,11 @@ import java.util.List;
 })
 public final class CorePlugin extends SonarPlugin {
 
-  @SuppressWarnings("unchecked")
+  @SuppressWarnings("rawtypes")
   public List getExtensions() {
-    return ImmutableList.of(
+    ImmutableList.Builder<Object> extensions = ImmutableList.builder();
+
+    extensions.add(
         DefaultResourceTypes.class,
         UserManagedMetrics.class,
         Periods.class,
@@ -524,5 +527,9 @@ public final class CorePlugin extends SonarPlugin {
         // Notify alerts on my favourite projects
         NewAlerts.class,
         NewAlerts.newMetadata());
+
+    extensions.addAll(IgnoreIssuesPlugin.getExtensions());
+
+    return extensions.build();
   }
 }
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/Constants.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/Constants.java
new file mode 100644 (file)
index 0000000..632d038
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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.plugins.core.issue.ignore;
+
+public interface Constants {
+  // New Properties
+  String SUB_CATEGORY_IGNORE_ISSUES = "Ignore Issues";
+
+  String CORE_KEY_PREFIX = "sonar.issue.ignore";
+  String DEPRECATED_PLUGIN_KEY_PREFIX = "sonar.switchoffviolations";
+
+  String MULTICRITERIA_SUFFIX = ".multicriteria";
+  String PATTERNS_MULTICRITERIA_KEY = CORE_KEY_PREFIX + MULTICRITERIA_SUFFIX;
+  String DEPRECATED_MULTICRITERIA_KEY = DEPRECATED_PLUGIN_KEY_PREFIX + MULTICRITERIA_SUFFIX;
+  String RESOURCE_KEY = "resourceKey";
+  String RULE_KEY = "ruleKey";
+  String LINE_RANGE_KEY = "lineRange";
+
+  String BLOCK_SUFFIX = ".block";
+  String PATTERNS_BLOCK_KEY = CORE_KEY_PREFIX + BLOCK_SUFFIX;
+  String DEPRECATED_BLOCK_KEY = DEPRECATED_PLUGIN_KEY_PREFIX + BLOCK_SUFFIX;
+  String BEGIN_BLOCK_REGEXP = "beginBlockRegexp";
+  String END_BLOCK_REGEXP = "endBlockRegexp";
+
+  String ALLFILE_SUFFIX = ".allfile";
+  String PATTERNS_ALLFILE_KEY = CORE_KEY_PREFIX + ALLFILE_SUFFIX;
+  String DEPRECATED_ALLFILE_KEY = DEPRECATED_PLUGIN_KEY_PREFIX + ALLFILE_SUFFIX;
+  String FILE_REGEXP = "fileRegexp";
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesConfiguration.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesConfiguration.java
new file mode 100644 (file)
index 0000000..045425c
--- /dev/null
@@ -0,0 +1,109 @@
+// POLOP_IS_GREAT
+/*
+ * 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.plugins.core.issue.ignore;
+
+import org.sonar.api.PropertyType;
+
+import org.sonar.api.config.PropertyFieldDefinition;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.CoreProperties;
+import com.google.common.collect.ImmutableList;
+import org.sonar.api.config.PropertyDefinition;
+
+import java.util.List;
+
+public class IgnoreIssuesConfiguration {
+
+
+  static final int LARGE_SIZE = 20;
+  static final int SMALL_SIZE = 10;
+
+  public static List<PropertyDefinition> getPropertyDefinitions() {
+    return ImmutableList.of(
+      PropertyDefinition.builder(Constants.PATTERNS_MULTICRITERIA_KEY)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(Constants.SUB_CATEGORY_IGNORE_ISSUES)
+        .deprecatedKey(Constants.DEPRECATED_MULTICRITERIA_KEY)
+        .name("Resource Key Pattern")
+        .description("Patterns used to identify which violations to switch off.<br/>" +
+          "More information on the <a href=\"http://docs.codehaus.org/display/SONAR/Project+Administration#ProjectAdministration-IgnoringIssues\">Project Administration page</a>.<br/>")
+        .onQualifiers(Qualifiers.PROJECT)
+        .fields(
+          PropertyFieldDefinition.build(Constants.RESOURCE_KEY)
+            .name("Resource Key Pattern")
+            .description("Pattern used to match resources which should be ignored")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build(),
+          PropertyFieldDefinition.build(Constants.RULE_KEY)
+            .name("Rule Key Pattern")
+            .description("Pattern used to match rules which should be ignored")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build(),
+          PropertyFieldDefinition.build(Constants.LINE_RANGE_KEY)
+            .name("Line Range")
+            .description("Range of lines that should be ignored.")
+            .type(PropertyType.STRING)
+            .indicativeSize(SMALL_SIZE)
+            .build())
+        .build(),
+        PropertyDefinition.builder(Constants.PATTERNS_BLOCK_KEY)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(Constants.SUB_CATEGORY_IGNORE_ISSUES)
+        .deprecatedKey(Constants.DEPRECATED_BLOCK_KEY)
+        .name("Block exclusion patterns")
+        .description("Patterns used to identify blocks in which violations are switched off.<br/>" +
+          "More information on the <a href=\"http://docs.codehaus.org/display/SONAR/Project+Administration#ProjectAdministration-IgnoringIssues\">Project Administration page</a>.<br/>")
+        .onQualifiers(Qualifiers.PROJECT)
+        .fields(
+          PropertyFieldDefinition.build(Constants.BEGIN_BLOCK_REGEXP)
+            .name("Regular expression for start of block")
+            .description("If this regular expression is found in a resource, then following lines are ignored until end of block.")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build(),
+          PropertyFieldDefinition.build(Constants.END_BLOCK_REGEXP)
+            .name("Regular expression for end of block")
+            .description("If specified, this regular expression is used to determine the end of code blocks to ignore. If not, then block ends at the end of file.")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build())
+        .build(),
+        PropertyDefinition.builder(Constants.PATTERNS_ALLFILE_KEY)
+        .category(CoreProperties.CATEGORY_EXCLUSIONS)
+        .subCategory(Constants.SUB_CATEGORY_IGNORE_ISSUES)
+        .deprecatedKey(Constants.DEPRECATED_ALLFILE_KEY)
+        .name("File exclusion patterns")
+        .description("Patterns used to identify files in which violations are switched off.<br/>" +
+          "More information on the <a href=\"http://docs.codehaus.org/display/SONAR/Project+Administration#ProjectAdministration-IgnoringIssues\">Project Administration page</a>.<br/>")
+        .onQualifiers(Qualifiers.PROJECT)
+        .fields(
+          PropertyFieldDefinition.build(Constants.FILE_REGEXP)
+            .name("Regular expression")
+            .description("If this regular expression is found in a resource, then following lines are ignored.")
+            .type(PropertyType.STRING)
+            .indicativeSize(LARGE_SIZE)
+            .build())
+        .build());
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilter.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilter.java
new file mode 100644 (file)
index 0000000..c60b9b7
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.plugins.core.issue.ignore;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.IssueFilter;
+import org.sonar.plugins.core.issue.ignore.pattern.Pattern;
+import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer;
+
+import java.util.List;
+
+public final class IgnoreIssuesFilter implements IssueFilter {
+
+  private static final Logger LOG = LoggerFactory.getLogger(IgnoreIssuesFilter.class);
+
+  private PatternsInitializer patternsInitializer;
+
+  public IgnoreIssuesFilter(PatternsInitializer patternsInitializer) {
+    this.patternsInitializer = patternsInitializer;
+  }
+
+  public boolean accept(Issue issue) {
+    Pattern extraPattern = patternsInitializer.getExtraPattern(issue.componentKey());
+    LOG.debug("Extra pattern for resource {}: {}", issue.componentKey(), extraPattern);
+    if (extraPattern != null && extraPattern.match(issue)) {
+      logExclusion(issue, extraPattern);
+      return false;
+    }
+
+    List<Pattern> patterns = patternsInitializer.getMulticriteriaPatterns();
+    for (Pattern pattern : patterns) {
+      if (pattern.match(issue)) {
+        logExclusion(issue, pattern);
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private void logExclusion(Issue issue, Pattern pattern) {
+    LOG.debug("Issue {} ignored by {}", issue, pattern);
+  }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPlugin.java
new file mode 100644 (file)
index 0000000..29d0048
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.plugins.core.issue.ignore;
+
+import com.google.common.collect.ImmutableList;
+import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer;
+import org.sonar.plugins.core.issue.ignore.scanner.RegexpScanner;
+import org.sonar.plugins.core.issue.ignore.scanner.SourceScanner;
+
+import java.util.List;
+
+public final class IgnoreIssuesPlugin {
+
+  public static List<?> getExtensions() {
+    ImmutableList.Builder<Object> extensions = ImmutableList.builder();
+
+    extensions.addAll(IgnoreIssuesConfiguration.getPropertyDefinitions());
+    extensions.add(
+        PatternsInitializer.class,
+        RegexpScanner.class,
+        SourceScanner.class,
+        IgnoreIssuesFilter.class);
+
+    return extensions.build();
+  }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/LineRange.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/LineRange.java
new file mode 100644 (file)
index 0000000..743533d
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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.plugins.core.issue.ignore.pattern;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+public class LineRange {
+  private int from;
+  private int to;
+
+  public LineRange(int from, int to) {
+    Preconditions.checkArgument(from <= to, "Line range is not valid: %s must be greater than %s", from, to);
+
+    this.from = from;
+    this.to = to;
+  }
+
+  public boolean in(int lineId) {
+    return from <= lineId && lineId <= to;
+  }
+
+  public Set<Integer> toLines() {
+    Set<Integer> lines = Sets.newLinkedHashSet();
+    for (int index = from; index <= to; index++) {
+      lines.add(index);
+    }
+    return lines;
+  }
+
+  @Override
+  public String toString() {
+    return "[" + from + "-" + to + "]";
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + from;
+    result = prime * result + to;
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    if (fieldsDiffer((LineRange) obj)) {
+      return false;
+    }
+    return true;
+  }
+
+  private boolean fieldsDiffer(LineRange other) {
+    if (from != other.from) {
+      return true;
+    }
+    if (to != other.to) {
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/Pattern.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/Pattern.java
new file mode 100644 (file)
index 0000000..ae3a8eb
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * 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.plugins.core.issue.ignore.pattern;
+
+import com.google.common.collect.Sets;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.WildcardPattern;
+
+import java.util.Set;
+
+public class Pattern {
+
+  private WildcardPattern resourcePattern;
+  private WildcardPattern rulePattern;
+  private Set<Integer> lines = Sets.newLinkedHashSet();
+  private Set<LineRange> lineRanges = Sets.newLinkedHashSet();
+  private String beginBlockRegexp;
+  private String endBlockRegexp;
+  private String allFileRegexp;
+  private boolean checkLines = true;
+
+  public Pattern() {
+  }
+
+  public Pattern(String resourcePattern, String rulePattern) {
+    this.resourcePattern = WildcardPattern.create(resourcePattern);
+    this.rulePattern = WildcardPattern.create(rulePattern);
+  }
+
+  public Pattern(String resourcePattern, String rulePattern, Set<LineRange> lineRanges) {
+    this(resourcePattern, rulePattern);
+    this.lineRanges = lineRanges;
+  }
+
+  public WildcardPattern getResourcePattern() {
+    return resourcePattern;
+  }
+
+  public WildcardPattern getRulePattern() {
+    return rulePattern;
+  }
+
+  public String getBeginBlockRegexp() {
+    return beginBlockRegexp;
+  }
+
+  public String getEndBlockRegexp() {
+    return endBlockRegexp;
+  }
+
+  public String getAllFileRegexp() {
+    return allFileRegexp;
+  }
+
+  Pattern addLineRange(int fromLineId, int toLineId) {
+    lineRanges.add(new LineRange(fromLineId, toLineId));
+    return this;
+  }
+
+  Pattern addLine(int lineId) {
+    lines.add(lineId);
+    return this;
+  }
+
+  boolean isCheckLines() {
+    return checkLines;
+  }
+
+  Pattern setCheckLines(boolean b) {
+    this.checkLines = b;
+    return this;
+  }
+
+  Pattern setBeginBlockRegexp(String beginBlockRegexp) {
+    this.beginBlockRegexp = beginBlockRegexp;
+    return this;
+  }
+
+  Pattern setEndBlockRegexp(String endBlockRegexp) {
+    this.endBlockRegexp = endBlockRegexp;
+    return this;
+  }
+
+  Pattern setAllFileRegexp(String allFileRegexp) {
+    this.allFileRegexp = allFileRegexp;
+    return this;
+  }
+
+  Set<Integer> getAllLines() {
+    Set<Integer> allLines = Sets.newLinkedHashSet(lines);
+    for (LineRange lineRange : lineRanges) {
+      allLines.addAll(lineRange.toLines());
+    }
+    return allLines;
+  }
+
+  public boolean match(Issue violation) {
+    boolean match = matchResource(violation.componentKey()) && matchRule(violation.ruleKey());
+    if (checkLines && violation.line() != null) {
+      match = match && matchLine(violation.line());
+    }
+    return match;
+  }
+
+  boolean matchLine(int lineId) {
+    if (lines.contains(lineId)) {
+      return true;
+    }
+
+    for (LineRange range : lineRanges) {
+      if (range.in(lineId)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  boolean matchRule(RuleKey rule) {
+    System.out.printf("Matching rule {} against pattern {}", rule, rulePattern);
+    if (rule == null) {
+      return false;
+    }
+
+    String key = new StringBuilder().append(rule.repository()).append(':').append(rule.rule()).toString();
+    return rulePattern.match(key);
+  }
+
+  boolean matchResource(String resource) {
+    System.out.printf("Matching resource {} against pattern {}", resource, resourcePattern);
+    return resource != null && resourcePattern.match(resource);
+  }
+
+  @Override
+  public String toString() {
+    return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoder.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoder.java
new file mode 100644 (file)
index 0000000..d40388b
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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.plugins.core.issue.ignore.pattern;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.SonarException;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+public class PatternDecoder {
+
+  private static final int THREE_FIELDS_PER_LINE = 3;
+  private static final String LINE_RANGE_REGEXP = "\\[((\\d+|\\d+-\\d+),?)*\\]";
+
+  public List<Pattern> decode(String patternsList) {
+    List<Pattern> patterns = Lists.newLinkedList();
+    String[] patternsLines = StringUtils.split(patternsList, "\n");
+    for (String patternLine : patternsLines) {
+      Pattern pattern = decodeLine(patternLine.trim());
+      if (pattern != null) {
+        patterns.add(pattern);
+      }
+    }
+    return patterns;
+  }
+
+  public List<Pattern> decode(File file) {
+    try {
+      List<String> lines = FileUtils.readLines(file);
+      List<Pattern> patterns = Lists.newLinkedList();
+      for (String line : lines) {
+        Pattern pattern = decodeLine(line);
+        if (pattern != null) {
+          patterns.add(pattern);
+        }
+      }
+      return patterns;
+
+    } catch (IOException e) {
+      throw new SonarException("Fail to load the file: " + file.getAbsolutePath(), e);
+    }
+  }
+
+  /**
+   * Main method that decodes a line which defines a pattern
+   */
+  public Pattern decodeLine(String line) {
+    if (isBlankOrComment(line)) {
+      return null;
+    }
+
+    String[] fields = StringUtils.splitPreserveAllTokens(line, ';');
+    if (fields.length > THREE_FIELDS_PER_LINE) {
+      throw new SonarException("Invalid format. The following line has more than 3 fields separated by comma: " + line);
+    }
+
+    Pattern pattern;
+    if (fields.length == THREE_FIELDS_PER_LINE) {
+      checkRegularLineConstraints(line, fields);
+      pattern = new Pattern(StringUtils.trim(fields[0]), StringUtils.trim(fields[1]));
+      decodeRangeOfLines(pattern, fields[2]);
+    } else if (fields.length == 2) {
+      checkDoubleRegexpLineConstraints(line, fields);
+      pattern = new Pattern().setBeginBlockRegexp(fields[0]).setEndBlockRegexp(fields[1]);
+    } else {
+      pattern = new Pattern().setAllFileRegexp(fields[0]);
+    }
+
+    return pattern;
+  }
+
+  private void checkRegularLineConstraints(String line, String[] fields) {
+    if (!isResource(fields[0])) {
+      throw new SonarException("Invalid format. The first field does not define a resource pattern: " + line);
+    }
+    if (!isRule(fields[1])) {
+      throw new SonarException("Invalid format. The second field does not define a rule pattern: " + line);
+    }
+    if (!isLinesRange(fields[2])) {
+      throw new SonarException("Invalid format. The third field does not define a range of lines: " + line);
+    }
+  }
+
+  private void checkDoubleRegexpLineConstraints(String line, String[] fields) {
+    if (!isRegexp(fields[0])) {
+      throw new SonarException("Invalid format. The first field does not define a regular expression: " + line);
+    }
+    if (!isRegexp(fields[1])) {
+      throw new SonarException("Invalid format. The second field does not define a regular expression: " + line);
+    }
+  }
+
+  public static void decodeRangeOfLines(Pattern pattern, String field) {
+    if (StringUtils.equals(field, "*")) {
+      pattern.setCheckLines(false);
+    } else {
+      pattern.setCheckLines(true);
+      String s = StringUtils.substringBetween(StringUtils.trim(field), "[", "]");
+      String[] parts = StringUtils.split(s, ',');
+      for (String part : parts) {
+        if (StringUtils.contains(part, '-')) {
+          String[] range = StringUtils.split(part, '-');
+          pattern.addLineRange(Integer.valueOf(range[0]), Integer.valueOf(range[1]));
+        } else {
+          pattern.addLine(Integer.valueOf(part));
+        }
+      }
+    }
+  }
+
+  @VisibleForTesting
+  boolean isLinesRange(String field) {
+    return StringUtils.equals(field, "*") || java.util.regex.Pattern.matches(LINE_RANGE_REGEXP, field);
+  }
+
+  @VisibleForTesting
+  boolean isBlankOrComment(String line) {
+    return StringUtils.isBlank(line) || StringUtils.startsWith(line, "#");
+  }
+
+  @VisibleForTesting
+  boolean isResource(String field) {
+    return StringUtils.isNotBlank(field);
+  }
+
+  @VisibleForTesting
+  boolean isRule(String field) {
+    return StringUtils.isNotBlank(field);
+  }
+
+  @VisibleForTesting
+  boolean isRegexp(String field) {
+    return StringUtils.isNotBlank(field);
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializer.java
new file mode 100644 (file)
index 0000000..bfc3bdb
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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.plugins.core.issue.ignore.pattern;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.config.Settings;
+import org.sonar.plugins.core.issue.ignore.Constants;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Objects.firstNonNull;
+import static com.google.common.base.Strings.nullToEmpty;
+
+public class PatternsInitializer implements BatchExtension {
+
+  private final Settings settings;
+
+  private List<Pattern> multicriteriaPatterns;
+  private List<Pattern> blockPatterns;
+  private List<Pattern> allFilePatterns;
+  private Map<String, Pattern> extraPatternByResource = Maps.newHashMap();
+
+  public PatternsInitializer(Settings settings) {
+    this.settings = settings;
+    initPatterns();
+  }
+
+  public List<Pattern> getMulticriteriaPatterns() {
+    return multicriteriaPatterns;
+  }
+
+  public List<Pattern> getBlockPatterns() {
+    return blockPatterns;
+  }
+
+  public List<Pattern> getAllFilePatterns() {
+    return allFilePatterns;
+  }
+
+  public Pattern getExtraPattern(String resource) {
+    return extraPatternByResource.get(resource.substring(resource.lastIndexOf(":") + 1));
+  }
+
+  @VisibleForTesting
+  protected final void initPatterns() {
+    multicriteriaPatterns = Lists.newArrayList();
+    blockPatterns = Lists.newArrayList();
+    allFilePatterns = Lists.newArrayList();
+
+    loadPatternsFromNewProperties();
+  }
+
+  private void loadPatternsFromNewProperties() {
+    // Patterns Multicriteria
+    String patternConf = StringUtils.defaultIfBlank(settings.getString(Constants.PATTERNS_MULTICRITERIA_KEY), "");
+    for (String id : StringUtils.split(patternConf, ',')) {
+      String propPrefix = Constants.PATTERNS_MULTICRITERIA_KEY + "." + id + ".";
+      String resourceKeyPattern = settings.getString(propPrefix + Constants.RESOURCE_KEY);
+      String ruleKeyPattern = settings.getString(propPrefix + Constants.RULE_KEY);
+      Pattern pattern = new Pattern(firstNonNull(resourceKeyPattern, "*"), firstNonNull(ruleKeyPattern, "*"));
+      String lineRange = settings.getString(propPrefix + Constants.LINE_RANGE_KEY);
+      PatternDecoder.decodeRangeOfLines(pattern, firstNonNull(lineRange, "*"));
+      multicriteriaPatterns.add(pattern);
+    }
+
+    // Patterns Block
+    patternConf = StringUtils.defaultIfBlank(settings.getString(Constants.PATTERNS_BLOCK_KEY), "");
+    for (String id : StringUtils.split(patternConf, ',')) {
+      String propPrefix = Constants.PATTERNS_BLOCK_KEY + "." + id + ".";
+      String beginBlockRegexp = settings.getString(propPrefix + Constants.BEGIN_BLOCK_REGEXP);
+      String endBlockRegexp = settings.getString(propPrefix + Constants.END_BLOCK_REGEXP);
+      Pattern pattern = new Pattern().setBeginBlockRegexp(nullToEmpty(beginBlockRegexp)).setEndBlockRegexp(nullToEmpty(endBlockRegexp));
+      blockPatterns.add(pattern);
+    }
+
+    // Patterns All File
+    patternConf = StringUtils.defaultIfBlank(settings.getString(Constants.PATTERNS_ALLFILE_KEY), "");
+    for (String id : StringUtils.split(patternConf, ',')) {
+      String propPrefix = Constants.PATTERNS_ALLFILE_KEY + "." + id + ".";
+      String allFileRegexp = settings.getString(propPrefix + Constants.FILE_REGEXP);
+      Pattern pattern = new Pattern().setAllFileRegexp(nullToEmpty(allFileRegexp));
+      allFilePatterns.add(pattern);
+    }
+  }
+
+  public void addPatternToExcludeResource(String resource) {
+    extraPatternByResource.put(resource, new Pattern(resource, "*").setCheckLines(false));
+  }
+
+  public void addPatternToExcludeLines(String resource, Set<LineRange> lineRanges) {
+    extraPatternByResource.put(resource, new Pattern(resource, "*", lineRanges));
+  }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScanner.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScanner.java
new file mode 100644 (file)
index 0000000..e25ec67
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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.plugins.core.issue.ignore.scanner;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.apache.commons.io.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.BatchExtension;
+import org.sonar.plugins.core.issue.ignore.pattern.LineRange;
+import org.sonar.plugins.core.issue.ignore.pattern.Pattern;
+import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Set;
+
+public class RegexpScanner implements BatchExtension {
+
+  private static final Logger LOG = LoggerFactory.getLogger(RegexpScanner.class);
+
+  private PatternsInitializer patternsInitializer;
+  private List<java.util.regex.Pattern> allFilePatterns;
+  private List<DoubleRegexpMatcher> blockMatchers;
+
+  // fields to be reset at every new scan
+  private DoubleRegexpMatcher currentMatcher;
+  private int fileLength;
+  private List<LineExclusion> lineExclusions;
+  private LineExclusion currentLineExclusion;
+
+  public RegexpScanner(PatternsInitializer patternsInitializer) {
+    this.patternsInitializer = patternsInitializer;
+
+    lineExclusions = Lists.newArrayList();
+    allFilePatterns = Lists.newArrayList();
+    blockMatchers = Lists.newArrayList();
+
+    for (Pattern pattern : this.patternsInitializer.getAllFilePatterns()) {
+      allFilePatterns.add(java.util.regex.Pattern.compile(pattern.getAllFileRegexp()));
+    }
+    for (Pattern pattern : this.patternsInitializer.getBlockPatterns()) {
+      blockMatchers.add(new DoubleRegexpMatcher(
+          java.util.regex.Pattern.compile(pattern.getBeginBlockRegexp()),
+          java.util.regex.Pattern.compile(pattern.getEndBlockRegexp())));
+    }
+
+    init();
+  }
+
+  private void init() {
+    currentMatcher = null;
+    fileLength = 0;
+    lineExclusions.clear();
+    currentLineExclusion = null;
+  }
+
+  public void scan(String resource, File file, Charset sourcesEncoding) throws IOException {
+    LOG.debug("Scanning {}", resource);
+    init();
+
+    List<String> lines = FileUtils.readLines(file, sourcesEncoding.name());
+    int lineIndex = 0;
+    for (String line : lines) {
+      lineIndex++;
+      if (line.trim().length() == 0) {
+        continue;
+      }
+
+      // first check the single regexp patterns that can be used to totally exclude a file
+      for (java.util.regex.Pattern pattern : allFilePatterns) {
+        if (pattern.matcher(line).find()) {
+          patternsInitializer.addPatternToExcludeResource(resource);
+          // nothing more to do on this file
+          LOG.debug("- Exclusion pattern '{}': every violation in this file will be ignored.", pattern);
+          return;
+        }
+      }
+
+      // then check the double regexps if we're still here
+      checkDoubleRegexps(line, lineIndex);
+    }
+
+    // now create the new line-based pattern for this file if there are exclusions
+    fileLength = lineIndex;
+    if (!lineExclusions.isEmpty()) {
+      Set<LineRange> lineRanges = convertLineExclusionsToLineRanges();
+      LOG.debug("- Line exclusions found: {}", lineRanges);
+      patternsInitializer.addPatternToExcludeLines(resource, lineRanges);
+    }
+  }
+
+  private Set<LineRange> convertLineExclusionsToLineRanges() {
+    Set<LineRange> lineRanges = Sets.newHashSet();
+    for (LineExclusion lineExclusion : lineExclusions) {
+      lineRanges.add(lineExclusion.toLineRange());
+    }
+    return lineRanges;
+  }
+
+  private void checkDoubleRegexps(String line, int lineIndex) {
+    if (currentMatcher == null) {
+      for (DoubleRegexpMatcher matcher : blockMatchers) {
+        if (matcher.matchesFirstPattern(line)) {
+          startExclusion(lineIndex);
+          currentMatcher = matcher;
+          break;
+        }
+      }
+    } else {
+      if (currentMatcher.matchesSecondPattern(line)) {
+        endExclusion(lineIndex);
+        currentMatcher = null;
+      }
+    }
+  }
+
+  private void startExclusion(int lineIndex) {
+    currentLineExclusion = new LineExclusion(lineIndex);
+    lineExclusions.add(currentLineExclusion);
+  }
+
+  private void endExclusion(int lineIndex) {
+    currentLineExclusion.setEnd(lineIndex);
+    currentLineExclusion = null;
+  }
+
+  private class LineExclusion {
+
+    private int start;
+    private int end;
+
+    LineExclusion(int start) {
+      this.start = start;
+      this.end = -1;
+    }
+
+    void setEnd(int end) {
+      this.end = end;
+    }
+
+    public LineRange toLineRange() {
+      return new LineRange(start, (end == -1 ? fileLength : end));
+    }
+
+  }
+
+  private static class DoubleRegexpMatcher {
+
+    private java.util.regex.Pattern firstPattern;
+    private java.util.regex.Pattern secondPattern;
+
+    DoubleRegexpMatcher(java.util.regex.Pattern firstPattern, java.util.regex.Pattern secondPattern) {
+      this.firstPattern = firstPattern;
+      this.secondPattern = secondPattern;
+    }
+
+    boolean matchesFirstPattern(String line) {
+      return firstPattern.matcher(line).find();
+    }
+
+    boolean matchesSecondPattern(String line) {
+      return secondPattern.matcher(line).find();
+    }
+
+  }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScanner.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScanner.java
new file mode 100644 (file)
index 0000000..55fec58
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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.plugins.core.issue.ignore.scanner;
+
+import org.sonar.api.batch.Phase;
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.JavaFile;
+import org.sonar.api.resources.Project;
+import org.sonar.api.scan.filesystem.FileQuery;
+import org.sonar.api.scan.filesystem.ModuleFileSystem;
+import org.sonar.api.utils.SonarException;
+import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer;
+
+import java.io.File;
+import java.nio.charset.Charset;
+import java.util.List;
+
+@Phase(name = Phase.Name.PRE)
+public final class SourceScanner implements Sensor {
+
+  private final RegexpScanner regexpScanner;
+  private final PatternsInitializer patternsInitializer;
+  private final ModuleFileSystem fileSystem;
+
+  public SourceScanner(RegexpScanner regexpScanner, PatternsInitializer patternsInitializer, ModuleFileSystem fileSystem) {
+    this.regexpScanner = regexpScanner;
+    this.patternsInitializer = patternsInitializer;
+    this.fileSystem = fileSystem;
+  }
+
+  public boolean shouldExecuteOnProject(Project project) {
+    return patternsInitializer.getAllFilePatterns().size() > 0 || patternsInitializer.getBlockPatterns().size() > 0;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void analyse(Project project, SensorContext context) {
+    parseDirs(project, false);
+    parseDirs(project, true);
+  }
+
+  protected void parseDirs(Project project, boolean isTest) {
+    Charset sourcesEncoding = fileSystem.sourceCharset();
+
+    List<File> files;
+    if (isTest) {
+      files = fileSystem.files(FileQuery.onTest().onLanguage(project.getLanguageKey()));
+    } else {
+      files = fileSystem.files(FileQuery.onSource().onLanguage(project.getLanguageKey()));
+    }
+
+    for (File inputFile : files) {
+      try {
+        String resource = defineResource(inputFile, fileSystem, project, isTest);
+        if (resource != null) {
+          regexpScanner.scan(resource, inputFile, sourcesEncoding);
+        }
+      } catch (Exception e) {
+        throw new SonarException("Unable to read the source file : '" + inputFile.getAbsolutePath() + "' with the charset : '"
+          + sourcesEncoding.name() + "'.", e);
+      }
+    }
+  }
+
+  /*
+   * This method is necessary because Java resources are not treated as every other resource...
+   */
+  private String defineResource(File inputFile, ModuleFileSystem fileSystem, Project project, boolean isTest) {
+    if (Java.KEY.equals(project.getLanguageKey()) && Java.isJavaFile(inputFile)) {
+      List<File> sourceDirs = null;
+      if (isTest) {
+        sourceDirs = fileSystem.testDirs();
+      } else {
+        sourceDirs = fileSystem.sourceDirs();
+      }
+      JavaFile file = JavaFile.fromIOFile(inputFile, sourceDirs, isTest);
+      if (file == null) {
+        return null;
+      } else {
+        return file.getKey();
+      }
+    }
+    return inputFile.getPath();
+  }
+
+  @Override
+  public String toString() {
+    return "Ignore Issues - Source Scanner";
+  }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilterTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesFilterTest.java
new file mode 100644 (file)
index 0000000..0a37fc7
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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.plugins.core.issue.ignore;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+import org.sonar.plugins.core.issue.ignore.pattern.Pattern;
+import org.sonar.plugins.core.issue.ignore.pattern.PatternDecoder;
+import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class IgnoreIssuesFilterTest {
+
+  public static final Rule CHECKSTYLE_RULE = Rule.create("checkstyle", "MagicNumber", "");
+  public static final String JAVA_FILE = "org.foo.Hello";
+
+  private PatternsInitializer patternsInitializer;
+  private IgnoreIssuesFilter filter;
+
+  @Before
+  public void init() {
+    patternsInitializer = mock(PatternsInitializer.class);
+    when(patternsInitializer.getMulticriteriaPatterns()).thenReturn(Collections.<Pattern> emptyList());
+
+    filter = new IgnoreIssuesFilter(patternsInitializer);
+  }
+
+  @Test
+  public void shouldBeDeactivatedWhenPropertyIsMissing() {
+    assertThat(filter.accept(create(CHECKSTYLE_RULE, JAVA_FILE, null))).isTrue();
+  }
+
+  @Test
+  public void shouldBeIgnoredWithStandardPatterns() throws IOException {
+    when(patternsInitializer.getMulticriteriaPatterns()).thenReturn(createPatterns("org.foo.Bar;*;*\norg.foo.Hello;checkstyle:MagicNumber;[15-200]"));
+
+    assertThat(filter.accept(create(CHECKSTYLE_RULE, JAVA_FILE, 150))).isFalse();
+  }
+
+  @Test
+  public void shouldNotBeIgnoredWithStandardPatterns() throws IOException {
+    when(patternsInitializer.getMulticriteriaPatterns()).thenReturn(createPatterns("org.foo.Bar;*;*\norg.foo.Hello;checkstyle:MagicNumber;[15-200]"));
+
+    assertThat(filter.accept(create(CHECKSTYLE_RULE, JAVA_FILE, 5))).isTrue();
+  }
+
+  @Test
+  public void shouldBeIgnoredWithExtraPattern() throws IOException {
+    when(patternsInitializer.getExtraPattern(JAVA_FILE)).thenReturn(createPatterns("org.foo.Hello;*;[15-200]").get(0));
+
+    assertThat(filter.accept(create(CHECKSTYLE_RULE, JAVA_FILE, 150))).isFalse();
+  }
+
+  @Test
+  public void shouldNotBeIgnoredWithExtraPattern() throws IOException {
+    when(patternsInitializer.getExtraPattern(JAVA_FILE)).thenReturn(createPatterns("org.foo.Hello;*;[15-200]").get(0));
+
+    assertThat(filter.accept(create(CHECKSTYLE_RULE, JAVA_FILE, 5))).isTrue();
+  }
+
+  private Issue create(Rule rule, String component, Integer line) {
+    Issue mockIssue = mock(Issue.class);
+    RuleKey ruleKey = null;
+    if (rule != null) {
+      ruleKey = rule.ruleKey();
+    }
+    when(mockIssue.ruleKey()).thenReturn(ruleKey);
+    when(mockIssue.componentKey()).thenReturn(component);
+    when(mockIssue.line()).thenReturn(line);
+    return mockIssue;
+  }
+
+  private List<Pattern> createPatterns(String line) {
+    return new PatternDecoder().decode(line);
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPluginTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/IgnoreIssuesPluginTest.java
new file mode 100644 (file)
index 0000000..c478e71
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.plugins.core.issue.ignore;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class IgnoreIssuesPluginTest {
+  @Test
+  public void justForCoverage() {
+    assertThat(new IgnoreIssuesPlugin().getExtensions()).hasSize(3 /* properties */ + 4 /* extensions */);
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/LineRangeTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/LineRangeTest.java
new file mode 100644 (file)
index 0000000..3c4c02a
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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.plugins.core.issue.ignore.pattern;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class LineRangeTest {
+
+  @Test(expected = IllegalArgumentException.class)
+  public void lineRangeShouldBeOrdered() {
+    new LineRange(25, 12);
+  }
+
+  @Test
+  public void shouldConvertLineRangeToLines() {
+    LineRange range = new LineRange(12, 15);
+
+    assertThat(range.toLines()).containsOnly(12, 13, 14, 15);
+  }
+
+  @Test
+  public void shouldTestInclusionInRangeOfLines() {
+    LineRange range = new LineRange(12, 15);
+
+    assertThat(range.in(3)).isFalse();
+    assertThat(range.in(12)).isTrue();
+    assertThat(range.in(13)).isTrue();
+    assertThat(range.in(14)).isTrue();
+    assertThat(range.in(15)).isTrue();
+    assertThat(range.in(16)).isFalse();
+  }
+
+  @Test
+  public void testToString() throws Exception {
+    assertThat(new LineRange(12, 15).toString()).isEqualTo("[12-15]");
+  }
+
+  @Test
+  public void testEquals() throws Exception {
+    LineRange range = new LineRange(12, 15);
+    assertThat(range).isEqualTo(range);
+    assertThat(range).isEqualTo(new LineRange(12, 15));
+    assertThat(range).isNotEqualTo(new LineRange(12, 2000));
+    assertThat(range).isNotEqualTo(new LineRange(1000, 2000));
+    assertThat(range).isNotEqualTo(null);
+    assertThat(range).isNotEqualTo(new StringBuffer());
+  }
+
+  @Test
+  public void testHashCode() throws Exception {
+    assertThat(new LineRange(12, 15).hashCode()).isEqualTo(new LineRange(12, 15).hashCode());
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoderTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternDecoderTest.java
new file mode 100644 (file)
index 0000000..3d904b4
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * 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.plugins.core.issue.ignore.pattern;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.SonarException;
+
+import java.io.File;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class PatternDecoderTest {
+
+  private PatternDecoder decoder = new PatternDecoder();
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Test
+  public void shouldReadString() {
+    String patternsList = "# a comment followed by a blank line\n\n" +
+      "# suppress all violations\n" +
+      "*;*;*\n\n" +
+      "# exclude a Java file\n" +
+      "com.foo.Bar;*;*\n\n" +
+      "# exclude a Java package\n" +
+      "com.foo.*;*;*\n\n" +
+      "# exclude a specific rule\n" +
+      "*;checkstyle:IllegalRegexp;*\n\n" +
+      "# exclude a specific rule on a specific file\n" +
+      "com.foo.Bar;checkstyle:IllegalRegexp;*\n";
+    List<Pattern> patterns = decoder.decode(patternsList);
+
+    assertThat(patterns).hasSize(5);
+  }
+
+  @Test
+  public void shouldCheckFormatOfResource() {
+    assertThat(decoder.isResource("")).isFalse();
+    assertThat(decoder.isResource("*")).isTrue();
+    assertThat(decoder.isResource("com.foo.*")).isTrue();
+  }
+
+  @Test
+  public void shouldCheckFormatOfRule() {
+    assertThat(decoder.isRule("")).isFalse();
+    assertThat(decoder.isRule("*")).isTrue();
+    assertThat(decoder.isRule("com.foo.*")).isTrue();
+  }
+
+  @Test
+  public void shouldCheckFormatOfLinesRange() {
+    assertThat(decoder.isLinesRange("")).isFalse();
+    assertThat(decoder.isLinesRange("   ")).isFalse();
+    assertThat(decoder.isLinesRange("12")).isFalse();
+    assertThat(decoder.isLinesRange("12,212")).isFalse();
+
+    assertThat(decoder.isLinesRange("*")).isTrue();
+    assertThat(decoder.isLinesRange("[]")).isTrue();
+    assertThat(decoder.isLinesRange("[13]")).isTrue();
+    assertThat(decoder.isLinesRange("[13,24]")).isTrue();
+    assertThat(decoder.isLinesRange("[13,24,25-500]")).isTrue();
+    assertThat(decoder.isLinesRange("[24-65]")).isTrue();
+    assertThat(decoder.isLinesRange("[13,24-65,84-89,122]")).isTrue();
+  }
+
+  @Test
+  public void shouldReadStarPatterns() {
+    Pattern pattern = decoder.decodeLine("*;*;*");
+
+    assertThat(pattern.getResourcePattern().toString()).isEqualTo("*");
+    assertThat(pattern.getRulePattern().toString()).isEqualTo("*");
+    assertThat(pattern.isCheckLines()).isFalse();
+  }
+
+  @Test
+  public void shouldReadLineIds() {
+    Pattern pattern = decoder.decodeLine("*;*;[10,25,98]");
+
+    assertThat(pattern.isCheckLines()).isTrue();
+    assertThat(pattern.getAllLines()).containsOnly(10, 25, 98);
+  }
+
+  @Test
+  public void shouldReadRangeOfLineIds() {
+    Pattern pattern = decoder.decodeLine("*;*;[10-12,25,97-100]");
+
+    assertThat(pattern.isCheckLines()).isTrue();
+    assertThat(pattern.getAllLines()).containsOnly(10, 11, 12, 25, 97, 98, 99, 100);
+  }
+
+  @Test
+  public void shouldNotExcludeLines() {
+    // [] is different than *
+    // - all violations are excluded on *
+    // * no violations are excluded on []
+    Pattern pattern = decoder.decodeLine("*;*;[]");
+
+    assertThat(pattern.isCheckLines()).isTrue();
+    assertThat(pattern.getAllLines()).isEmpty();
+  }
+
+  @Test
+  public void shouldReadBlockPattern() {
+    Pattern pattern = decoder.decodeLine("SONAR-OFF;SONAR-ON");
+
+    assertThat(pattern.getResourcePattern()).isNull();
+    assertThat(pattern.getBeginBlockRegexp()).isEqualTo("SONAR-OFF");
+    assertThat(pattern.getEndBlockRegexp()).isEqualTo("SONAR-ON");
+  }
+
+  @Test
+  public void shouldReadAllFilePattern() {
+    Pattern pattern = decoder.decodeLine("SONAR-ALL-OFF");
+
+    assertThat(pattern.getResourcePattern()).isNull();
+    assertThat(pattern.getAllFileRegexp()).isEqualTo("SONAR-ALL-OFF");
+  }
+
+  @Test
+  public void shouldFailIfUnexistingFile() {
+    thrown.expect(SonarException.class);
+    thrown.expectMessage("Fail to load the file");
+
+    decoder.decode(new File("foo"));
+  }
+
+  @Test
+  public void shouldFailToReadUncorrectLine1() {
+    thrown.expect(SonarException.class);
+    thrown.expectMessage("Invalid format. The following line has more than 3 fields separated by comma");
+
+    decoder.decode(";;;;");
+  }
+
+  @Test
+  public void shouldFailToReadUncorrectLine3() {
+    thrown.expect(SonarException.class);
+    thrown.expectMessage("Invalid format. The first field does not define a resource pattern");
+
+    decoder.decode(";*;*");
+  }
+
+  @Test
+  public void shouldFailToReadUncorrectLine4() {
+    thrown.expect(SonarException.class);
+    thrown.expectMessage("Invalid format. The second field does not define a rule pattern");
+
+    decoder.decode("*;;*");
+  }
+
+  @Test
+  public void shouldFailToReadUncorrectLine5() {
+    thrown.expect(SonarException.class);
+    thrown.expectMessage("Invalid format. The third field does not define a range of lines");
+
+    decoder.decode("*;*;blabla");
+  }
+
+  @Test
+  public void shouldFailToReadUncorrectLine6() {
+    thrown.expect(SonarException.class);
+    thrown.expectMessage("Invalid format. The first field does not define a regular expression");
+
+    decoder.decode(";ON");
+  }
+
+  @Test
+  public void shouldFailToReadUncorrectLine7() {
+    thrown.expect(SonarException.class);
+    thrown.expectMessage("Invalid format. The second field does not define a regular expression");
+
+    decoder.decode("OFF;");
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternTest.java
new file mode 100644 (file)
index 0000000..da10e85
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * 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.plugins.core.issue.ignore.pattern;
+
+import org.junit.Test;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.Rule;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class PatternTest {
+
+  @Test
+  public void shouldMatchLines() {
+    Pattern pattern = new Pattern("*", "*");
+    pattern.addLine(12).addLine(15).addLineRange(20, 25);
+
+    assertThat(pattern.matchLine(3)).isFalse();
+    assertThat(pattern.matchLine(12)).isTrue();
+    assertThat(pattern.matchLine(14)).isFalse();
+    assertThat(pattern.matchLine(21)).isTrue();
+    assertThat(pattern.matchLine(6599)).isFalse();
+  }
+
+  @Test
+  public void shouldMatchJavaFile() {
+    String javaFile = "org.foo.Bar";
+    assertThat(new Pattern("org.foo.Bar", "*").matchResource(javaFile)).isTrue();
+    assertThat(new Pattern("org.foo.*", "*").matchResource(javaFile)).isTrue();
+    assertThat(new Pattern("*Bar", "*").matchResource(javaFile)).isTrue();
+    assertThat(new Pattern("*", "*").matchResource(javaFile)).isTrue();
+    assertThat(new Pattern("org.*.?ar", "*").matchResource(javaFile)).isTrue();
+
+    assertThat(new Pattern("org.other.Hello", "*").matchResource(javaFile)).isFalse();
+    assertThat(new Pattern("org.foo.Hello", "*").matchResource(javaFile)).isFalse();
+    assertThat(new Pattern("org.*.??ar", "*").matchResource(javaFile)).isFalse();
+    assertThat(new Pattern("org.*.??ar", "*").matchResource(null)).isFalse();
+    assertThat(new Pattern("org.*.??ar", "*").matchResource("plop")).isFalse();
+  }
+
+  @Test
+  public void shouldMatchRule() {
+    RuleKey rule = Rule.create("checkstyle", "IllegalRegexp", "").ruleKey();
+    assertThat(new Pattern("*", "*").matchRule(rule)).isTrue();
+    assertThat(new Pattern("*", "checkstyle:*").matchRule(rule)).isTrue();
+    assertThat(new Pattern("*", "checkstyle:IllegalRegexp").matchRule(rule)).isTrue();
+    assertThat(new Pattern("*", "checkstyle:Illegal*").matchRule(rule)).isTrue();
+    assertThat(new Pattern("*", "*:*Illegal*").matchRule(rule)).isTrue();
+
+    assertThat(new Pattern("*", "pmd:IllegalRegexp").matchRule(rule)).isFalse();
+    assertThat(new Pattern("*", "pmd:*").matchRule(rule)).isFalse();
+    assertThat(new Pattern("*", "*:Foo*IllegalRegexp").matchRule(rule)).isFalse();
+  }
+
+  @Test
+  public void shouldMatchViolation() {
+    Rule rule = Rule.create("checkstyle", "IllegalRegexp", "");
+    String javaFile = "org.foo.Bar";
+
+    Pattern pattern = new Pattern("*", "*");
+    pattern.addLine(12);
+
+    assertThat(pattern.match(create(rule, javaFile, null))).isTrue();
+    assertThat(pattern.match(create(rule, javaFile, 12))).isTrue();
+    assertThat(pattern.match(create((Rule) null, javaFile, 5))).isFalse();
+    assertThat(pattern.match(create(rule, null, null))).isFalse();
+    assertThat(pattern.match(create((Rule) null, null, null))).isFalse();
+  }
+
+  private Issue create(Rule rule, String component, Integer line) {
+    Issue mockIssue = mock(Issue.class);
+    RuleKey ruleKey = null;
+    if (rule != null) {
+      ruleKey = rule.ruleKey();
+    }
+    when(mockIssue.ruleKey()).thenReturn(ruleKey);
+    when(mockIssue.componentKey()).thenReturn(component);
+    when(mockIssue.line()).thenReturn(line);
+    return mockIssue;
+  }
+
+  @Test
+  public void shouldNotMatchNullRule() {
+    assertThat(new Pattern("*", "*").matchRule(null)).isFalse();
+  }
+
+  @Test
+  public void shouldPrintPatternToString() {
+    Pattern pattern = new Pattern("*", "checkstyle:*");
+
+    assertThat(pattern.toString()).isEqualTo("Pattern[resourcePattern=*,rulePattern=checkstyle:*,lines=[],lineRanges=[],beginBlockRegexp=<null>,endBlockRegexp=<null>,allFileRegexp=<null>,checkLines=true]");
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/pattern/PatternsInitializerTest.java
new file mode 100644 (file)
index 0000000..ec6a652
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * 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.plugins.core.issue.ignore.pattern;
+
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.config.Settings;
+import org.sonar.plugins.core.issue.ignore.Constants;
+import org.sonar.plugins.core.issue.ignore.IgnoreIssuesPlugin;
+
+import java.util.Set;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class PatternsInitializerTest {
+
+  private PatternsInitializer patternsInitializer;
+
+  private Settings settings;
+
+  @Before
+  public void init() {
+    settings = new Settings(new PropertyDefinitions(new IgnoreIssuesPlugin()));
+    patternsInitializer = new PatternsInitializer(settings);
+  }
+
+  @Test
+  public void testNoConfiguration() {
+    patternsInitializer.initPatterns();
+    assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0);
+  }
+
+  @Test
+  public void shouldReturnExtraPatternForResource() {
+    String file = "foo";
+    patternsInitializer.addPatternToExcludeResource(file);
+
+    Pattern extraPattern = patternsInitializer.getExtraPattern(file);
+    assertThat(extraPattern.matchResource(file)).isTrue();
+    assertThat(extraPattern.isCheckLines()).isFalse();
+  }
+
+  @Test
+  public void shouldReturnExtraPatternForLinesOfResource() {
+    String file = "foo";
+    Set<LineRange> lineRanges = Sets.newHashSet();
+    lineRanges.add(new LineRange(25, 28));
+    patternsInitializer.addPatternToExcludeLines(file, lineRanges);
+
+    Pattern extraPattern = patternsInitializer.getExtraPattern(file);
+    assertThat(extraPattern.matchResource(file)).isTrue();
+    assertThat(extraPattern.getAllLines()).isEqualTo(Sets.newHashSet(25, 26, 27, 28));
+  }
+
+  @Test
+  public void shouldReturnMulticriteriaPattern() {
+    settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY, "1,2");
+    settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".1." + Constants.RESOURCE_KEY, "org.foo.Bar");
+    settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".1." + Constants.RULE_KEY, "*");
+    settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".1." + Constants.RESOURCE_KEY, "*");
+    settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".2." + Constants.LINE_RANGE_KEY, "org.foo.Hello");
+    settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".2." + Constants.RULE_KEY, "checkstyle:MagicNumber");
+    settings.setProperty(Constants.PATTERNS_MULTICRITERIA_KEY + ".2." + Constants.LINE_RANGE_KEY, "[15-200]");
+    patternsInitializer.initPatterns();
+
+    assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(2);
+    assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(0);
+    assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(0);
+  }
+
+  @Test
+  public void shouldReturnBlockPattern() {
+    settings.setProperty(Constants.PATTERNS_BLOCK_KEY, "1,2");
+    settings.setProperty(Constants.PATTERNS_BLOCK_KEY + ".1." + Constants.BEGIN_BLOCK_REGEXP, "// SONAR-OFF");
+    settings.setProperty(Constants.PATTERNS_BLOCK_KEY + ".1." + Constants.END_BLOCK_REGEXP, "// SONAR-ON");
+    settings.setProperty(Constants.PATTERNS_BLOCK_KEY + ".2." + Constants.BEGIN_BLOCK_REGEXP, "// FOO-OFF");
+    settings.setProperty(Constants.PATTERNS_BLOCK_KEY + ".2." + Constants.END_BLOCK_REGEXP, "// FOO-ON");
+    patternsInitializer.initPatterns();
+
+    assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0);
+    assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(2);
+    assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(0);
+  }
+
+  @Test
+  public void shouldReturnAllFilePattern() {
+    settings.setProperty(Constants.PATTERNS_ALLFILE_KEY, "1,2");
+    settings.setProperty(Constants.PATTERNS_ALLFILE_KEY + ".1." + Constants.FILE_REGEXP, "@SONAR-IGNORE-ALL");
+    settings.setProperty(Constants.PATTERNS_ALLFILE_KEY + ".2." + Constants.FILE_REGEXP, "//FOO-IGNORE-ALL");
+    patternsInitializer.initPatterns();
+
+    assertThat(patternsInitializer.getMulticriteriaPatterns().size()).isEqualTo(0);
+    assertThat(patternsInitializer.getBlockPatterns().size()).isEqualTo(0);
+    assertThat(patternsInitializer.getAllFilePatterns().size()).isEqualTo(2);
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest.java
new file mode 100644 (file)
index 0000000..9454ac5
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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.plugins.core.issue.ignore.scanner;
+
+import com.google.common.collect.Sets;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.sonar.plugins.core.issue.ignore.pattern.LineRange;
+import org.sonar.plugins.core.issue.ignore.pattern.Pattern;
+import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer;
+import org.sonar.test.TestUtils;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Set;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class RegexpScannerTest {
+
+  private RegexpScanner regexpScanner;
+
+  private String javaFile;
+  @Mock
+  private PatternsInitializer patternsInitializer;
+  @Mock
+  private Pattern allFilePattern;
+  @Mock
+  private Pattern blockPattern1;
+  @Mock
+  private Pattern blockPattern2;
+
+  @Before
+  public void init() {
+    MockitoAnnotations.initMocks(this);
+
+    when(allFilePattern.getAllFileRegexp()).thenReturn("@SONAR-IGNORE-ALL");
+    when(blockPattern1.getBeginBlockRegexp()).thenReturn("// SONAR-OFF");
+    when(blockPattern1.getEndBlockRegexp()).thenReturn("// SONAR-ON");
+    when(blockPattern2.getBeginBlockRegexp()).thenReturn("// FOO-OFF");
+    when(blockPattern2.getEndBlockRegexp()).thenReturn("// FOO-ON");
+    when(patternsInitializer.getAllFilePatterns()).thenReturn(Arrays.asList(allFilePattern));
+    when(patternsInitializer.getBlockPatterns()).thenReturn(Arrays.asList(blockPattern1, blockPattern2));
+
+    regexpScanner = new RegexpScanner(patternsInitializer);
+    verify(patternsInitializer, times(1)).getAllFilePatterns();
+    verify(patternsInitializer, times(1)).getBlockPatterns();
+
+    javaFile = "org.sonar.test.MyFile";
+  }
+
+  @Test
+  public void shouldDoNothing() throws IOException {
+    regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-no-regexp.txt"), UTF_8);
+
+    verifyNoMoreInteractions(patternsInitializer);
+  }
+
+  @Test
+  public void shouldAddPatternToExcludeFile() throws IOException {
+    regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-single-regexp.txt"), UTF_8);
+
+    verify(patternsInitializer, times(1)).addPatternToExcludeResource(javaFile);
+    verifyNoMoreInteractions(patternsInitializer);
+  }
+
+  @Test
+  public void shouldAddPatternToExcludeFileEvenIfAlsoDoubleRegexps() throws IOException {
+    regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-single-regexp-and-double-regexp.txt"), UTF_8);
+
+    verify(patternsInitializer, times(1)).addPatternToExcludeResource(javaFile);
+    verifyNoMoreInteractions(patternsInitializer);
+  }
+
+  @Test
+  public void shouldAddPatternToExcludeLines() throws IOException {
+    regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-double-regexp.txt"), UTF_8);
+
+    Set<LineRange> lineRanges = Sets.newHashSet();
+    lineRanges.add(new LineRange(21, 25));
+    verify(patternsInitializer, times(1)).addPatternToExcludeLines(javaFile, lineRanges);
+    verifyNoMoreInteractions(patternsInitializer);
+  }
+
+  @Test
+  public void shouldAddPatternToExcludeLinesTillTheEnd() throws IOException {
+    regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-double-regexp-unfinished.txt"), UTF_8);
+
+    Set<LineRange> lineRanges = Sets.newHashSet();
+    lineRanges.add(new LineRange(21, 34));
+    verify(patternsInitializer, times(1)).addPatternToExcludeLines(javaFile, lineRanges);
+    verifyNoMoreInteractions(patternsInitializer);
+  }
+
+  @Test
+  public void shouldAddPatternToExcludeSeveralLineRanges() throws IOException {
+    regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-double-regexp-twice.txt"), UTF_8);
+
+    Set<LineRange> lineRanges = Sets.newHashSet();
+    lineRanges.add(new LineRange(21, 25));
+    lineRanges.add(new LineRange(29, 33));
+    verify(patternsInitializer, times(1)).addPatternToExcludeLines(javaFile, lineRanges);
+    verifyNoMoreInteractions(patternsInitializer);
+  }
+
+  @Test
+  public void shouldAddPatternToExcludeLinesWithWrongOrder() throws IOException {
+    regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-double-regexp-wrong-order.txt"), UTF_8);
+
+    Set<LineRange> lineRanges = Sets.newHashSet();
+    lineRanges.add(new LineRange(25, 35));
+    verify(patternsInitializer, times(1)).addPatternToExcludeLines(javaFile, lineRanges);
+    verifyNoMoreInteractions(patternsInitializer);
+  }
+
+  @Test
+  public void shouldAddPatternToExcludeLinesWithMess() throws IOException {
+    regexpScanner.scan(javaFile, TestUtils.getResource(getClass(), "file-with-double-regexp-mess.txt"), UTF_8);
+
+    Set<LineRange> lineRanges = Sets.newHashSet();
+    lineRanges.add(new LineRange(21, 29));
+    verify(patternsInitializer, times(1)).addPatternToExcludeLines(javaFile, lineRanges);
+    verifyNoMoreInteractions(patternsInitializer);
+  }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScannerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/ignore/scanner/SourceScannerTest.java
new file mode 100644 (file)
index 0000000..954512e
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * 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.plugins.core.issue.ignore.scanner;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.sonar.api.resources.Project;
+import org.sonar.api.scan.filesystem.FileQuery;
+import org.sonar.api.scan.filesystem.ModuleFileSystem;
+import org.sonar.api.utils.SonarException;
+import org.sonar.plugins.core.issue.ignore.pattern.Pattern;
+import org.sonar.plugins.core.issue.ignore.pattern.PatternsInitializer;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class SourceScannerTest {
+
+  private SourceScanner scanner;
+
+  @Mock
+  private RegexpScanner regexpScanner;
+  @Mock
+  private PatternsInitializer patternsInitializer;
+  @Mock
+  private Project project;
+  @Mock
+  private ModuleFileSystem fileSystem;
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  @Before
+  public void init() {
+    MockitoAnnotations.initMocks(this);
+
+    when(fileSystem.sourceCharset()).thenReturn(UTF_8);
+
+    scanner = new SourceScanner(regexpScanner, patternsInitializer, fileSystem);
+  }
+
+  @Test
+  public void testToString() throws Exception {
+    assertThat(scanner.toString()).isEqualTo("Ignore Issues - Source Scanner");
+  }
+
+  @Test
+  public void shouldExecute() throws IOException {
+    when(patternsInitializer.getAllFilePatterns()).thenReturn(Arrays.asList(new Pattern(), new Pattern()));
+    assertThat(scanner.shouldExecuteOnProject(null)).isTrue();
+
+    when(patternsInitializer.getAllFilePatterns()).thenReturn(Collections.<Pattern>emptyList());
+    when(patternsInitializer.getBlockPatterns()).thenReturn(Arrays.asList(new Pattern(), new Pattern()));
+    assertThat(scanner.shouldExecuteOnProject(null)).isTrue();
+
+    when(patternsInitializer.getAllFilePatterns()).thenReturn(Collections.<Pattern>emptyList());
+    when(patternsInitializer.getBlockPatterns()).thenReturn(Collections.<Pattern>emptyList());
+    assertThat(scanner.shouldExecuteOnProject(null)).isFalse();
+  }
+
+  @Test
+  public void shouldAnalyseJavaProject() throws IOException {
+    File sourceFile = new File("src/main/java/Foo.java");
+    File testFile = new File("src/test/java/FooTest.java");
+
+    when(project.getLanguageKey()).thenReturn("java");
+    when(fileSystem.files(Mockito.isA(FileQuery.class)))
+      .thenReturn(Arrays.asList(sourceFile))
+      .thenReturn(Arrays.asList(testFile));
+    when(fileSystem.sourceDirs()).thenReturn(Arrays.asList(new File("src/main/java")));
+    when(fileSystem.testDirs()).thenReturn(Arrays.asList(new File("src/test/java")));
+
+    scanner.analyse(project, null);
+
+    verify(regexpScanner).scan("[default].Foo", sourceFile, UTF_8);
+    verify(regexpScanner).scan("[default].FooTest", testFile, UTF_8);
+  }
+
+  @Test
+  public void shouldAnalyseOtherProject() throws IOException {
+    File sourceFile = new File("Foo.php");
+    File testFile = new File("FooTest.php");
+
+    when(project.getLanguageKey()).thenReturn("php");
+    when(fileSystem.files(Mockito.isA(FileQuery.class)))
+      .thenReturn(Arrays.asList(sourceFile))
+      .thenReturn(Arrays.asList(testFile));
+
+    scanner.analyse(project, null);
+
+    verify(regexpScanner).scan("Foo.php", sourceFile, UTF_8);
+    verify(regexpScanner).scan("FooTest.php", testFile, UTF_8);
+  }
+
+  @Test
+  public void shouldAnalyseJavaProjectWithNonJavaFile() throws IOException {
+    File sourceFile = new File("src/main/java/Foo.java");
+    File otherFile = new File("other.js");
+
+    when(project.getLanguageKey()).thenReturn("java");
+    List<File> empty = Collections.emptyList();
+    when(fileSystem.files(Mockito.isA(FileQuery.class)))
+      .thenReturn(Arrays.asList(sourceFile, otherFile))
+      .thenReturn(empty);
+    when(fileSystem.sourceDirs()).thenReturn(Arrays.asList(new File("src/main/java")));
+    when(fileSystem.testDirs()).thenReturn(Arrays.asList(new File("src/test/java")));
+
+    scanner.analyse(project, null);
+
+    verify(regexpScanner, never()).scan("other.js", sourceFile, UTF_8);
+  }
+
+  @Test
+  public void shouldIgnoreInvalidFile() throws IOException {
+    File sourceFile = new File("invalid.java");
+
+    when(project.getLanguageKey()).thenReturn("java");
+    List<File> empty = Collections.emptyList();
+    when(fileSystem.files(Mockito.isA(FileQuery.class)))
+      .thenReturn(Arrays.asList(sourceFile))
+      .thenReturn(empty);
+    when(fileSystem.sourceDirs()).thenReturn(Arrays.asList(new File("src/main/java")));
+    when(fileSystem.testDirs()).thenReturn(Arrays.asList(new File("src/test/java")));
+
+    scanner.analyse(project, null);
+
+    verifyNoMoreInteractions(regexpScanner);
+  }
+
+  @Test
+  public void shouldReportFailure() throws IOException {
+    File sourceFile = new File("Foo.php");
+
+    when(project.getLanguageKey()).thenReturn("php");
+    when(fileSystem.files(Mockito.isA(FileQuery.class))).thenReturn(Arrays.asList(sourceFile));
+    doThrow(new IOException("BUG")).when(regexpScanner).scan("Foo.php", sourceFile, UTF_8);
+
+    thrown.expect(SonarException.class);
+    thrown.expectMessage("Unable to read the source file");
+
+    scanner.analyse(project, null);
+  }
+}
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-mess.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-mess.txt
new file mode 100644 (file)
index 0000000..48d30c9
--- /dev/null
@@ -0,0 +1,37 @@
+package org.sonar.plugins.switchoffviolations.pattern;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * 
+ */
+public class LineRange {
+  int from, to;
+
+  public LineRange(int from, int to) {
+    if (to < from) {
+      throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to);
+    }
+    this.from = from;
+    this.to = to;
+  }
+
+  // SONAR-OFF
+  public boolean in(int lineId) {
+    return from <= lineId && lineId <= to;
+  }
+  // FOO-OFF
+
+  public Set<Integer> toLines() {
+    Set<Integer> lines = Sets.newLinkedHashSet();
+    // SONAR-ON
+    for (int index = from; index <= to; index++) {
+      lines.add(index);
+    }
+    // FOO-ON
+    return lines;
+  }
+
+}
\ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-twice.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-twice.txt
new file mode 100644 (file)
index 0000000..9ae63dc
--- /dev/null
@@ -0,0 +1,37 @@
+package org.sonar.plugins.switchoffviolations.pattern;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * 
+ */
+public class LineRange {
+  int from, to;
+
+  public LineRange(int from, int to) {
+    if (to < from) {
+      throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to);
+    }
+    this.from = from;
+    this.to = to;
+  }
+
+  // SONAR-OFF
+  public boolean in(int lineId) {
+    return from <= lineId && lineId <= to;
+  }
+  // SONAR-ON
+
+  public Set<Integer> toLines() {
+    Set<Integer> lines = Sets.newLinkedHashSet();
+    // FOO-OFF
+    for (int index = from; index <= to; index++) {
+      lines.add(index);
+    }
+    // FOO-ON
+    return lines;
+  }
+
+}
\ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-unfinished.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-unfinished.txt
new file mode 100644 (file)
index 0000000..dd76561
--- /dev/null
@@ -0,0 +1,34 @@
+package org.sonar.plugins.switchoffviolations.pattern;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * 
+ */
+public class LineRange {
+  int from, to;
+
+  public LineRange(int from, int to) {
+    if (to < from) {
+      throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to);
+    }
+    this.from = from;
+    this.to = to;
+  }
+
+  // SONAR-OFF
+  public boolean in(int lineId) {
+    return from <= lineId && lineId <= to;
+  }
+
+  public Set<Integer> toLines() {
+    Set<Integer> lines = Sets.newLinkedHashSet();
+    for (int index = from; index <= to; index++) {
+      lines.add(index);
+    }
+    return lines;
+  }
+
+}
\ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-wrong-order.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp-wrong-order.txt
new file mode 100644 (file)
index 0000000..7cac0b9
--- /dev/null
@@ -0,0 +1,35 @@
+package org.sonar.plugins.switchoffviolations.pattern;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * 
+ */
+public class LineRange {
+  int from, to;
+
+  public LineRange(int from, int to) {
+    if (to < from) {
+      throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to);
+    }
+    this.from = from;
+    this.to = to;
+  }
+
+  // SONAR-ON
+  public boolean in(int lineId) {
+    return from <= lineId && lineId <= to;
+  }
+  // SONAR-OFF
+
+  public Set<Integer> toLines() {
+    Set<Integer> lines = Sets.newLinkedHashSet();
+    for (int index = from; index <= to; index++) {
+      lines.add(index);
+    }
+    return lines;
+  }
+
+}
\ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-double-regexp.txt
new file mode 100644 (file)
index 0000000..002169f
--- /dev/null
@@ -0,0 +1,35 @@
+package org.sonar.plugins.switchoffviolations.pattern;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * 
+ */
+public class LineRange {
+  int from, to;
+
+  public LineRange(int from, int to) {
+    if (to < from) {
+      throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to);
+    }
+    this.from = from;
+    this.to = to;
+  }
+
+  // SONAR-OFF
+  public boolean in(int lineId) {
+    return from <= lineId && lineId <= to;
+  }
+  // SONAR-ON
+
+  public Set<Integer> toLines() {
+    Set<Integer> lines = Sets.newLinkedHashSet();
+    for (int index = from; index <= to; index++) {
+      lines.add(index);
+    }
+    return lines;
+  }
+
+}
\ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-no-regexp.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-no-regexp.txt
new file mode 100644 (file)
index 0000000..f18fa5b
--- /dev/null
@@ -0,0 +1,33 @@
+package org.sonar.plugins.switchoffviolations.pattern;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * 
+ */
+public class LineRange {
+  int from, to;
+
+  public LineRange(int from, int to) {
+    if (to < from) {
+      throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to);
+    }
+    this.from = from;
+    this.to = to;
+  }
+
+  public boolean in(int lineId) {
+    return from <= lineId && lineId <= to;
+  }
+
+  public Set<Integer> toLines() {
+    Set<Integer> lines = Sets.newLinkedHashSet();
+    for (int index = from; index <= to; index++) {
+      lines.add(index);
+    }
+    return lines;
+  }
+
+}
\ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp-and-double-regexp.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp-and-double-regexp.txt
new file mode 100644 (file)
index 0000000..e09ecd7
--- /dev/null
@@ -0,0 +1,36 @@
+package org.sonar.plugins.switchoffviolations.pattern;
+
+import com.google.common.collect.Sets;
+
+  // SONAR-OFF
+
+import java.util.Set;
+
+/**
+ * @SONAR-IGNORE-ALL
+ */
+public class LineRange {
+  int from, to;
+
+  public LineRange(int from, int to) {
+    if (to < from) {
+      throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to);
+    }
+    this.from = from;
+    this.to = to;
+  }
+
+  public boolean in(int lineId) {
+    return from <= lineId && lineId <= to;
+  }
+  // SONAR-ON
+
+  public Set<Integer> toLines() {
+    Set<Integer> lines = Sets.newLinkedHashSet();
+    for (int index = from; index <= to; index++) {
+      lines.add(index);
+    }
+    return lines;
+  }
+
+}
\ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp.txt b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/issue/ignore/scanner/RegexpScannerTest/file-with-single-regexp.txt
new file mode 100644 (file)
index 0000000..ef135eb
--- /dev/null
@@ -0,0 +1,33 @@
+package org.sonar.plugins.switchoffviolations.pattern;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
+
+/**
+ * @SONAR-IGNORE-ALL
+ */
+public class LineRange {
+  int from, to;
+
+  public LineRange(int from, int to) {
+    if (to < from) {
+      throw new IllegalArgumentException("Line range is not valid: " + from + " must be greater than " + to);
+    }
+    this.from = from;
+    this.to = to;
+  }
+
+  public boolean in(int lineId) {
+    return from <= lineId && lineId <= to;
+  }
+
+  public Set<Integer> toLines() {
+    Set<Integer> lines = Sets.newLinkedHashSet();
+    for (int index = from; index <= to; index++) {
+      lines.add(index);
+    }
+    return lines;
+  }
+
+}
\ No newline at end of file