-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
IntegrateIssuesVisitor.java
124 lines (107 loc) · 5.29 KB
/
IntegrateIssuesVisitor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.server.computation.task.projectanalysis.issue;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.server.computation.task.projectanalysis.component.Component;
import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit;
import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter;
import org.sonar.server.util.cache.DiskCache;
import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter {
private final IssueCache issueCache;
private final IssueLifecycle issueLifecycle;
private final IssueVisitors issueVisitors;
private final IssueTrackingDelegator issueTracking;
private final ShortBranchIssueStatusCopier issueStatusCopier;
private final AnalysisMetadataHolder analysisMetadataHolder;
public IntegrateIssuesVisitor(IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors,
AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, ShortBranchIssueStatusCopier issueStatusCopier) {
super(CrawlerDepthLimit.FILE, POST_ORDER);
this.issueCache = issueCache;
this.issueLifecycle = issueLifecycle;
this.issueVisitors = issueVisitors;
this.analysisMetadataHolder = analysisMetadataHolder;
this.issueTracking = issueTracking;
this.issueStatusCopier = issueStatusCopier;
}
@Override
public void visitAny(Component component) {
try (DiskCache<DefaultIssue>.DiskAppender cacheAppender = issueCache.newAppender()) {
issueVisitors.beforeComponent(component);
TrackingResult tracking = issueTracking.track(component);
fillNewOpenIssues(component, tracking.newIssues(), cacheAppender);
fillExistingOpenIssues(component, tracking.issuesToMerge(), cacheAppender);
closeIssues(component, tracking.issuesToClose(), cacheAppender);
copyIssues(component, tracking.issuesToCopy(), cacheAppender);
issueVisitors.afterComponent(component);
} catch (Exception e) {
throw new IllegalStateException(String.format("Fail to process issues of component '%s'", component.getKey()), e);
}
}
private void fillNewOpenIssues(Component component, Iterable<DefaultIssue> newIssues, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
List<DefaultIssue> list = new ArrayList<>();
newIssues.forEach(issue -> {
issueLifecycle.initNewOpenIssue(issue);
list.add(issue);
});
if (list.isEmpty()) {
return;
}
if (analysisMetadataHolder.isLongLivingBranch()) {
issueStatusCopier.updateStatus(component, list);
}
for (DefaultIssue issue : list) {
process(component, issue, cacheAppender);
}
}
private void copyIssues(Component component, Map<DefaultIssue, DefaultIssue> matched, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
for (Map.Entry<DefaultIssue, DefaultIssue> entry : matched.entrySet()) {
DefaultIssue raw = entry.getKey();
DefaultIssue base = entry.getValue();
issueLifecycle.copyExistingOpenIssueFromLongLivingBranch(raw, base);
process(component, raw, cacheAppender);
}
}
private void fillExistingOpenIssues(Component component, Map<DefaultIssue, DefaultIssue> matched, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
for (Map.Entry<DefaultIssue, DefaultIssue> entry : matched.entrySet()) {
DefaultIssue raw = entry.getKey();
DefaultIssue base = entry.getValue();
issueLifecycle.mergeExistingOpenIssue(raw, base);
process(component, raw, cacheAppender);
}
}
private void closeIssues(Component component, Iterable<DefaultIssue> issues, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
for (DefaultIssue issue : issues) {
// TODO should replace flag "beingClosed" by express call to transition "automaticClose"
issue.setBeingClosed(true);
// TODO manual issues -> was updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext);. Is it a problem ?
process(component, issue, cacheAppender);
}
}
private void process(Component component, DefaultIssue issue, DiskCache<DefaultIssue>.DiskAppender cacheAppender) {
issueLifecycle.doAutomaticTransition(issue);
issueVisitors.onIssue(component, issue);
cacheAppender.append(issue);
}
}