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.

AbstractTracker.java 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 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.core.issue.tracking;
  21. import com.google.common.collect.ArrayListMultimap;
  22. import com.google.common.collect.Multimap;
  23. import java.util.Collection;
  24. import java.util.Objects;
  25. import java.util.function.Function;
  26. import javax.annotation.Nonnull;
  27. import org.apache.commons.lang.StringUtils;
  28. import org.sonar.api.rule.RuleKey;
  29. import static java.util.Comparator.comparing;
  30. public class AbstractTracker<RAW extends Trackable, BASE extends Trackable> {
  31. protected void match(Tracking<RAW, BASE> tracking, Function<Trackable, SearchKey> searchKeyFactory) {
  32. if (tracking.isComplete()) {
  33. return;
  34. }
  35. Multimap<SearchKey, BASE> baseSearch = ArrayListMultimap.create();
  36. tracking.getUnmatchedBases()
  37. .forEach(base -> baseSearch.put(searchKeyFactory.apply(base), base));
  38. tracking.getUnmatchedRaws().forEach(raw -> {
  39. SearchKey rawKey = searchKeyFactory.apply(raw);
  40. Collection<BASE> bases = baseSearch.get(rawKey);
  41. bases.stream()
  42. // Choose the more recently updated issue first to get the latest changes in siblings
  43. .sorted(comparing(Trackable::getUpdateDate).reversed())
  44. .findFirst()
  45. .ifPresent(match -> {
  46. tracking.match(raw, match);
  47. baseSearch.remove(rawKey, match);
  48. });
  49. });
  50. }
  51. protected interface SearchKey {
  52. }
  53. protected static class LineAndLineHashKey implements SearchKey {
  54. private final RuleKey ruleKey;
  55. private final String lineHash;
  56. private final Integer line;
  57. protected LineAndLineHashKey(Trackable trackable) {
  58. this.ruleKey = trackable.getRuleKey();
  59. this.line = trackable.getLine();
  60. this.lineHash = StringUtils.defaultString(trackable.getLineHash(), "");
  61. }
  62. @Override
  63. public boolean equals(@Nonnull Object o) {
  64. if (this == o) {
  65. return true;
  66. }
  67. if (o == null || getClass() != o.getClass()) {
  68. return false;
  69. }
  70. LineAndLineHashKey that = (LineAndLineHashKey) o;
  71. // start with most discriminant field
  72. return Objects.equals(line, that.line) && lineHash.equals(that.lineHash) && ruleKey.equals(that.ruleKey);
  73. }
  74. @Override
  75. public int hashCode() {
  76. return Objects.hash(ruleKey, lineHash, line != null ? line : 0);
  77. }
  78. }
  79. protected static class LineAndLineHashAndMessage implements SearchKey {
  80. private final RuleKey ruleKey;
  81. private final String lineHash;
  82. private final String message;
  83. private final Integer line;
  84. protected LineAndLineHashAndMessage(Trackable trackable) {
  85. this.ruleKey = trackable.getRuleKey();
  86. this.line = trackable.getLine();
  87. this.message = trackable.getMessage();
  88. this.lineHash = StringUtils.defaultString(trackable.getLineHash(), "");
  89. }
  90. @Override
  91. public boolean equals(Object o) {
  92. if (this == o) {
  93. return true;
  94. }
  95. if (o == null || getClass() != o.getClass()) {
  96. return false;
  97. }
  98. LineAndLineHashAndMessage that = (LineAndLineHashAndMessage) o;
  99. // start with most discriminant field
  100. return Objects.equals(line, that.line) && lineHash.equals(that.lineHash) && Objects.equals(message, that.message) && ruleKey.equals(that.ruleKey);
  101. }
  102. @Override
  103. public int hashCode() {
  104. return Objects.hash(ruleKey, lineHash, message, line != null ? line : 0);
  105. }
  106. }
  107. protected static class LineHashAndMessageKey implements SearchKey {
  108. private final RuleKey ruleKey;
  109. private final String message;
  110. private final String lineHash;
  111. LineHashAndMessageKey(Trackable trackable) {
  112. this.ruleKey = trackable.getRuleKey();
  113. this.message = trackable.getMessage();
  114. this.lineHash = StringUtils.defaultString(trackable.getLineHash(), "");
  115. }
  116. @Override
  117. public boolean equals(Object o) {
  118. if (this == o) {
  119. return true;
  120. }
  121. if (o == null || getClass() != o.getClass()) {
  122. return false;
  123. }
  124. LineHashAndMessageKey that = (LineHashAndMessageKey) o;
  125. // start with most discriminant field
  126. return lineHash.equals(that.lineHash) && Objects.equals(message, that.message) && ruleKey.equals(that.ruleKey);
  127. }
  128. @Override
  129. public int hashCode() {
  130. return Objects.hash(ruleKey, message, lineHash);
  131. }
  132. }
  133. protected static class LineAndMessageKey implements SearchKey {
  134. private final RuleKey ruleKey;
  135. private final String message;
  136. private final Integer line;
  137. LineAndMessageKey(Trackable trackable) {
  138. this.ruleKey = trackable.getRuleKey();
  139. this.message = trackable.getMessage();
  140. this.line = trackable.getLine();
  141. }
  142. @Override
  143. public boolean equals(Object o) {
  144. if (this == o) {
  145. return true;
  146. }
  147. if (o == null || getClass() != o.getClass()) {
  148. return false;
  149. }
  150. LineAndMessageKey that = (LineAndMessageKey) o;
  151. // start with most discriminant field
  152. return Objects.equals(line, that.line) && Objects.equals(message, that.message) && ruleKey.equals(that.ruleKey);
  153. }
  154. @Override
  155. public int hashCode() {
  156. return Objects.hash(ruleKey, message, line != null ? line : 0);
  157. }
  158. }
  159. protected static class LineHashKey implements SearchKey {
  160. private final RuleKey ruleKey;
  161. private final String lineHash;
  162. LineHashKey(Trackable trackable) {
  163. this.ruleKey = trackable.getRuleKey();
  164. this.lineHash = StringUtils.defaultString(trackable.getLineHash(), "");
  165. }
  166. @Override
  167. public boolean equals(Object o) {
  168. if (this == o) {
  169. return true;
  170. }
  171. if (o == null || getClass() != o.getClass()) {
  172. return false;
  173. }
  174. LineHashKey that = (LineHashKey) o;
  175. // start with most discriminant field
  176. return lineHash.equals(that.lineHash) && ruleKey.equals(that.ruleKey);
  177. }
  178. @Override
  179. public int hashCode() {
  180. return Objects.hash(ruleKey, lineHash);
  181. }
  182. }
  183. }