You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MultilineIssuesSensor.java 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2022 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.xoo.rule;
  21. import com.google.common.collect.HashBasedTable;
  22. import com.google.common.collect.Lists;
  23. import com.google.common.collect.Table;
  24. import java.io.IOException;
  25. import java.nio.file.Files;
  26. import java.util.Collections;
  27. import java.util.HashMap;
  28. import java.util.List;
  29. import java.util.Map;
  30. import java.util.regex.Matcher;
  31. import java.util.regex.Pattern;
  32. import org.sonar.api.batch.fs.FilePredicates;
  33. import org.sonar.api.batch.fs.FileSystem;
  34. import org.sonar.api.batch.fs.InputFile;
  35. import org.sonar.api.batch.fs.InputFile.Type;
  36. import org.sonar.api.batch.fs.TextPointer;
  37. import org.sonar.api.batch.sensor.Sensor;
  38. import org.sonar.api.batch.sensor.SensorContext;
  39. import org.sonar.api.batch.sensor.SensorDescriptor;
  40. import org.sonar.api.batch.sensor.issue.NewIssue;
  41. import org.sonar.api.batch.sensor.issue.NewIssueLocation;
  42. import org.sonar.api.rule.RuleKey;
  43. import org.sonar.xoo.Xoo;
  44. public class MultilineIssuesSensor implements Sensor {
  45. public static final String RULE_KEY = "MultilineIssue";
  46. private static final Pattern START_ISSUE_PATTERN = Pattern.compile("\\{xoo-start-issue:([0-9]+)\\}");
  47. private static final Pattern END_ISSUE_PATTERN = Pattern.compile("\\{xoo-end-issue:([0-9]+)\\}");
  48. private static final Pattern START_FLOW_PATTERN = Pattern.compile("\\{xoo-start-flow:([0-9]+):([0-9]+):([0-9]+)\\}");
  49. private static final Pattern END_FLOW_PATTERN = Pattern.compile("\\{xoo-end-flow:([0-9]+):([0-9]+):([0-9]+)\\}");
  50. @Override
  51. public void describe(SensorDescriptor descriptor) {
  52. descriptor
  53. .name("Multiline Issues")
  54. .onlyOnLanguages(Xoo.KEY)
  55. .createIssuesForRuleRepositories(XooRulesDefinition.XOO_REPOSITORY);
  56. }
  57. @Override
  58. public void execute(SensorContext context) {
  59. FileSystem fs = context.fileSystem();
  60. FilePredicates p = fs.predicates();
  61. for (InputFile file : fs.inputFiles(p.and(p.hasLanguages(Xoo.KEY), p.hasType(Type.MAIN)))) {
  62. createIssues(file, context);
  63. }
  64. }
  65. private static void createIssues(InputFile file, SensorContext context) {
  66. Map<Integer, TextPointer> startIssuesPositions = new HashMap<>();
  67. Map<Integer, TextPointer> endIssuesPositions = new HashMap<>();
  68. Map<Integer, Table<Integer, Integer, TextPointer>> startFlowsPositions = new HashMap<>();
  69. Map<Integer, Table<Integer, Integer, TextPointer>> endFlowsPositions = new HashMap<>();
  70. parseIssues(file, context, startIssuesPositions, endIssuesPositions);
  71. parseFlows(file, startFlowsPositions, endFlowsPositions);
  72. createIssues(file, context, startIssuesPositions, endIssuesPositions, startFlowsPositions, endFlowsPositions);
  73. }
  74. private static void parseFlows(InputFile file, Map<Integer, Table<Integer, Integer, TextPointer>> startFlowsPositions,
  75. Map<Integer, Table<Integer, Integer, TextPointer>> endFlowsPositions) {
  76. int currentLine = 0;
  77. try {
  78. for (String lineStr : Files.readAllLines(file.path(), file.charset())) {
  79. currentLine++;
  80. Matcher m = START_FLOW_PATTERN.matcher(lineStr);
  81. while (m.find()) {
  82. Integer issueId = Integer.parseInt(m.group(1));
  83. Integer issueFlowId = Integer.parseInt(m.group(2));
  84. Integer issueFlowNum = Integer.parseInt(m.group(3));
  85. TextPointer newPointer = file.newPointer(currentLine, m.end());
  86. if (!startFlowsPositions.containsKey(issueId)) {
  87. startFlowsPositions.put(issueId, HashBasedTable.create());
  88. }
  89. startFlowsPositions.get(issueId).row(issueFlowId).put(issueFlowNum, newPointer);
  90. }
  91. m = END_FLOW_PATTERN.matcher(lineStr);
  92. while (m.find()) {
  93. Integer issueId = Integer.parseInt(m.group(1));
  94. Integer issueFlowId = Integer.parseInt(m.group(2));
  95. Integer issueFlowNum = Integer.parseInt(m.group(3));
  96. TextPointer newPointer = file.newPointer(currentLine, m.start());
  97. if (!endFlowsPositions.containsKey(issueId)) {
  98. endFlowsPositions.put(issueId, HashBasedTable.create());
  99. }
  100. endFlowsPositions.get(issueId).row(issueFlowId).put(issueFlowNum, newPointer);
  101. }
  102. }
  103. } catch (IOException e) {
  104. throw new IllegalStateException("Unable to read file", e);
  105. }
  106. }
  107. private static void createIssues(InputFile file, SensorContext context, Map<Integer, TextPointer> startPositions,
  108. Map<Integer, TextPointer> endPositions, Map<Integer, Table<Integer, Integer, TextPointer>> startFlowsPositions,
  109. Map<Integer, Table<Integer, Integer, TextPointer>> endFlowsPositions) {
  110. RuleKey ruleKey = RuleKey.of(XooRulesDefinition.XOO_REPOSITORY, RULE_KEY);
  111. for (Map.Entry<Integer, TextPointer> entry : startPositions.entrySet()) {
  112. NewIssue newIssue = context.newIssue().forRule(ruleKey);
  113. Integer issueId = entry.getKey();
  114. NewIssueLocation primaryLocation = newIssue.newLocation()
  115. .on(file)
  116. .at(file.newRange(entry.getValue(), endPositions.get(issueId)));
  117. newIssue.at(primaryLocation.message("Primary location"));
  118. if (startFlowsPositions.containsKey(issueId)) {
  119. Table<Integer, Integer, TextPointer> flows = startFlowsPositions.get(issueId);
  120. for (Map.Entry<Integer, Map<Integer, TextPointer>> flowEntry : flows.rowMap().entrySet()) {
  121. Integer flowId = flowEntry.getKey();
  122. List<NewIssueLocation> flowLocations = Lists.newArrayList();
  123. List<Integer> flowNums = Lists.newArrayList(flowEntry.getValue().keySet());
  124. Collections.sort(flowNums);
  125. for (Integer flowNum : flowNums) {
  126. TextPointer start = flowEntry.getValue().get(flowNum);
  127. TextPointer end = endFlowsPositions.get(issueId).row(flowId).get(flowNum);
  128. NewIssueLocation newLocation = newIssue.newLocation()
  129. .on(file)
  130. .at(file.newRange(start, end))
  131. .message("Flow step #" + flowNum);
  132. flowLocations.add(newLocation);
  133. }
  134. if (flowLocations.size() == 1) {
  135. newIssue.addLocation(flowLocations.get(0));
  136. } else {
  137. newIssue.addFlow(flowLocations);
  138. }
  139. }
  140. }
  141. newIssue.save();
  142. }
  143. }
  144. private static void parseIssues(InputFile file, SensorContext context, Map<Integer, TextPointer> startPositions,
  145. Map<Integer, TextPointer> endPositions) {
  146. int currentLine = 0;
  147. try {
  148. for (String lineStr : Files.readAllLines(file.path(), file.charset())) {
  149. currentLine++;
  150. Matcher m = START_ISSUE_PATTERN.matcher(lineStr);
  151. while (m.find()) {
  152. Integer issueId = Integer.parseInt(m.group(1));
  153. TextPointer newPointer = file.newPointer(currentLine, m.end());
  154. startPositions.put(issueId, newPointer);
  155. }
  156. m = END_ISSUE_PATTERN.matcher(lineStr);
  157. while (m.find()) {
  158. Integer issueId = Integer.parseInt(m.group(1));
  159. TextPointer newPointer = file.newPointer(currentLine, m.start());
  160. endPositions.put(issueId, newPointer);
  161. }
  162. }
  163. } catch (IOException e) {
  164. throw new IllegalStateException("Unable to read file", e);
  165. }
  166. }
  167. }