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 package org.eclipse.jgit.patch;
45
46 import static org.eclipse.jgit.util.RawParseUtils.match;
47 import static org.eclipse.jgit.util.RawParseUtils.nextLF;
48 import static org.eclipse.jgit.util.RawParseUtils.parseBase10;
49
50 import java.io.IOException;
51 import java.io.OutputStream;
52 import java.text.MessageFormat;
53
54 import org.eclipse.jgit.diff.Edit;
55 import org.eclipse.jgit.diff.EditList;
56 import org.eclipse.jgit.internal.JGitText;
57 import org.eclipse.jgit.lib.AbbreviatedObjectId;
58 import org.eclipse.jgit.util.MutableInteger;
59
60
61
62
63 public class HunkHeader {
64
65 public abstract static class OldImage {
66
67 int startLine;
68
69
70 int lineCount;
71
72
73 int nDeleted;
74
75
76 int nAdded;
77
78
79 public int getStartLine() {
80 return startLine;
81 }
82
83
84 public int getLineCount() {
85 return lineCount;
86 }
87
88
89 public int getLinesDeleted() {
90 return nDeleted;
91 }
92
93
94 public int getLinesAdded() {
95 return nAdded;
96 }
97
98
99 public abstract AbbreviatedObjectId getId();
100 }
101
102 final FileHeader file;
103
104
105 final int startOffset;
106
107
108 int endOffset;
109
110 private final OldImage old;
111
112
113 int newStartLine;
114
115
116 int newLineCount;
117
118
119 int nContext;
120
121 private EditList editList;
122
123 HunkHeader(FileHeader fh, int offset) {
124 this(fh, offset, new OldImage() {
125 @Override
126 public AbbreviatedObjectId getId() {
127 return fh.getOldId();
128 }
129 });
130 }
131
132 HunkHeader(FileHeader fh, int offset, OldImage oi) {
133 file = fh;
134 startOffset = offset;
135 old = oi;
136 }
137
138 HunkHeader(FileHeader fh, EditList editList) {
139 this(fh, fh.buf.length);
140 this.editList = editList;
141 endOffset = startOffset;
142 nContext = 0;
143 if (editList.isEmpty()) {
144 newStartLine = 0;
145 newLineCount = 0;
146 } else {
147 newStartLine = editList.get(0).getBeginB();
148 Edit last = editList.get(editList.size() - 1);
149 newLineCount = last.getEndB() - newStartLine;
150 }
151 }
152
153
154
155
156
157
158 public FileHeader getFileHeader() {
159 return file;
160 }
161
162
163
164
165
166
167 public byte[] getBuffer() {
168 return file.buf;
169 }
170
171
172
173
174
175
176 public int getStartOffset() {
177 return startOffset;
178 }
179
180
181
182
183
184
185 public int getEndOffset() {
186 return endOffset;
187 }
188
189
190
191
192
193
194 public OldImage getOldImage() {
195 return old;
196 }
197
198
199
200
201
202
203 public int getNewStartLine() {
204 return newStartLine;
205 }
206
207
208
209
210
211
212 public int getNewLineCount() {
213 return newLineCount;
214 }
215
216
217
218
219
220
221 public int getLinesContext() {
222 return nContext;
223 }
224
225
226
227
228
229
230 public EditList toEditList() {
231 if (editList == null) {
232 editList = new EditList();
233 final byte[] buf = file.buf;
234 int c = nextLF(buf, startOffset);
235 int oLine = old.startLine;
236 int nLine = newStartLine;
237 Edit in = null;
238
239 SCAN: for (; c < endOffset; c = nextLF(buf, c)) {
240 switch (buf[c]) {
241 case ' ':
242 case '\n':
243 in = null;
244 oLine++;
245 nLine++;
246 continue;
247
248 case '-':
249 if (in == null) {
250 in = new Edit(oLine - 1, nLine - 1);
251 editList.add(in);
252 }
253 oLine++;
254 in.extendA();
255 continue;
256
257 case '+':
258 if (in == null) {
259 in = new Edit(oLine - 1, nLine - 1);
260 editList.add(in);
261 }
262 nLine++;
263 in.extendB();
264 continue;
265
266 case '\\':
267 continue;
268
269 default:
270 break SCAN;
271 }
272 }
273 }
274 return editList;
275 }
276
277 void parseHeader() {
278
279
280 final byte[] buf = file.buf;
281 final MutableInteger ptr = new MutableInteger();
282 ptr.value = nextLF(buf, startOffset, ' ');
283 old.startLine = -parseBase10(buf, ptr.value, ptr);
284 if (buf[ptr.value] == ',')
285 old.lineCount = parseBase10(buf, ptr.value + 1, ptr);
286 else
287 old.lineCount = 1;
288
289 newStartLine = parseBase10(buf, ptr.value + 1, ptr);
290 if (buf[ptr.value] == ',')
291 newLineCount = parseBase10(buf, ptr.value + 1, ptr);
292 else
293 newLineCount = 1;
294 }
295
296 int parseBody(Patch script, int end) {
297 final byte[] buf = file.buf;
298 int c = nextLF(buf, startOffset), last = c;
299
300 old.nDeleted = 0;
301 old.nAdded = 0;
302
303 SCAN: for (; c < end; last = c, c = nextLF(buf, c)) {
304 switch (buf[c]) {
305 case ' ':
306 case '\n':
307 nContext++;
308 continue;
309
310 case '-':
311 old.nDeleted++;
312 continue;
313
314 case '+':
315 old.nAdded++;
316 continue;
317
318 case '\\':
319 continue;
320
321 default:
322 break SCAN;
323 }
324 }
325
326 if (last < end && nContext + old.nDeleted - 1 == old.lineCount
327 && nContext + old.nAdded == newLineCount
328 && match(buf, last, Patch.SIG_FOOTER) >= 0) {
329
330
331
332
333
334 old.nDeleted--;
335 return last;
336 }
337
338 if (nContext + old.nDeleted < old.lineCount) {
339 final int missingCount = old.lineCount - (nContext + old.nDeleted);
340 script.error(buf, startOffset, MessageFormat.format(
341 JGitText.get().truncatedHunkOldLinesMissing,
342 Integer.valueOf(missingCount)));
343
344 } else if (nContext + old.nAdded < newLineCount) {
345 final int missingCount = newLineCount - (nContext + old.nAdded);
346 script.error(buf, startOffset, MessageFormat.format(
347 JGitText.get().truncatedHunkNewLinesMissing,
348 Integer.valueOf(missingCount)));
349
350 } else if (nContext + old.nDeleted > old.lineCount
351 || nContext + old.nAdded > newLineCount) {
352 final String oldcnt = old.lineCount + ":" + newLineCount;
353 final String newcnt = (nContext + old.nDeleted) + ":"
354 + (nContext + old.nAdded);
355 script.warn(buf, startOffset, MessageFormat.format(
356 JGitText.get().hunkHeaderDoesNotMatchBodyLineCountOf, oldcnt, newcnt));
357 }
358
359 return c;
360 }
361
362 void extractFileLines(OutputStream[] out) throws IOException {
363 final byte[] buf = file.buf;
364 int ptr = startOffset;
365 int eol = nextLF(buf, ptr);
366 if (endOffset <= eol)
367 return;
368
369
370
371
372
373 out[0].write(buf, ptr, eol - ptr);
374
375 SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
376 eol = nextLF(buf, ptr);
377 switch (buf[ptr]) {
378 case ' ':
379 case '\n':
380 case '\\':
381 out[0].write(buf, ptr, eol - ptr);
382 out[1].write(buf, ptr, eol - ptr);
383 break;
384 case '-':
385 out[0].write(buf, ptr, eol - ptr);
386 break;
387 case '+':
388 out[1].write(buf, ptr, eol - ptr);
389 break;
390 default:
391 break SCAN;
392 }
393 }
394 }
395
396 void extractFileLines(final StringBuilder sb, final String[] text,
397 final int[] offsets) {
398 final byte[] buf = file.buf;
399 int ptr = startOffset;
400 int eol = nextLF(buf, ptr);
401 if (endOffset <= eol)
402 return;
403 copyLine(sb, text, offsets, 0);
404 SCAN: for (ptr = eol; ptr < endOffset; ptr = eol) {
405 eol = nextLF(buf, ptr);
406 switch (buf[ptr]) {
407 case ' ':
408 case '\n':
409 case '\\':
410 copyLine(sb, text, offsets, 0);
411 skipLine(text, offsets, 1);
412 break;
413 case '-':
414 copyLine(sb, text, offsets, 0);
415 break;
416 case '+':
417 copyLine(sb, text, offsets, 1);
418 break;
419 default:
420 break SCAN;
421 }
422 }
423 }
424
425 void copyLine(final StringBuilder sb, final String[] text,
426 final int[] offsets, final int fileIdx) {
427 final String s = text[fileIdx];
428 final int start = offsets[fileIdx];
429 int end = s.indexOf('\n', start);
430 if (end < 0)
431 end = s.length();
432 else
433 end++;
434 sb.append(s, start, end);
435 offsets[fileIdx] = end;
436 }
437
438 void skipLine(final String[] text, final int[] offsets,
439 final int fileIdx) {
440 final String s = text[fileIdx];
441 final int end = s.indexOf('\n', offsets[fileIdx]);
442 offsets[fileIdx] = end < 0 ? s.length() : end + 1;
443 }
444
445
446 @SuppressWarnings("nls")
447 @Override
448 public String toString() {
449 StringBuilder buf = new StringBuilder();
450 buf.append("HunkHeader[");
451 buf.append(getOldImage().getStartLine());
452 buf.append(',');
453 buf.append(getOldImage().getLineCount());
454 buf.append("->");
455 buf.append(getNewStartLine()).append(',').append(getNewLineCount());
456 buf.append(']');
457 return buf.toString();
458 }
459 }