]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6754 Add support of execution flow in Xoo
authorJulien HENRY <julien.henry@sonarsource.com>
Wed, 5 Aug 2015 14:32:47 +0000 (16:32 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Wed, 5 Aug 2015 14:42:02 +0000 (16:42 +0200)
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/MultilineIssuesSensor.java
sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java
sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/WithFlow.xoo [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssue.java
sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java

index 81891faf809154ee18d93d1449e03d115dbe703c..0bb2bf643b2ad14bba43525b69330faa42cd6aa1 100644 (file)
  */
 package org.sonar.xoo.rule;
 
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Table;
 import java.io.IOException;
 import java.nio.file.Files;
-import java.util.HashMap;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -41,8 +46,11 @@ import org.sonar.xoo.Xoo;
 public class MultilineIssuesSensor implements Sensor {
 
   public static final String RULE_KEY = "MultilineIssue";
-  private static final String START_ISSUE_PATTERN = "\\{xoo-start-issue:([0-9]+):([0-9]+)\\}";
-  private static final String END_ISSUE_PATTERN = "\\{xoo-end-issue:([0-9]+):([0-9]+)\\}";
+  private static final Pattern START_ISSUE_PATTERN = Pattern.compile("\\{xoo-start-issue:([0-9]+):([0-9]+)\\}");
+  private static final Pattern END_ISSUE_PATTERN = Pattern.compile("\\{xoo-end-issue:([0-9]+):([0-9]+)\\}");
+
+  private static final Pattern START_FLOW_PATTERN = Pattern.compile("\\{xoo-start-flow:([0-9]+):([0-9]+):([0-9]+)\\}");
+  private static final Pattern END_FLOW_PATTERN = Pattern.compile("\\{xoo-end-flow:([0-9]+):([0-9]+):([0-9]+)\\}");
 
   @Override
   public void describe(SensorDescriptor descriptor) {
@@ -62,62 +70,114 @@ public class MultilineIssuesSensor implements Sensor {
   }
 
   private static void createIssues(InputFile file, SensorContext context) {
-    Pattern startPattern = Pattern.compile(START_ISSUE_PATTERN);
-    Pattern endPattern = Pattern.compile(END_ISSUE_PATTERN);
-    Map<Integer, Map<Integer, TextPointer>> startPositions = new HashMap<>();
-    Map<Integer, Map<Integer, TextPointer>> endPositions = new HashMap<>();
+    Table<Integer, Integer, TextPointer> startIssuesPositions = HashBasedTable.create();
+    Table<Integer, Integer, TextPointer> endIssuesPositions = HashBasedTable.create();
+    Map<Integer, Table<Integer, Integer, TextPointer>> startFlowsPositions = Maps.newHashMap();
+    Map<Integer, Table<Integer, Integer, TextPointer>> endFlowsPositions = Maps.newHashMap();
 
-    RuleKey ruleKey = RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY);
-    parse(file, context, startPattern, endPattern, startPositions, endPositions);
-    createIssues(file, context, startPositions, endPositions, ruleKey);
+    parseIssues(file, context, startIssuesPositions, endIssuesPositions);
+    parseFlows(file, context, startFlowsPositions, endFlowsPositions);
+    createIssues(file, context, startIssuesPositions, endIssuesPositions, startFlowsPositions, endFlowsPositions);
   }
 
-  private static void createIssues(InputFile file, SensorContext context, Map<Integer, Map<Integer, TextPointer>> startPositions,
-    Map<Integer, Map<Integer, TextPointer>> endPositions,
-    RuleKey ruleKey) {
-    for (Map.Entry<Integer, Map<Integer, TextPointer>> entry : startPositions.entrySet()) {
+  private static void parseFlows(InputFile file, SensorContext context, Map<Integer, Table<Integer, Integer, TextPointer>> startFlowsPositions,
+    Map<Integer, Table<Integer, Integer, TextPointer>> endFlowsPositions) {
+    int currentLine = 0;
+    try {
+      for (String lineStr : Files.readAllLines(file.path(), context.fileSystem().encoding())) {
+        currentLine++;
+
+        Matcher m = START_FLOW_PATTERN.matcher(lineStr);
+        while (m.find()) {
+          Integer issueId = Integer.parseInt(m.group(1));
+          Integer issueFlowId = Integer.parseInt(m.group(2));
+          Integer issueFlowNum = Integer.parseInt(m.group(3));
+          TextPointer newPointer = file.newPointer(currentLine, m.end());
+          if (!startFlowsPositions.containsKey(issueId)) {
+            startFlowsPositions.put(issueId, HashBasedTable.<Integer, Integer, TextPointer>create());
+          }
+          startFlowsPositions.get(issueId).row(issueFlowId).put(issueFlowNum, newPointer);
+        }
+
+        m = END_FLOW_PATTERN.matcher(lineStr);
+        while (m.find()) {
+          Integer issueId = Integer.parseInt(m.group(1));
+          Integer issueFlowId = Integer.parseInt(m.group(2));
+          Integer issueFlowNum = Integer.parseInt(m.group(3));
+          TextPointer newPointer = file.newPointer(currentLine, m.start());
+          if (!endFlowsPositions.containsKey(issueId)) {
+            endFlowsPositions.put(issueId, HashBasedTable.<Integer, Integer, TextPointer>create());
+          }
+          endFlowsPositions.get(issueId).row(issueFlowId).put(issueFlowNum, newPointer);
+        }
+      }
+    } catch (IOException e) {
+      throw new IllegalStateException("Unable to read file", e);
+    }
+  }
+
+  private static void createIssues(InputFile file, SensorContext context, Table<Integer, Integer, TextPointer> startPositions,
+    Table<Integer, Integer, TextPointer> endPositions, Map<Integer, Table<Integer, Integer, TextPointer>> startFlowsPositions,
+    Map<Integer, Table<Integer, Integer, TextPointer>> endFlowsPositions) {
+    RuleKey ruleKey = RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY);
+
+    for (Map.Entry<Integer, Map<Integer, TextPointer>> entry : startPositions.rowMap().entrySet()) {
+      Integer issueId = entry.getKey();
       NewIssue newIssue = context.newIssue().forRule(ruleKey);
       for (Map.Entry<Integer, TextPointer> location : entry.getValue().entrySet()) {
         NewIssueLocation newLocation = newIssue.newLocation()
           .on(file)
-          .at(file.newRange(location.getValue(), endPositions.get(entry.getKey()).get(location.getKey())));
+          .at(file.newRange(location.getValue(), endPositions.row(entry.getKey()).get(location.getKey())));
         if (location.getKey() == 1) {
           newIssue.at(newLocation.message("Primary location"));
         } else {
           newIssue.addLocation(newLocation.message("Location #" + location.getKey()));
         }
       }
+      if (startFlowsPositions.containsKey(issueId)) {
+        Table<Integer, Integer, TextPointer> flows = startFlowsPositions.get(issueId);
+        for (Map.Entry<Integer, Map<Integer, TextPointer>> flowEntry : flows.rowMap().entrySet()) {
+          Integer flowId = flowEntry.getKey();
+          List<NewIssueLocation> flowLocations = Lists.newArrayList();
+          List<Integer> flowNums = Lists.newArrayList(flowEntry.getValue().keySet());
+          Collections.sort(flowNums);
+          for (Integer flowNum : flowNums) {
+            TextPointer start = flowEntry.getValue().get(flowNum);
+            TextPointer end = endFlowsPositions.get(issueId).row(flowId).get(flowNum);
+            NewIssueLocation newLocation = newIssue.newLocation()
+              .on(file)
+              .at(file.newRange(start, end))
+              .message("Flow step #" + flowNum);
+            flowLocations.add(newLocation);
+          }
+          newIssue.addExecutionFlow(flowLocations);
+        }
+      }
       newIssue.save();
     }
   }
 
-  private static void parse(InputFile file, SensorContext context, Pattern startPattern, Pattern endPattern, Map<Integer, Map<Integer, TextPointer>> startPositions,
-    Map<Integer, Map<Integer, TextPointer>> endPositions) {
+  private static void parseIssues(InputFile file, SensorContext context, Table<Integer, Integer, TextPointer> startPositions,
+    Table<Integer, Integer, TextPointer> endPositions) {
     int currentLine = 0;
     try {
       for (String lineStr : Files.readAllLines(file.path(), context.fileSystem().encoding())) {
         currentLine++;
 
-        Matcher m = startPattern.matcher(lineStr);
+        Matcher m = START_ISSUE_PATTERN.matcher(lineStr);
         while (m.find()) {
           Integer issueId = Integer.parseInt(m.group(1));
           Integer issueLocationId = Integer.parseInt(m.group(2));
           TextPointer newPointer = file.newPointer(currentLine, m.end());
-          if (!startPositions.containsKey(issueId)) {
-            startPositions.put(issueId, new HashMap<Integer, TextPointer>());
-          }
-          startPositions.get(issueId).put(issueLocationId, newPointer);
+          startPositions.row(issueId).put(issueLocationId, newPointer);
         }
 
-        m = endPattern.matcher(lineStr);
+        m = END_ISSUE_PATTERN.matcher(lineStr);
         while (m.find()) {
           Integer issueId = Integer.parseInt(m.group(1));
           Integer issueLocationId = Integer.parseInt(m.group(2));
           TextPointer newPointer = file.newPointer(currentLine, m.start());
-          if (!endPositions.containsKey(issueId)) {
-            endPositions.put(issueId, new HashMap<Integer, TextPointer>());
-          }
-          endPositions.get(issueId).put(issueLocationId, newPointer);
+          endPositions.row(issueId).put(issueLocationId, newPointer);
         }
       }
     } catch (IOException e) {
index 8ba2d4834ba992652ffd7522bb4b1dc2428fc952..b5a6cdc14dda025735ea4822894192f6b6f1ecd9 100644 (file)
@@ -29,6 +29,7 @@ import org.junit.rules.TemporaryFolder;
 import org.sonar.batch.mediumtest.BatchMediumTester;
 import org.sonar.batch.mediumtest.TaskResult;
 import org.sonar.batch.protocol.input.ActiveRule;
+import org.sonar.batch.protocol.output.BatchReport.ExecutionFlow;
 import org.sonar.batch.protocol.output.BatchReport.Issue;
 import org.sonar.batch.protocol.output.BatchReport.IssueLocation;
 import org.sonar.xoo.XooPlugin;
@@ -120,4 +121,17 @@ public class MultilineIssuesMediumTest {
     assertThat(additionalLocation.getTextRange().getEndLine()).isEqualTo(7);
     assertThat(additionalLocation.getTextRange().getEndOffset()).isEqualTo(52);
   }
+
+  @Test
+  public void testExecutionFlows() throws Exception {
+    List<Issue> issues = result.issuesFor(result.inputFile("xources/hello/WithFlow.xoo"));
+    assertThat(issues).hasSize(1);
+    Issue issue = issues.get(0);
+    assertThat(issue.getExecutionFlowList()).hasSize(1);
+
+    ExecutionFlow executionFlow = issue.getExecutionFlow(0);
+    assertThat(executionFlow.getLocationList()).hasSize(2);
+
+    // TODO more assertions
+  }
 }
diff --git a/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/WithFlow.xoo b/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/WithFlow.xoo
new file mode 100644 (file)
index 0000000..31e11c6
--- /dev/null
@@ -0,0 +1,14 @@
+package hello;
+
+public class HelloJava {
+
+  public static void main(String[] args) {
+    {xoo-start-flow:1:1:1}if (true){xoo-end-flow:1:1:1} {
+      {xoo-start-flow:1:1:2}if (true){xoo-end-flow:1:1:2} {
+        {xoo-start-issue:1:1}if (true){xoo-end-issue:1:1} {
+          System.out.println("Hello");
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
index 8ad694545b3e0642eaa5546756a2b9e2908e5318..5ae14bcda20680b3ef947b3b569d0db7a9b055b5 100644 (file)
@@ -65,7 +65,13 @@ public interface NewIssue {
    * @since 5.2
    * Register an execution flow for this issue.
    */
-  NewIssue addExecutionFlow(NewIssueLocation... flow);
+  NewIssue addExecutionFlow(NewIssueLocation... locations);
+
+  /**
+   * @since 5.2
+   * Register an execution flow for this issue.
+   */
+  NewIssue addExecutionFlow(Iterable<NewIssueLocation> locations);
 
   /**
    * @since 5.2
index 32737cc3a0eb2ea464e93938cb1ee1ce9e17cd7e..5538e155b7b571e143fd74eeb3a1d5646c62df72 100644 (file)
@@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -106,13 +107,18 @@ public class DefaultIssue extends DefaultStorable implements Issue, NewIssue {
   }
 
   @Override
-  public DefaultIssue addExecutionFlow(NewIssueLocation... flow) {
+  public DefaultIssue addExecutionFlow(NewIssueLocation... locations) {
+    return addExecutionFlow(Arrays.asList(locations));
+  }
+
+  @Override
+  public DefaultIssue addExecutionFlow(Iterable<NewIssueLocation> locations) {
     List<IssueLocation> flowAsList = new ArrayList<>();
-    for (NewIssueLocation issueLocation : flow) {
+    for (NewIssueLocation issueLocation : locations) {
       flowAsList.add((DefaultIssueLocation) issueLocation);
     }
     executionFlows.add(flowAsList);
-    return null;
+    return this;
   }
 
   @Override