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.

IssueDto.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 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.db.issue;
  21. import com.google.common.base.Joiner;
  22. import com.google.common.base.Preconditions;
  23. import com.google.common.base.Splitter;
  24. import com.google.common.collect.ImmutableSet;
  25. import com.google.protobuf.InvalidProtocolBufferException;
  26. import java.io.Serializable;
  27. import java.util.Collection;
  28. import java.util.Date;
  29. import java.util.HashSet;
  30. import java.util.Map;
  31. import java.util.Optional;
  32. import java.util.Set;
  33. import java.util.stream.Collectors;
  34. import javax.annotation.CheckForNull;
  35. import javax.annotation.Nullable;
  36. import org.apache.commons.lang.builder.ToStringBuilder;
  37. import org.apache.commons.lang.builder.ToStringStyle;
  38. import org.jetbrains.annotations.NotNull;
  39. import org.sonar.api.issue.impact.Severity;
  40. import org.sonar.api.issue.impact.SoftwareQuality;
  41. import org.sonar.api.rule.RuleKey;
  42. import org.sonar.api.rules.CleanCodeAttribute;
  43. import org.sonar.api.rules.RuleType;
  44. import org.sonar.api.utils.Duration;
  45. import org.sonar.core.issue.DefaultIssue;
  46. import org.sonar.core.issue.status.IssueStatus;
  47. import org.sonar.core.util.Uuids;
  48. import org.sonar.db.component.ComponentDto;
  49. import org.sonar.db.protobuf.DbIssues;
  50. import org.sonar.db.rule.RuleDto;
  51. import static com.google.common.base.Preconditions.checkArgument;
  52. import static java.lang.String.format;
  53. import static java.util.stream.Collectors.toUnmodifiableMap;
  54. import static org.sonar.api.utils.DateUtils.dateToLong;
  55. import static org.sonar.api.utils.DateUtils.longToDate;
  56. public final class IssueDto implements Serializable {
  57. public static final int AUTHOR_MAX_SIZE = 255;
  58. private static final char STRING_LIST_SEPARATOR = ',';
  59. private static final Joiner STRING_LIST_JOINER = Joiner.on(STRING_LIST_SEPARATOR).skipNulls();
  60. private static final Splitter STRING_LIST_SPLITTER = Splitter.on(STRING_LIST_SEPARATOR).trimResults().omitEmptyStrings();
  61. private int type;
  62. private String kee;
  63. private String componentUuid;
  64. private String projectUuid;
  65. private String ruleUuid;
  66. private String severity;
  67. private boolean manualSeverity;
  68. private String message;
  69. private byte[] messageFormattings;
  70. private Integer line;
  71. private Double gap;
  72. private Long effort;
  73. private String status;
  74. private String resolution;
  75. private String checksum;
  76. private String assigneeUuid;
  77. private String assigneeLogin;
  78. private String authorLogin;
  79. private String securityStandards;
  80. private byte[] locations;
  81. private long createdAt;
  82. private long updatedAt;
  83. private boolean quickFixAvailable;
  84. private boolean isNewCodeReferenceIssue;
  85. private String ruleDescriptionContextKey;
  86. // functional dates stored as Long
  87. private Long issueCreationDate;
  88. private Long issueUpdateDate;
  89. private Long issueCloseDate;
  90. /**
  91. * Temporary date used only during scan
  92. */
  93. private Long selectedAt;
  94. // joins
  95. private String ruleKey;
  96. private String ruleRepo;
  97. private boolean isExternal;
  98. private String language;
  99. private String componentKey;
  100. private String projectKey;
  101. private String filePath;
  102. private String tags;
  103. private String codeVariants;
  104. // populate only when retrieving closed issue for issue tracking
  105. private String closedChangeData;
  106. private Set<ImpactDto> impacts = new HashSet<>();
  107. //non-persisted fields
  108. private Set<ImpactDto> ruleDefaultImpacts = new HashSet<>();
  109. private CleanCodeAttribute cleanCodeAttribute;
  110. private CleanCodeAttribute ruleCleanCodeAttribute;
  111. public IssueDto() {
  112. // nothing to do
  113. }
  114. /**
  115. * On batch side, component keys and uuid are useless
  116. */
  117. public static IssueDto toDtoForComputationInsert(DefaultIssue issue, String ruleUuid, long now) {
  118. return new IssueDto()
  119. .setKee(issue.key())
  120. .setType(issue.type())
  121. .setLine(issue.line())
  122. .setLocations((DbIssues.Locations) issue.getLocations())
  123. .setMessage(issue.message())
  124. .setMessageFormattings((DbIssues.MessageFormattings) issue.getMessageFormattings())
  125. .setGap(issue.gap())
  126. .setEffort(issue.effortInMinutes())
  127. .setResolution(issue.resolution())
  128. .setStatus(issue.status())
  129. .setSeverity(issue.severity())
  130. .setManualSeverity(issue.manualSeverity())
  131. .setChecksum(issue.checksum())
  132. .setAssigneeUuid(issue.assignee())
  133. .setRuleUuid(ruleUuid)
  134. .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
  135. .setExternal(issue.isFromExternalRuleEngine())
  136. .setTags(issue.tags())
  137. .setRuleDescriptionContextKey(issue.getRuleDescriptionContextKey().orElse(null))
  138. .setComponentUuid(issue.componentUuid())
  139. .setComponentKey(issue.componentKey())
  140. .setProjectUuid(issue.projectUuid())
  141. .setProjectKey(issue.projectKey())
  142. .setAuthorLogin(issue.authorLogin())
  143. .setIssueCreationDate(issue.creationDate())
  144. .setIssueCloseDate(issue.closeDate())
  145. .setIssueUpdateDate(issue.updateDate())
  146. .setSelectedAt(issue.selectedAt())
  147. .setQuickFixAvailable(issue.isQuickFixAvailable())
  148. .setIsNewCodeReferenceIssue(issue.isNewCodeReferenceIssue())
  149. .setCodeVariants(issue.codeVariants())
  150. .replaceAllImpacts(mapToImpactDto(issue.impacts()))
  151. .setCleanCodeAttribute(issue.getCleanCodeAttribute())
  152. // technical dates
  153. .setCreatedAt(now)
  154. .setUpdatedAt(now);
  155. }
  156. @NotNull
  157. private static Set<ImpactDto> mapToImpactDto(Map<SoftwareQuality, Severity> impacts) {
  158. return impacts.entrySet().stream().map(e -> new ImpactDto()
  159. .setUuid(Uuids.createFast())
  160. .setSoftwareQuality(e.getKey())
  161. .setSeverity(e.getValue()))
  162. .collect(Collectors.toSet());
  163. }
  164. /**
  165. * On server side, we need component keys and uuid
  166. */
  167. public static IssueDto toDtoForServerInsert(DefaultIssue issue, ComponentDto component, ComponentDto project, String ruleUuid, long now) {
  168. return toDtoForComputationInsert(issue, ruleUuid, now)
  169. .setComponent(component)
  170. .setProject(project);
  171. }
  172. public static IssueDto toDtoForUpdate(DefaultIssue issue, long now) {
  173. // Invariant fields, like key and rule, can't be updated
  174. return new IssueDto()
  175. .setKee(issue.key())
  176. .setType(issue.type())
  177. .setLine(issue.line())
  178. .setLocations((DbIssues.Locations) issue.getLocations())
  179. .setMessage(issue.message())
  180. .setMessageFormattings((DbIssues.MessageFormattings) issue.getMessageFormattings())
  181. .setGap(issue.gap())
  182. .setEffort(issue.effortInMinutes())
  183. .setResolution(issue.resolution())
  184. .setStatus(issue.status())
  185. .setSeverity(issue.severity())
  186. .setChecksum(issue.checksum())
  187. .setManualSeverity(issue.manualSeverity())
  188. .setAssigneeUuid(issue.assignee())
  189. .setAuthorLogin(issue.authorLogin())
  190. .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
  191. .setExternal(issue.isFromExternalRuleEngine())
  192. .setTags(issue.tags())
  193. .setRuleDescriptionContextKey(issue.getRuleDescriptionContextKey().orElse(null))
  194. .setComponentUuid(issue.componentUuid())
  195. .setComponentKey(issue.componentKey())
  196. .setProjectUuid(issue.projectUuid())
  197. .setProjectKey(issue.projectKey())
  198. .setIssueCreationDate(issue.creationDate())
  199. .setIssueCloseDate(issue.closeDate())
  200. .setIssueUpdateDate(issue.updateDate())
  201. .setSelectedAt(issue.selectedAt())
  202. .setQuickFixAvailable(issue.isQuickFixAvailable())
  203. .setIsNewCodeReferenceIssue(issue.isNewCodeReferenceIssue())
  204. .setCodeVariants(issue.codeVariants())
  205. .replaceAllImpacts(mapToImpactDto(issue.impacts()))
  206. .setCleanCodeAttribute(issue.getCleanCodeAttribute())
  207. // technical date
  208. .setUpdatedAt(now);
  209. }
  210. public String getKey() {
  211. return getKee();
  212. }
  213. public String getKee() {
  214. return kee;
  215. }
  216. public IssueDto setKee(String s) {
  217. this.kee = s;
  218. return this;
  219. }
  220. public IssueDto setComponent(ComponentDto component) {
  221. this.componentKey = component.getKey();
  222. this.componentUuid = component.uuid();
  223. this.filePath = component.path();
  224. return this;
  225. }
  226. /**
  227. * The project branch where the issue is located.
  228. * Note that the name is misleading - it should be branch.
  229. */
  230. public IssueDto setProject(ComponentDto branch) {
  231. this.projectKey = branch.getKey();
  232. this.projectUuid = branch.uuid();
  233. return this;
  234. }
  235. public String getRuleUuid() {
  236. return ruleUuid;
  237. }
  238. /**
  239. * please use setRule(RuleDto rule)
  240. */
  241. public IssueDto setRuleUuid(String ruleUuid) {
  242. this.ruleUuid = ruleUuid;
  243. return this;
  244. }
  245. @CheckForNull
  246. public String getSeverity() {
  247. return severity;
  248. }
  249. public IssueDto setSeverity(@Nullable String s) {
  250. checkArgument(s == null || s.length() <= 10, "Value is too long for issue severity: %s", s);
  251. this.severity = s;
  252. return this;
  253. }
  254. public boolean isManualSeverity() {
  255. return manualSeverity;
  256. }
  257. public IssueDto setManualSeverity(boolean manualSeverity) {
  258. this.manualSeverity = manualSeverity;
  259. return this;
  260. }
  261. @CheckForNull
  262. public String getMessage() {
  263. return message;
  264. }
  265. public IssueDto setMessage(@Nullable String s) {
  266. checkArgument(s == null || s.length() <= 4000, "Value is too long for issue message: %s", s);
  267. this.message = s;
  268. return this;
  269. }
  270. public byte[] getMessageFormattings() {
  271. return messageFormattings;
  272. }
  273. public IssueDto setMessageFormattings(byte[] messageFormattings) {
  274. this.messageFormattings = messageFormattings;
  275. return this;
  276. }
  277. public IssueDto setMessageFormattings(@Nullable DbIssues.MessageFormattings messageFormattings) {
  278. if (messageFormattings == null) {
  279. this.messageFormattings = null;
  280. } else {
  281. this.messageFormattings = messageFormattings.toByteArray();
  282. }
  283. return this;
  284. }
  285. @CheckForNull
  286. public DbIssues.MessageFormattings parseMessageFormattings() {
  287. if (messageFormattings != null) {
  288. try {
  289. return DbIssues.MessageFormattings.parseFrom(messageFormattings);
  290. } catch (InvalidProtocolBufferException e) {
  291. throw new IllegalStateException(format("Fail to read ISSUES.MESSAGE_FORMATTINGS [KEE=%s]", kee), e);
  292. }
  293. }
  294. return null;
  295. }
  296. @CheckForNull
  297. public Integer getLine() {
  298. return line;
  299. }
  300. public IssueDto setLine(@Nullable Integer i) {
  301. checkArgument(i == null || i >= 0, "Value of issue line must be positive: %d", i);
  302. this.line = i;
  303. return this;
  304. }
  305. @CheckForNull
  306. public Double getGap() {
  307. return gap;
  308. }
  309. public IssueDto setGap(@Nullable Double d) {
  310. checkArgument(d == null || d >= 0, "Value of issue gap must be positive: %d", d);
  311. this.gap = d;
  312. return this;
  313. }
  314. @CheckForNull
  315. public Long getEffort() {
  316. return effort;
  317. }
  318. public IssueDto setEffort(@Nullable Long l) {
  319. checkArgument(l == null || l >= 0, "Value of issue effort must be positive: %d", l);
  320. this.effort = l;
  321. return this;
  322. }
  323. public String getStatus() {
  324. return status;
  325. }
  326. public IssueStatus getIssueStatus() {
  327. checkArgument(status != null, "Status must be initialized to retrieve issue status");
  328. return IssueStatus.of(status, resolution);
  329. }
  330. public IssueDto setStatus(@Nullable String s) {
  331. checkArgument(s == null || s.length() <= 20, "Value is too long for issue status: %s", s);
  332. this.status = s;
  333. return this;
  334. }
  335. @CheckForNull
  336. public String getResolution() {
  337. return resolution;
  338. }
  339. public IssueDto setResolution(@Nullable String s) {
  340. checkArgument(s == null || s.length() <= 20, "Value is too long for issue resolution: %s", s);
  341. this.resolution = s;
  342. return this;
  343. }
  344. @CheckForNull
  345. public String getChecksum() {
  346. return checksum;
  347. }
  348. public IssueDto setChecksum(@Nullable String s) {
  349. checkArgument(s == null || s.length() <= 1000, "Value is too long for issue checksum: %s", s);
  350. this.checksum = s;
  351. return this;
  352. }
  353. @CheckForNull
  354. public String getAssigneeUuid() {
  355. return assigneeUuid;
  356. }
  357. public IssueDto setAssigneeUuid(@Nullable String s) {
  358. checkArgument(s == null || s.length() <= 255, "Value is too long for issue assigneeUuid: %s", s);
  359. this.assigneeUuid = s;
  360. return this;
  361. }
  362. @CheckForNull
  363. public String getAssigneeLogin() {
  364. return assigneeLogin;
  365. }
  366. public IssueDto setAssigneeLogin(@Nullable String s) {
  367. this.assigneeLogin = s;
  368. return this;
  369. }
  370. @CheckForNull
  371. public String getAuthorLogin() {
  372. return authorLogin;
  373. }
  374. public IssueDto setAuthorLogin(@Nullable String s) {
  375. checkArgument(s == null || s.length() <= AUTHOR_MAX_SIZE, "Value is too long for issue author login: %s", s);
  376. this.authorLogin = s;
  377. return this;
  378. }
  379. public IssueDto setSecurityStandards(@Nullable String s) {
  380. this.securityStandards = s;
  381. return this;
  382. }
  383. public Set<String> getSecurityStandards() {
  384. return RuleDto.deserializeSecurityStandardsString(securityStandards);
  385. }
  386. /**
  387. * Technical date
  388. */
  389. public long getCreatedAt() {
  390. return createdAt;
  391. }
  392. public IssueDto setCreatedAt(long createdAt) {
  393. this.createdAt = createdAt;
  394. return this;
  395. }
  396. /**
  397. * Technical date
  398. */
  399. public long getUpdatedAt() {
  400. return updatedAt;
  401. }
  402. public IssueDto setUpdatedAt(long updatedAt) {
  403. this.updatedAt = updatedAt;
  404. return this;
  405. }
  406. public Long getIssueCreationTime() {
  407. return issueCreationDate;
  408. }
  409. public IssueDto setIssueCreationTime(Long time) {
  410. this.issueCreationDate = time;
  411. return this;
  412. }
  413. public Date getIssueCreationDate() {
  414. return longToDate(issueCreationDate);
  415. }
  416. public IssueDto setIssueCreationDate(@Nullable Date d) {
  417. this.issueCreationDate = dateToLong(d);
  418. return this;
  419. }
  420. public Long getIssueUpdateTime() {
  421. return issueUpdateDate;
  422. }
  423. public IssueDto setIssueUpdateTime(Long time) {
  424. this.issueUpdateDate = time;
  425. return this;
  426. }
  427. public Date getIssueUpdateDate() {
  428. return longToDate(issueUpdateDate);
  429. }
  430. public IssueDto setIssueUpdateDate(@Nullable Date d) {
  431. this.issueUpdateDate = dateToLong(d);
  432. return this;
  433. }
  434. public Long getIssueCloseTime() {
  435. return issueCloseDate;
  436. }
  437. public IssueDto setIssueCloseTime(Long time) {
  438. this.issueCloseDate = time;
  439. return this;
  440. }
  441. public Date getIssueCloseDate() {
  442. return longToDate(issueCloseDate);
  443. }
  444. public IssueDto setIssueCloseDate(@Nullable Date d) {
  445. this.issueCloseDate = dateToLong(d);
  446. return this;
  447. }
  448. public String getRule() {
  449. return ruleKey;
  450. }
  451. public IssueDto setRule(RuleDto rule) {
  452. Preconditions.checkNotNull(rule.getUuid(), "Rule must be persisted.");
  453. this.ruleUuid = rule.getUuid();
  454. this.ruleKey = rule.getRuleKey();
  455. this.ruleRepo = rule.getRepositoryKey();
  456. this.language = rule.getLanguage();
  457. this.isExternal = rule.isExternal();
  458. this.cleanCodeAttribute = rule.getCleanCodeAttribute();
  459. return this;
  460. }
  461. public String getRuleRepo() {
  462. return ruleRepo;
  463. }
  464. public RuleKey getRuleKey() {
  465. return RuleKey.of(ruleRepo, ruleKey);
  466. }
  467. public String getLanguage() {
  468. return language;
  469. }
  470. /**
  471. * Should only be used to persist in E/S
  472. * <p/>
  473. * Please use {@link #setRule(RuleDto)} instead
  474. */
  475. public IssueDto setLanguage(String language) {
  476. this.language = language;
  477. return this;
  478. }
  479. public boolean isExternal() {
  480. return isExternal;
  481. }
  482. public IssueDto setExternal(boolean external) {
  483. isExternal = external;
  484. return this;
  485. }
  486. public String getComponentKey() {
  487. return componentKey;
  488. }
  489. /**
  490. * Should only be used to persist in E/S
  491. * <p/>
  492. * Please use {@link #setComponent(ComponentDto)} instead
  493. */
  494. public IssueDto setComponentKey(String componentKey) {
  495. this.componentKey = componentKey;
  496. return this;
  497. }
  498. /**
  499. * Can be null on Views or Devs
  500. */
  501. @CheckForNull
  502. public String getComponentUuid() {
  503. return componentUuid;
  504. }
  505. /**
  506. * Should only be used to persist in E/S
  507. * <p/>
  508. * Please use {@link #setComponent(ComponentDto)} instead
  509. */
  510. public IssueDto setComponentUuid(@Nullable String s) {
  511. checkArgument(s == null || s.length() <= 50, "Value is too long for column ISSUES.COMPONENT_UUID: %s", s);
  512. this.componentUuid = s;
  513. return this;
  514. }
  515. /**
  516. * Used by the issue tracking mechanism, but it should used the component uuid instead
  517. */
  518. public String getProjectKey() {
  519. return projectKey;
  520. }
  521. /**
  522. * Should only be used to persist in E/S
  523. * <p/>
  524. * Please use {@link #setProject(ComponentDto)} instead
  525. */
  526. public IssueDto setProjectKey(String projectKey) {
  527. this.projectKey = projectKey;
  528. return this;
  529. }
  530. /**
  531. * The project branch where the issue is located.
  532. * Note that the name is misleading - it should be 'branchUuid'.
  533. */
  534. public String getProjectUuid() {
  535. return projectUuid;
  536. }
  537. /**
  538. * This is branch uuid, not a project uuid. The naming is wrong, the javadoc is right.
  539. * Please use {@link #setProject(ComponentDto)} instead
  540. */
  541. public IssueDto setProjectUuid(String s) {
  542. checkArgument(s.length() <= 50, "Value is too long for column ISSUES.PROJECT_UUID: %s", s);
  543. this.projectUuid = s;
  544. return this;
  545. }
  546. @CheckForNull
  547. public Long getSelectedAt() {
  548. return selectedAt;
  549. }
  550. public IssueDto setSelectedAt(@Nullable Long d) {
  551. this.selectedAt = d;
  552. return this;
  553. }
  554. /**
  555. * Should only be used to persist in E/S
  556. * <p/>
  557. * Please use {@link #setRule(RuleDto)} instead
  558. */
  559. public IssueDto setRuleKey(String repo, String rule) {
  560. this.ruleRepo = repo;
  561. this.ruleKey = rule;
  562. return this;
  563. }
  564. /**
  565. * Should only be used to persist in E/S
  566. * <p/>
  567. * Please use {@link #setProject(ComponentDto)} instead
  568. */
  569. public String getFilePath() {
  570. return filePath;
  571. }
  572. /**
  573. * Should only be used to persist in E/S
  574. * <p/>
  575. * Please use {@link #setProject(ComponentDto)} instead
  576. */
  577. public IssueDto setFilePath(String filePath) {
  578. this.filePath = filePath;
  579. return this;
  580. }
  581. public Set<String> getTags() {
  582. return ImmutableSet.copyOf(STRING_LIST_SPLITTER.split(tags == null ? "" : tags));
  583. }
  584. public IssueDto setTags(@Nullable Collection<String> tags) {
  585. if (tags == null || tags.isEmpty()) {
  586. setTagsString(null);
  587. } else {
  588. setTagsString(STRING_LIST_JOINER.join(tags));
  589. }
  590. return this;
  591. }
  592. public IssueDto setTagsString(@Nullable String s) {
  593. checkArgument(s == null || s.length() <= 4000, "Value is too long for column ISSUES.TAGS: %s", s);
  594. this.tags = s;
  595. return this;
  596. }
  597. public String getTagsString() {
  598. return tags;
  599. }
  600. public Set<String> getCodeVariants() {
  601. return ImmutableSet.copyOf(STRING_LIST_SPLITTER.split(codeVariants == null ? "" : codeVariants));
  602. }
  603. public String getCodeVariantsString() {
  604. return codeVariants;
  605. }
  606. public IssueDto setCodeVariants(@Nullable Collection<String> codeVariants) {
  607. if (codeVariants == null || codeVariants.isEmpty()) {
  608. setCodeVariantsString(null);
  609. } else {
  610. setCodeVariantsString(STRING_LIST_JOINER.join(codeVariants));
  611. }
  612. return this;
  613. }
  614. public IssueDto setCodeVariantsString(@Nullable String codeVariants) {
  615. checkArgument(codeVariants == null || codeVariants.length() <= 4000,
  616. "Value is too long for column ISSUES.CODE_VARIANTS: %codeVariants", codeVariants);
  617. this.codeVariants = codeVariants;
  618. return this;
  619. }
  620. @CheckForNull
  621. public byte[] getLocations() {
  622. return locations;
  623. }
  624. @CheckForNull
  625. public DbIssues.Locations parseLocations() {
  626. if (locations != null) {
  627. try {
  628. return DbIssues.Locations.parseFrom(locations);
  629. } catch (InvalidProtocolBufferException e) {
  630. throw new IllegalStateException(format("Fail to read ISSUES.LOCATIONS [KEE=%s]", kee), e);
  631. }
  632. }
  633. return null;
  634. }
  635. public IssueDto setLocations(@Nullable byte[] locations) {
  636. this.locations = locations;
  637. return this;
  638. }
  639. public IssueDto setLocations(@Nullable DbIssues.Locations locations) {
  640. if (locations == null) {
  641. this.locations = null;
  642. } else {
  643. this.locations = locations.toByteArray();
  644. }
  645. return this;
  646. }
  647. public boolean isQuickFixAvailable() {
  648. return quickFixAvailable;
  649. }
  650. public IssueDto setQuickFixAvailable(boolean quickFixAvailable) {
  651. this.quickFixAvailable = quickFixAvailable;
  652. return this;
  653. }
  654. public boolean isNewCodeReferenceIssue() {
  655. return isNewCodeReferenceIssue;
  656. }
  657. public IssueDto setIsNewCodeReferenceIssue(boolean isNewCodeReferenceIssue) {
  658. this.isNewCodeReferenceIssue = isNewCodeReferenceIssue;
  659. return this;
  660. }
  661. public int getType() {
  662. return type;
  663. }
  664. public IssueDto setType(int type) {
  665. this.type = type;
  666. return this;
  667. }
  668. public IssueDto setType(RuleType type) {
  669. this.type = type.getDbConstant();
  670. return this;
  671. }
  672. @CheckForNull
  673. public CleanCodeAttribute getEffectiveCleanCodeAttribute() {
  674. if (cleanCodeAttribute != null) {
  675. return cleanCodeAttribute;
  676. }
  677. return ruleCleanCodeAttribute;
  678. }
  679. public IssueDto setCleanCodeAttribute(CleanCodeAttribute cleanCodeAttribute) {
  680. this.cleanCodeAttribute = cleanCodeAttribute;
  681. return this;
  682. }
  683. public IssueDto setRuleCleanCodeAttribute(CleanCodeAttribute ruleCleanCodeAttribute) {
  684. this.ruleCleanCodeAttribute = ruleCleanCodeAttribute;
  685. return this;
  686. }
  687. public Optional<String> getClosedChangeData() {
  688. return Optional.ofNullable(closedChangeData);
  689. }
  690. public Optional<String> getOptionalRuleDescriptionContextKey() {
  691. return Optional.ofNullable(ruleDescriptionContextKey);
  692. }
  693. public IssueDto setRuleDescriptionContextKey(@Nullable String ruleDescriptionContextKey) {
  694. this.ruleDescriptionContextKey = ruleDescriptionContextKey;
  695. return this;
  696. }
  697. /**
  698. * Return impacts defined on this issue.
  699. *
  700. * @return Collection of impacts
  701. */
  702. public Set<ImpactDto> getImpacts() {
  703. return impacts;
  704. }
  705. public IssueDto addImpact(ImpactDto impact) {
  706. impacts.stream().filter(impactDto -> impactDto.getSoftwareQuality() == impact.getSoftwareQuality()).findFirst()
  707. .ifPresent(impactDto -> {
  708. throw new IllegalStateException(format("Impact already defined on issue for Software Quality [%s]", impact.getSoftwareQuality()));
  709. });
  710. impacts.add(impact);
  711. return this;
  712. }
  713. public IssueDto setRuleDefaultImpacts(Set<ImpactDto> ruleDefaultImpacts) {
  714. this.ruleDefaultImpacts = new HashSet<>(ruleDefaultImpacts);
  715. return this;
  716. }
  717. public IssueDto replaceAllImpacts(Collection<ImpactDto> newImpacts) {
  718. Set<SoftwareQuality> newSoftwareQuality = newImpacts.stream().map(ImpactDto::getSoftwareQuality).collect(Collectors.toSet());
  719. if (newSoftwareQuality.size() != newImpacts.size()) {
  720. throw new IllegalStateException("Impacts must have unique Software Quality values");
  721. }
  722. impacts.clear();
  723. impacts.addAll(newImpacts);
  724. return this;
  725. }
  726. Set<ImpactDto> getRuleDefaultImpacts() {
  727. return ruleDefaultImpacts;
  728. }
  729. /**
  730. * Returns effective impacts defined on this issue along with default ones.
  731. *
  732. * @return Unmodifiable Map of impacts
  733. */
  734. public Map<SoftwareQuality, Severity> getEffectiveImpacts() {
  735. return impacts.isEmpty() ? toImpactMap(ruleDefaultImpacts) : toImpactMap(impacts);
  736. }
  737. private static Map<SoftwareQuality, Severity> toImpactMap(Collection<ImpactDto> impacts) {
  738. return impacts.stream()
  739. .collect(toUnmodifiableMap(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity));
  740. }
  741. @Override
  742. public String toString() {
  743. return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
  744. }
  745. public DefaultIssue toDefaultIssue() {
  746. DefaultIssue issue = new DefaultIssue();
  747. issue.setKey(kee);
  748. issue.setType(RuleType.valueOf(type));
  749. issue.setStatus(status);
  750. issue.setResolution(resolution);
  751. issue.setMessage(message);
  752. issue.setMessageFormattings(parseMessageFormattings());
  753. issue.setGap(gap);
  754. issue.setEffort(effort != null ? Duration.create(effort) : null);
  755. issue.setLine(line);
  756. issue.setChecksum(checksum);
  757. issue.setSeverity(severity);
  758. issue.setAssigneeUuid(assigneeUuid);
  759. issue.setAssigneeLogin(assigneeLogin);
  760. issue.setComponentKey(componentKey);
  761. issue.setComponentUuid(componentUuid);
  762. issue.setProjectUuid(projectUuid);
  763. issue.setProjectKey(projectKey);
  764. issue.setManualSeverity(manualSeverity);
  765. issue.setRuleKey(getRuleKey());
  766. issue.setTags(getTags());
  767. issue.setRuleDescriptionContextKey(ruleDescriptionContextKey);
  768. issue.setLanguage(language);
  769. issue.setAuthorLogin(authorLogin);
  770. issue.setNew(false);
  771. issue.setCreationDate(longToDate(issueCreationDate));
  772. issue.setCloseDate(longToDate(issueCloseDate));
  773. issue.setUpdateDate(longToDate(issueUpdateDate));
  774. issue.setSelectedAt(selectedAt);
  775. issue.setLocations(parseLocations());
  776. issue.setIsFromExternalRuleEngine(isExternal);
  777. issue.setQuickFixAvailable(quickFixAvailable);
  778. issue.setIsNewCodeReferenceIssue(isNewCodeReferenceIssue);
  779. issue.setCodeVariants(getCodeVariants());
  780. issue.setCleanCodeAttribute(cleanCodeAttribute);
  781. impacts.forEach(i -> issue.addImpact(i.getSoftwareQuality(), i.getSeverity()));
  782. return issue;
  783. }
  784. }