From 105176fb19c3ab8a82eec620abd99b32b1387139 Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Wed, 5 Aug 2015 16:32:47 +0200 Subject: [PATCH] SONAR-6754 Add support of execution flow in Xoo --- .../sonar/xoo/rule/MultilineIssuesSensor.java | 114 +++++++++++++----- .../issues/MultilineIssuesMediumTest.java | 14 +++ .../xources/hello/WithFlow.xoo | 14 +++ .../api/batch/sensor/issue/NewIssue.java | 8 +- .../sensor/issue/internal/DefaultIssue.java | 12 +- 5 files changed, 131 insertions(+), 31 deletions(-) create mode 100644 sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/WithFlow.xoo diff --git a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/MultilineIssuesSensor.java b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/MultilineIssuesSensor.java index 81891faf809..0bb2bf643b2 100644 --- a/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/MultilineIssuesSensor.java +++ b/plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/MultilineIssuesSensor.java @@ -19,9 +19,14 @@ */ 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> startPositions = new HashMap<>(); - Map> endPositions = new HashMap<>(); + Table startIssuesPositions = HashBasedTable.create(); + Table endIssuesPositions = HashBasedTable.create(); + Map> startFlowsPositions = Maps.newHashMap(); + Map> 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> startPositions, - Map> endPositions, - RuleKey ruleKey) { - for (Map.Entry> entry : startPositions.entrySet()) { + private static void parseFlows(InputFile file, SensorContext context, Map> startFlowsPositions, + Map> 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.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.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 startPositions, + Table endPositions, Map> startFlowsPositions, + Map> endFlowsPositions) { + RuleKey ruleKey = RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY); + + for (Map.Entry> entry : startPositions.rowMap().entrySet()) { + Integer issueId = entry.getKey(); NewIssue newIssue = context.newIssue().forRule(ruleKey); for (Map.Entry 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 flows = startFlowsPositions.get(issueId); + for (Map.Entry> flowEntry : flows.rowMap().entrySet()) { + Integer flowId = flowEntry.getKey(); + List flowLocations = Lists.newArrayList(); + List 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> startPositions, - Map> endPositions) { + private static void parseIssues(InputFile file, SensorContext context, Table startPositions, + Table 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()); - } - 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()); - } - endPositions.get(issueId).put(issueLocationId, newPointer); + endPositions.row(issueId).put(issueLocationId, newPointer); } } } catch (IOException e) { diff --git a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java index 8ba2d4834ba..b5a6cdc14dd 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/mediumtest/issues/MultilineIssuesMediumTest.java @@ -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 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 index 00000000000..31e11c695e9 --- /dev/null +++ b/sonar-batch/src/test/resources/mediumtest/xoo/sample-multiline/xources/hello/WithFlow.xoo @@ -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 diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssue.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssue.java index 8ad694545b3..5ae14bcda20 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/NewIssue.java @@ -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 locations); /** * @since 5.2 diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java index 32737cc3a0e..5538e155b7b 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/sensor/issue/internal/DefaultIssue.java @@ -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 locations) { List flowAsList = new ArrayList<>(); - for (NewIssueLocation issueLocation : flow) { + for (NewIssueLocation issueLocation : locations) { flowAsList.add((DefaultIssueLocation) issueLocation); } executionFlows.add(flowAsList); - return null; + return this; } @Override -- 2.39.5