Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

IssueCounter.java 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 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.server.measure.live;
  21. import java.util.Collection;
  22. import java.util.EnumMap;
  23. import java.util.HashMap;
  24. import java.util.Map;
  25. import java.util.Optional;
  26. import javax.annotation.Nullable;
  27. import org.sonar.api.issue.IssueStatus;
  28. import org.sonar.api.issue.impact.Severity;
  29. import org.sonar.api.issue.impact.SoftwareQuality;
  30. import org.sonar.api.rules.RuleType;
  31. import org.sonar.db.issue.IssueGroupDto;
  32. import org.sonar.db.issue.IssueImpactGroupDto;
  33. import org.sonar.db.rule.SeverityUtil;
  34. import org.sonar.server.measure.ImpactMeasureBuilder;
  35. import static org.sonar.api.rule.Severity.INFO;
  36. import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
  37. class IssueCounter {
  38. private final Map<RuleType, HighestSeverity> highestSeverityOfUnresolved = new EnumMap<>(RuleType.class);
  39. private final Map<RuleType, Effort> effortOfUnresolved = new EnumMap<>(RuleType.class);
  40. private final Map<String, Count> unresolvedBySeverity = new HashMap<>();
  41. private final Map<RuleType, Count> unresolvedByType = new EnumMap<>(RuleType.class);
  42. private final Map<String, Count> byResolution = new HashMap<>();
  43. private final Map<String, Count> byStatus = new HashMap<>();
  44. private final Map<String, Count> hotspotsByStatus = new HashMap<>();
  45. private final Count unresolved = new Count();
  46. private final Count highImpactAccepted = new Count();
  47. private final Map<SoftwareQuality, Map<Severity, Count>> bySoftwareQualityAndSeverity = new EnumMap<>(SoftwareQuality.class);
  48. IssueCounter(Collection<IssueGroupDto> groups, Collection<IssueImpactGroupDto> impactGroups) {
  49. for (IssueGroupDto group : groups) {
  50. if (RuleType.valueOf(group.getRuleType()).equals(SECURITY_HOTSPOT)) {
  51. processHotspotGroup(group);
  52. } else {
  53. processGroup(group);
  54. }
  55. }
  56. for (IssueImpactGroupDto group : impactGroups) {
  57. processImpactGroup(group);
  58. }
  59. }
  60. private void processHotspotGroup(IssueGroupDto group) {
  61. if (group.getResolution() == null) {
  62. unresolvedByType
  63. .computeIfAbsent(SECURITY_HOTSPOT, k -> new Count())
  64. .add(group);
  65. }
  66. if (group.getStatus() != null) {
  67. hotspotsByStatus
  68. .computeIfAbsent(group.getStatus(), k -> new Count())
  69. .add(group);
  70. }
  71. }
  72. private void processGroup(IssueGroupDto group) {
  73. if (group.getResolution() == null) {
  74. RuleType ruleType = RuleType.valueOf(group.getRuleType());
  75. highestSeverityOfUnresolved
  76. .computeIfAbsent(ruleType, k -> new HighestSeverity())
  77. .add(group);
  78. effortOfUnresolved
  79. .computeIfAbsent(ruleType, k -> new Effort())
  80. .add(group);
  81. unresolvedBySeverity
  82. .computeIfAbsent(group.getSeverity(), k -> new Count())
  83. .add(group);
  84. unresolvedByType
  85. .computeIfAbsent(ruleType, k -> new Count())
  86. .add(group);
  87. unresolved.add(group);
  88. } else {
  89. byResolution
  90. .computeIfAbsent(group.getResolution(), k -> new Count())
  91. .add(group);
  92. }
  93. if (group.getStatus() != null) {
  94. byStatus
  95. .computeIfAbsent(group.getStatus(), k -> new Count())
  96. .add(group);
  97. }
  98. }
  99. private void processImpactGroup(IssueImpactGroupDto group) {
  100. IssueStatus issueStatus = IssueStatus.of(group.getStatus(), group.getResolution());
  101. if (IssueStatus.OPEN == issueStatus || IssueStatus.CONFIRMED == issueStatus) {
  102. bySoftwareQualityAndSeverity
  103. .computeIfAbsent(group.getSoftwareQuality(), k -> new EnumMap<>(Severity.class))
  104. .computeIfAbsent(group.getSeverity(), k -> new Count())
  105. .add(group);
  106. }
  107. if (Severity.HIGH == group.getSeverity() && IssueStatus.ACCEPTED == issueStatus) {
  108. highImpactAccepted.add(group);
  109. }
  110. }
  111. public Optional<String> getHighestSeverityOfUnresolved(RuleType ruleType, boolean onlyInLeak) {
  112. return Optional.ofNullable(highestSeverityOfUnresolved.get(ruleType))
  113. .map(hs -> hs.severity(onlyInLeak));
  114. }
  115. public double sumEffortOfUnresolved(RuleType type, boolean onlyInLeak) {
  116. Effort effort = effortOfUnresolved.get(type);
  117. if (effort == null) {
  118. return 0.0;
  119. }
  120. return onlyInLeak ? effort.leak : effort.absolute;
  121. }
  122. public long countUnresolvedBySeverity(String severity, boolean onlyInLeak) {
  123. return value(unresolvedBySeverity.get(severity), onlyInLeak);
  124. }
  125. public long countByResolution(String resolution, boolean onlyInLeak) {
  126. return value(byResolution.get(resolution), onlyInLeak);
  127. }
  128. public long countUnresolvedByType(RuleType type, boolean onlyInLeak) {
  129. return value(unresolvedByType.get(type), onlyInLeak);
  130. }
  131. public long countByStatus(String status, boolean onlyInLeak) {
  132. return value(byStatus.get(status), onlyInLeak);
  133. }
  134. public long countUnresolved(boolean onlyInLeak) {
  135. return value(unresolved, onlyInLeak);
  136. }
  137. public long countHighImpactAccepted(boolean onlyInLeak) {
  138. return value(highImpactAccepted, onlyInLeak);
  139. }
  140. public long countHotspotsByStatus(String status, boolean onlyInLeak) {
  141. return value(hotspotsByStatus.get(status), onlyInLeak);
  142. }
  143. private static long value(@Nullable Count count, boolean onlyInLeak) {
  144. if (count == null) {
  145. return 0;
  146. }
  147. return onlyInLeak ? count.leak : count.absolute;
  148. }
  149. public String getBySoftwareQuality(SoftwareQuality softwareQuality, boolean onlyInLeak) {
  150. Map<Severity, Count> severityToCount = bySoftwareQualityAndSeverity.get(softwareQuality);
  151. ImpactMeasureBuilder impactMeasureBuilder;
  152. if (severityToCount != null) {
  153. impactMeasureBuilder = ImpactMeasureBuilder.newInstance();
  154. for (Severity severity : Severity.values()) {
  155. impactMeasureBuilder = impactMeasureBuilder.setSeverity(severity, value(severityToCount.get(severity), onlyInLeak));
  156. }
  157. impactMeasureBuilder = impactMeasureBuilder.setTotal(severityToCount.values().stream().mapToLong(count -> value(count, onlyInLeak)).sum());
  158. } else {
  159. impactMeasureBuilder = ImpactMeasureBuilder.createEmpty();
  160. }
  161. return impactMeasureBuilder.buildAsString();
  162. }
  163. private static class Count {
  164. private long absolute = 0L;
  165. private long leak = 0L;
  166. void add(IssueGroupDto group) {
  167. absolute += group.getCount();
  168. if (group.isInLeak()) {
  169. leak += group.getCount();
  170. }
  171. }
  172. public void add(IssueImpactGroupDto group) {
  173. absolute += group.getCount();
  174. if (group.isInLeak()) {
  175. leak += group.getCount();
  176. }
  177. }
  178. }
  179. private static class Effort {
  180. private double absolute = 0.0;
  181. private double leak = 0.0;
  182. void add(IssueGroupDto group) {
  183. absolute += group.getEffort();
  184. if (group.isInLeak()) {
  185. leak += group.getEffort();
  186. }
  187. }
  188. }
  189. private static class HighestSeverity {
  190. private int absolute = SeverityUtil.getOrdinalFromSeverity(INFO);
  191. private int leak = SeverityUtil.getOrdinalFromSeverity(INFO);
  192. void add(IssueGroupDto group) {
  193. int severity = SeverityUtil.getOrdinalFromSeverity(group.getSeverity());
  194. absolute = Math.max(severity, absolute);
  195. if (group.isInLeak()) {
  196. leak = Math.max(severity, leak);
  197. }
  198. }
  199. String severity(boolean inLeak) {
  200. return SeverityUtil.getSeverityFromOrdinal(inLeak ? leak : absolute);
  201. }
  202. }
  203. }