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