2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.diff.builtin;
44
import java.io.BufferedReader;
45
import java.io.PushbackReader;
46
import java.io.Reader;
47
import java.io.IOException;
48
import java.util.ArrayList;
49
import java.util.List;
50
import java.util.Iterator;
51
import java.util.regex.Pattern;
52
import java.util.regex.PatternSyntaxException;
54
import org.netbeans.api.diff.Difference;
56
import org.netbeans.modules.diff.cmdline.CmdlineDiffProvider;
59
* Utility class for patch application.
61
* @author Martin Entlicher
63
public class Patch extends Reader {
65
private static final int CONTEXT_DIFF = 0;
66
private static final int NORMAL_DIFF = 1;
67
private static final int UNIFIED_DIFF = 2;
69
private Difference[] diffs;
70
private PushbackReader source;
71
private int currDiff = 0;
73
private String newLine = null; // String, that is used to separate lines
74
private StringBuffer buff = new StringBuffer();
76
/** Creates a new instance of Patch */
77
private Patch(Difference[] diffs, Reader source) {
79
this.source = new PushbackReader(new BufferedReader(source), 1);
83
* Apply the patch to the source.
84
* @param diffs The differences to patch
85
* @param source The source stream
86
* @return The patched stream
87
* @throws IOException When reading from the source stread fails
88
* @throws ParseException When the source does not match the patch to be applied
90
public static Reader apply(Difference[] diffs, Reader source) {//throws IOException, ParseException {
91
return new Patch(diffs, source);
95
* Parse the differences.
97
public static Difference[] parse(Reader source) throws IOException {
98
return parseContextDiff(source);
103
* Parse the differences and corresponding file names.
105
public static FileDifferences[] parse(Reader source) throws IOException {
106
List<FileDifferences> fileDifferences = new ArrayList<FileDifferences>();
107
//int pushBackLimit = DIFFERENCE_DELIMETER.length();
108
//PushbackReader recognizedSource = new PushbackReader(source, pushBackLimit);
109
Patch.SinglePatchReader patchReader = new Patch.SinglePatchReader(source);
110
int[] diffType = new int[1];
111
String[] fileName = new String[2];
112
while (patchReader.hasNextPatch(diffType, fileName)) {
113
//System.out.println("Have a next patch of name '"+fileName[0]+"'");
114
Difference[] diffs = null;
115
switch (diffType[0]) {
117
diffs = parseContextDiff(patchReader);
120
diffs = parseUnifiedDiff(patchReader);
123
diffs = parseNormalDiff(patchReader);
127
fileDifferences.add(new FileDifferences(fileName[0], fileName[1], diffs));
130
return fileDifferences.toArray(new FileDifferences[fileDifferences.size()]);
133
public int read(char[] cbuf, int off, int length) throws java.io.IOException {
134
if (buff.length() < length) {
135
doRetrieve(length - buff.length());
137
int ret = Math.min(buff.length(), length);
138
if (ret == 0) return -1;
139
String retStr = buff.substring(0, ret);
140
char[] retChars = retStr.toCharArray();
141
System.arraycopy(retChars, 0, cbuf, off, ret);
146
public void close() throws java.io.IOException {
147
if (currDiff < diffs.length) {
148
throw new IOException("There are " + (diffs.length - currDiff) + " pending hunks!");
153
private void doRetrieve(int length) throws IOException {
154
for (int size = 0; size < length; line++) {
155
if (currDiff < diffs.length &&
156
((Difference.ADD == diffs[currDiff].getType() &&
157
line == (diffs[currDiff].getFirstStart() + 1)) ||
158
(Difference.ADD != diffs[currDiff].getType() &&
159
line == diffs[currDiff].getFirstStart()))) {
160
if (compareText(source, diffs[currDiff].getFirstText())) {
161
String text = convertNewLines(diffs[currDiff].getSecondText(), newLine);
165
throw new IOException("Patch not applicable.");
168
StringBuffer newLineBuffer = null;
169
if (newLine == null) {
170
newLineBuffer = new StringBuffer();
172
String lineStr = readLine(source, newLineBuffer);
173
if (newLineBuffer != null) newLine = newLineBuffer.toString();
174
if (lineStr == null) break;
175
buff.append(lineStr);
176
buff.append(newLine);
180
/** Reads a line and returns the char sequence for newline */
181
private static String readLine(PushbackReader r, StringBuffer nl) throws IOException {
182
StringBuffer line = new StringBuffer();
184
if (ic == -1) return null;
186
while (c != '\n' && c != '\r') {
200
if (c != '\n') r.unread(c);
201
else if (nl != null) nl.append(c);
203
} catch (IOException ioex) {}
205
return line.toString();
208
private static String convertNewLines(String text, String newLine) {
209
if (text == null) return ""; // NOI18N
210
if (newLine == null) return text;
211
StringBuffer newText = new StringBuffer();
212
for (int i = 0; i < text.length(); i++) {
213
char c = text.charAt(i);
214
if (c == '\n') newText.append(newLine);
215
else if (c == '\r') {
216
if ((i + 1) < text.length() && text.charAt(i + 1) == '\n') {
218
newText.append(newLine);
220
} else newText.append(c);
222
return newText.toString();
225
private boolean compareText(PushbackReader source, String text) throws IOException {
226
if (text == null || text.length() == 0) return true;
227
text = adjustTextNL(text);
228
char[] chars = new char[text.length()];
233
n = source.read(chars, 0, chars.length - pos);
236
readStr = readStr + new String(chars, 0, n);
238
if (readStr.endsWith("\r")) {
240
char c = (char) source.read();
241
if (c != '\n') source.unread(c);
243
} catch (IOException ioex) {}
245
readStr = adjustTextNL(readStr);
246
pos = readStr.length();
247
} while (n > 0 && pos < chars.length);
248
readStr.getChars(0, readStr.length(), chars, 0);
249
line += numChars('\n', chars);
250
//System.out.println("Comparing text of the diff:\n'"+text+"'\nWith the read text:\n'"+readStr+"'\n");
251
//System.out.println(" EQUALS = "+readStr.equals(text));
252
return readStr.equals(text);
256
* When comparing the two texts, it's important to ignore different line endings.
257
* This method assures, that only '\n' is used as the line ending.
259
private String adjustTextNL(String text) {
260
text = org.openide.util.Utilities.replaceString(text, "\r\n", "\n");
261
text = org.openide.util.Utilities.replaceString(text, "\n\r", "\n");
262
text = org.openide.util.Utilities.replaceString(text, "\r", "\n");
266
private static int numChars(char c, char[] chars) {
268
for (int i = 0; i < chars.length; i++) {
269
if (chars[i] == c) n++;
274
private static final String CONTEXT_MARK1B = "*** ";
275
// private static final String CONTEXT_MARK1E = " ****";
276
private static final String CONTEXT_MARK2B = "--- ";
277
// private static final String CONTEXT_MARK2E = " ----";
278
private static final String CONTEXT_MARK_DELIMETER = ",";
279
private static final String DIFFERENCE_DELIMETER = "***************";
280
// private static final String LINE_PREP = " ";
281
private static final String LINE_PREP_ADD = "+ ";
282
private static final String LINE_PREP_REMOVE = "- ";
283
private static final String LINE_PREP_CHANGE = "! ";
285
private static Difference[] parseContextDiff(Reader in) throws IOException {
286
BufferedReader br = new BufferedReader(in);
287
ArrayList<Difference> diffs = new ArrayList<Difference>();
290
if (line == null || !DIFFERENCE_DELIMETER.equals(line)) {
292
line = br.readLine();
293
} while (line != null && !DIFFERENCE_DELIMETER.equals(line));
295
int[] firstInterval = new int[2];
296
line = br.readLine();
297
if (line != null && line.startsWith(CONTEXT_MARK1B)) {
299
readNums(line, CONTEXT_MARK1B.length(), firstInterval);
300
} catch (NumberFormatException nfex) {
301
throw new IOException(nfex.getLocalizedMessage());
304
ArrayList<Object> firstChanges = new ArrayList<Object>(); // List of intervals and texts
305
line = fillChanges(firstInterval, br, CONTEXT_MARK2B, firstChanges);
306
int[] secondInterval = new int[2];
307
if (line != null && line.startsWith(CONTEXT_MARK2B)) {
309
readNums(line, CONTEXT_MARK2B.length(), secondInterval);
310
} catch (NumberFormatException nfex) {
311
throw new IOException(nfex.getLocalizedMessage());
314
ArrayList<Object> secondChanges = new ArrayList<Object>(); // List of intervals and texts
315
line = fillChanges(secondInterval, br, DIFFERENCE_DELIMETER, secondChanges);
316
if (changesCountInvariant(firstChanges, secondChanges) == false) {
317
throw new IOException("Diff file format error. Number of new and old file changes in one hunk must be same!"); // NOI18N
319
mergeChanges(firstInterval, secondInterval, firstChanges, secondChanges, diffs);
320
} while (line != null);
321
return diffs.toArray(new Difference[diffs.size()]);
325
private static boolean changesCountInvariant(List<Object> changes1, List<Object> changes2) { // both are Union<int[],String>
327
Iterator it = changes1.iterator();
328
while (it.hasNext()) {
329
int[] ints = (int[]) it.next();
333
String skip = (String) it.next();
337
it = changes2.iterator();
338
while (it.hasNext()) {
339
int[] ints = (int[]) it.next();
343
String skip = (String) it.next();
349
private static void readNums(String str, int off, int[] values) throws NumberFormatException {
350
int end = str.indexOf(CONTEXT_MARK_DELIMETER, off);
352
values[0] = Integer.parseInt(str.substring(off, end).trim());
353
} else throw new NumberFormatException("Missing comma.");
355
end = str.indexOf(' ', off);
357
values[1] = Integer.parseInt(str.substring(off, end).trim());
358
} else throw new NumberFormatException("Missing final space.");
361
private static String fillChanges(int[] interval, BufferedReader br,
362
String untilStartsWith, List<Object/* int[3] or String*/> changes) throws IOException {
363
String line = br.readLine();
364
for (int pos = interval[0]; pos <= interval[1]; pos++) {
365
if (line == null || line.startsWith(untilStartsWith)) break;
366
if (line.startsWith(LINE_PREP_ADD)) {
367
int[] changeInterval = new int[3];
368
changeInterval[0] = pos;
369
changeInterval[2] = Difference.ADD;
370
StringBuffer changeText = new StringBuffer();
371
changeText.append(line.substring(LINE_PREP_ADD.length()));
372
changeText.append('\n');
374
line = br.readLine();
377
if (line.startsWith(LINE_PREP_ADD)) {
378
changeText.append(line.substring(LINE_PREP_ADD.length()));
379
changeText.append('\n');
385
changeInterval[1] = pos;
386
changes.add(changeInterval);
387
changes.add(changeText.toString());
388
} else if (line.startsWith(LINE_PREP_REMOVE)) {
389
int[] changeInterval = new int[3];
390
changeInterval[0] = pos;
391
changeInterval[2] = Difference.DELETE;
392
StringBuffer changeText = new StringBuffer();
393
changeText.append(line.substring(LINE_PREP_REMOVE.length()));
394
changeText.append('\n');
396
line = br.readLine();
399
if (line.startsWith(LINE_PREP_REMOVE)) {
400
changeText.append(line.substring(LINE_PREP_REMOVE.length()));
401
changeText.append('\n');
407
changeInterval[1] = pos;
408
changes.add(changeInterval);
409
changes.add(changeText.toString());
410
} else if (line.startsWith(LINE_PREP_CHANGE)) {
411
int[] changeInterval = new int[3];
412
changeInterval[0] = pos;
413
changeInterval[2] = Difference.CHANGE;
414
StringBuffer changeText = new StringBuffer();
415
changeText.append(line.substring(LINE_PREP_CHANGE.length()));
416
changeText.append('\n');
418
line = br.readLine();
421
if (line.startsWith(LINE_PREP_CHANGE)) {
422
changeText.append(line.substring(LINE_PREP_CHANGE.length()));
423
changeText.append('\n');
429
changeInterval[1] = pos;
430
changes.add(changeInterval);
431
changes.add(changeText.toString());
433
line = br.readLine();
439
private static void mergeChanges(int[] firstInterval, int[] secondInterval,
440
List firstChanges, List secondChanges, List<Difference> diffs) {
444
int n1 = firstChanges.size();
445
int n2 = secondChanges.size();
446
//System.out.println("mergeChanges(("+firstInterval[0]+", "+firstInterval[1]+"), ("+secondInterval[0]+", "+secondInterval[1]+"))");
447
//System.out.println("firstChanges.size() = "+n1);
448
//System.out.println("secondChanges.size() = "+n2);
449
int firstToSecondIntervalShift = secondInterval[0] - firstInterval[0];
450
//System.out.println("shift = "+firstToSecondIntervalShift);
451
for (p1 = p2 = 0; p1 < n1 || p2 < n2; ) {
452
boolean isAddRemove = true;
453
while (isAddRemove && p1 < n1) {
454
int[] interval = (int[]) firstChanges.get(p1);
456
int[] interval2 = (int[]) secondChanges.get(p2);
457
if (interval[0] + firstToSecondIntervalShift > interval2[0]) {
460
// We need to set differences successively. Differences with
461
// higher line numbers must not precede differences with
462
// smaller line numbers
464
isAddRemove = interval[2] == Difference.ADD || interval[2] == Difference.DELETE;
466
if (interval[2] == Difference.ADD) {
467
diffs.add(new Difference(interval[2], interval[0] - 1, 0,
468
interval[0] + firstToSecondIntervalShift,
469
interval[1] + firstToSecondIntervalShift,
470
(String) firstChanges.get(p1 + 1), ""));
471
firstToSecondIntervalShift += interval[1] - interval[0] + 1;
473
diffs.add(new Difference(interval[2], interval[0], interval[1],
474
interval[0] + firstToSecondIntervalShift - 1, 0,
475
(String) firstChanges.get(p1 + 1), ""));
476
firstToSecondIntervalShift -= interval[1] - interval[0] + 1;
479
//System.out.println("added diff = "+diffs.get(diffs.size() - 1));
480
//System.out.println("new shift = "+firstToSecondIntervalShift);
484
while (isAddRemove && p2 < n2) {
485
int[] interval = (int[]) secondChanges.get(p2);
486
isAddRemove = interval[2] == Difference.ADD || interval[2] == Difference.DELETE;
488
if (interval[2] == Difference.ADD) {
489
diffs.add(new Difference(interval[2],
490
interval[0] - firstToSecondIntervalShift - 1, 0,
491
interval[0], interval[1],
492
"", (String) secondChanges.get(p2 + 1)));
493
firstToSecondIntervalShift += interval[1] - interval[0] + 1;
495
diffs.add(new Difference(interval[2],
496
interval[0] - firstToSecondIntervalShift,
497
interval[1] - firstToSecondIntervalShift,
499
"", (String) secondChanges.get(p2 + 1)));
500
firstToSecondIntervalShift -= interval[1] - interval[0] + 1;
503
//System.out.println("added diff = "+diffs.get(diffs.size() - 1));
504
//System.out.println("new shift = "+firstToSecondIntervalShift);
507
// Change is remaining
508
if (p1 < n1 && p2 < n2) {
509
int[] interval1 = (int[]) firstChanges.get(p1);
510
if (interval1[2] == Difference.CHANGE) { // double check the break above
511
int[] interval2 = (int[]) secondChanges.get(p2);
512
diffs.add(new Difference(interval1[2], interval1[0], interval1[1],
513
interval2[0], interval2[1],
514
(String) firstChanges.get(p1 + 1),
515
(String) secondChanges.get(p2 + 1)));
518
firstToSecondIntervalShift += interval2[1] - interval2[0] - (interval1[1] - interval1[0]);
519
//System.out.println("added diff = "+diffs.get(diffs.size() - 1));
520
//System.out.println("new shift = "+firstToSecondIntervalShift);
526
private static final String UNIFIED_MARK = "@@";
527
private static final String UNIFIED_MARK1 = "--- ";
528
// private static final String UNIFIED_MARK2 = "+++ ";
529
private static final String LINE_PREP_UNIF_ADD = "+";
530
private static final String LINE_PREP_UNIF_REMOVE = "-";
531
// private static final String LINE_PREP_UNIF_CHANGE = null;
533
private static Difference[] parseUnifiedDiff(Reader in) throws IOException {
534
BufferedReader br = new BufferedReader(in);
535
List<Difference> diffs = new ArrayList<Difference>();
538
while (line == null || !(line.startsWith(UNIFIED_MARK) &&
539
line.length() > UNIFIED_MARK.length() &&
540
line.endsWith(UNIFIED_MARK))) {
541
line = br.readLine();
542
if (line == null) break;
544
if (line == null) continue;
545
int[] intervals = new int[4];
547
readUnifiedNums(line, UNIFIED_MARK.length(), intervals);
548
} catch (NumberFormatException nfex) {
549
IOException ioex = new IOException("Can not parse: " + line);
550
ioex.initCause(nfex);
553
line = fillUnidifChanges(intervals, br, diffs);
554
} while (line != null);
555
return diffs.toArray(new Difference[diffs.size()]);
558
private static void readUnifiedNums(String str, int off, int[] values) throws NumberFormatException {
559
while (str.charAt(off) == ' ' || str.charAt(off) == '-') off++;
560
int end = str.indexOf(CONTEXT_MARK_DELIMETER, off);
562
values[0] = Integer.parseInt(str.substring(off, end).trim());
563
} else throw new NumberFormatException("Missing comma.");
565
end = str.indexOf(' ', off);
567
values[1] = Integer.parseInt(str.substring(off, end).trim());
568
} else throw new NumberFormatException("Missing middle space.");
570
while (str.charAt(off) == ' ' || str.charAt(off) == '+') off++;
571
end = str.indexOf(CONTEXT_MARK_DELIMETER, off);
573
values[2] = Integer.parseInt(str.substring(off, end).trim());
574
} else throw new NumberFormatException("Missing second comma.");
576
end = str.indexOf(' ', off);
578
values[3] = Integer.parseInt(str.substring(off, end).trim());
579
} else throw new NumberFormatException("Missing final space.");
580
values[1] += values[0] - 1;
581
values[3] += values[2] - 1;
584
private static String fillUnidifChanges(int[] interval, BufferedReader br,
585
List<Difference> diffs) throws IOException {
586
String line = br.readLine();
587
int pos1 = interval[0];
588
int pos2 = interval[2];
589
while (line != null && pos1 <= interval[1] && pos2 <= interval[3]) {
590
if (line.startsWith(LINE_PREP_UNIF_ADD)) {
592
StringBuffer changeText = new StringBuffer();
593
changeText.append(line.substring(LINE_PREP_UNIF_ADD.length()));
594
changeText.append('\n');
596
line = br.readLine();
598
if (line.startsWith(LINE_PREP_UNIF_ADD)) {
599
changeText.append(line.substring(LINE_PREP_UNIF_ADD.length()));
600
changeText.append('\n');
605
Difference diff = null;
606
if (diffs.size() > 0) {
607
Difference previousDiff = (Difference) diffs.get(diffs.size() - 1);
608
if (Difference.DELETE == previousDiff.getType() && previousDiff.getFirstEnd() == (pos1 - 1)) {
609
diff = new Difference(Difference.CHANGE,
610
previousDiff.getFirstStart(), previousDiff.getFirstEnd(),
611
begin, pos2 - 1, previousDiff.getFirstText(), changeText.toString());
612
diffs.remove(diffs.size() - 1);
616
diff = new Difference(Difference.ADD, pos1 - 1, 0, begin, pos2 - 1, null, changeText.toString());
619
} else if (line.startsWith(LINE_PREP_UNIF_REMOVE)) {
621
StringBuffer changeText = new StringBuffer();
622
changeText.append(line.substring(LINE_PREP_UNIF_REMOVE.length()));
623
changeText.append('\n');
625
line = br.readLine();
627
if (line.startsWith(LINE_PREP_UNIF_REMOVE)) {
628
changeText.append(line.substring(LINE_PREP_UNIF_REMOVE.length()));
629
changeText.append('\n');
634
Difference diff = null;
635
if (diffs.size() > 0) {
636
Difference previousDiff = (Difference) diffs.get(diffs.size() - 1);
637
if (Difference.ADD == previousDiff.getType() && previousDiff.getSecondEnd() == (pos2 - 1)) {
638
diff = new Difference(Difference.CHANGE, begin, pos1 - 1,
639
previousDiff.getFirstStart(), previousDiff.getFirstEnd(),
640
changeText.toString(), previousDiff.getFirstText());
641
diffs.remove(diffs.size() - 1);
645
diff = new Difference(Difference.DELETE, begin, pos1 - 1, pos2 - 1, 0, changeText.toString(), null);
649
line = br.readLine();
657
private static Difference[] parseNormalDiff(Reader in) throws IOException {
660
normRegexp = Pattern.compile(CmdlineDiffProvider.DIFF_REGEXP);
661
} catch (PatternSyntaxException rsex) {
664
StringBuffer firstText = new StringBuffer();
665
StringBuffer secondText = new StringBuffer();
666
BufferedReader br = new BufferedReader(in);
667
List<Difference> diffs = new ArrayList<Difference>();
669
while ((line = br.readLine()) != null) {
670
CmdlineDiffProvider.outputLine(line, normRegexp, diffs, firstText, secondText);
672
CmdlineDiffProvider.setTextOnLastDifference(diffs, firstText, secondText);
673
return diffs.toArray(new Difference[diffs.size()]);
677
* A reader, that will not read more, than a single patch content
678
* from the supplied reader with possibly more patches.
680
private static class SinglePatchReader extends Reader {
682
private static final int BUFF_SIZE = 512;
683
private PushbackReader in;
684
private char[] buffer = new char[BUFF_SIZE];
685
private int buffLength = 0;
686
private int buffPos = 0;
687
private boolean isAtEndOfPatch = false;
689
public SinglePatchReader(Reader in) {
690
this.in = new PushbackReader(in, BUFF_SIZE);
693
public int read(char[] values, int offset, int length) throws java.io.IOException {
694
//System.out.println("SinglePatchReader.read("+offset+", "+length+")");
698
if (length < buffLength) {
699
buffCopyLength = length;
702
if (buffLength > 0) {
703
buffCopyLength = buffLength;
704
length -= buffLength;
706
if (isAtEndOfPatch) {
710
buffLength = readTillEndOfPatch(buffer);
712
if (buffLength <= 0) {
715
buffCopyLength = Math.min(length, buffLength);
716
length -= buffCopyLength;
721
if (buffCopyLength > 0) {
722
System.arraycopy(buffer, buffPos, values, offset, buffCopyLength);
723
offset += buffCopyLength;
724
buffLength -= buffCopyLength;
725
buffPos += buffCopyLength;
726
totRead += buffCopyLength;
731
if (totRead == 0) totRead = -1;
732
//System.out.println(" read = '"+((totRead >= 0) ? new String(values, 0, totRead) : "NOTHING")+"', totRead = "+totRead);
736
private int readTillEndOfPatch(char[] buffer) throws IOException {
737
int length = in.read(buffer);
738
String input = new String(buffer);
740
if (input.startsWith(FILE_INDEX) || ((end = input.indexOf("\n"+FILE_INDEX))) >= 0) {
741
isAtEndOfPatch = true;
743
end = input.lastIndexOf('\n');
746
if (end >= 0 && end < length) {
747
in.unread(buffer, end, length - end);
750
if (end == 0) length = -1;
754
public void close() throws java.io.IOException {
758
private static final String FILE_INDEX = "Index: "; // NOI18N
760
private boolean hasNextPatch(int[] diffType, String[] fileName) throws IOException {
761
isAtEndOfPatch = false; // We're prepared for the next patch
762
PushbackReader patchSource = in;
763
char[] buff = new char[DIFFERENCE_DELIMETER.length()];
766
boolean contextBeginDetected = false;
768
normRegexp = Pattern.compile(CmdlineDiffProvider.DIFF_REGEXP);
769
} catch (PatternSyntaxException rsex) {
772
while ((length = patchSource.read(buff)) > 0) {
773
String input = new String(buff, 0, length);
775
int nln = input.indexOf('\n');
776
int nlr = input.indexOf('\r');
777
if (nln < 0) nl = nlr;
780
if (nln > 0 && nln == nlr + 1) {
781
input = input.substring(0, nl - 1);
783
input = input.substring(0, nl);
785
if (nl + 1 < length) {
786
patchSource.unread(buff, nl + 1, length - (nl + 1));
790
if (input.equals(DIFFERENCE_DELIMETER)) {
791
diffType[0] = CONTEXT_DIFF;
792
patchSource.unread(buff, 0, length);
794
} else if (input.startsWith(UNIFIED_MARK + " ")) {
795
diffType[0] = UNIFIED_DIFF;
796
patchSource.unread(buff, 0, length);
798
} else if (input.startsWith(FILE_INDEX)) {
799
StringBuffer name = new StringBuffer(input.substring(FILE_INDEX.length()));
803
while ((c = (char) (r = patchSource.read())) != '\n' && r != -1 && r != '\r') {
807
fileName[1] = name.toString();
808
} else if (input.startsWith(CONTEXT_MARK1B) || !contextBeginDetected && input.startsWith(UNIFIED_MARK1)) {
810
if (input.startsWith(CONTEXT_MARK1B)) {
811
contextBeginDetected = true;
812
name = new StringBuffer(input.substring(CONTEXT_MARK1B.length()));
814
name = new StringBuffer(input.substring(UNIFIED_MARK1.length()));
816
String sname = name.toString();
817
int spaceIndex = sname.indexOf('\t');
818
if (spaceIndex > 0) {
819
name = name.delete(spaceIndex, name.length());
824
if (spaceIndex < 0) {
825
while ((c = (char) (r = patchSource.read())) != '\n' && c != '\r' && c != '\t' && r != -1) {
829
if (c != '\n' && c != '\r' && r != -1) {
830
while ((c = (char) (r = patchSource.read())) != '\n' && c != '\r' && r != -1) ; // Read the rest of the line
833
r = patchSource.read();
836
if (c != '\n') patchSource.unread(c);
840
fileName[0] = name.toString();
841
} else if (normRegexp != null && normRegexp.matcher(input).matches()) {
842
diffType[0] = NORMAL_DIFF;
843
patchSource.unread(buff, 0, length);
845
} else { // Read the rest of the garbaged line
849
while ((c = (char) (r = patchSource.read())) != '\n' && c != '\r' && r != -1) ;
851
r = patchSource.read();
854
if (c != '\n') patchSource.unread(c);
865
public static class FileDifferences extends Object {
867
private String fileName;
868
private String indexName;
869
private Difference[] diffs;
871
public FileDifferences(String fileName, String indexName, Difference[] diffs) {
872
this.fileName = fileName;
874
this.indexName = indexName;
878
* @return header filename (typically absolute path on source host) or null
880
public final String getFileName() {
885
* @return relative Index: file name or null
887
public final String getIndexName() {
891
public final Difference[] getDifferences() {