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 package org.eluder.coveralls.maven.plugin.domain;
26
27 import com.fasterxml.jackson.annotation.JsonIgnore;
28 import com.fasterxml.jackson.annotation.JsonProperty;
29
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Objects;
34 import java.util.regex.Pattern;
35
36
37
38
39 public final class Source implements JsonObject {
40
41
42 private static final long serialVersionUID = 1L;
43
44
45 private static final Pattern NEWLINE = Pattern.compile("\r\n|\r|\n");
46
47
48
49
50
51
52 String name;
53
54
55 String digest;
56
57
58 Integer[] coverage;
59
60
61 List<Branch> branches;
62
63
64 String classifier;
65
66
67
68
69
70
71
72
73
74
75
76 public Source(final String name, final String source, final String digest) {
77 this(name, Source.getLines(source), digest, null);
78 }
79
80
81
82
83
84
85
86
87
88
89
90
91
92 public Source(final String name, final int lines, final String digest, final String classifier) {
93 this.name = name;
94 this.digest = digest;
95 this.coverage = new Integer[lines];
96 this.classifier = classifier;
97 this.branches = new ArrayList<>();
98 }
99
100
101
102
103
104
105 @JsonIgnore
106 public String getName() {
107 return this.name;
108 }
109
110
111
112
113
114
115 @JsonProperty("name")
116 public String getFullName() {
117 return this.name;
118
119
120
121 }
122
123
124
125
126
127
128 @JsonProperty("source_digest")
129 public String getDigest() {
130 return this.digest;
131 }
132
133
134
135
136
137
138 @JsonProperty("coverage")
139 public Integer[] getCoverage() {
140 return this.coverage;
141 }
142
143
144
145
146
147
148 @JsonProperty("branches")
149 public Integer[] getBranches() {
150 final List<Integer> branchesRaw = new ArrayList<>(this.branches.size() * 4);
151 for (final Branch b : this.branches) {
152 branchesRaw.add(b.getLineNumber());
153 branchesRaw.add(b.getBlockNumber());
154 branchesRaw.add(b.getBranchNumber());
155 branchesRaw.add(b.getHits());
156 }
157 return branchesRaw.toArray(new Integer[branchesRaw.size()]);
158 }
159
160
161
162
163
164
165 public List<Branch> getBranchesList() {
166 return Collections.unmodifiableList(this.branches);
167 }
168
169
170
171
172
173
174 @JsonIgnore
175 public String getClassifier() {
176 return this.classifier;
177 }
178
179
180
181
182
183
184
185 public void setClassifier(final String classifier) {
186 this.classifier = classifier;
187 }
188
189
190
191
192
193
194
195 private void checkLineRange(final int lineNumber) {
196 final var index = lineNumber - 1;
197 if (index >= this.coverage.length) {
198 throw new IllegalArgumentException(
199 "Line number " + lineNumber + " is greater than the source file " + this.name + " size");
200 }
201 }
202
203
204
205
206
207
208
209
210
211 public void addCoverage(final int lineNumber, final Integer coverage) {
212 this.checkLineRange(lineNumber);
213 this.coverage[lineNumber - 1] = coverage;
214 }
215
216
217
218
219
220
221
222
223
224
225
226
227
228 public void addBranchCoverage(final int lineNumber, final int blockNumber, final int branchNumber, final int hits) {
229 this.addBranchCoverage(false, lineNumber, blockNumber, branchNumber, hits);
230 }
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 private void addBranchCoverage(final boolean merge, final int lineNumber, final int blockNumber,
247 final int branchNumber, final int hits) {
248 this.checkLineRange(lineNumber);
249 var hitSum = hits;
250 final var it = this.branches.listIterator();
251 while (it.hasNext()) {
252 final var b = it.next();
253 if (b.getLineNumber() == lineNumber && b.getBlockNumber() == blockNumber
254 && b.getBranchNumber() == branchNumber) {
255 it.remove();
256 if (merge) {
257 hitSum += b.getHits();
258 }
259 }
260 }
261 this.branches.add(new Branch(lineNumber, blockNumber, branchNumber, hitSum));
262 }
263
264
265
266
267
268
269
270
271
272 public Source merge(final Source source) {
273 final var copy = new Source(this.name, this.coverage.length, this.digest, this.classifier);
274 System.arraycopy(this.coverage, 0, copy.coverage, 0, this.coverage.length);
275 copy.branches.addAll(this.branches);
276 if (copy.equals(source)) {
277 for (var i = 0; i < copy.coverage.length; i++) {
278 if (source.coverage[i] != null) {
279 final var base = copy.coverage[i] != null ? copy.coverage[i] : 0;
280 copy.coverage[i] = base + source.coverage[i];
281 }
282 }
283 for (final Branch b : source.branches) {
284 copy.addBranchCoverage(true, b.getLineNumber(), b.getBlockNumber(), b.getBranchNumber(), b.getHits());
285 }
286 }
287 return copy;
288 }
289
290 @Override
291 public boolean equals(final Object obj) {
292 if (!(obj instanceof Source)) {
293 return false;
294 }
295 final var other = (Source) obj;
296 return Objects.equals(this.name, other.name) && Objects.equals(this.digest, other.digest)
297 && this.coverage.length == other.coverage.length;
298 }
299
300 @Override
301 public int hashCode() {
302 return Objects.hash(this.name, this.digest, this.coverage.length);
303 }
304
305
306
307
308
309
310
311
312
313 private static int getLines(final String source) {
314 var lines = 1;
315 final var matcher = Source.NEWLINE.matcher(source);
316 while (matcher.find()) {
317 lines++;
318 }
319 return lines;
320 }
321 }